Merge pull request #4832 from okxlin/feat/wakapi

Add Wakapi app
This commit is contained in:
okxlin
2026-07-15 22:29:28 +08:00
committed by GitHub
19 changed files with 472 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
PANEL_APP_PORT_HTTP=3000
WAKAPI_PASSWORD_SALT=replace-with-a-random-32-character-salt
WAKAPI_INSECURE_COOKIES=true
WAKAPI_ALLOW_SIGNUP=true
APP_DATA_DIR=./data
CONTAINER_NAME=wakapi
+95
View File
@@ -0,0 +1,95 @@
additionalProperties:
formFields:
- default: 3000
edit: true
envKey: PANEL_APP_PORT_HTTP
labelEn: Web Port
labelZh: Web 端口
label:
en: Web Port
zh: Web 端口
zh-Hant: Web 連接埠
ja: Web ポート
ko: Web 포트
ru: Веб-порт
ms: Port Web
pt-br: Porta Web
required: true
rule: paramPort
type: number
- default: change-me
edit: true
envKey: WAKAPI_PASSWORD_SALT
labelEn: Password Salt
labelZh: 密码盐
label:
en: Password Salt
zh: 密码盐
zh-Hant: 密碼鹽
ja: パスワードソルト
ko: 비밀번호 솔트
ru: Соль паролей
ms: Garam kata laluan
pt-br: Sal de senha
random: true
required: true
rule: paramComplexity
type: password
- default: "true"
edit: true
envKey: WAKAPI_INSECURE_COOKIES
labelEn: Allow Insecure Cookies
labelZh: 允许非安全 Cookie
label:
en: Allow Insecure Cookies
zh: 允许非安全 Cookie
zh-Hant: 允許非安全 Cookie
ja: 非セキュア Cookie を許可
ko: 보안이 적용되지 않은 Cookie 허용
ru: Разрешить небезопасные cookie
ms: Benarkan kuki tidak selamat
pt-br: Permitir cookies inseguros
required: true
type: select
values:
- label: "true"
value: "true"
- label: "false"
value: "false"
- default: "true"
edit: true
envKey: WAKAPI_ALLOW_SIGNUP
labelEn: Allow Sign-up
labelZh: 允许注册
label:
en: Allow Sign-up
zh: 允许注册
zh-Hant: 允許註冊
ja: ユーザー登録を許可
ko: 회원가입 허용
ru: Разрешить регистрацию
ms: Benarkan pendaftaran
pt-br: Permitir cadastro
required: true
type: select
values:
- label: "true"
value: "true"
- label: "false"
value: "false"
- default: ./data
edit: true
envKey: APP_DATA_DIR
labelEn: Data Directory
labelZh: 数据目录
label:
en: Data Directory
zh: 数据目录
zh-Hant: 資料目錄
ja: データディレクトリ
ko: 데이터 디렉터리
ru: Каталог данных
ms: Direktori data
pt-br: Diretório de dados
required: true
type: text
View File
+30
View File
@@ -0,0 +1,30 @@
services:
wakapi:
image: "n1try/wakapi:2.17.4"
container_name: ${CONTAINER_NAME}
init: true
restart: unless-stopped
networks:
- 1panel-network
ports:
- "${PANEL_APP_PORT_HTTP}:3000"
environment:
- WAKAPI_DB_TYPE=sqlite3
- WAKAPI_DB_NAME=/data/wakapi.db
- WAKAPI_PASSWORD_SALT=${WAKAPI_PASSWORD_SALT}
- WAKAPI_INSECURE_COOKIES=${WAKAPI_INSECURE_COOKIES}
- WAKAPI_ALLOW_SIGNUP=${WAKAPI_ALLOW_SIGNUP}
volumes:
- "${APP_DATA_DIR}:/data"
healthcheck:
test: ["CMD", "/app/healthcheck"]
interval: 60s
timeout: 3s
start_period: 120s
retries: 3
labels:
createdBy: "Apps"
networks:
1panel-network:
external: true
View File
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}"
APP_DATA_DIR_RAW="${APP_DATA_DIR:-}"
WAKAPI_UID=65532
WAKAPI_GID=65532
if [[ -z "$APP_DATA_DIR_RAW" && -f "$ENV_FILE" ]]; then
APP_DATA_DIR_RAW="$(grep '^APP_DATA_DIR=' "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2- || true)"
fi
APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW:-./data}"
case "$APP_DATA_DIR_RAW" in
\"*\")
APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW#\"}"
APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW%\"}"
;;
\'*\')
APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW#\'}"
APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW%\'}"
;;
esac
if [[ -z "$APP_DATA_DIR_RAW" ]]; then
echo "APP_DATA_DIR must not be empty" >&2
exit 1
fi
case "$APP_DATA_DIR_RAW" in
/*)
APP_DATA_DIR_ABS="$(realpath -m -- "$APP_DATA_DIR_RAW")"
APP_DATA_DIR_SCOPE="absolute"
;;
*)
APP_DATA_DIR_ABS="$(realpath -m -- "$ROOT_DIR/${APP_DATA_DIR_RAW#./}")"
case "$APP_DATA_DIR_ABS" in
"$ROOT_DIR"/*) ;;
*)
echo "Relative APP_DATA_DIR must stay inside the application directory" >&2
exit 1
;;
esac
APP_DATA_DIR_SCOPE="application"
;;
esac
DATA_DIR_EXISTED=false
[[ -d "$APP_DATA_DIR_ABS" ]] && DATA_DIR_EXISTED=true
mkdir -p "$APP_DATA_DIR_ABS"
if [[ "$APP_DATA_DIR_SCOPE" == "application" || "$DATA_DIR_EXISTED" == "false" ]]; then
chown -R "$WAKAPI_UID:$WAKAPI_GID" "$APP_DATA_DIR_ABS"
else
CURRENT_UID="$(stat -c '%u' "$APP_DATA_DIR_ABS")"
CURRENT_GID="$(stat -c '%g' "$APP_DATA_DIR_ABS")"
if [[ "$CURRENT_UID" != "$WAKAPI_UID" || "$CURRENT_GID" != "$WAKAPI_GID" ]]; then
echo "Existing absolute APP_DATA_DIR must be owned by ${WAKAPI_UID}:${WAKAPI_GID}" >&2
exit 1
fi
fi
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
docker-compose down --volumes
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
set -euo pipefail
exit 0
+45
View File
@@ -0,0 +1,45 @@
# Wakapi
## 产品介绍
Wakapi 是一个极简、自托管且兼容 WakaTime 客户端的开发统计后端,可用于记录并分析项目、语言、编辑器、主机和操作系统维度的编码活动。
## 主要功能
- 兼容 WakaTime 客户端和插件
- 项目、编程语言、编辑器和主机统计
- REST API、统计徽章和 Prometheus 指标导出
- 周报邮件和多种数据导入方式
## 访问说明
- 默认 Web 端口为 `3000`,可在安装表单中修改。
- 首次访问后注册账户,然后在 Wakapi 页面中获取 API Key。
- 在 WakaTime 客户端的 `~/.wakatime.cfg` 中将 `api_url` 设为 `http(s)://<Wakapi 域名或 IP>/api`,并填入账户的 API Key。
- 创建首个账户后,建议在 1Panel 应用参数中将“允许注册”改为 `false`。
## 数据与安全
- 此应用包使用 Wakapi 官方 Docker 镜像的默认 SQLite 模式,数据库位于持久化目录中的 `wakapi.db`。Wakapi 上游也支持 PostgreSQL 和 MySQL,但本应用包未启用外部数据库联动。
- “密码盐”用于密码哈希,安装时应使用随机值;创建账户后不要修改,并在备份时一并保留应用参数。
- 官方镜像以 UID/GID `65532:65532` 的非 root 用户运行。应用内的默认相对数据目录会在安装时自动设置权限;如果指定已存在的绝对路径,请先将该目录属主设为 `65532:65532`。
- 直接通过 HTTP 访问时保持“允许非安全 Cookie”为 `true`。如果通过带 SSL 证书的 HTTPS 反向代理访问,应将其改为 `false`。
## Introduction
Wakapi is a minimalist, self-hosted WakaTime-compatible backend for coding statistics. It tracks activity by project, language, editor, host and operating system.
## Features
- WakaTime client and plugin compatibility
- Coding statistics across projects, languages, editors and hosts
- REST API, badges and Prometheus exports
- Weekly e-mail reports and data imports
This package uses the official image's SQLite mode and persists `/data`. Keep the generated password salt unchanged after accounts are created. Disable sign-up after creating the initial account, and set insecure cookies to `false` when serving Wakapi through HTTPS.
## 参考资料
- 项目主页:<https://wakapi.dev/>
- 安装与配置:<https://github.com/muety/wakapi>
- 源码仓库:<https://github.com/muety/wakapi>
+31
View File
@@ -0,0 +1,31 @@
name: Wakapi
tags:
- 开发工具
title: 一个自托管的 WakaTime 兼容开发统计后端
description: 一个自托管的 WakaTime 兼容开发统计后端
additionalProperties:
key: wakapi
name: Wakapi
tags:
- DevTool
shortDescZh: 一个自托管的 WakaTime 兼容开发统计后端
shortDescEn: A self-hosted WakaTime-compatible backend for coding statistics
description:
en: A self-hosted WakaTime-compatible backend for coding statistics
zh: 一个自托管的 WakaTime 兼容开发统计后端
zh-Hant: 一個自託管、相容 WakaTime 的程式開發統計後端
ja: コーディング統計向けのセルフホスト型 WakaTime 互換バックエンド
ko: 코딩 통계를 위한 자체 호스팅 WakaTime 호환 백엔드
ru: Самостоятельно размещаемый WakaTime-совместимый сервер статистики программирования
ms: Backend serasi WakaTime yang dihos sendiri untuk statistik pengekodan
pt-br: Backend compatível com WakaTime e auto-hospedado para estatísticas de programação
type: tool
crossVersionUpdate: true
limit: 0
recommend: 0
website: https://wakapi.dev/
github: https://github.com/muety/wakapi
document: https://github.com/muety/wakapi
architectures:
- amd64
- arm64
+6
View File
@@ -0,0 +1,6 @@
PANEL_APP_PORT_HTTP=3000
WAKAPI_PASSWORD_SALT=replace-with-a-random-32-character-salt
WAKAPI_INSECURE_COOKIES=true
WAKAPI_ALLOW_SIGNUP=true
APP_DATA_DIR=./data
CONTAINER_NAME=wakapi
+95
View File
@@ -0,0 +1,95 @@
additionalProperties:
formFields:
- default: 3000
edit: true
envKey: PANEL_APP_PORT_HTTP
labelEn: Web Port
labelZh: Web 端口
label:
en: Web Port
zh: Web 端口
zh-Hant: Web 連接埠
ja: Web ポート
ko: Web 포트
ru: Веб-порт
ms: Port Web
pt-br: Porta Web
required: true
rule: paramPort
type: number
- default: change-me
edit: true
envKey: WAKAPI_PASSWORD_SALT
labelEn: Password Salt
labelZh: 密码盐
label:
en: Password Salt
zh: 密码盐
zh-Hant: 密碼鹽
ja: パスワードソルト
ko: 비밀번호 솔트
ru: Соль паролей
ms: Garam kata laluan
pt-br: Sal de senha
random: true
required: true
rule: paramComplexity
type: password
- default: "true"
edit: true
envKey: WAKAPI_INSECURE_COOKIES
labelEn: Allow Insecure Cookies
labelZh: 允许非安全 Cookie
label:
en: Allow Insecure Cookies
zh: 允许非安全 Cookie
zh-Hant: 允許非安全 Cookie
ja: 非セキュア Cookie を許可
ko: 보안이 적용되지 않은 Cookie 허용
ru: Разрешить небезопасные cookie
ms: Benarkan kuki tidak selamat
pt-br: Permitir cookies inseguros
required: true
type: select
values:
- label: "true"
value: "true"
- label: "false"
value: "false"
- default: "true"
edit: true
envKey: WAKAPI_ALLOW_SIGNUP
labelEn: Allow Sign-up
labelZh: 允许注册
label:
en: Allow Sign-up
zh: 允许注册
zh-Hant: 允許註冊
ja: ユーザー登録を許可
ko: 회원가입 허용
ru: Разрешить регистрацию
ms: Benarkan pendaftaran
pt-br: Permitir cadastro
required: true
type: select
values:
- label: "true"
value: "true"
- label: "false"
value: "false"
- default: ./data
edit: true
envKey: APP_DATA_DIR
labelEn: Data Directory
labelZh: 数据目录
label:
en: Data Directory
zh: 数据目录
zh-Hant: 資料目錄
ja: データディレクトリ
ko: 데이터 디렉터리
ru: Каталог данных
ms: Direktori data
pt-br: Diretório de dados
required: true
type: text
View File
+30
View File
@@ -0,0 +1,30 @@
services:
wakapi:
image: "n1try/wakapi:latest"
container_name: ${CONTAINER_NAME}
init: true
restart: unless-stopped
networks:
- 1panel-network
ports:
- "${PANEL_APP_PORT_HTTP}:3000"
environment:
- WAKAPI_DB_TYPE=sqlite3
- WAKAPI_DB_NAME=/data/wakapi.db
- WAKAPI_PASSWORD_SALT=${WAKAPI_PASSWORD_SALT}
- WAKAPI_INSECURE_COOKIES=${WAKAPI_INSECURE_COOKIES}
- WAKAPI_ALLOW_SIGNUP=${WAKAPI_ALLOW_SIGNUP}
volumes:
- "${APP_DATA_DIR}:/data"
healthcheck:
test: ["CMD", "/app/healthcheck"]
interval: 60s
timeout: 3s
start_period: 120s
retries: 3
labels:
createdBy: "Apps"
networks:
1panel-network:
external: true
View File
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}"
APP_DATA_DIR_RAW="${APP_DATA_DIR:-}"
WAKAPI_UID=65532
WAKAPI_GID=65532
if [[ -z "$APP_DATA_DIR_RAW" && -f "$ENV_FILE" ]]; then
APP_DATA_DIR_RAW="$(grep '^APP_DATA_DIR=' "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2- || true)"
fi
APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW:-./data}"
case "$APP_DATA_DIR_RAW" in
\"*\")
APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW#\"}"
APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW%\"}"
;;
\'*\')
APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW#\'}"
APP_DATA_DIR_RAW="${APP_DATA_DIR_RAW%\'}"
;;
esac
if [[ -z "$APP_DATA_DIR_RAW" ]]; then
echo "APP_DATA_DIR must not be empty" >&2
exit 1
fi
case "$APP_DATA_DIR_RAW" in
/*)
APP_DATA_DIR_ABS="$(realpath -m -- "$APP_DATA_DIR_RAW")"
APP_DATA_DIR_SCOPE="absolute"
;;
*)
APP_DATA_DIR_ABS="$(realpath -m -- "$ROOT_DIR/${APP_DATA_DIR_RAW#./}")"
case "$APP_DATA_DIR_ABS" in
"$ROOT_DIR"/*) ;;
*)
echo "Relative APP_DATA_DIR must stay inside the application directory" >&2
exit 1
;;
esac
APP_DATA_DIR_SCOPE="application"
;;
esac
DATA_DIR_EXISTED=false
[[ -d "$APP_DATA_DIR_ABS" ]] && DATA_DIR_EXISTED=true
mkdir -p "$APP_DATA_DIR_ABS"
if [[ "$APP_DATA_DIR_SCOPE" == "application" || "$DATA_DIR_EXISTED" == "false" ]]; then
chown -R "$WAKAPI_UID:$WAKAPI_GID" "$APP_DATA_DIR_ABS"
else
CURRENT_UID="$(stat -c '%u' "$APP_DATA_DIR_ABS")"
CURRENT_GID="$(stat -c '%g' "$APP_DATA_DIR_ABS")"
if [[ "$CURRENT_UID" != "$WAKAPI_UID" || "$CURRENT_GID" != "$WAKAPI_GID" ]]; then
echo "Existing absolute APP_DATA_DIR must be owned by ${WAKAPI_UID}:${WAKAPI_GID}" >&2
exit 1
fi
fi
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
docker-compose down --volumes
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
set -euo pipefail
exit 0
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB