Synaplan
기업을 위한 오픈소스 AI 플랫폼
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
Booking platform for Israeli beauty and wellness businesses: scheduling, a booking page per salon, and a marketplace.
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A booking platform for Israeli beauty and wellness businesses: a free scheduling app for salons,
a booking page for each one, and a consumer marketplace on top. Tori is the Hebrew word for
"my turn".
Status: deployed at tori-rho.vercel.app, not launched. There are
no real merchants or bookings yet, and the venues you will see there are demo data. Merchant login
is a phone code, so real logins need an SMS gateway configured.
Booking from a salon's own link, off-peak deals priced in
Confirmation, with a free push reminder and a calendar file
The merchant's occupancy heatmap (demo history)README 전문 보기 ↗A booking platform for Israeli beauty and wellness businesses: a free scheduling app for salons, a booking page for each one, and a consumer marketplace on top. Tori is the Hebrew word for "my turn".
Status: deployed at tori-rho.vercel.app, not launched. There are no real merchants or bookings yet, and the venues you will see there are demo data. Merchant login is a phone code, so real logins need an SMS gateway configured.
The interface is Hebrew and right to left throughout.
Three surfaces from one codebase:
/b/<slug>, the link a salon puts in its Instagram bio. A server-rendered
booking wizard with deal-aware pricing, verified reviews and self-cancel./salons. Search by city, category and free text, with each venue's
real next free slot.Plus an operator metrics page at /ops, which is cross-tenant and therefore gated on an allowlist.
One scheduling engine, three callers. Slot computation lives in a standalone package,
packages/scheduling, with no dependency on the app. The merchant calendar,
the storefront wizard and the marketplace's next-free-slot search all ask the same engine
(schedule.ts, discovery.ts),
so a slot the marketplace advertises is one the booking page will accept. Israeli holidays are data,
not code: israel.ts reads the Hebrew calendar, closes the
salon on a holiday and shortens the day before one.
Login codes are bound to a challenge, not to a phone number. A salon's owner phone is printed on
its own storefront and the login endpoints are unauthenticated, so anything keyed on the number alone
is a lever a stranger can pull. Each request mints a random challenge held in an httpOnly cookie, and
a code is only ever judged against that row (auth.ts). Spending a guess
is a conditional UPDATE rather than read, check, then write, so a burst of concurrent guesses
cannot get past the five-attempt budget (otp-policy.ts). With no
SMS gateway configured, production refuses the login instead of pretending a code was sent.
Generate, compile, then migrate. The Vercel build (vercel.json) runs
prisma generate, then next build, then prisma migrate deploy against the unpooled connection
string, because DDL will not go through PgBouncer's transaction pooling. A build that fails on a type
error therefore cannot leave the database migrated ahead of the code serving it. CI
(ci.yml) applies every migration to an empty Postgres and fails if
schema.prisma has drifted from the migrated result.
Money effects are recorded before anything is charged. Every booking records where it came
from, and a marketplace booking that is a client's first at that salon records the commission it
would carry (commission.ts). Nothing is billed. Cancellation
fees work the same way: they default to 0% and are clipped at the moment of use to the cap the code
takes from Israeli consumer-protection law, 5% of the price or 100 shekels, whichever is lower
(cancellation.ts). That reading has not been reviewed by a
lawyer yet, which is one more reason nothing is charged.
@hebcal/core for dates and the Hebrew calendarnpm test runs 19 scheduling-engine tests and 184 web unit tests. CI runs them with a production
build on every push, plus a second job that migrates a real Postgres, checks for schema drift and
runs both seed scripts.
I built Tori solo, with Claude Code as a pair programmer. I own the architecture, the tests and the decisions, which is why the commits carry a Claude co-author line.
Local development uses Postgres, same as production. The quickest database is a container:
docker run -d --name tori-pg -e POSTGRES_PASSWORD=tori -e POSTGRES_USER=tori \
-e POSTGRES_DB=tori -p 55432:5432 postgres:16-alpine
export DATABASE_URL="postgresql://tori:tori@localhost:55432/tori"
npm install
npm run db:migrate -w @app/web # apply prisma/migrations
npm run db:seed -w @app/web # demo venues: /b/demo-salon, /b/demo-barber
npm run db:seed-history -w @app/web # optional: 8 weeks of past bookings for the heatmap
npm run dev -w @app/web
Log in with any Israeli mobile number. In development the code is 123456, and it is also printed
to the server console. The seeded demo owners are 050-0000000 and 050-0000001.
Other variables are listed in apps/web/.env.example. Reminders need VAPID
keys (npx web-push generate-vapid-keys); without them the app runs with push disabled. Real logins
need SMS_PROVIDER set to twilio or http, the second being a generic adapter for any Israeli
gateway described by a URL and a body template.
All rights reserved. See LICENSE.