尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Invoices, payments and reminders for small businesses: Next.js, Microsoft Entra ID/Graph, Stripe, Twilio, AI
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Invoices, payments and reminders for small businesses: a portfolio project by Jason McElvain, built in public one weekend at a time.
Live demo: https://macplussupport-office.vercel.app (sign in with any Microsoft account; you get a private sandbox business with fictional clients)
| Sign in | New invoice | Invoice |
|---|---|---|
| Weekend | Feature | Status |
|---|---|---|
| 1 | Microsoft sign-in (Entra ID, MSAL, PKCE), per-user demo business, race-safe invoice numbers | ✅ |
| 2 | PDF invoices, searchable history, send through Outlook with Microsoft Graph | ⏳ |
| 3 | Stripe "Pay this invoice", webhooks, care-plan subscriptions | ⏳ |
| 4 | Twilio SMS / WhatsApp reminders with delivery status | ⏳ |
| 5 | AI assistant that answers only from the business's own documents | ⏳ |
| 6 | Hardening: rate limits, tests in CI, demo reset | ⏳ |
| 7 | Google Wallet membership pass | ⏳ |
| 8 | Shopify app on a dev store | ⏳ |
Invoice numbers look like INV-2026-0042, restart each year, and must never repeat or skip, even when two people save at the same moment.
The common approach, select max(seq) + 1, fails under load: two requests read the same max and both try to use it. This repo includes a test that fires 50 invoice creations at once:
$ npm run test:race -- 50 --naive
50 concurrent creates (NAIVE max+1) in 1348 ms (pool size 5)
succeeded: 16
failed: 34 ← duplicate key errors
FAIL
$ npm run test:race -- 300
300 concurrent creates (locked counter) in 24805 ms (pool size 5)
succeeded: 300
duplicates: 0
gaps: 0
range: INV-2026-0001 … INV-2026-0300
PASS: every number used exactly once
Real output from a run against Neon Postgres (AWS us-east-1, pooled connection): docs/race-test-output.txt. The locked run is slower because requests deliberately wait their turn for the counter row; correctness, not speed, is the point.
The fix is one statement (src/lib/invoices.ts): an upsert on a per-business, per-year counter row, run in the same transaction as the invoice insert. Postgres locks that row until the transaction commits, so requests take turns, and a failed insert rolls the counter back, so there are no gaps. A unique (business_id, number) constraint backs it up.
@azure/msal-node (no wrapper library), plus a state check against CSRF.User.Read; Mail.Send is requested later through incremental consent, the first time a user sends an invoice./common authority).business_id in the signed session cookie, never by an id from a form or URL. Another business's invoice id returns a plain 404.Next.js 16 (App Router, server actions) · TypeScript · Postgres on Neon · pg · @azure/msal-node · iron-session · zod · Tailwind · Vercel
Prerequisites: Node 20.6+, a free Neon database, and a Microsoft Entra app registration (steps below).
git clone https://github.com/MacSupportPlus/macplussupport-office.git
cd macplussupport-office
npm install
cp .env.example .env.local # then fill in the values
npm run db:migrate
npm run test:race # optional: prove the numbering
npm run dev # http://localhost:3000
http://localhost:3000/api/auth/callback. Add your Vercel URL + /api/auth/callback later.AZURE_CLIENT_SECRET.User.Read (already there by default). No admin consent needed.Personal Microsoft account with no directory? Signing in to Entra fails with "Selected user account does not exist in tenant 'Microsoft Services'". Personal accounts need their own tenant to register apps: create a free Azure account with the same login, which gives you a "Default Directory", then register the app there.
Hosted on Vercel and connected to this repo, so every push to main deploys to production.
.env.example under Vercel → Project → Settings → Environment Variables, with APP_URL set to the production URL. Generate fresh SESSION_SECRET and TOKEN_CACHE_KEY values for production.https://<your-project>.vercel.app/api/auth/callback.APP_URL (for example localhost, not 127.0.0.1), or the sign-in cookie won't match and you'll see "Sign-in expired".INV-<year>-0001 and 0002.0001.npm run test:race -- 50 --naive failing and npm run test:race -- 300 passing..env.local is git-ignored; .env.example documents every variable.MIT