Synaplan
开源 AI 软件平台,面向企业:自托管 AI 代理,符合 GDPR 的聊天机器人小部件,RAG 知识库,WhatsApp 与 Outlook AI,MCP,多模型路由。支持 Docker、Kubernetes 或在 Germ 托管。
AI 翻译
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Google Apps Script(GAS)+スプレッドシートで動いていた N-PAS 勤怠管理システムを、 Cloudflare Workers + Cloudflare D1 に移したものです。 スタッフ用の画面は、今の見た目と操作をそのまま引き継いでいます(変わったのはログイン画面の PIN 入力欄の追加だけです)。
現在の状態
- 本番環境を Vercel に公開済み:https://npas-attendance.vercel.app/ (本物のデータ用。テスト用アカウントは無効)
- テスト環境(試用版):https://npas-attendance-trial.vercel.app/ (テスト用データのみ)
- どちらも開くには Vercel へのログインが必要(後述)。ログインは「名前を選ぶだけ」(AUTH_MODE=name)。
- 本番用の構成(Cloudflare Workers + D1)のコードも完成済み。Cloudflare への公開は、あなたのアカウントでの操作が必要です(4章)。
https://npas-attendance.vercel.app/admin.html を開き、「初期設定」で最初の管理者を作る(合言葉は別途お伝えしたもの)。毎日 3:00(日本時間)にデータベース全体を自動保存します(30日分)。管理画面から一覧・ダウンロード・復元ができます。
| 画面 | URL |
|---|---|
| スタッフ画面 | https://npas-attendance-trial.vercel.app/ |
| 管理画面 | https://npas-attendance-trial.vercel.app/admin.html |
| ボタン1つの動作確認 | https://npas-attendance-trial.vercel.app/selftest.html |
testadmin・test-password-1234)。platform/vercel/handler.mjs)。書き込みは「誰も更新していないときだけ保存」する条件付き書き込みで、同時に更新された場合は最新を読み直してやり直すため、取りこぼしや二重登録は起きません。main に送ると、自動で公開し直されます。設定 AUTH_MODE で切り替えます。name=名前を選ぶだけ(旧システムと同じ。現在の設定)、pin=名前+PIN。
名前を選ぶだけの方式は本人確認ではありません(誰でも他人の名前で打刻・申請できます)。操作はすべて操作履歴に残り、記録の変更・削除は管理者の承認が必要です。
スタッフ画面の「打刻修正」で記録を選び、「修正理由」に理由を書いて「この記録の削除を申請する」。管理者が「修正・削除申請」で承認すると削除され、削除前の内容は操作履歴に残ります。
| 部分 | 使う技術 | 役割 |
|---|---|---|
| 画面 | Workers の静的アセット(public/) | HTML・画像・JavaScript を世界中の拠点から配信 |
| サーバー処理 | Cloudflare Workers(src/) | 勤怠の保存・取得・認証などの API |
| データベース | Cloudflare D1(SQLite) | スタッフ・勤怠・申請・履歴を保存 |
選んだ理由(指示書の選定基準に沿って比較しました)
検討して選ばなかったもの:スプレッドシートをそのまま DB として使い続ける(指示書で禁止・遅さの原因)、Supabase など別の DB サービス(Workers と組み合わせると設定と費用が増える)、Durable Objects(D1 で足りるため)。
public/index.html)は、旧 Index.html を機械的に変換して作りました(変換スクリプト:tools/convert-original-html.py)。| 旧(GAS の関数) | 新(API) | 主な改善 |
|---|---|---|
listStaff | GET /api/staff | — |
| (なし) | POST /api/login | PIN による本人確認を追加 |
clockStatus | GET /api/clock/status | — |
clockIn | POST /api/clock/in | 二重出勤を DB の一意制約で防止 |
clockOut | POST /api/clock/out | 保存と出勤解除を1つのトランザクションで。再送しても二重登録しない |
saveAttendance | POST /api/attendance | 再送しても二重登録しない |
getHistory | GET /api/history | 全件読み込み → この人・この月だけをインデックスで取得 |
npasSaveBulkAttendance | POST /api/bulk | 重複チェックと登録を1つのSQL文で(同時実行でも重複しない) |
npasSubmitCorrection | POST /api/corrections | 「申請中は1記録1件」を DB が保証 |
npasUpdateSummaryNow など | (不要) | 集計表を保存せず、表示のたびに元データから計算(下記) |
| 月別集計シート・個人別シート | 管理画面 | 管理者用の新しい画面(もともと画面がなかったため新規) |
| 打刻修正の承認(シートを手で直す) | 管理画面の「承認」ボタン | 変更前・変更後を操作履歴に自動記録 |
月別集計の考え方:旧版は「保存 → 別の通信で集計シートを更新」でした。新版は集計表を持たず、表示するときに元の勤怠記録から計算します。インデックスのおかげで1か月・100人分でも数ミリ秒です。これで「保存は成功したが集計が古い」「集計の失敗で保存が失敗扱いになる」ということが起きなくなりました。画面側の集計更新の呼び出しは、通信せずに成功を返すようにしています(見た目と動きはそのまま)。
勤怠計算のルールは旧版と完全に同じです(日付・時刻の形式、翌日退勤、休憩 0〜1439分、小数第2位の丸め、手当の上限100万円、業務内容200文字、24時間以上の打刻は管理者へ連絡)。エラーメッセージも同じ文言です。 ただし 業務内容は必須 です(2026-10-29 変更)。スタッフの勤怠申請・一括申請・修正申請・退勤打刻で空欄だと「業務内容を入力してください」と表示して保存しません。管理者による記録の追加・修正と、旧データの移行では空欄も受け付けます。
Cloudflare のアカウントがなくても、パソコン上で動かして確認できます(ローカル模擬環境。本物の Cloudflare ではありません)。
npm start
テスト用アカウント(ローカルとテスト環境だけ。本番では使えません)
| 画面 | 入力するもの |
|---|---|
| スタッフ画面 | 学年「1年生」→ 名前「テスト 一郎」→ PIN 1234 |
| 管理画面 | ログインID testadmin / パスワード test-password-1234 |
データは .local/dev.sqlite に保存されます。最初からやり直したいときは、このファイルを削除してから npm start してください。止めるときはターミナルで Ctrl + C を押します。
ここからは あなたの操作が必要 です。費用が発生する操作はありません(無料プランの範囲)。 まず テスト環境(staging) に公開して確認し、問題がなければ 本番環境(production) に公開します。
npm install
npx wrangler login
npx wrangler d1 create npas-attendance-staging
表示された database_id = "xxxxxxxx-...." の値を、wrangler.toml の REPLACE_WITH_STAGING_DATABASE_ID と置き換えて保存します。npm run db:migrate:staging
node -e "console.log(require('crypto').randomBytes(24).toString('base64url'))"
1つ目を PIN 用、2つ目を初期設定用の合言葉として設定します(Enter a secret value: と聞かれたら貼り付けて Enter)。
npx wrangler secret put PIN_PEPPER --env staging
npx wrangler secret put ADMIN_SETUP_TOKEN --env staging
この2つの値は、パスワード管理アプリなど安全な場所に必ず保管してください。 PIN_PEPPER をなくすと、全員の PIN とパスワードの再発行が必要になります。npm run deploy:staging
初めての公開では、workers.dev のサブドメイン(URLの一部になる名前)を決めるよう聞かれることがあります。好きな英数字を入力してください。
最後に表示される https://npas-attendance-staging.<あなたのサブドメイン>.workers.dev が テスト用URL です。テスト用URL/selftest.html を開き、「▶ テストを実行」を押します。全部 ✅ になれば、本物の Cloudflare 上で動いています。 表示される応答時間(ms)が実際の速さです。4-2 と同じことを、名前を変えて行います。
npx wrangler d1 create npas-attendance
(表示された ID を wrangler.toml の REPLACE_WITH_PRODUCTION_DATABASE_ID に貼る)
npm run db:migrate:production
npx wrangler secret put PIN_PEPPER --env production
npx wrangler secret put ADMIN_SETUP_TOKEN --env production
npm run deploy:production
本番用の
PIN_PEPPERとADMIN_SETUP_TOKENは、テスト環境とは別の値にしてください。 本番環境では、テスト用アカウントとテスト画面の機能は自動的に無効になります。
本番URL/admin.html を開くと「初期設定:最初の管理者を作成」が表示されます。ADMIN_SETUP_TOKEN に設定した合言葉、ログインID、名前、パスワード(10文字以上)を入力して「管理者を作成」。npm test (自動テスト)
npm run deploy:staging (テスト環境で確認)
npm run deploy:production (本番へ)
元のスプレッドシートは一切変更しません(読み取るだけです)。同じ ID のデータは上書きしないので、何度実行しても重複しません。
コード.gs の末尾に tools/gas-export.gs の内容を貼り付けて保存し、関数 npasExportForMigration を実行します。
npas-export-日時.json)がドライブに作られます。実行ログに URL と件数が表示されます。移行のルール
移行の後:スタッフは PIN が未設定なのでログインできません。管理画面の「スタッフ・PIN」→「PIN未設定の人にまとめて発行」で PIN を発行し、本人に配ってください(6章)。
| やりたいこと | 操作 |
|---|---|
| スタッフに PIN を配る | 管理画面「スタッフ・PIN」→「PIN発行」(表示は1回だけ。本人に個別に伝える) |
| PIN を忘れた | 同じ画面で「PIN再発行」 |
| PIN を5回間違えてロックされた | 15分待つか、「ロック解除」 |
| 退勤を押し忘れて24時間を超えた | 「スタッフ・PIN」→「出勤を取消」→「勤怠記録」から正しい記録を追加 |
| 修正申請を処理する | 「修正申請」→ 変更前・変更後を確認 →「承認して記録を変更」または「却下」 |
| 月別集計を見る・保存する | 「月別集計」→ 月を選ぶ →「CSVで保存」 |
| 個人別の勤怠申請書を印刷する | 「個人別申請書」→ 人と月を選ぶ →「印刷・PDF保存」 |
| スタッフを追加・卒業 | 「スタッフ・PIN」→「追加」/「編集」で有効をオフ |
| 誰が何をしたか確認する | 「操作履歴」 |
| エラーの詳細を見る | Cloudflare の管理画面 → Workers & Pages → npas-attendance → Logs |
旧 npasResetTestHistory に相当します。スタッフ名簿と PIN は残し、勤怠・申請・履歴を消します。
npm run backup:production
npx wrangler d1 execute npas-attendance --remote --env production --file scripts/reset-test-history.sql
1行目のバックアップが成功したことを確認してから、2行目を実行してください。
バックアップは3種類あります。
| 方法 | 内容 | 使い方 |
|---|---|---|
| Time Travel(自動) | D1 が自動で記録。過去の任意の時点に戻せる | 無料プラン7日・有料プラン30日 |
| SQL バックアップ | データベース全体をファイルに保存 | npm run backup:production → backups/ に保存 |
| 管理画面のバックアップ | 旧シートと同じ列名の JSON(PINは含まない) | 「データ移行・バックアップ」→「バックアップをダウンロード」 |
おすすめ:月に1回、SQL バックアップか管理画面のバックアップを保存してください。
npx wrangler d1 time-travel info npas-attendance --env production
npx wrangler d1 time-travel restore npas-attendance --env production --timestamp=2026-11-01T09:00:00+09:00
⚠️ 指定した時点より後の変更はすべて消えます。先に npm run backup:production で今の状態を保存してください。npx wrangler d1 execute <新しいDB名> --remote --file backups/npas-production-日時.sql を実行し、wrangler.toml の ID を新しい DB に変えて公開します。旧システムは名前を選ぶだけで、本人確認がありませんでした。 新システムでは、名前の選択は本人確認として扱いません。
| 対策 | 内容 |
|---|---|
| 本人確認 | 名前 + PIN(4〜6桁)。サーバーは PIN をハッシュ(PBKDF2 + 秘密の値)で保存し、平文は持たない |
| 総当たり対策 | PIN を5回続けて間違えると15分ロック |
| 他人のデータを見られない | サーバーはリクエストのスタッフIDを信用せず、ログイン時の Cookie で本人を特定。他人の ID を指定すると拒否(403) |
| 権限の分離 | 管理者は別アカウント(ID+10文字以上のパスワード)。スタッフの Cookie では管理者 API は使えない |
| セッション | HttpOnly・Secure・SameSite=Strict の Cookie。スタッフ12時間・管理者8時間で失効 |
| CSRF・CORS | 他のサイトからの書き込みは拒否。CORS ヘッダーは返さない(同じURLの画面からのみ利用可) |
| SQL インジェクション | すべてプレースホルダ(?)で値を渡す |
| 入力検証 | 画面だけでなくサーバー側で全項目を検証 |
| 画面の保護 | Content-Security-Policy で外部スクリプトを禁止、他サイトへの埋め込み禁止 |
| 秘密の値 | PIN_PEPPER・ADMIN_SETUP_TOKEN は Cloudflare の secret に保存(コードに書かない) |
| ログ | パス・結果・時間だけを記録。氏名・PIN・勤怠内容はログに出さない。想定外のエラーの詳細は利用者に見せない |
| 操作履歴 | 申請・打刻・承認・修正・削除・PIN発行などをすべて記録(PIN そのものは記録しない) |
| DB の公開 | D1 はインターネットから直接アクセスできない(Workers 経由のみ) |
| テスト機能 | テスト用アカウントとリセット機能は、本番環境では自動的に無効 |
npm run bench で測定。スタッフ100名、勤怠データ 1,000件/2万件/10万件で比較しました(中央値)。
| 処理 | 1,000件 | 20,000件 | 100,000件 |
|---|---|---|---|
| 月別履歴(1人・1か月) | 0.56 ms | 0.60 ms | 0.55 ms |
| 通常の勤怠申請(保存) | 0.89 ms | 0.95 ms | 0.84 ms |
| 出勤→退勤(2回の合計) | 1.37 ms | 1.41 ms | 1.84 ms |
| 一括勤怠申請(100名) | 2.19 ms | 2.54 ms | 3.24 ms |
| 月別集計(全員・1か月) | 1.09 ms | 1.53 ms | 3.37 ms |
| 個人別勤怠申請書 | 0.64 ms | 0.82 ms | 0.65 ms |
| ログイン(PIN照合) | 4.56 ms | 6.12 ms | 4.31 ms |
データが100倍に増えても、ほぼ同じ速さです(インデックスで必要な行だけを読むため)。
画面の読み込み量:旧版は毎回 837KB。新版は初回 645KB、2回目以降は約45KB(画像をキャッシュ)。
selftest.html に各処理の応答時間が表示されます(ネットワーク時間込み)。npm test (API・計算・移行のテスト 62件)
npm run test:ui (ブラウザで画面を操作するテスト 18件。Playwright が必要)
すべて ローカル模擬環境(Node.js 標準の SQLite で D1 を再現)で実行し、成功しています。結果の詳細は docs/TEST_RESULTS.md にあります。
| 分類 | 主なテスト内容 |
|---|---|
| 勤怠計算 | 日付(うるう年・存在しない日)、時刻、翌日退勤、0分以下の拒否、休憩の範囲、丸め、手当、業務内容200文字、日本時間への変換 |
| スタッフ操作 | 一覧、ログイン、PIN ロック、申請、出勤・退勤、二重出勤の拒否(同時5回)、未出勤での退勤拒否、24時間の境界、保存失敗時に出勤状態が残ること、再送で二重登録しないこと |
| 一括申請 | 重複スキップ、件数と理由、100名、同時に2つの一括申請、監査記録 |
| 修正申請 | 申請だけでは変わらない、承認・却下、申請後に記録が変わっていたら承認しない、2人同時承認 |
| 集計 | 月別集計(実績なしも0表示・並び順)、CSV、個人別申請書(月の日数・複数勤務の合算) |
| 権限 | 未ログイン、他人のID指定、管理者 API、CSRF、本番でのテスト機能無効 |
| データ移行 | 確認のみ、取り込み、照合、再実行で重複しない、上書きしない、日付・時刻・数値の変換、CSV、バックアップからの復元 |
| 画面 | 旧版との画像比較(5画面で差分0)、ブラウザでのログイン〜承認〜移行の一連の操作 |
模擬環境と本物の違い:ネットワークの遅延、D1 の細かい同時実行の挙動、Cloudflare の CPU 時間制限は再現していません。テスト環境に公開後、selftest.html で本物の環境でも確認してください。
BULK_POLICY で一括申請を止めることができます(代表者だけに許す機能は未実装)。public/ 画面(そのまま配信される)
index.html スタッフ画面(旧 Index.html から変換)
js/app.js スタッフ画面の動き(旧 Index.html のスクリプト)
js/api.js 旧 google.script.run の代わりに API を呼ぶ部分
admin.html, js/admin.js, css/admin.css 管理画面
selftest.html, js/selftest.js ボタン1つの動作確認
js/migrate-client.js データ移行・バックアップの画面側の処理
img/ キャラクター画像(旧 HTML から取り出したもの)
_headers セキュリティ・キャッシュの設定
src/ サーバー処理(Cloudflare Workers)
index.js 入口(URL ごとの振り分け)
lib/ 共通処理(入力検証・認証・暗号・時刻など)
routes/ 機能ごとの処理
migrations/ データベースの表の定義(再実行しても安全)
scripts/ 管理用の SQL(本番開始前のテストデータ削除)
tools/ 開発・運用の道具
dev-server.mjs ローカル確認用サーバー(npm start)
d1-node.mjs ローカル用の D1 の代わり
gas-export.gs 旧 GAS 用の書き出し関数(移行用)
backup.mjs SQL バックアップ
bench.mjs 速度測定
convert-original-html.py 旧 Index.html → 新画面への変換(記録)
test/ 自動テスト
docs/ 設計書・テスト結果
wrangler.toml Cloudflare の設定
設計の詳細(データベースの表、API の一覧)は docs/DESIGN.md を見てください。