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。 以下為已儲存的摘錄。
Multi-tenant booking platform: FastAPI + PostgreSQL + SQLAlchemy + Alembic
backend, with a Next.js dashboard for shop owners. This replaces the earlier
Flask + Google Sheets prototype.
2. Create your Supabase project (managed Postgres)檢視完整 README ↗Multi-tenant booking platform: FastAPI + PostgreSQL + SQLAlchemy + Alembic backend, with a Next.js dashboard for shop owners. This replaces the earlier Flask + Google Sheets prototype.
app/
core/config.py # Settings, loaded from .env
core/security.py # Password hashing (argon2) + JWT issuing/verification
core/logging.py # Root logger configuration
db/session.py # SQLAlchemy engine + DB session dependency
db/base.py # Declarative Base — all models inherit from this
db/mixins.py # TimestampMixin (created_at/updated_at)
db/seed.py # One-off script seeding a demo tenant
models/ # Tenant, User, Customer, Service, WorkingHours, Appointment, WhatsappConversation
schemas/ # Pydantic request/response models
services/ # booking.py (shared booking/slot logic), whatsapp_flow.py
# (conversation state machine), whatsapp_i18n.py (ar/he/en),
# whatsapp_client.py (Meta API calls)
api/deps.py # get_current_user / get_current_tenant_id
api/routes/
health.py # /api/health, /api/health/db
auth.py # /api/auth/signup, /api/auth/login
availability.py # /api/availability
appointments.py # /api/appointments (create, list, get, cancel, reschedule)
customers.py # /api/customers (full CRUD)
services.py # /api/services (full CRUD)
working_hours.py # /api/working-hours (get + upsert per day)
tenants.py # /api/tenants/me (shop name/phone/address)
whatsapp.py # /api/whatsapp/webhook (Meta Cloud API)
main.py # FastAPI app entrypoint, CORS, error handling
alembic/ # DB migrations, wired to app.core.config
tests/ # pytest + httpx, runs against real Postgres
Dockerfile, docker-compose.yml # Containerized app + local Postgres
.github/workflows/ci.yml # Lint, migrate, test on every push/PR
pyproject.toml
.env.example
dashboard/ # Next.js shop-owner UI (separate app, see below)
Every tenant-owned table is scoped by tenant_id, resolved from the caller's
JWT (not a hardcoded value) via get_current_tenant_id. A tenant cannot read,
modify, or book against another tenant's data — covered by
tests/test_tenant_isolation.py. Double-booking is prevented both at the
application level (an overlap check before insert) and at the database level
(a Postgres exclusion constraint on appointments), so even two truly
simultaneous requests for the same slot can't both succeed.
You'll need Python 3.11+ and uv (fast Python package manager).
# Install uv (pick one)
pip install uv
# or: curl -LsSf https://astral.sh/uv/install.sh | sh
cd barbershop-saas
cp .env.example .env
uv sync
barbershop-saas), a strong DB password (save it), and a region close to Israel (e.g. eu-central-1).env as DATABASE_URL, replacing the placeholderAlternatively, run docker compose up -d db for a local Postgres instead of Supabase (see "Running with Docker" below).
python -c "import secrets; print(secrets.token_hex(32))"
Put the output in .env as JWT_SECRET_KEY.
uv run alembic upgrade head
uv run uvicorn app.main:app --reload
Then check:
{"message": "Barbershop SaaS API is running", ...}POST /api/auth/signup or paste a token you already haveIf /api/health/db fails, double check the DATABASE_URL — this is the #1 place
people get tripped up (wrong password, wrong host, or the pooler vs. direct port).
Tests need a real Postgres (the double-booking exclusion constraint uses
gist/tsrange, which has no SQLite equivalent) and isolate everything into
their own test schema, so they never touch whatever else lives in the
target database:
docker compose up -d db
TEST_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/barbershop uv run pytest
(Omit TEST_DATABASE_URL to default to localhost:5432/postgres.)
docker compose up -d db
docker compose run --rm api uv run alembic upgrade head # first time, and after new migrations
docker compose up
This runs the API against a local Postgres container rather than Supabase —
docker-compose.yml overrides DATABASE_URL for the api service regardless
of what's in .env.
cd dashboard
npm install
cp .env.local.example .env.local # defaults to http://localhost:8000, fine for local dev
npm run dev
Then open http://localhost:3000 — sign up (creates a tenant + logs you in),
and manage appointments/customers/services/working hours. It talks to
whatever NEXT_PUBLIC_API_URL points at, so make sure the backend (either
uv run uvicorn ... or docker compose up) is running first. CORS is
already configured backend-side for http://localhost:3000 by default.
A customer can message the shop's WhatsApp number and book entirely by
tapping clickable options — no manual dashboard entry needed. The flow:
a welcome menu (book / change language / call the shop / the address) →
pick a service → pick a date (up to 2 weeks out) → pick an open time →
booked, with a summary message and the conversation ending there. Their
Customer record is created automatically from their WhatsApp number and
profile name the first time they book. Defaults to Arabic, with Hebrew and
English available from the menu — the chosen language is remembered per
phone number across visits. Every list (services/dates/times) pages past
10 items via a "more options" row, since WhatsApp caps list messages at 10
rows. Reuses the exact same validation as the REST API
(app/services/booking.py) so working hours, overlaps, and the
double-booking exclusion constraint all apply identically.
The shop's phone number and address (used by the "call"/"address" menu options) are set on the Settings page in the dashboard.
Not tenant-scoped yet — WHATSAPP_TENANT_ID says which single shop this
WhatsApp number belongs to (there's no phone-number-to-tenant mapping until
multiple real shops are on WhatsApp). Also not in scope yet: cancelling or
rescheduling via WhatsApp (use the dashboard/API for that), and conversation
state doesn't expire — an abandoned flow just continues from where it left
off the next time that number messages in.
To set it up:
https://<public-url>/api/whatsapp/webhook (needs real HTTPS — use ngrok http 8000 locally, or your Render URL once deployed), verify token = anything you choose..env as WHATSAPP_VERIFY_TOKEN / WHATSAPP_ACCESS_TOKEN / WHATSAPP_PHONE_NUMBER_ID / WHATSAPP_TENANT_ID.If WHATSAPP_TENANT_ID is unset, incoming messages just get a static
"coming soon" reply instead of the booking flow — safe default until you've
set up a real shop to receive bookings.
DATABASE_URL=<prod-url> uv run alembic upgrade head.render.yaml and prompts for the sync: false values (prod
DATABASE_URL, a freshly generated JWT_SECRET_KEY, CORS_ALLOWED_ORIGINS
as a placeholder for now, the real WHATSAPP_* values). Deploy → you get
a https://<name>.onrender.com URL. render.yaml's dockerCommand runs
migrations on every boot and binds to Render's $PORT.dashboard → env var NEXT_PUBLIC_API_URL = the Render
URL → deploy → you get a https://<name>.vercel.app URL.CORS_ALLOWED_ORIGINS to that Vercel URL and
redeploy.https://<render-url>/api/whatsapp/webhook (same verify token) — no
more ngrok, and it stops changing on every restart.WHATSAPP_TENANT_ID
on Render to that tenant's id.Beyond DATABASE_URL and JWT_SECRET_KEY, see .env.example for:
CORS_ALLOWED_ORIGINS — comma-separated origins allowed to call this API (e.g. the dashboard's dev/prod URLs)DEBUG — leave true locally; a deployment that leaves this unset defaults to false so stack traces never leak to clientsSENTRY_DSN — optional; once set, unhandled exceptions are reported to SentryWHATSAPP_VERIFY_TOKEN / WHATSAPP_ACCESS_TOKEN / WHATSAPP_PHONE_NUMBER_ID / WHATSAPP_TENANT_ID — see "WhatsApp Cloud API webhook" abovegit init
git add .
git commit -m "Initial FastAPI skeleton"
Create a new repo at https://github.com/new, then:
git remote add origin <your-repo-url>
git branch -M main
git push -u origin main
.gitignore is already set up to exclude .env, .venv/, and any service-account
JSON/PEM files. Pushing to main or opening a PR runs .github/workflows/ci.yml
(lint, migrate, test against a fresh Postgres container).
Tenant, User, Customer, Service,
WorkingHours, Appointment, every tenant-owned table carrying a tenant_id..env to actually talk to real
WhatsApp (see "WhatsApp Cloud API webhook" above) — the handshake and
message-receiving logic are verified, sending just needs real
credentials to complete the loop.| When | Service | Why |
|---|---|---|
| Now | Supabase | Postgres hosting |
| Now | GitHub | Version control |
| Milestone 1 end | Render | Backend hosting |
| Milestone 1 end | Vercel | Next.js dashboard hosting |
| Milestone 2 | Meta Developer account + WhatsApp Cloud API app | Customer-facing WhatsApp bot |
| Whenever you want error alerts | Sentry | Error monitoring (SENTRY_DSN in .env) |
| Later | Domain registrar | Your own domain |