Synaplan
开源 AI 软件平台,面向企业:自托管 AI 代理,符合 GDPR 的聊天机器人小部件,RAG 知识库,WhatsApp 与 Outlook AI,MCP,多模型路由。支持 Docker、Kubernetes 或在 Germ 托管。
AI 翻译
尚未收集仓库信息。
尚未检测到更新。GitHub 发布版本后会显示在这里。
Hire interns in India who are already interviewed, tested and scored. Three published plans from free to ₹200/hr, one flat ₹5,000 per-hire fee, billed only on approved hours. Students: get assessed and placed in a paid internship.
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗The platform behind findtern.in: the public site, candidate app, hiring app, placement-officer console and admin console, in one Next.js codebase.
101 screens · 137 API routes · 67 unit-test files · strict TypeScript · one npm run build
What it does · Architecture · Run it locally · Going live · How we keep it safe · Project map
Findtern sits between companies that need interns and students who want real work. Every candidate is screened (coding, aptitude, communication and an AI interview) before an employer ever sees them. That Findtern Score sets the price, so employers pay for proven skill and candidates get paid for the hours they log.
sequenceDiagram
autonumber
actor C as Candidate
participant F as Findtern
actor E as Employer
C->>F: Sign up, build profile, pay activation fee
F->>C: AI interview → Findtern Score (sets the price band)
E->>F: Search, shortlist, book interview (3 slots)
F->>C: Invite email
C->>F: Picks a slot
F-->>C: Meet link + calendar invite
F-->>E: Meet link + calendar invite
E->>F: Sends offer (priced server-side from the band)
C->>F: Accepts (terms + NDA)
E->>F: Pays via Razorpay (upfront or monthly)
F-->>C: You're hired 🎉
loop Every period
C->>F: Timesheet
E->>F: Approve
F-->>C: Stipend transferred
end
flowchart LR
subgraph Browser
M[Marketing site]
I[Candidate app]
EM[Hiring app]
A[Admin console]
C[College console]
end
subgraph Next["Next.js 16 · one Node process"]
P[proxy.ts<br/>admin permissions]
RSC[Server components<br/>read straight from the DB]
API[105 route handlers<br/>requireMutation on every write]
J[Schedulers<br/>reminders and nudges]
end
DB[(Postgres<br/>shared with the legacy app<br/>during cutover)]
FS[[Private file store<br/>signed, expiring links]]
M & I & EM & A & C --> P --> RSC & API
RSC & API & J --> DB
API --> FS
API <--> RZ[Razorpay]
API --> G[Google<br/>sign-in, Calendar, Meet]
API & J --> SMTP[Email]
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 16 App Router, React 19 | Server components fetch data in place; one deployable for site and apps |
| Data | Postgres + Drizzle ORM | Typed queries, plain-SQL migrations we can read before they run |
| Auth | argon2id, HMAC-signed session cookie, CSRF double-submit | No auth vendor, no JWT footguns, works behind any proxy |
| Payments | Razorpay over fetch | Two calls and a signature check; no SDK |
OAuth, Calendar, Meet over fetch | Replaced a 239 MB SDK with about 100 lines | |
| Styling | Tailwind v4 with design tokens in globals.css | One source of colour, type and spacing |
| Motion | GSAP + Motion, reduced-motion aware | Marketing polish without hurting the apps |
Needs Node 20+, a local Postgres, and a local
findternrole.
git clone https://github.com/WynautBhav/nextjs-findtern.git
cd nextjs-findtern
npm ci
cp .env.example .env.local # fill in what you need; defaults are safe
npm run db:seed # fresh local DB with demo data
npm run dev # → http://localhost:3000
Demo logins (password Findtern@2026):
| Role | Sign in at | |
|---|---|---|
| Candidate | [email protected] | /login |
| Employer | [email protected] | /employer/login |
| Admin | [email protected] | /admin/login |
| Placement officer* | [email protected] | /college/login |
* Run npm run db:seed-colleges after npm run db:seed to add the college demo data.
💡 Locally, emails are written to
logs/mail/instead of being sent, scheduled jobs stay off, and Razorpay is off unless you add test keys. You can click anything without notifying a real person.
# one time: let the local role create databases
sudo -u postgres psql -c 'ALTER ROLE findtern CREATEDB'
# .env.local: LIVE_SSH=root@<server> LIVE_DB=findtern LIVE_UPLOADS=/path/to/uploads
npm run db:pull-live # read-only dump of live → local findtern_live
DATABASE_URL=postgresql://findtern:findtern_dev@localhost:5432/findtern_live npm run db:migrate
npm run db:make-admin [email protected] 'YourPassword1'
| Command | What it does |
|---|---|
npm run dev | Dev server with hot reload |
npm run build · npm start | Production build and server |
npm test | Unit tests for pricing, GST, offers, onboarding, college drives and security |
npm run lint | ESLint |
npm run db:seed | Reset the local DB and load demo data (refuses anything but localhost) |
npm run db:migrate | Apply pending migrations; additive, recorded, safe to re-run |
npm run db:verify | Row counts plus real logins through the route handlers |
npm run db:pull-live | Create a local testing copy of production data (never use it as an app database) |
Before every push: npx tsc --noEmit → npm run lint → npm test → npm run test:security → npm run db:verify → npm run build
The new app runs on the Hostinger KVM next to Postgres, behind nginx, on port 3001. The legacy app keeps port 5000 until we're done, so rolling back is a one-line nginx change.
flowchart LR
A[1 · Back up<br/>deploy/backup.sh] --> B[2 · Restore test<br/>row counts compared]
B --> C[3 · Rehearse<br/>migrate the restored copy]
C --> D[4 · Release<br/>deploy/release.sh]
D --> E[5 · Switch nginx<br/>5000 → 3001]
E --> F[6 · Team tests<br/>on findtern.in]
E -. one line .-> R[↩ Rollback to 5000]
| File | Purpose |
|---|---|
deploy/backup.sh | Dumps the live DB and uploads, restores the dump into a scratch DB and compares every table |
deploy/release.sh | Pull staging → install → build → migrate → reload PM2 |
deploy/ecosystem.config.cjs | PM2: one process (schedulers must run exactly once) |
deploy/nginx-findtern.conf | TLS, streaming, 60 MB uploads, rollback line |
.env.example | Every setting, grouped and explained |
DATABASE_URL, PUBLIC_ORIGIN=https://findtern.inSESSION_SECRET, SIGNING_SECRET (32+ random characters each)RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET, RAZORPAY_WEBHOOK_SECRET; webhook → /api/webhooks/razorpaySMTP_* and MAIL_MODE=sendFINDTERN_GSTIN, FINDTERN_BILLING_ADDRESSUPLOAD_ROOT (persistent), LEGACY_UPLOAD_ROOT (old uploads/)SCHEDULERS=on on one server onlyPASSWORD_UPGRADE=off until the legacy app is retired🔐 Accounts
💳 Money
📁 Files
🗃 Data
src/
├── app/
│ ├── (marketing)/ Public site: home, pricing, employers, interns, blog, legal
│ ├── (auth)/ Login, signup, email codes, password reset
│ ├── intern/ Candidate app
│ ├── employer/ Hiring app
│ ├── admin/ Admin console
│ └── api/ Route handlers (each write starts with requireMutation)
├── components/ UI by area: app, employer, admin, home, marketing, payments, ui
├── lib/
│ ├── offer.ts The one way to read or write an offer (legacy money shape)
│ ├── checkout.ts What an employer pays: per-hire, upfront discount, monthly, full-time
│ ├── gst.ts Place of supply and the CGST / SGST / IGST split
│ ├── onboarding.ts Candidate profile ⇄ legacy storage shape
│ ├── payments.ts Idempotent settlement for checkout and webhooks
│ ├── notify.ts In-app notification + email, deduplicated
│ ├── schedulers.ts Interview reminders and nudges
│ ├── admin/ Permissions, CMS collections, CSV, ratings import
│ ├── security/ Sessions, CSRF, passwords, rate limits, signed URLs, file store
│ └── content/ Marketing copy, DB first with built-in fallbacks
└── proxy.ts Admin permission gate for pages and APIs
drizzle/ SQL migrations (read them before they run)
scripts/ Seed, migrate, live-copy tools, legacy password patch
deploy/ Backup, release, PM2 and nginx
master; keep pull requests small.requireMutation({ req, roles }).lib/offer.ts and lib/onboarding.ts; never read the JSON directly.npm run db:generate, read the SQL, commit it.src/app/globals.css.Built for the people who hire and the people who get hired.
© Findtern · findtern.in