← 一覧に戻る

dev-timer

GitHub ↗ TypeScript 最終push: 2026/4/27 18:39

WIP(現在進行中)

Work In Progress

このプロジェクトで現在進行中の作業と、過去のスナップショットを記録する。

現在の状況

ステータス: Phase 1(Backend MVP)+ Phase 2(Lightsail デプロイ)完了。本番運用開始。

完了したこと

  • Phase 1: Hono + SQLite + ntfy + CLI を実装、ローカル動作確認 PASS
  • Phase 2: https://dev-timer.ikapps.com で本番稼働中
    • systemd dev-timer.service(deploy ユーザー / port 3001 / Restart=on-failure / メモリ ~57MB)
    • DB: /var/lib/dev-timer/dev-timer.db
    • Nginx + Let's Encrypt(HTTPS、HTTP→HTTPS 301、自動更新)
    • deploy ユーザーの crontab で毎分 /api/check-due を発火
    • DNS: Route 53 で A レコード(dev-timer.ikapps.com13.230.63.19
    • ntfy トピック: dev-timer-ichirokisanuki-{random12} 形式で確定(実値は両環境の .env のみ)
    • iPhone の ntfy アプリで購読済 / e2e 通知到達確認済(CLI から add --in 2m → 2分後に着信)
  • 設定ファイルの真実のコピーを deploy/ 配下で git 管理(dev-timer.service / nginx/dev-timer.ikapps.com

次にやること

Phase 3: Web UI(Astro、ROADMAP の「今月」枠)

  1. Astro セットアップ(dev-tracker のスタックを流用、apps/webfrontend/ か配置場所は要検討)
  2. 一覧画面(pending / 全件タブ切替)
  3. 新規作成フォーム(fire_at--in <duration> 風 + datetime-local の両対応?)
  4. done / 削除ボタン
  5. デプロイ:
    • 静的サイトとして build → 既存の Hono に同居して app.use('/*', serveStatic({ root: './dist-web' })) で配信、または別 vhost
    • 認証なし方針なので URL 知っていれば誰でも叩ける(DECISIONS で確認済)

運用面の TODO(任意・優先度低)

  • GitHub Actions で自動デプロイ(現状は手動 rsync)
  • Lightsail 側のリソース監視(memory tight、~150MB 余裕しかない)

詰まっていること

なし


過去のWIPアーカイブ

(新しい「現在の状況」を書く前に、古いものをここに追記でアーカイブする。新しいものが上)

2026-04-27 17:50 時点のスナップショット

ステータス: 設計合意済み・プロジェクト初期化のみ完了。実装はこれから。

完了したこと

  • 要件と設計の合意(dev-tracker と分離する判断、技術スタック確定)
  • ~/cdev/dev-timer 初期化(git, .devnotes/, README, CLAUDE.md)
  • GitHub private リポ作成 + push
  • dev-tracked topic 付与済(dev-tracker の一覧に出る)

次にやること(Phase 1: Backend MVP)

  1. package.json と TypeScript セットアップ
  2. データモデル定義(SQLite スキーマ)
  3. Hono サーバー実装
  4. ntfy 連携
  5. CLI
  6. ローカルでの動作確認

次にやること(Phase 2: サーバーデプロイ)

  1. Lightsail 側準備(Node.js / /opt/dev-timer/ / SQLite / systemd / Nginx / certbot / DNS / cron)
  2. ntfy トピック名を決定
  3. iPhone に ntfy アプリ install + topic 購読

次にやること(Phase 3: Web UI)

  1. Astro フロントエンド
  2. デプロイ(GitHub Actions → rsync)

詰まっていること

なし(実装着手前)

ROADMAP(計画)

ロードマップ

今週

  • Phase 1: Backend MVP
    • package.json + TypeScript + Hono + better-sqlite3 セットアップ
    • SQLite スキーマ定義(timers テーブル)
    • REST API 実装(CRUD + check-due)
    • ntfy.sh 送信ロジック
    • CLI 実装(add / list / done)
    • ローカル動作確認
  • Phase 2: サーバーデプロイ
    • Lightsail に Node.js install
    • systemd unit 作成・起動
    • Nginx vhost + certbot で SSL
    • DNS A レコード追加(Route 53、AWS CLI)
    • crontab で /api/check-due を毎分叩く
    • ntfy トピック名確定 + iPhone 購読
    • エンドツーエンド疎通確認

今月

  • Phase 3: Web UI
    • Astro セットアップ(dev-tracker のスタイル流用)
    • 一覧 / 作成フォーム / done / delete
    • GitHub Actions で自動デプロイ(現状は手動 rsync)

今四半期

  • dev-tracker の詳細ページから「このプロジェクトの open フォローアップ」リンク追加(dev-timer の API を叩く)
  • スヌーズ機能(通知時に「1時間後にもう一度」)

いつか

  • 通知に Action ボタン(done/snooze)を埋め込む(ntfy のクリックアクション)
  • 繰り返しタイマー(毎週月曜 9:00 など)
  • Slack / メール通知も選択可に
  • 通知履歴の振り返りページ(「今月発火したフォローアップは N 件」など)

DECISIONS(意思決定)

意思決定記録

このプロジェクトで下した重要な意思決定を記録する。 最新が上に来る。


2026-04-27: ntfy トピック名は dev-timer-ichirokisanuki-<12文字> 形式

背景: ntfy.sh は subscribe にトークンが要らず誰でも購読可能なため、トピック名自体が実質的なシークレットになる。一方で覚えやすさも欲しい。

決定: 接頭辞を dev-timer-ichirokisanuki- に固定し、接尾辞を node -e 'crypto.randomBytes(9).toString("base64url")' で生成(base64url 12 文字、英数 + -_)。実値はサーバーの /opt/dev-timer/.env とローカルの .env(どちらも gitignore 済)にのみ保管し、git には残さない。

理由: 接頭辞でプロジェクト・所有者を識別、接尾辞 12 文字で 70bit 弱のエントロピーが入るので推測攻撃に十分耐える。これに加えて「タイマー本文に詳細コンテキストを書かない」方針(既存 DECISIONS)を併用すれば、漏れても被害は限定的。


2026-04-27: 設定ファイルの真実のコピーは deploy/ 配下で git 管理

背景: systemd unit と Nginx vhost をサーバー側 (/etc/systemd/system/ / /etc/nginx/sites-available/) に直接置く運用は、設定の追跡性が悪く再現性も低い。dev-tracker は GitHub Actions YAML だけリポ管理で、サーバー側設定はリポ外(multi-purpose-lightsail-server1 の DEVLOG / DECISIONS にメモするのみ)。

決定: dev-timer ではプロジェクトリポ直下の deploy/ に systemd unit と Nginx vhost を置き、サーバー側はそれを scp/install で配置する運用にする。サーバー側で certbot 等が書き換えた場合は、ローカルにコピーを取り直して反映させる。

理由: dev-timer はサーバー常駐型サービスなので、systemd の再現は dev-tracker(静的ファイル rsync のみ)より重要。ローカルで diff 取れる・PR レビューできる利点が大きい。deploy/ 配下にあれば「アプリのソースとは別物」という意図も明確(誤って npm 系のパスと混同しない)。


2026-04-27: ローカル CLI のデフォルト DEV_TIMER_API は本番 URL

背景: CLI は本番運用が主、ローカル開発時のサーバー起動はたまにしか発生しない。デフォルトをどちらに振るかで普段の使い勝手が変わる。

決定: .env / .env.example のデフォルトを https://dev-timer.ikapps.com にする。ローカル開発で API を立ち上げて叩きたいときは、DEV_TIMER_API=http://127.0.0.1:3001 npx tsx bin/dev-timer.ts ... のように env で一時上書き する運用。

理由: 普段の dev-timer add --in 3d "..." は本番に書き込むのが意図したい挙動。ローカル開発のために env を毎回設定するより、開発時だけ env を一時的にセットする方が頻度的に合理的。逆向きの設定(ローカルがデフォルト)だと、CLI を本番運用に使うたびに env 必要で煩わしい。


2026-04-27: dev-tracker と分離して新規プロジェクトにする

背景: 開発中の follow-up リマインダ機能を dev-tracker に組み込むか、別プロジェクトにするか議論。

決定: 別プロジェクト dev-timer として分離し、~/cdev/dev-timer に作成する。

理由: dev-tracker は「GitHub から pull した read-only スナップショット」、dev-timer は「ユーザーが書き込む follow-up タスク」。データソースもライフサイクルも違うので、同居させると概念がブレる。dev-tracker は静的サイト(ビルド済rsync)で済むが dev-timer はサーバー常駐が必要、という運用面の差も大きい。


2026-04-27: iPhone 通知は ntfy.sh を使う

背景: 通知方法の候補は ntfy.sh / Pushover / Slack DM / Telegram bot。

決定: ntfy.sh を採用。

理由: OSS、無料、curl 1発で送信可能、iOS app あり。Pushover は安定だが $5 課金で買い切るほど大規模に使う想定ではない。Slack/Telegram はチャンネル設計が必要で大袈裟。トピック名は推測しにくい長い文字列にすれば実質プライベート(誰でも subscribe できる仕様への対処)。


2026-04-27: データはサーバー側(Lightsail)に置く

背景: タイマーの永続化先は Mac か Lightsail か。

決定: Lightsail 側に SQLite で持つ。

理由: push 通知のために常時走る cron が必要。Mac はスリープしたら cron が動かないので不可。Lightsail は常時稼働で既に他サービスを動かしているので、リソース面でも自然。CLI からは API 経由で書き込む。


2026-04-27: アクセス制御は付けない

背景: タイマー本文に SQL や Issue 参照などの開発コンテキストが入る場合のリスクを議論。

決定: Basic 認証 / API キー等の認証は付けない(dev-tracker と同様、URL を知っている人は誰でも見られる)。

理由: ユーザー方針: 「通知はただアラートだけで、実行する内容を詳細にかかないから大丈夫」。タイマー本文は「ALTER TABLE の効果確認」程度の薄いラベルに留めるため、認証コストを払わない。後から必要になったら追加可能。


2026-04-27: バックエンドは Hono + SQLite

背景: ランタイム選定。Express / Fastify / Hono、DB は SQLite / PostgreSQL / file-JSON。

決定: Hono + better-sqlite3。

理由: Hono は軽量・型安全・モダンで、個人ツールに最適。SQLite はファイル1個で済み運用が楽、個人スケール(タイマー数百〜千件)なら全く問題ない。Postgres は overkill、JSON ファイルは同時書き込み制御が面倒。


2026-04-27: フロントは Astro(dev-tracker と統一)

背景: Web UI のフレームワーク選定。

決定: Astro を採用。

理由: dev-tracker と同じスタックで揃えると学習コスト 0、コンポーネント・スタイルを流用しやすい。dev-timer は SSR 寄りだが、Astro は server endpoints を持てるのでバックエンドへの fetch も自然。


2026-04-27: ポート 3001 を使う

背景: 既存サービスとのポート衝突回避。

決定: 3001 で起動。

理由: 既存: 7channel=8001, aix=5001, mydb=php-fpm。3000番台は空き、3001 が見やすい。

DEVLOG(作業ログ)

開発日誌

このプロジェクトでの作業を時系列で記録する。 最新のエントリが上に来る。


2026-04-27

18:25 - Phase 2: Lightsail デプロイ完了・iPhone 通知 e2e 確認

やったこと

  • ntfy トピック名生成: dev-timer-ichirokisanuki-{random12} 形式で確定(node の crypto.randomBytes(9) を base64url 化)。実値は両環境の .env のみに保管
  • iPhone に ntfy アプリ install + topic 購読 + 通知許可(ユーザー側で実施、curl publish で届くこと確認)
  • Route 53 で A レコード追加: dev-timer.ikapps.com13.230.63.19(AWS CLI、UPSERT、伝播 INSYNC まで確認)
  • Lightsail(multi-purpose-lightsail-server1)への SSH 経由でのセットアップ:
    • Node.js 22 LTS を NodeSource からインストール(v22.22.2 / npm 10.9.7)+ build-essential
    • /opt/dev-timer//var/lib/dev-timer/ を deploy ユーザー所有で作成
    • ローカルから /tmp/dev-timer-stage に rsync → サーバー側で sudo rsync で /opt/dev-timer/ へ移動 + chown deploy
    • サーバー上で npm ci + npm run build(better-sqlite3 のネイティブビルドも完走)
    • /opt/dev-timer/.env 配置(PORT=3001 / DB_PATH=/var/lib/dev-timer/dev-timer.db / NTFY_TOPIC / NTFY_SERVER、600 / deploy 所有)
    • dev-timer.service/etc/systemd/system/ に install + enable + start(active running、メモリ 57M)
    • Nginx vhost を /etc/nginx/sites-available/ に配置 + sites-enabled にリンク + reload
    • certbot --nginx -d dev-timer.ikapps.com で Let's Encrypt SSL 発行(自動更新スケジュール込み)
    • deploy ユーザーの crontab に * * * * * curl -sS -X POST http://127.0.0.1:3001/api/check-due > /dev/null 2>&1 を追加
  • e2e 確認: ローカル CLI(DEV_TIMER_API=https://dev-timer.ikapps.com)から add --in 2m で予約 → 約 2 分後に iPhone に通知到達
  • 後処理:
    • ローカル .env / .env.exampleDEV_TIMER_API を本番 URL(https://dev-timer.ikapps.com)に変更
    • certbot 適用後の Nginx vhost を deploy/nginx/dev-timer.ikapps.com に反映(真実のコピーを git 管理)

決めたこと(DECISIONS.md にも転記)

  • ntfy トピックは node -e 'randomBytes(9).toString("base64url")' ベースで生成し、dev-timer-ichirokisanuki-<12文字> 形式に統一
  • サーバー側設定ファイル(systemd unit / Nginx vhost)は ローカル deploy/ 配下に真実のコピーを置いて git 管理
  • ローカル CLI のデフォルト DEV_TIMER_API は本番 URL にする(ローカル開発で API を起動したいときだけ env 上書き)

詰まったこと / 気づき

  • deploy ユーザーは ssh 鍵が違うので直接ログインできず、ubuntu 経由 → /tmp に rsync → sudo rsync で /opt に移動 + chown という二段構えで配置した
  • 初回 npm ci で deprecated 警告(prebuild-install)出たが better-sqlite3 11.x は prebuilt binary を引いて動作 OK
  • 127.0.0.1 直叩きの Host: dev-timer.ikapps.com で 404(default_server に流れた)が出たが、外部から FQDN で叩くと正しくルーティング → 実害なし

次回やること

  • Phase 3: Web UI(Astro)。dev-tracker のスタックを流用
  • 必要に応じて GitHub Actions で自動デプロイ(現状は手動 rsync)

18:00 - Phase 1: Backend MVP 実装(Hono + SQLite + ntfy + CLI)

やったこと

  • TypeScript + ESM、Node.js 20+ 前提のスケルトンを作成
  • src/config.ts: dotenv.env 読み込み(PORT / DB_PATH / NTFY_TOPIC / NTFY_SERVER)
  • src/db.ts: better-sqlite3 で timers テーブル + WAL モード、status は pending / fired / done / cancelled の 4 値
  • src/notifier.ts: ntfy.sh への POST(Title ヘッダは ASCII のみ許容なので非ASCII は RFC2047 で base64 エンコード)
  • src/server.ts: Hono の REST API(POST /api/timers / GET /api/timers / PATCH /api/timers/:id / DELETE /api/timers/:id / POST /api/check-due)、zod でバリデーション
  • src/index.ts: @hono/node-server で port listen
  • bin/dev-timer.ts: CLI(add --in 30m|2h|3d|1w / --at ISO8601 / --project / list [--status] / done / cancel / rm / check
  • .env.example.gitignore(SQLite 生成物 *.db / *.db-shm / *.db-wal を追加)整備
  • ローカル動作確認: add → list → check-due(過去日 timer が fired に遷移)→ done → rm まで一通り PASS、tsc --noEmit もエラーなし
  • commit & push(bf01466

気づき

  • noUncheckedIndexedAccess: true を有効にしているので parseDurationm[1]! のような non-null assertion を明示的に書く必要があった
  • Phase 2 の rsync 戦略を見越して dist/ は git 管理外、deploy/ 配下に systemd unit / Nginx vhost のひな形を置く方針も同時に固めた

次回やること

  • Phase 2: Lightsail デプロイ

17:45 - プロジェクト発足・設計合意・初期化

やったこと

  • dev-tracker のセッション中に follow-up リマインダの実装を相談、別プロジェクトとして分離する判断
  • 設計合意:
    • 通知: ntfy.sh
    • データ: Lightsail (SQLite)
    • インターフェース: CLI + Web UI(Astro)
    • 認証: なし(タイマー本文に詳細コンテキストを書かない方針)
    • ポート: 3001
  • ~/cdev/dev-timer 初期化(new-project-setup skill 経由)
    • git init / .gitignore / README / CLAUDE.md / .devnotes/ 4ファイル
    • GitHub private リポ作成
    • dev-tracked topic 付与
  • 引き継ぎ用に CLAUDE.md / WIP.md / DECISIONS.md / ROADMAP.md を充実

次回やること

  • Phase 1(Backend MVP)から着手: package.json と TypeScript セットアップ → Hono サーバー → SQLite スキーマ → ntfy 連携 → CLI

詰まったこと / 気づき

  • なし(着手前)

最近のコミット

README

dev-timer

概要

(記入予定)

セットアップ

(記入予定)

使い方

(記入予定)