Synaplan
開源 AI 平台,供企業使用:自行託管的 AI 代理、符合 GDPR 的聊天機器人小工具、RAG 知識庫、WhatsApp 與 Outlook AI、MCP、多模型路由。Docker、Kubernetes 或託管於 Germ
AI 翻譯
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Compliance and practice-management platform for an accounting firm: client portal, staff dashboard, service master, smart forms, ledger, payroll and corporate secretary modules. Next.js 14 (App Router) on Vercel, PostgreSQL behind it.
| Part | Where |
|---|---|
| The app | Vercel project, production on the main branch |
| The database | Neon (PostgreSQL), created from the Vercel project's Storage tab. It holds everything: firms, users, clients, forms, ledgers, payroll and every uploaded file. |
| Login sessions | Signed with JWT_SECRET from the project's environment variables |
Nothing else is needed. There is no Supabase, Turso or SQLite in the live setup, and the app refuses to connect to a Supabase address even if one is left in the environment variables (it logs which variable to delete). Nothing is stored on the web server's disk: Vercel's disk is read-only.
DATABASE_URL, DATABASE_URL_UNPOOLED, or the same names with a prefix
you choose). If it says a DATABASE_URL already exists, delete the old
one under Settings → Environment Variables first. The app only ever
uses Neon addresses (pooled first) and ignores any other cloud database.JWT_SECRET with a long random value (40+ characters), then redeploy.
Without it, sessions are signed with a built-in development value that
everyone who can read the source knows.Then open /login. The first login on a fresh database takes a few seconds:
the app creates every table (from src/db/schema.sql and the migrations in
src/db/migrations) and seeds the demo accounts, then signs you in.
The seed creates demo accounts; the login page lists them. Every seeded
account uses the password shown there. Change them (or remove the demo block
from src/app/login/page.tsx) before real client data goes in.
psql: copy DATABASE_URL from Vercel →
Settings → Environment Variables and run psql "<that value>". Treat that
value like a password.tax_intake_*: answers, their full change history, records, documents, review items, frozen submissions), accounting ledgers, payroll, corporate secretary
(cs_*), settings, and schema_migrations (which migration files have
been applied).pg_dump "<DATABASE_URL>" > backup.sql) before relying on it.src/db/seed-reference.ts, data in src/db/reference/). Sample content that earlier one-off
scripts created (sample corporations, sample ledger entries, sample forms) is demo data and is not
created. Real data from a previous database is brought over with Platform → Import data (below).document_blobs table, next
to the row that describes them, and are served by /api/documents/<id>/download after a permission
check. The limit is 4 MB per file because Vercel rejects larger request bodies.Signed in as a platform admin, open Platform → Import data. Paste the old database's address (for a
Supabase project use the Session pooler address from Connect, with the database password filled in), press
Check the old database, choose Replace (empties this database first; you must type REPLACE) or Add
(keeps what is here, skips rows that already exist), and press Start copying. The page copies table by table
with a progress bar and finishes with a report comparing the row counts of both sides. After a Replace, sign in
again: the accounts now come from the old database.
How it stays exact (src/lib/db-import.ts): values are copied as text so the database converts them to the
column's own type; only columns that exist on both sides are copied (an old database that lacks a newer column,
or has an extra one, still imports and the page warns about what is not copied); tables go in foreign-key order
and a row whose parent has not arrived yet is parked and placed at the end; counters for new records are moved
past the highest copied id. The address is used for the one request, never stored, logged or sent back. It refuses
to import a database into itself and refuses a Replace when the old database has no active platform admin.
Uploaded files are not in the old database (they were on a disk); only their records are copied.
Tested by tests/verify/import.py and tests/verify/import_browser.cjs.
| Name | Purpose |
|---|---|
DATABASE_URL | The Neon connection string, added by Vercel when Neon is connected. Prefixed variants (DATABASE_POSTGRES_URL, *_URL_UNPOOLED, ...) are tried too, pooled Neon addresses first. A Supabase address is never used. A local PostgreSQL address also works for development and tests. |
JWT_SECRET | Signs login sessions. Required in production. |
CRON_SECRET | Lets Vercel's scheduler run the daily jobs (/api/cron/*). Any long random value; Vercel sends it automatically once it exists. Without it only a signed-in platform admin can run those jobs. |
See .env.example.
cp .env.example .env.local # then point DATABASE_URL at a local Postgres
npm install
npm run dev # http://localhost:3000
The schema installs itself on the first request. To install it ahead of time, or to check a database by hand:
node --env-file=.env.local scripts/db-setup.js
src/db/schema.sql is the base schema.src/db/migrations/NNN_name.sql are applied after it, in file-name order,
each exactly once; applied names are recorded in the schema_migrations
table.src/db/migrations/. It is bundled
automatically (src/db/migrations/index.ts) and runs on the next deploy's
first request, or on demand with POST /api/migrate.src/lib/db.ts waits for the schema before every query, so no route needs
to think about it.npm run build / npm start — production build and servernpm run lintnpm test — Playwright tests (tests/)