Bambu Bar
Monitor Bambu Lab printers from your Mac menu bar: print progress, the live camera, AMS filament, alerts and desktop widgets. Bambu Bar for macOS 26+.
一人用の作業時間記録
Translation pending
ProductivityTypeScript · ShellGitHub @macmariyaOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
TypeScript と React 19(Next.js 16)で作った一人用の作業時間記録アプリ。PostgreSQL 18・Docker Compose で自宅 NAS に置いて使う
Translation pending
Loading the full README.
Read the full README ↗TypeScript と React 19(Next.js 16)で作った、一人用の作業時間記録アプリ。事業/私用と案件・作業種別で時間を記録し、家事按分の根拠と案件別の工数を貯める。
本番は LAN と Tailscale の内側(自宅 NAS の Docker)で動かし、WAN には公開しない。デモデータだけを入れた版を Cloudflare Workers に置いている(デモ、パスワード demo。毎日 4:00 JST に初期状態へ戻る)。
画面はすべてデモデータ(seed/seed.demo.json と scripts/seed-demo-entries.ts)で、実際の案件名や記録は含まない。
A single-user time tracker built with TypeScript and React 19 (Next.js 16), self-hosted on a home NAS (Docker, LAN and Tailscale only). Japanese sole proprietors need time-based evidence to split home expenses between business and private use, so each entry is tagged business or private, with a project and an activity type.
pg_dump backup with a restore test that compares row counts and content hashes against the live database.demo, reset daily at 4:00 JST). It runs on Cloudflare Workers through OpenNext, with Neon PostgreSQL behind Hyperdrive./api/month/YYYY-MM/entries.csv・集計 /api/month/YYYY-MM/summary.csv(UTF-8 BOM・CRLF・JST)backup コンテナが毎日 3:00 JST に pg_dump -Fc、30 日分を保持)と復元テストのスクリプトGET /api/timer・POST /api/timer/start・POST /api/timer/stop・GET /api/day/YYYY-MM-DDON time_entries ((true)) WHERE ended_at IS NULL で DB が保証するtimestamptz(UTC)で保存し、JST の日付は IMMUTABLE 関数 jst_date() を使った生成列 entry_date に持つREPORT_START(任意)より前の日は、月次レポートの分母にも分子にも入れない(記録を月の途中から始めた月のため)TypeScript / React 19 / Next.js 16(App Router)/ Drizzle ORM / PostgreSQL 18 / Docker Compose(app・db・backup の 3 コンテナ)。Node.js 24。
/api/timer など)と CSV 出力だけ Route Handler にしているuseOptimistic で開始・停止を先に画面へ反映、useActionState でログインのエラー表示、useTransition で送信中の表示)cp .env.example .env # 値を入れる(AUTH_SECRET は openssl rand -hex 32)
docker compose up -d --build
docker compose exec app npm run db:migrate
docker compose exec app npm run db:seed # seed/seed.demo.json(汎用名)を入れる
http://localhost:3100 を開き、.env の APP_PASSWORD でログインする。
画面を試すだけなら、直近 28 日分のデモ記録も入れられる(記録が 1 件でもある DB では何もしない)。
docker compose exec app npm run db:seed-demo-entries
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d db # DB だけ 127.0.0.1:5433 に出す
npm ci
npm test # Vitest(JST の日付分割・認証・月次集計と CSV)
npm run typecheck
npm run lint
マイグレーションは npm run db:generate で drizzle/ に SQL を作り、中身を読んでからコミットする。本番では起動時に自動適用しない。
.env.deploy(コミットしない)に DEPLOY_HOST(ssh の接続名)・DEPLOY_DIR・DEPLOY_URL を書き、./scripts/deploy-nas.sh を実行する。
既定はソースを送って NAS 上でビルドする。--image を付けると Mac で linux/amd64 のイメージを作って docker save | docker load で持ち込む。
ポートフォリオ用に、デモデータだけを入れた版を Cloudflare Workers に置いている(本番の記録は置かない)。
@opennextjs/cloudflare)で Workers 用に変換する。DB は Neon の PostgreSQL を Hyperdrive 経由で使い、スキーマとマイグレーションは NAS 版と同じものを流すsrc/db/client.ts は DATABASE_URL が無いときだけ、Hyperdrive からリクエストごとに接続を作る(プールは Hyperdrive が持つ)cloudflare/worker.ts)が案件・作業種別・記録を入れ直す。中身は src/lib/demo.ts の resetDemonext build は .env を読んでバンドルに埋め込む。手元には NAS 本番の .env があるので、scripts/cf-demo.sh は Git が追うファイルだけを一時フォルダに写してビルドし、本番の値がバンドルに入っていないか確かめてから配置するwrangler.jsonc の LOGIN_HINT があるときだけ出る(NAS 版には出ない)src/proxy.ts(Node.js ランタイム)は、OpenNext では実験的な扱いになっている./scripts/cf-demo.sh preview # ローカルの workerd で動かす(デモ DB は 127.0.0.1:5434、秘密は .dev.vars)
./scripts/cf-demo.sh deploy # Cloudflare に配置する
初回だけ、Hyperdrive の設定(キャッシュなし)を作って wrangler.jsonc に ID を書き、npx wrangler secret put APP_PASSWORD と AUTH_SECRET を入れる。Neon にはマイグレーションを流してから WORKLOG_DEMO=1 node dist-scripts/reset-demo.mjs でデモデータを入れる(npm run build:scripts の後)。reset-demo は WORKLOG_DEMO=1 が無いと動かない。
backup コンテナ(postgres:18.6-alpine と busybox の crond)が毎日 3:00(JST)に backups/worklog_YYYYMMDD.dump を書く。書いた直後に pg_restore --list で読めることを確かめ、BACKUP_KEEP_DAYS(既定 30)日より古いものを消す。ダンプは個人のデータを含むので 600 で置く。
docker compose exec backup /scripts/backup.sh # 今すぐ取る
docker compose exec backup /scripts/restore-test.sh # 最新のダンプを別 DB に戻し、表ごとに件数と中身のハッシュを本番と突合する
docker compose logs backup # 定期実行の結果
本番へ戻すときは app を止めてから pg_restore --clean --if-exists --no-owner -d worklog <dump> を流す(この手順はまだ本番で試していない。試したのは別 DB への復元と突合まで)。