尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Pay Up is a fine jar for yourself. Make a jar for the habit you want to catch: negativity, swearing, doom-forecasting. Set a fine per slip, tap the big pink button every time you catch yourself, add a note if it deserves one. The jar keeps an honest tally of what you owe; settle it however you like. Share a jar and let friends watch it grow.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Make a jar for the thing you keep doing. Tap it when you do it. Settle up when it hurts.
nvm use (Node 22.22 or newer), then npm install.cp .env.example .env and fill in SESSION_SECRET (openssl rand -base64 32) and at least one provider.npm run dev and open http://localhost:5173. In development APP_URL must match that origin, so set APP_URL=http://localhost:5173 in .env and register callbacks against it.${APP_URL}/auth/github/callback. Copy client id and secret into .env.${APP_URL}/auth/google/callback. Configure the consent screen with the openid, email, profile scopes.A provider whose pair is empty in .env simply has no button on the landing page.
npm testnpm run typechecknpm run build then npm start serves the production build on PORT (default 3000) through server.js, a small Express server that trusts the reverse proxy in front of it (see TRUST_PROXY). A /healthz endpoint answers ok.cp .env.example .env # fill it in; APP_URL is the public https origin
docker compose up --build -d
The SQLite file lives in ./data on the host. Back it up by copying that folder. Cookies are marked secure in production, so put the container behind HTTPS.
| Variable | Required | Default |
|---|---|---|
SESSION_SECRET | yes | |
APP_URL | yes | |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET | one provider pair | |
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET | one provider pair | |
DATABASE_PATH | no | ./data/payup.db (/app/data/payup.db in Docker) |
APP_LOCALE | no | es-ES, used when the browser sends no usable Accept-Language |
OPERATOR_NAME / CONTACT_EMAIL | for a public deployment | shown on the Terms and Privacy pages; the pages say they are not configured until you set them |
TRUST_PROXY | behind a reverse proxy | loopback, linklocal, uniquelocal: trusts X-Forwarded-* from private-network proxies such as a Docker network or a proxy on the same host. Set to true to trust any, or an Express trust-proxy value. The server also treats every request as addressed to APP_URL, so form posts work behind any proxy as long as APP_URL is the public origin. |
PORT | no | 3000 |
Legal pages live at /terms and /privacy (plain-language drafts, not legal advice; set OPERATOR_NAME and CONTACT_EMAIL). There is no cookie banner because the only cookies are the session, the ten-minute sign-in cookie and the timezone cookie. Users can delete their account from their profile page (/profile, behind the avatar in the top-right corner), which also shows their stats.
/admin/stats shows combined figures for the whole deployment: people, jars, fines and settle-ups, sign-ups and fines per week, sign-in providers, public and private jars, and money per currency. It never names a person, jar or note. Admins reach it from the avatar menu; everyone else gets a 404.
Nobody is an admin until you set the flag by hand. Sign in once, then:
docker compose exec payup node -e "console.log(require('better-sqlite3')('/app/data/payup.db').prepare('UPDATE users SET is_admin = 1 WHERE email = ?').run(process.argv[1]).changes)" [email protected]
It prints 1 when it found the account and 0 when no account has that email. It takes effect on the next page load. Use is_admin = 0 to take it away again, and ./data/payup.db instead of the /app/data path to run it outside Docker.
Public links are /j/<slug>: the slug is made from the title (or typed by you), checked live for availability, and changing it on the edit page stops the old link working.
Dates render in the viewer's browser timezone (a tz cookie set on first load); money renders in the viewer's locale.