一个极简主义的、自托管的、兼容WakaTime的编码统计后端。
安装说明可以在下方和Wiki中找到。
🚀 特性
- ✅ 免费开源
- ✅ 由开发者为开发者打造
- ✅ 支持项目、语言、编辑器、主机和操作系统的统计
- ✅ 徽章
- ✅ 每周电子邮件报告
- ✅ REST API
- ✅ 部分兼容WakaTime
- ✅ WakaTime集成
- ✅ 支持Prometheus导出
- ✅ 极速运行
- ✅ 自托管
⌨️ 如何使用?
Wakapi有多种使用方式,从我们托管的云服务到自托管。无论您选择哪种方式,都需要额外进行客户端设置。
☁️ 方式1:使用wakapi.dev
如果您想尝试免费的托管云服务,只需创建一个账户,然后设置客户端工具(见下文)。
📦 方式2:快速运行发布版本
$ curl -L https://wakapi.dev/get | bash
或者使用eget:
$ eget muety/wakapi
🐳 方式3:使用Docker
# 创建持久卷
$ docker volume create wakapi-data
$ SALT="$(cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w ${1:-32} | head -n 1)"
# 运行容器
$ docker run -d \
-p 3000:3000 \
-e "WAKAPI_PASSWORD_SALT=$SALT" \
-v wakapi-data:/data \
--name wakapi \
ghcr.io/muety/wakapi:latest
注意: 默认使用SQLite作为数据库。要在Docker中使用MySQL或Postgres运行Wakapi,请参阅Dockerfile和config.default.yml了解更多选项。
如果您想在Kubernetes上运行Wakapi,可以使用wakapi-helm-chart快速轻松部署。
🧑💻 方式4:从源代码编译并运行
# 构建并安装
# 或者:go build -o wakapi
$ go install github.com/muety/wakapi@latest
# 获取默认配置并自定义
$ curl -o wakapi.yml https://raw.githubusercontent.com/muety/wakapi/master/config.default.yml
$ vi wakapi.yml
# 运行
$ ./wakapi -config wakapi.yml
注意: 查看config.yml
中的注释,了解安全配置等最佳实践。
💡 独立运行Wakapi(不使用Docker)时,建议将其作为SystemD服务运行。
💻 客户端设置
Wakapi依赖开源的WakaTime客户端工具。要为Wakapi收集统计数据,您需要设置这些工具。
- 为您的特定IDE或编辑器设置WakaTime。请参考相应的插件指南
- 按如下方式**编辑您本地的
~/.wakatime.cfg
**文件。
[settings]
# 您的Wakapi服务器URL或使用云服务器时的'https://wakapi.dev/api'
api_url = http://localhost:3000/api
# 您的Wakapi API密钥(创建账户后从网页界面获取)
api_key = 406fe41f-6d69-4183-a4cc-121e0c524c2b
此外,您还可以设置客户端代理。
🔧 配置选项
您可以通过配置文件(默认:config.yml
,可通过-c
参数自定义)或环境变量指定配置选项。以下是所有选项的概览。
YAML 键 / 环境变量 | 默认值 | 描述 |
---|---|---|
env /ENVIRONMENT | dev | 是否使用开发或生产设置 |
app.leaderboard_enabled /WAKAPI_LEADERBOARD_ENABLED | true | 是否启用公共排行榜 |
app.leaderboard_scope /WAKAPI_LEADERBOARD_SCOPE | 7_days | 公共排行榜的聚合间隔(参见此处允许的值) |
app.leaderboard_generation_time /WAKAPI_LEADERBOARD_GENERATION_TIME | 0 0 6 * * *,0 0 18 * * * | 重新计算排行榜的一个或多个时间点 |
app.aggregation_time /WAKAPI_AGGREGATION_TIME | 0 15 2 * * * | 定期为所有用户生成摘要的时间点 |
app.report_time_weekly /WAKAPI_REPORT_TIME_WEEKLY | 0 0 18 * * 5 | 发送电子邮件报告的星期和时间 |
app.data_cleanup_time /WAKAPI_DATA_CLEANUP_TIME | 0 0 6 * * 0 | 执行数据清理操作的时间(参见 app.data_retention_months ) |
app.import_enabled /WAKAPI_IMPORT_ENABLED | true | 是否允许从 WakaTime 或其他 Wakapi 实例导入数据 |
app.import_batch_size /WAKAPI_IMPORT_BATCH_SIZE | 50 | 从外部服务导入时插入数据库的心跳批次大小 |
app.import_backoff_min /WAKAPI_IMPORT_BACKOFF_MIN | 5 | 用户可以尝试再次数据导入前的"冷却"期(分钟) |
app.import_max_rate /WAKAPI_IMPORT_MAX_RATE | 24 | 成功数据导入后用户可以尝试另一次导入的最小等待小时数 |
app.inactive_days /WAKAPI_INACTIVE_DAYS | 7 | 将用户视为不活跃的天数(仅用于指标) |
app.heartbeat_max_age / WAKAPI_HEARTBEAT_MAX_AGE | 4320h | 心跳可接受的最大年龄(参见 ParseDuration ) |
app.custom_languages | - | 文件扩展名到语言名称的映射 |
app.avatar_url_template /WAKAPI_AVATAR_URL_TEMPLATE | (参见 config.default.yml ) | 外部用户头像图片的 URL 模板(例如来自 Dicebear 或 Gravatar) |
app.date_format /WAKAPI_DATE_FORMAT | Mon, 02 Jan 2006 | 格式化人类可读日期的 Go 时间格式字符串(参见 Time.Format ) |
app.datetime_format /WAKAPI_DATETIME_FORMAT | Mon, 02 Jan 2006 15:04 | 格式化人类可读日期时间的 Go 时间格式字符串(参见 Time.Format ) |
app.support_contact /WAKAPI_SUPPORT_CONTACT | hostmaster@wakapi.dev | 在页面上显示为支持联系方式的电子邮件地址 |
app.data_retention_months /WAKAPI_DATA_RETENTION_MONTHS | -1 | 用户数据(心跳)的最大保留期(月)(-1 表示无限制) |
app.max_inactive_months /WAKAPI_MAX_INACTIVE_MONTHS | 12 | 删除无数据用户账户的最大不活跃月数(-1 表示无限制) |
server.port /WAKAPI_PORT | 3000 | 监听端口 |
server.listen_ipv4 /WAKAPI_LISTEN_IPV4 | 127.0.0.1 | 监听的 IPv4 网络地址(设置为 '-' 以禁用 IPv4) |
server.listen_ipv6 /WAKAPI_LISTEN_IPV6 | ::1 | 监听的 IPv6 网络地址(设置为 '-' 以禁用 IPv6) |
server.listen_socket /WAKAPI_LISTEN_SOCKET | - | 监听的 UNIX 套接字(设置为 '-' 以禁用 UNIX 套接字) |
server.listen_socket_mode /WAKAPI_LISTEN_SOCKET_MODE | 0666 | 创建 UNIX 套接字的权限模式 |
server.timeout_sec /WAKAPI_TIMEOUT_SEC | 30 | 请求超时时间(秒) |
server.tls_cert_path /WAKAPI_TLS_CERT_PATH | - | SSL 服务器证书路径(留空表示不使用 HTTPS) |
server.tls_key_path /WAKAPI_TLS_KEY_PATH | - | SSL 服务器私钥路径(留空表示不使用 HTTPS) |
server.base_path /WAKAPI_BASE_PATH | / | Web 基础路径(在代理后的子路径下运行时更改) |
server.public_url /WAKAPI_PUBLIC_URL | http://localhost:3000 | Wakapi 实例可公开访问的 URL |
security.password_salt /WAKAPI_PASSWORD_SALT | - | 用于密码哈希的盐值 |
security.insecure_cookies /WAKAPI_INSECURE_COOKIES | false | 是否允许通过 HTTP 使用 cookie |
security.cookie_max_age /WAKAPI_COOKIE_MAX_AGE | 172800 | 认证 cookie 的生存时间(秒)或 0 表示使用会话 cookie |
security.allow_signup /WAKAPI_ALLOW_SIGNUP | true | 是否启用用户注册 |
security.signup_captcha /WAKAPI_SIGNUP_CAPTCHA | false | 注册表单是否需要解决验证码 |
security.invite_codes /WAKAPI_INVITE_CODES | true | 是否启用邀请码注册。主要用于禁用注册时(仅邀请服务器)。 |
security.disable_frontpage /WAKAPI_DISABLE_FRONTPAGE | false | 是否禁用登录页面(适用于个人实例) |
security.expose_metrics /WAKAPI_EXPOSE_METRICS | false | 是否在 /api/metrics 下暴露 Prometheus 指标 |
security.trusted_header_auth /WAKAPI_TRUSTED_HEADER_AUTH | false | 是否为反向代理启用可信头部认证(参见 #534)。谨慎使用! |
security.trusted_header_auth_key /WAKAPI_TRUSTED_HEADER_AUTH_KEY | Remote-User | 可信头部认证的头部字段。注意: 代理必须配置为从客户端请求中剥离此头部! |
security.trust_reverse_proxy_ips /WAKAPI_TRUST_REVERSE_PROXY_IPS | - | 信任处理认证的反向代理的 IPv4 或 IPv6 地址列表,以逗号分隔。 |
security.signup_max_rate /WAKAPI_SIGNUP_MAX_RATE | 5/1h | 注册端点的速率限制配置,格式为 <最大请求数>/<倍数><单位> ,其中 单位 可以是 s 、m 或 h 。 |
security.login_max_rate /WAKAPI_LOGIN_MAX_RATE | 10/1m | 登录端点的速率限制配置,格式为 <最大请求数>/<倍数><单位> ,其中 单位 可以是 s 、m 或 h 。 |
security.password_reset_max_rate /WAKAPI_PASSWORD_RESET_MAX_RATE | 5/1h | 密码重置端点的速率限制配置,格式为 <最大请求数>/<倍数><单位> ,其中 单位 可以是 s 、m 或 h 。 |
db.host /WAKAPI_DB_HOST | - | 数据库主机 |
db.port /WAKAPI_DB_PORT | - | 数据库端口 |
db.socket /WAKAPI_DB_SOCKET | - | 数据库UNIX套接字(替代 host )(仅适用于MySQL) |
db.user /WAKAPI_DB_USER | - | 数据库用户 |
db.password /WAKAPI_DB_PASSWORD | - | 数据库密码 |
db.name /WAKAPI_DB_NAME | wakapi_db.db | 数据库名称 |
db.dialect /WAKAPI_DB_TYPE | sqlite3 | 数据库类型(可选 sqlite3 、mysql 、postgres 、cockroach 、mssql ) |
db.charset /WAKAPI_DB_CHARSET | utf8mb4 | 数据库连接字符集(仅适用于MySQL) |
db.max_conn /WAKAPI_DB_MAX_CONNECTIONS | 2 | 最大数据库连接数 |
db.ssl /WAKAPI_DB_SSL | false | 是否对数据库连接使用TLS加密(仅适用于Postgres和CockroachDB) |
db.automgirate_fail_silently /WAKAPI_DB_AUTOMIGRATE_FAIL_SILENTLY | false | 启动时是否忽略架构自动迁移失败 |
mail.enabled /WAKAPI_MAIL_ENABLED | true | 是否允许Wakapi发送电子邮件(例如用于密码重置) |
mail.sender /WAKAPI_MAIL_SENDER | Wakapi <noreply@wakapi.dev> | 发送邮件的默认发件人地址 |
mail.provider /WAKAPI_MAIL_PROVIDER | smtp | 用于发送邮件的实现方式(可选 [smtp ]) |
mail.smtp.host /WAKAPI_MAIL_SMTP_HOST | - | SMTP服务器地址(如果使用 smtp 邮件提供商) |
mail.smtp.port /WAKAPI_MAIL_SMTP_PORT | - | SMTP服务器端口(通常为465) |
mail.smtp.username /WAKAPI_MAIL_SMTP_USER | - | SMTP服务器认证用户名 |
mail.smtp.password /WAKAPI_MAIL_SMTP_PASS | - | SMTP服务器认证密码 |
mail.smtp.tls /WAKAPI_MAIL_SMTP_TLS | false | SMTP服务器是否需要TLS加密(false 表示STARTTLS或无加密) |
mail.smtp.skip_verify /WAKAPI_MAIL_SMTP_SKIP_VERIFY | false | 是否允许TLS加密的SMTP使用无效或自签名证书 |
sentry.dsn /WAKAPI_SENTRY_DSN | – | 用于集成 Sentry 进行错误日志记录和跟踪的DSN(留空则禁用) |
sentry.enable_tracing /WAKAPI_SENTRY_TRACING | false | 是否启用Sentry请求跟踪 |
sentry.sample_rate /WAKAPI_SENTRY_SAMPLE_RATE | 0.75 | Sentry跟踪请求的概率 |
sentry.sample_rate_heartbeats /WAKAPI_SENTRY_SAMPLE_RATE_HEARTBEATS | 0.1 | Sentry跟踪心跳请求的概率 |
quick_start /WAKAPI_QUICK_START | false | 是否跳过初始启动任务。仅用于开发目的! |
enable_pprof /WAKAPI_ENABLE_PPROF | false | 是否将 pprof 分析数据作为调试端点暴露 |
支持的数据库
Wakapi 使用 GORM 作为 ORM。因此,它支持一系列不同的关系型数据库。
- SQLite(默认,易于设置)
- MySQL(推荐,因为经过最广泛的测试)
- MariaDB(开源 MySQL 替代品)
- Postgres(同样是开源的)
- CockroachDB(云原生、分布式、兼容 Postgres API)
- Microsoft SQL Server(微软 SQL Server)
🔐 身份认证
Wakapi 支持不同类型的用户身份认证。
- **Cookie:**这种方法在浏览器中使用。用户通过发送一个加密的、安全的、仅限 HTTP 的 cookie(
wakapi_auth
)进行认证,该 cookie 在登录时由服务器的响应设置。 - API 密钥:
- **通过头部:**这种方法受 WakaTime 的认证机制启发,是对 API 端点进行认证的常用方式。用户将
Authorization
头部设置为Basic <BASE64_TOKEN>
,其中后一部分对应于您的 base64 哈希 API 密钥。 - **通过查询参数:**或者,用户也可以在每个请求的 URL 中以查询参数的形式传递他们的纯 API 密钥(例如
?api_key=86648d74-19c5-452b-ba01-fb3ec70d4c2f
)。
- **通过头部:**这种方法受 WakaTime 的认证机制启发,是对 API 端点进行认证的常用方式。用户将
- 可信头部:这种机制允许将认证委托给反向代理(例如用于 SSO),Wakapi 将完全信任该代理。详见 #534。
- 必须通过配置文件中的
trusted_header_auth
启用,并配置trust_reverse_proxy_ip
- 警告:这种认证类型很容易配置错误。请确保您的反向代理正确地从客户端请求中剥离相关头部。
- 必须通过配置文件中的
🔧 API 端点
请参阅我们的 Swagger API 文档。
生成 Swagger 文档
$ go install github.com/swaggo/swag/cmd/swag@latest
$ swag init -o static/docs
🤝 集成
Prometheus 导出
您可以将 Wakapi 统计数据导出到 Prometheus,以便在 Grafana 仪表板或类似工具中查看。以下是操作方法。
# 1. 启用该功能并启动 Wakapi
$ export WAKAPI_EXPOSE_METRICS=true
$ ./wakapi
# 2. 获取您的 API 密钥并进行哈希处理
$ echo "<YOUR_API_KEY>" | base64
# 3. 在 prometheus.yml 中添加 Prometheus 抓取配置(见下文)
抓取配置示例
# prometheus.yml
# (假设您的 Wakapi 实例在 localhost 的 3000 端口监听)
scrape_configs:
- job_name: 'wakapi'
scrape_interval: 1m
metrics_path: '/api/metrics'
bearer_token: '<YOUR_BASE64_HASHED_TOKEN>'
static_configs:
- targets: [ 'localhost:3000' ]
Grafana
wakatime_exporter 的作者还提供了一个不错的 Grafana 仪表板。
WakaTime 集成
Wakapi 可以与 WakaTime 很好地配合使用。一方面,您可以从 Wakapi 向 WakaTime 转发心跳,从而同时有效地使用两种服务。此外,还可以选择从 WakaTime 导入历史数据,以确保两个服务之间的一致性。这两个功能都可以在 Wakapi 实例设置页面的"集成"部分启用。
GitHub Readme Stats 集成
Wakapi 还集成了 GitHub Readme Stats,可以为您生成精美的卡片。以下是一个示例。要使用此功能,别忘了在 设置 -> 权限 下启用公开数据。
点击查看代码
![](https://github-readme-stats.vercel.app/api/wakatime?username={yourusername}&api_domain=wakapi.dev&bg_color=2D3748&title_color=2F855A&icon_color=2F855A&text_color=ffffff&custom_title=Wakapi%20Week%20Stats&layout=compact)
Github Readme Metrics 集成
GitHub Metrics 有一个 WakaTime 插件,也兼容 Wakapi。要使用此功能,别忘了在 设置 -> 权限 下启用公开数据。
预览:
点击查看代码
- uses: lowlighter/metrics@latest
with:
# ... 其他选项
plugin_wakatime: yes
plugin_wakatime_token: ${{ secrets.WAKATIME_TOKEN }} # 必需
plugin_wakatime_days: 7 # 显示上周统计
plugin_wakatime_sections: time, projects, projects-graphs # 显示时间和项目部分,以及项目图表
plugin_wakatime_limit: 4 # 每个图表显示 4 个条目
plugin_wakatime_url: http://wakapi.dev # Wakatime 网址端点
plugin_wakatime_user: .user.login # 用户
浏览器插件(Chrome 和 Firefox)
browser-wakatime 插件使您能够在 WakaTime(当然也包括 Wakapi)中跟踪您的网页浏览。访问的网站将作为"文件"出现在摘要中。按照以下说明开始使用:
- 从官方商店安装浏览器扩展(Firefox,Chrome)
- 打开扩展设置对话框
- 按如下方式配置(见下方截图):
- API 密钥:您的个人 API 密钥(在 wakapi.dev 获取)
- 日志记录类型:仅域名
- API URL:
https://wakapi.dev/api/compat/wakatime/v1
(或者将 wakapi.dev 替换为您的自托管实例主机名)
- 保存
- 开始浏览!
注意:插件只会偶尔同步心跳,所以它们可能需要一些时间才能出现在 Wakapi 上。要"强制"同步,只需打开插件的主对话框。
Gnome 扩展
如果您使用 GNOME 桌面,有一种快速方法可以在状态栏中显示今天的编码统计信息。
只需安装 Executor 扩展,并将以下命令添加为状态栏指示器:
~/.wakatime/wakatime-cli-linux-amd64 --today
📦 数据导出
你可以将Wakapi的编码活动数据以原始心跳的形式导出为CSV文件。虽然无法直接通过网页界面完成此操作,但我们提供了一个易于使用的Python 脚本来实现。
$ pip install requests tqdm
$ python scripts/download_heartbeats.py --api_key API密钥 [--url URL] [--from 起始日期] [--to 结束日期] [--output 输出文件]
示例
python scripts/download_heartbeats.py --api_key 04648d14-15c9-432b-b901-dbeec70d4eaf \
--url https://wakapi.dev/api \
--from 2023-01-01 \
--to 2023-01-31 \
--output wakapi_export.csv
👍 最佳实践
建议在反向代理后使用wakapi,如Caddy或nginx,以启用TLS加密(HTTPS)。
但是,如果你仍然想将wakapi实例公开暴露,你需要在config.yml
中将server.listen_ipv4
设置为0.0.0.0
。
🧪 测试
单元测试
单元测试旨在对业务逻辑进行细粒度测试。它们作为应用程序的一部分实现,使用Go的testing包以及stretchr/testify。
如何运行
$ CGO_ENABLED=0 go test `go list ./... | grep -v 'github.com/muety/wakapi/scripts'` -json -coverprofile=coverage/coverage.out ./... -run ./...
API测试
API测试作为黑盒测试实现,通过HTTP请求与完整独立的Wakapi进行交互。它们旨在检查Wakapi的Web堆栈和端点,包括响应代码、标头和语法层面的数据,而不是检查返回的实际内容。
我们的API(或在某种程度上是端到端)测试是作为Postman集合实现的,可以在Postman内部运行,也可以使用newman作为命令行运行器。
为了获得可预测的环境,测试在一个全新且干净的Wakapi实例上运行,该实例使用SQLite数据库,仅填充了一些种子数据(参见data.sql)。通常建议软件测试应该是安全、无状态且无副作用的。与该范式相反,我们的API测试严格要求固定的执行顺序(Postman确保这一点),其断言可能依赖于特定的先前测试已成功。
前提条件(仅限Linux)
# 1. sqlite (cli)
$ sudo apt install sqlite # Fedora: sudo dnf install sqlite
# 2. newman
$ npm install -g newman
如何运行(仅限Linux)
$ ./testing/run_api_tests.sh
🤓 开发者注意事项
构建Web资源
为了保持精简,所有JS和CSS资源都作为静态文件包含并提交到Git中。TailwindCSS和Iconify需要额外的构建步骤。为了只在开发时需要这些步骤,编译后的资源也提交到Git中。
$ yarn
$ yarn build # 或: yarn watch
可以通过编辑scripts/bundle_icons.js中的icons
数组来添加新图标。
预压缩
如#284中所解释的,某些资源的预压缩(使用Brotli)版本被提供以节省额外的带宽。这受到Caddy的precompressed
指令的启发。gzipped.FileServer
检查每个静态文件的.br
或.gz
等效文件,如果存在,则提供这些文件而不是实际文件,并附带Content-Encoding: br
。目前,压缩后的资源简单地提交到Git中。以后我们可能会将其作为新的构建步骤的一部分。
要预压缩文件,请运行以下命令:
# 首先安装brotli
$ sudo apt install brotli # 或: sudo dnf install brotli
# 监视、构建和压缩
$ yarn watch:compress
# 或者:仅构建和压缩
$ yarn build:all:compress
# 或者:仅压缩
$ yarn compress
❔ 常见问题
由于Wakapi在很大程度上依赖于WakaTime提供的概念,他们的常见问题在很大程度上也适用于Wakapi。你可能会在那里找到答案。
哪些数据会发送到Wakapi?
- 文件名
- 项目名称
- 编辑器名称
- 你的计算机主机名
- 你在编辑器中每个操作的时间戳
- ...
有关详细信息,请参阅相关的WakaTime常见问题部分。
如果你自己托管Wakapi,你可以控制所有数据。但是,如果你使用我们的网络服务并关心隐私,你也可以排除或混淆某些文件或项目名称。
离线时会发生什么?
所有数据都在本地计算机上缓存,并在你重新上线时批量发送。
Wakapi是如何诞生的?
Wakapi是在我还是学生时开始的,当时我想跟踪有关编码时间的详细统计信息。虽然我是WakaTime的忠实粉丝,但当时我不想每月支付9美元。幸运的是,WakaTime的大部分内容都是开源的!
Wakapi与WakaTime相比如何?
Wakapi是WakaTime的一个小子集,功能较少。WakaTime中缺少的一些很酷的功能包括:
- 排行榜
- 可嵌入的图表
- 个人目标
- 团队/组织支持
- 额外的集成(与GitLab等)
- 更丰富的API
WakaTime物有所值。但是,如果你只需要基本的统计信息,并希望保持对数据的主权,你可能会选择Wakapi。
持续时间是如何计算的?
从心跳推断编码时间的度量方式与WakaTime略有不同。虽然WakaTime有超时间隔,但Wakapi本质上只是为每个在较长暂停后发生的心跳添加2分钟的额外时间。
以下是一个示例(圆圈表示心跳):
|---o---o--------------o---o---|
| |10秒| 3分钟 |10秒| |
如何处理中间的3分钟并不明确。开发者是进行了3分钟的休息,还是只是没有发送心跳,例如因为开发者在盯着屏幕试图找到解决方案,但实际上并没有输入代码?
- WakaTime(5分钟超时):3分20秒
- WakaTime(2分钟超时):20秒
- Wakapi:10秒 + 2分钟 + 10秒 = 2分20秒
Wakapi在第三次心跳之前会添加两分钟的"填充"时间。这就是为什么Wakapi和WakaTime之间的总时间会略有不同。
👥 社区贡献
- 💻 [代码] Wakapi统计数据的图像生成器 – LacazeThomas/wakapi-stats(
Go
) - 💻 [代码] Wakapi的Discord集成 - LLoneDev6/Wakapi-Discord(
JavaScript
) - 💻 [代码] 替代心跳导出脚本 - wakapiexporter.nim(
Nim
) - 💻 [代码] 用于K8s部署的Wakapi Helm图表 - andreymaznyak/wakapi-helm-chart(
YAML
) - 🗒 [文章] Wakamonth:工时报告工具
👏 支持
在开源领域编程是我的热情所在,我希望有朝一日能够全职从事这项工作并以此为生。所以如果你喜欢这个项目,请考虑支持它🙂。你可以通过为我买杯咖啡或成为GitHub赞助者来捐赠。每一笔小额捐赠都备受感激,并能激励我继续改进Wakapi!
🙏 致谢
我非常感谢**@alanhamlett**和WakaTime团队的努力,也感谢他们将软件开源。
此外,感谢**server.camp**为Wakapi.dev赞助服务器基础设施。
📓 许可证
MIT © Ferdinand Mütsch