Synaplan
기업을 위한 오픈소스 AI 플랫폼
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Turn your help docs into a 24/7 AI support agent — built on a 100% free stack.
Live demo: https://support-ai-murex.vercel.app/ — click Try the live demo to explore a fully seeded workspace with no sign-up, or create your own account, upload a document, and chat with it in the playground.
SupportAI is a full-stack SaaS platform for businesses that want to automate customer support with AI. Upload your help docs (PDF, DOCX, TXT, Markdown, or a URL), and SupportAI creates a chat assistant that answers customer questions using Retrieval-Augmented Generation (RAG): every answer is grounded in your actual documentation and cites its sources instead of hallucinating. Embed it on your website with one script tag, or test it straight from the built-in playground.
Every piece of this project runs on a generous free tier, with no credit card required anywhere in the core product:
| Layer | Choice | Why it's free |
|---|---|---|
| Database | Neon Postgres | Free serverless Postgres tier, includes the pgvector extension |
| ORM | Prisma 7 | Open source, connects to Neon via @prisma/adapter-neon |
| Auth | NextAuth.js (Auth.js) v5 | Open source, email/password via bcrypt, no third-party auth bill |
| AI (chat + embeddings) | Google Gemini | Free API key: gemini-flash-latest for answers, gemini-embedding-2 (768 dimensions) for embeddings |
| Vector search | Postgres pgvector | Runs inside your free Neon database, no separate vector DB service |
| File parsing | unpdf + mammoth | Open source PDF/DOCX text extraction, no storage bucket needed |
| Payments | Stripe test mode | Test-mode API keys are free forever, no bank account or business verification needed. Skip it entirely and the app runs in demo billing mode automatically |
| Hosting | Vercel free tier (or anywhere Next.js runs) | — |
If you never add a STRIPE_SECRET_KEY, the billing page still works:
"upgrading" a plan just flips the workspace's plan in the database instantly
with a "demo mode" notice, so you can build and demo the whole product
without ever creating a Stripe account.
[1], [2] citation markers that point at the retrieved snippets<script> tag drops a floating, brand-coloured chat bubble on any website, backed by the same RAG pipeline (copy the snippet from Assistants → Embed)next-themes)motion animations throughout, including scroll-triggered reveals that replay every time a section re-enters view and a type/erase cursor effect on the homepage headlineproxy.ts), React 19, TypeScript 5motion for animation, next-themes for dark mode@prisma/adapter-neon driver adapter) + pgvector (vector(768) column, queried with raw SQL)ai, @ai-sdk/google, @ai-sdk/react useChat) + Google GeminiPrerequisites: Node.js 20.9+, a free Neon database, a free Gemini API key.
git clone <your-repo-url> support-ai
cd support-ai
npm install # also runs `prisma generate` (postinstall)
Create a .env file in the project root with this template; the next steps
fill it in:
# Neon → Connect → pooled connection string
DATABASE_URL="postgresql://user:[email protected]/neondb?sslmode=require"
# openssl rand -base64 32
NEXTAUTH_SECRET=""
NEXTAUTH_URL="http://localhost:3000"
# https://aistudio.google.com/apikey (free)
GOOGLE_GENERATIVE_AI_API_KEY=""
# Optional: leave blank for demo billing mode
STRIPE_SECRET_KEY=""
STRIPE_WEBHOOK_SECRET=""
STRIPE_PRICE_PRO=""
STRIPE_PRICE_AGENCY=""
NEXT_PUBLIC_APP_URL="http://localhost:3000"
# Optional: public demo account (see "Public demo mode")
# node -e "console.log(require('crypto').randomBytes(24).toString('base64url'))"
DEMO_PASSWORD=""
CRON_SECRET=""
.env as DATABASE_URL..env as GOOGLE_GENERATIVE_AI_API_KEY.openssl rand -base64 32
Paste the result into .env as NEXTAUTH_SECRET.
Skip this entirely if you don't need real checkout; billing falls back to demo mode automatically. To wire up real (test-mode, free) Stripe Checkout:
sk_test_...) from the API keys page.STRIPE_SECRET_KEY, STRIPE_PRICE_PRO, STRIPE_PRICE_AGENCY in .env.
(Checkout is a server-side redirect, so no publishable key is needed.)stripe listen --forward-to localhost:3000/api/webhooks/stripe and paste the printed secret into STRIPE_WEBHOOK_SECRET.
The webhook handles checkout.session.completed, customer.subscription.updated and customer.subscription.deleted.npx prisma db push
This creates all tables and enables the vector extension on your Neon
database. (If extension auto-creation ever fails on your Postgres version,
run CREATE EXTENSION IF NOT EXISTS vector; once in the Neon SQL editor —
takes five seconds.)
npm run dev
Open http://localhost:3000. Sign up, create a workspace, create an assistant, upload a document, and chat with it in the playground.
| Script | What it does |
|---|---|
npm run dev | Dev server on http://localhost:3000 |
npm run build | next build (does not change the database schema) |
npm start | Serve the production build |
npm run lint | ESLint |
npm run seed:demo | Create or reset the public demo workspace (see Public demo mode) |
npx prisma db push | Sync prisma/schema.prisma (and the vector extension) to the database |
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL | yes | Neon Postgres connection string (pooled is fine) |
NEXTAUTH_SECRET | yes | Signs session JWTs (AUTH_SECRET also works with Auth.js v5) |
NEXTAUTH_URL | local only | Base URL; not needed on Vercel |
GOOGLE_GENERATIVE_AI_API_KEY | yes | Gemini chat + embeddings; nothing can be ingested or answered without it |
NEXT_PUBLIC_APP_URL | for Stripe | Base URL for Stripe Checkout / Portal redirects |
STRIPE_SECRET_KEY | no | Real Stripe Checkout; empty = demo billing mode |
STRIPE_WEBHOOK_SECRET | with Stripe | Verifies webhook signatures |
STRIPE_PRICE_PRO / STRIPE_PRICE_AGENCY | with Stripe | Recurring Price IDs |
STRIPE_ALLOW_LIVE_KEYS | no | Live sk_live_ keys are refused unless this is true; test mode is the default |
DEMO_PASSWORD | for the demo | Random password of the shared demo account; "Try demo" signs in with it server-side |
CRON_SECRET | for the demo | Protects /api/cron/demo-reset; Vercel Cron sends it automatically |
SEED_USER_EMAIL | no | Account for scripts/seed-knowledge-base.ts if you don't pass one |
Open Assistants, click Embed on an assistant, and copy the snippet. It looks like this:
<script
src="https://support-ai-murex.vercel.app/api/public/widget/embed.js"
data-assistant="YOUR_ASSISTANT_ID"
data-title="Chat with us"
data-color="#ff5a36"></script>
| Attribute | Required | Meaning |
|---|---|---|
src | yes | embed.js served by your deployment; the widget calls the API on the same origin |
data-assistant | yes | The assistant's id (note: data-assistant, not data-assistant-id) |
data-title | no | Header text of the chat panel (default "Chat with us") |
data-color | no | Brand colour for the bubble, header and send button (default #FF5A36) |
The widget is plain JavaScript with no dependencies. It keeps an anonymous visitor
id in localStorage and the conversation id in sessionStorage, streams answers
from POST /api/public/widget/[assistantId]/chat (CORS-enabled), and every
conversation shows up under Conversations with the WIDGET channel.
To try it locally, open scripts/test-site.html, a mock
company page with the snippet already in place (pointed at the public demo assistant).
Point its src at your running app and set data-assistant to one of your assistant ids.
Or click Widget on any assistant card: /widget-preview/[assistantId] loads the real
widget on a sample website.
Visitors can click Try the live demo on the landing page or the login page and land
in a fully seeded workspace without signing up. The button posts to /api/demo/login,
which signs in to the shared account [email protected] on the server using
DEMO_PASSWORD, so the password never reaches the browser.
What's seeded (lib/demo/): an Agency-plan workspace with two assistants,
Nimbus Help Bot (a fictional project-management SaaS) and Bloom & Co Support (a fictional
skincare store), each with its own tone, prompt, brand colour and six Markdown help docs that
are chunked and embedded through the normal pipeline. There are also 30 conversations
(1-3 turns each, with citations) spread over the last 30 days, and two extra teammates. All
names, emails and domains are fictional (.demo / .example).
Protections (enforced in the API, shown in the UI as a friendly toast):
@supportai.demo domain.Seeding and the daily reset. npm run seed:demo (needs DEMO_PASSWORD) creates or resets
the demo. It only touches the supportai-demo workspace and the @supportai.demo users.
vercel.json schedules GET /api/cron/demo-reset daily at 05:00 UTC; it runs
the same reset, guarded by CRON_SECRET. Embeddings are reused: a document is only embedded
again if its seed text changes, so a normal reset makes no Gemini calls. To run a reset by hand:
curl -H "Authorization: Bearer $CRON_SECRET" https://<your-domain>/api/cron/demo-reset
scripts/seed-knowledge-base.ts adds a "Nimbus Support Bot" assistant with four
help-centre documents from scripts/demo-docs/ (refund policy,
shipping, warranty, subscriptions) to an existing account's workspace, plus
demo conversations. It goes through the real pipeline: documents are chunked and
embedded with Gemini, and the conversation answers are real model output with
real citations. Sign up and finish onboarding first, then:
npx tsx scripts/seed-knowledge-base.ts [email protected] --dry # preview, writes nothing
npx tsx scripts/seed-knowledge-base.ts [email protected] # insert
npx tsx scripts/check-seed.ts [email protected] # read-only summary
It is safe to re-run (the assistant, documents and conversations are matched and skipped if they exist). Because it makes real Gemini calls, a free-tier key may hit its quota part-way; whatever was saved stays, so run it again later to finish.
unpdf for PDF, mammoth for DOCX, UTF-8 for
TXT/MD, tag-stripping for URLs; capped at 500,000 characters) and split into
~1000-character paragraph-aware chunks with a 150-character overlap
(lib/chunker.ts).gemini-embedding-2 model, truncated to 768 dimensions
(outputDimensionality), and written to a vector(768) column on
DocumentChunk (lib/rag.ts). The document moves from
PROCESSING to READY, or to ERROR with the message.embedding <=> query_vector).
Only chunks shared with every assistant (assistantId null, which is what the
UI creates) or attached to this assistant via the API are eligible; the 6
closest are returned.[1], [2]. gemini-flash-latest streams the
answer; the user message, the answer and its citations (document + similarity)
are saved to the conversation.All routes except auth, public/* and the Stripe webhook require a session and
check workspace membership (and role where relevant).
| Method | Route | Purpose |
|---|---|---|
POST | /api/auth/signup | Create an account |
POST | /api/workspaces | Create a workspace |
GET | /api/workspaces/current | Current workspace and role |
PATCH | /api/workspaces/[id] | Update workspace settings |
GET | /api/workspaces/[id]/members | Members |
PATCH DELETE | /api/workspaces/[id]/members/[memberId] | Change role / remove member (Owner, Admin; the owner is protected) |
GET POST | /api/workspaces/[id]/invites | List / create invite links |
DELETE | /api/workspaces/[id]/invites/[inviteId] | Revoke an invite |
GET POST | /api/invites/[token] | Read / accept an invite |
GET POST | /api/assistants | List / create assistants |
GET PATCH DELETE | /api/assistants/[id] | Read / update / delete an assistant |
GET POST | /api/documents | List documents / ingest pasted text |
POST | /api/documents/upload | Upload and ingest a PDF / DOCX / TXT / MD file |
POST | /api/documents/fetch-url | Fetch a URL and return its text for ingestion |
GET DELETE | /api/documents/[id] | Read a document's text / delete it |
POST | /api/chat | Authenticated streaming RAG chat (playground) |
support-ai/
app/
page.tsx # Landing page
login/, signup/ # Auth pages
onboarding/ # First-run workspace + assistant wizard
invite/[token]/ # Team invite acceptance
widget-preview/[assistantId]/ # Sample website running the real widget
(dashboard)/ # Protected dashboard route group
dashboard/, assistants/ (+ [id]/playground), knowledge/,
conversations/, analytics/, team/, settings/, billing/
api/
auth/ # NextAuth handler + signup endpoint
workspaces/, assistants/, documents/, conversations/, analytics/, invites/
chat/ # Authenticated streaming RAG chat
public/widget/ # Embeddable widget script + its public chat API
billing/, webhooks/stripe/ # Stripe checkout, portal, webhook
components/
landing/ # Nav, Footer, Typewriter, TicketCard, FeatureMock
dashboard/ # Sidebar, Shell (workspace-aware layout), DocumentViewer
motion/ # Reveal (replays on scroll re-entry)
auth/ # Shared login/signup form
theme-toggle.tsx # Light / dark / system
lib/
rag.ts # Ingest, retrieve, RAG prompt, citations
gemini.ts # Chat model + embeddings
chunker.ts, parse-file.ts # Chunking and PDF/DOCX/TXT/MD extraction
chat.ts # Shared chat-route helpers (history caps, conversation ownership)
demo/ # Demo seed content, reset, rate limit
prisma.ts, auth.ts, auth.config.ts, session.ts, workspace.ts, stripe.ts, utils.ts
hooks/ # TanStack Query hooks
prisma/schema.prisma # Data model (users, workspaces, assistants, documents,
# vector chunks, conversations, messages, invites)
prisma/seed-demo.ts # `npm run seed:demo`
vercel.json # Daily demo-reset cron
scripts/
seed-knowledge-base.ts, check-seed.ts
demo-docs/ # Four demo help-centre documents
test-site.html # Mock website for testing the widget
types/next-auth.d.ts # Session type augmentation
proxy.ts # Redirects signed-out visitors (Next.js 16 "proxy")
DATABASE_URL, NEXTAUTH_SECRET (a new value), GOOGLE_GENERATIVE_AI_API_KEY,
NEXT_PUBLIC_APP_URL (your production URL), and the STRIPE_* keys if you use
real Stripe. NEXTAUTH_URL is not needed on Vercel.next build and never changes the
schema (there is no migrations folder; the schema is pushed with
npx prisma db push). Run npx prisma db push once against the production
database before the first deploy, and again after any schema change.https://<your-domain>/api/webhooks/stripe and put its signing secret in
STRIPE_WEBHOOK_SECRET.DEMO_PASSWORD and CRON_SECRET, run npm run seed:demo
once against the production database (with the same DEMO_PASSWORD), and the cron in
vercel.json keeps it fresh. The cron can also create the demo from scratch on first run.[n] markers.resolved
flag that analytics counts (the demo data sets it), but nothing in the app sets it yet.MIT — do whatever you want with it.
GET | /api/conversations | Conversations in a workspace |
GET | /api/conversations/[id]/messages | Messages with citations |
GET | /api/analytics | 30-day analytics data |
POST | /api/demo/login | "Try demo": sign in to the demo account server-side |
GET | /api/cron/demo-reset | Daily demo reset (Vercel Cron, CRON_SECRET) |
GET | /api/public/widget/embed.js | The embeddable widget script |
POST | /api/public/widget/[assistantId]/chat | Public widget chat (CORS, no auth) |
POST | /api/billing/checkout | Stripe Checkout, or instant plan switch in demo mode |
POST | /api/billing/portal | Stripe Customer Portal |
POST | /api/webhooks/stripe | Stripe webhook receiver |