Lalabase
Tickets, Doku, Zeiterfassung und Kundengespräche an einem Ort, durchsuchbar per KI. Für Softwarefirmen mit Kundenprojekten. Open Source: auf deinem eigenen Server oder in unserer Cloud in Deutschland.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证正在加载完整 README。 以下为已保存的摘录。
A standalone SaaS product that turns any business's contact form and inbound
email into a clean, threaded support inbox. Sign-up is invite-only: an
operator creates a client's workspace from /admin, the client receives a
branded invite email and signs up with that address, pastes a snippet onto their
existing site (or hands their AI assistant the ready-made integration prompt),
and from then on submissions flow into their ticket dashboard. Replies send as
real email from "Business Name" > — never from the
client's own (unverifiable) domain — and customer replies thread back in.查看完整 README ↗A standalone multi-tenant SaaS product. A business connects its website
contact form and its support email, and everything becomes a threaded ticket
inbox it replies from. Replies go out as real email from
"Business Name" <[email protected]> and the customer's reply threads back
into the same ticket. The same workspace carries the business's newsletter: a
signup form for its site, a welcome email in its own branding, a composer with
an image and products, and a send pipeline with unsubscribe and suppression.
Live at postbox.help. Its own repo, database and domain, completely separate
from any client site. See PROJECT_STATUS.md for where things stand,
DEPLOYMENT.md for the pipeline, STYLE-GUIDE.md before touching a screen,
and AGENTS.md for the engineering rules this codebase learned the hard way.
Client website form ──▶ POST /api/tickets/:key ─────▶ ┐
Forwarded email ──────▶ POST /api/inbound (Resend) ──▶ ├─▶ tickets + ticket_messages
┘ │
Dashboard (Clerk-authed) ◀───────────────┘
reply ─▶ Resend ─▶ customer ─▶ Reply-To: ticket+TKT-<id>-<secret>@postbox.help
└─▶ POST /api/inbound threads it back
Signup form ──▶ POST /api/subscribe/:key ──▶ subscribers ──▶ welcome email
Composer ─────▶ campaign_recipients ──▶ hourly sweep / Send now ──▶ Resend ──▶ bounces suppress
Tickets arrive three ways and land in one inbox: a contact-form POST with
a workspace or form key (CORS-enabled; fields are read by meaning so Shopify,
WordPress and Elementor names all work), an email forwarded to the workspace's
inbound address, and the same email flow with an order number in it, which
makes a higher-priority order ticket. Replies to a ticket carry a per-ticket
secret in the Reply-To address, so a sequential id alone cannot be used to
mail into another tenant's thread. Enquiries outside the workspace's business
hours get a held auto-reply once it opens.
Newsletters are single opt-in by default (an operator can put a workspace back on double opt-in), with consent evidence recorded on every row. Campaigns go to every confirmed subscriber in the workspace. Sending claims one recipient row at a time before calling the provider, so a crash loses at most one email and never sends one twice.
workspace_id, and every statement that touches them carries the predicate
inside the statement. tests/tenancy-invariants.test.ts reads every raw
SQL statement in the repo and fails if one does not.app/
page.tsx, pricing/, contact/, (legal)/ Public marketing and legal pages
(dashboard)/
inbox/ The inbox, folders, labels, search
tickets/[id]/ A thread, with the contact rail
newsletters/, newsletters/welcome/ The composer and the welcome email
subscribers/ The subscriber list
settings/ General, install, forms, labels, team,
auto-reply, billing, contacts, access log
(admin)/admin/ The operator console
s/, u/ Subscribe and unsubscribe pages a client's customer sees
api/ Route handlers: tickets, inbound, campaigns,
subscribe, billing, webhooks, cron, workspace
components/ mail/, newsletter/, marketing/, skeletons/
lib/ Domain logic; pure halves beside their IO halves
db/ Drizzle schema, migrations, guard, seed scripts
tests/ Vitest; render harnesses for every signed-in screen
proxy.ts Clerk route gating; the public-route list lives here
npm install (npm 10, Node 22)..env.example to .env.local and fill in Neon, Clerk and Resend.
Read the comments; most variables explain what happens when they are unset.db/guard.ts refuses to connect from a
laptop unless DATABASE_ENV=development declares one. docs/DEV-DATABASE.md
is the two-minute setup with a schema-only Neon branch.npm run db:migrate then npm run db:seed-dev for believable fake data.npm run dev.| Script | What it does |
|---|---|
npm run dev / build / start | Next.js |
npm run typecheck | next typegen then tsc --noEmit. Run it after your last edit, not before |
npm run lint | ESLint with the keyboard half of jsx-a11y turned on |
npm test / test:watch | Vitest |
npm run db:generate / db:migrate / db:push / db:studio | drizzle-kit. db:push is the sharp one |
npm run db:seed-dev | Fake data for the dev branch |
npm run db:seed / db:seed-client / db:bootstrap | Demo workspace, a client workspace, first admin |
npm run db:rekey-chains | Re-key the audit hash chains (see lib/rekey-chain.ts before running) |
DEPLOYMENT.md. In short: push to main, CI runs typecheck, lint, tests and
a build, Vercel deploys with migrations in the build command. Resend needs a
verified sending domain, the inbound MX on the root domain, and two webhook
endpoints (inbound mail, and delivery events). Clerk needs the production
domain. Scheduled sweeps run from GitHub Actions and need CRON_SECRET in two
places.
Settings → Install gives a client two ways to connect a contact form and one
to add a newsletter signup. The recommended way is a prompt to hand to
whatever AI assistant built their site: it can see the page, so it finds the
real form and keeps the markup and styling as they are. The no-code way points
an existing form's action at the endpoint, at the cost of the visitor
landing on a Postbox confirmation page. Each named form has its own key, so
one scraped page does not take down every form the business has.