diff --git a/apps/wakapi/2.17.4/.env.sample b/apps/wakapi/2.17.4/.env.sample new file mode 100644 index 000000000..d29ebede9 --- /dev/null +++ b/apps/wakapi/2.17.4/.env.sample @@ -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 diff --git a/apps/wakapi/2.17.4/data.yml b/apps/wakapi/2.17.4/data.yml new file mode 100644 index 000000000..782366078 --- /dev/null +++ b/apps/wakapi/2.17.4/data.yml @@ -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 diff --git a/apps/wakapi/2.17.4/data/.gitkeep b/apps/wakapi/2.17.4/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/wakapi/2.17.4/docker-compose.yml b/apps/wakapi/2.17.4/docker-compose.yml new file mode 100644 index 000000000..83c36a85b --- /dev/null +++ b/apps/wakapi/2.17.4/docker-compose.yml @@ -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 diff --git a/apps/wakapi/2.17.4/scripts/.gitkeep b/apps/wakapi/2.17.4/scripts/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/wakapi/2.17.4/scripts/init.sh b/apps/wakapi/2.17.4/scripts/init.sh new file mode 100755 index 000000000..2b1b96b3a --- /dev/null +++ b/apps/wakapi/2.17.4/scripts/init.sh @@ -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 diff --git a/apps/wakapi/2.17.4/scripts/uninstall.sh b/apps/wakapi/2.17.4/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/wakapi/2.17.4/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/wakapi/2.17.4/scripts/upgrade.sh b/apps/wakapi/2.17.4/scripts/upgrade.sh new file mode 100755 index 000000000..9879df4c3 --- /dev/null +++ b/apps/wakapi/2.17.4/scripts/upgrade.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -euo pipefail +exit 0 diff --git a/apps/wakapi/README.md b/apps/wakapi/README.md new file mode 100644 index 000000000..89e3b967a --- /dev/null +++ b/apps/wakapi/README.md @@ -0,0 +1,45 @@ +# Wakapi + +## 产品介绍 + +Wakapi 是一个极简、自托管且兼容 WakaTime 客户端的开发统计后端,可用于记录并分析项目、语言、编辑器、主机和操作系统维度的编码活动。 + +## 主要功能 + +- 兼容 WakaTime 客户端和插件 +- 项目、编程语言、编辑器和主机统计 +- REST API、统计徽章和 Prometheus 指标导出 +- 周报邮件和多种数据导入方式 + +## 访问说明 + +- 默认 Web 端口为 `3000`,可在安装表单中修改。 +- 首次访问后注册账户,然后在 Wakapi 页面中获取 API Key。 +- 在 WakaTime 客户端的 `~/.wakatime.cfg` 中将 `api_url` 设为 `http(s):///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. + +## 参考资料 + +- 项目主页: +- 安装与配置: +- 源码仓库: diff --git a/apps/wakapi/data.yml b/apps/wakapi/data.yml new file mode 100644 index 000000000..3335b729c --- /dev/null +++ b/apps/wakapi/data.yml @@ -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 diff --git a/apps/wakapi/latest/.env.sample b/apps/wakapi/latest/.env.sample new file mode 100644 index 000000000..d29ebede9 --- /dev/null +++ b/apps/wakapi/latest/.env.sample @@ -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 diff --git a/apps/wakapi/latest/data.yml b/apps/wakapi/latest/data.yml new file mode 100644 index 000000000..782366078 --- /dev/null +++ b/apps/wakapi/latest/data.yml @@ -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 diff --git a/apps/wakapi/latest/data/.gitkeep b/apps/wakapi/latest/data/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/wakapi/latest/docker-compose.yml b/apps/wakapi/latest/docker-compose.yml new file mode 100644 index 000000000..1522730cf --- /dev/null +++ b/apps/wakapi/latest/docker-compose.yml @@ -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 diff --git a/apps/wakapi/latest/scripts/.gitkeep b/apps/wakapi/latest/scripts/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/wakapi/latest/scripts/init.sh b/apps/wakapi/latest/scripts/init.sh new file mode 100755 index 000000000..2b1b96b3a --- /dev/null +++ b/apps/wakapi/latest/scripts/init.sh @@ -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 diff --git a/apps/wakapi/latest/scripts/uninstall.sh b/apps/wakapi/latest/scripts/uninstall.sh new file mode 100755 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/wakapi/latest/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/wakapi/latest/scripts/upgrade.sh b/apps/wakapi/latest/scripts/upgrade.sh new file mode 100755 index 000000000..9879df4c3 --- /dev/null +++ b/apps/wakapi/latest/scripts/upgrade.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -euo pipefail +exit 0 diff --git a/apps/wakapi/logo.png b/apps/wakapi/logo.png new file mode 100644 index 000000000..e237621fd Binary files /dev/null and b/apps/wakapi/logo.png differ