Javis OS
Javis OS is an Agentic AI that assists entrepreneurs in business tasks, helping them grow their work.
AI translation
Manage your shop's bookings, customers, services, and hours.
BusinessPython · TypeScriptGitHub @Hakkh-2312OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
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)Read the full 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 |