Synaplan
開源 AI 平台,供企業使用:自行託管的 AI 代理、符合 GDPR 的聊天機器人小工具、RAG 知識庫、WhatsApp 與 Outlook AI、MCP、多模型路由。Docker、Kubernetes 或託管於 Germ
AI 翻譯
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證real
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Committee recruitment portal for the Sunway Entrepreneurs Club. Public position board, five-question application step, and an admin dashboard for reviewers.
Runs on Vercel. No build step, no dependencies to install.
Create the repository under a club-owned GitHub account or organisation, not a personal one. A repo on a personal account leaves when that person graduates.
git init
git add .
git commit -m "SEC recruitment portal"
git remote add origin https://github.com/<club-account>/sec-recruitment-portal.git
git push -u origin main
vercel.com → Add New → Project → import the repo → Deploy. Framework preset: Other. No build command, no output directory.
The first deploy will show an error on the page. That's expected — the database isn't attached yet.
Vercel dashboard → your project → Storage → Create Database → Upstash for Redis → create → Connect to Project.
Vercel injects KV_REST_API_URL and KV_REST_API_TOKEN automatically. Nothing to copy or paste.
The free tier is far more than this needs.
Project → Settings → Environment Variables. Add these for all environments:
| Name | Value |
|---|---|
SESSION_SECRET | A long random string. Generate with openssl rand -base64 32 |
ADMIN_USERS | amadeus:somepassword,angelene:otherpassword |
ADMIN_USERS is a comma-separated list of username:password. Give every reviewer their own
login with their own password. Decisions and reviewer notes are recorded against these
usernames — a shared login makes attribution meaningless and means you can't revoke one person's
access when they leave the committee without locking everyone out.
Optional:
| Name | Value |
|---|---|
GEMINI_API_KEY | Your Gemini key. Can also be set later from the admin Settings tab. |
GEMINI_MODEL | Defaults to gemini-2.0-flash. Change if you want a different model. |
Deployments → the latest one → Redeploy. Environment variables only take effect on a new deploy.
Open the site → Committee sign in at the bottom → your ADMIN_USERS credentials.
On first load the database is seeded with SEC's current structure and the five open positions. From the admin panel you can then:
The Learning Portal lives on the same site. Open it from the footer link, or go straight to /#learning.
| Layer | Who | Where |
|---|---|---|
| Public recruitment page | Everyone | A short "after you're selected" section, text set from Admin |
| Learning Portal | Committee members | Footer → Learning Portal |
| SEC Admin → Learning portal tab | Admins | Edit that section, every level and module, assessments, and see completion |
No extra environment variable is needed. Committee members create their own account on the Learning Portal sign-in screen (Create account), using a join code that an admin sets in Admin → Learning portal → Committee sign-up. Share the code in the committee chat; change or clear it when people leave. Members can only use the Learning Portal. They never see applicants or the admin area, and an admin can remove a member at any time (their login stops working immediately). Passwords are stored as salted hashes. Anyone in ADMIN_USERS is an admin and can also use the Learning Portal.
Where the Learning Portal data lives. Accounts (passwords hashed), quiz progress, the module content you edit and the join code are all saved in the same Upstash Redis database as the applications, never in the browser or in the sign-in cookie. Signing out only deletes the cookie, so everything is still there when a member signs back in, from any device. Writes are safe when many people submit at once, and each saved version of members, progress and content is kept in a history list (the last 50, for 30 days) in case anything needs recovering. Only npm run dev uses temporary in-memory data that resets when it restarts.
Courses. The Learning Portal is a flat list of courses: no levels and no sub-modules. In Admin → Learning portal press Add first course, type the course title, choose one file (PDF, Word .docx, .txt or .md) and press Generate course. The AI writes exactly 15 flashcards and 10 multiple-choice questions from that file, and you can then edit, add or delete any of them by hand. Start empty makes a course you write yourself. A course made from a file can later be re-made from a different file.
The AI uses either a DeepSeek key (starts with sk-) or a Google Gemini key: paste it into the "AI for flashcards and questions" box (or set DEEPSEEK_API_KEY / GEMINI_API_KEY in Vercel). The portal works out which one it is. Files are read in your browser and only their text goes to that AI company (DeepSeek's servers are in China, so do not upload confidential files). The model is chosen automatically, so retired model names do not break it; DEEPSEEK_MODEL / GEMINI_MODEL in Vercel can pin one. Every generated card and question must carry a word-for-word quote that really appears in the file, and anything that does not is discarded on the server. Results always arrive as drafts, members only see published courses, and publishing an AI-made course asks you to confirm you have read it. Scanned PDFs (pictures of pages) have no text and cannot be used. Locally, npm run dev uses a mock AI so you can try the flow without a key.
Learning page. The "Selected? Training starts straight away" section now lives on its own public page, reached from Learning in the top bar and in the Menu. Switch it off in Admin and both links disappear.
Run npm run dev to try everything locally; it uses in-memory data, the test login admin/admin and the join code dev-join-code.
| Route | Method | Access | Purpose |
|---|---|---|---|
/api/state | GET | public | Position board. Draft questions and admin fields are stripped. |
/api/state | PUT | admin | Save positions and settings |
/api/session | POST / DELETE / GET | — | Sign in, sign out, who am I |
/api/applications | POST | public | Submit an application (validated server-side) |
/api/applications | GET / PATCH / DELETE | admin | Review, decide, remove |
/api/generate | POST | admin | Ask Gemini for four draft questions |
/api/export | GET | admin | CSV of applications and answers |
/api/backup | GET | admin | Full JSON backup of roles, settings and applications, including archived records |
Storage keys: sec:state, sec:applications, sec:gemini_key. The first two
remain the source of truth. Before every successful write, the previous value is
saved atomically in sec:state:history or sec:applications:history (last 50
versions, expiring 30 days after the last write). No data migration is required.
The Gemini key never reaches the browser. It lives in an environment variable or in server-side
storage. The API returns only a keySet boolean. A key in front-end code is readable by every
visitor.
Questions are frozen when published. Generate produces a draft that nobody can see. A human edits it and publishes. A position with no published questions shows Opening soon with no Apply button — never a button that leads nowhere. Editing questions that already have applications raises a warning, because applicants can't be compared fairly if they answered different things.
The fifth question is always the commitment question and is not generated. Availability is the best single predictor of whether someone lasts a term, so it isn't left to a model.
Answers are immutable. Nothing in the admin UI or the API can edit a submitted answer or its timestamp. Decisions and notes are attributed to the reviewer's username. That's what makes the dashboard evidence rather than an assertion.
Questions are snapshotted onto each application. If you later change a position's questions, old applications still show what that person was actually asked.
Server-side validation. iMail domain, five answers, the 150-word cap, duplicate applications, and applying to a closed position are all enforced in the API, not just the browser.
Data retention. Archiving in the dashboard is not deletion. Appoint a retention owner, agree the deletion date for each recruitment cycle, and remove expired records and their backup copies from the database in line with the club's approved policy. Do not let student emails accumulate across committees.
Archiving is recoverable. The dashboard's Archive action keeps the application and its answers in the same record. Open Archived in Applicants to restore it. Archived records still contain personal data and must be included in retention reviews.
Backups. Settings → Data & recovery downloads a full JSON backup. The CSV is
for review, not full recovery: it omits role settings and metadata. Download a
full backup before major changes and keep it only in club-controlled encrypted
storage. The backup contains private applicant answers. The admin-only endpoint
/api/backup?history=state&index=0 (or history=applications) downloads the
most recent previous revision; indices 0–49 are available while retained.
Neither backup endpoint exports the Gemini key or login credentials.
Matching is manual. A QR can't carry the applicant's name and iMail to the Google Form, so portal answers and Form responses have to be matched by hand on whatever email each person typed twice. Mark each one with Mark form received in the dashboard. Putting the position as a required dropdown on the Form makes reconciliation much easier.
Concurrency. State is still a single document, but saves now require the revision received when the editor loaded it. A stale tab is rejected instead of overwriting someone else's roles or settings. Download its unsaved draft before reloading. Applicant changes use an atomic compare-and-swap with retries, so simultaneous submissions or changes to different applications are not dropped. The UI checks that a reviewer note, decision or form flag has not changed since it was opened. Do not use multiple tabs to edit the same field at once.
If the portal goes down two days before the deadline, recruitment must still be able to run on the Google Form alone. That fallback is the reason the QR exists independently of this app.
The portal is worthless to SEC if it stops working when its builder graduates.
SESSION_SECRET and ADMIN_USERS recorded in the club's password manager — not in Drive,
not in WhatsApp, not in a documentADMIN_USERS updated at every committee change: add the new reviewers, remove the old onesNot code problems, but they should be closed before launch:
Sunway Entrepreneurs Club Assets.docx in the club Drive contains
plaintext passwords for the club Gmail, Instagram, Facebook and Linktree. Rotate them, move them
to a password manager, and delete the file.