Lalabase
티켓·문서·근무시간·고객대화를 한 곳에 모아 KI로 검색, 소프트웨어 기업 고객 프로젝트용, 오픈소스로 자체 서버·독일 클라우드에 배포
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Online booking, a shared calendar and a client list for businesses that run on appointments: barbers, salons, nail techs, makeup artists, spas, massage therapists, trainers and clinics. Built first for small businesses in Nigeria that currently run on WhatsApp, paper diaries and spreadsheets. It works in any currency and time zone.README 전문 보기 ↗Online booking, a shared calendar and a client list for businesses that run on appointments: barbers, salons, nail techs, makeup artists, spas, massage therapists, trainers and clinics. Built first for small businesses in Nigeria that currently run on WhatsApp, paper diaries and spreadsheets. It works in any currency and time zone.
There are three kinds of people in ServiceBook. Each one sees something different.
| They can | Where |
|---|---|
| Open the business's booking link from Instagram, WhatsApp or a printed QR poster | /book/:slug |
| Browse tabs: Book, Team, Our work (photos of finished styles), Reviews, Info (hours, directions, contact) | /book/:slug?tab=… |
| Book: pick a service, optionally a person, a day and a free time, then enter name and phone | /book/:slug |
| Join a waitlist when a day is fully booked | booking page |
| Chat with the business about a booking, cancel it, add it to their calendar, and rate the visit afterwards | /my-booking/:token |
| See all their appointments (past and upcoming) and book again without retyping their details | /c/:token |
| Join the business's client list (name, number, optional birthday) | /join/:slug |
| Ask to chat on WhatsApp, Instagram, Messenger, TikTok, X, Telegram or Snapchat. Their details are saved first, so every chat reaches the Inbox | booking page → "Chat with us" |
Payment happens at the appointment. Nothing is paid online yet.
| Area | What's there |
|---|---|
Home (/dashboard) | Today's schedule, what's next, setup checklist, this week's birthdays with a ready-to-send WhatsApp wish |
| Inbox | Booking chats with customers, and chat requests from social apps |
Bookings (/bookings) | List with search and filters. Each booking opens on its own page with confirm, mark done, no-show, move, cancel, notes and chat |
New booking (/bookings/new) | Search or quick-add a customer, then service, person, day and time |
| Calendar | Day, week and month views, time off shaded. Front desk view (/front-desk) is a full-screen version for a tablet at reception |
Calendar sync (/calendar/sync) | Subscribe from Google, Apple or Outlook, for the whole business or one person |
| Customers | Client list with history; each profile has a private link to send them (their own page) |
| Services, Staff | Prices, durations and who does what. Staff have hours, a bio, a location, ratings and an App access card to invite them |
| Showcase | Photos of finished work tagged by person and service, and the reviews customers leave |
| Time off | Block days or hours for one person or the whole business. Nobody can book those times, and clashes are listed |
| Waitlist | People waiting for a full day. You're notified when a spot opens |
| Settings | Business details, opening hours, chat apps, booking page on/off, your colour, emails, and a printable QR poster (/poster) |
| Notifications | A bell plus push alerts to the owner's phone or computer, even with the site closed |
| Roadmap & ideas | Vote on what gets built next and suggest ideas |
The owner invites someone from their staff page. The staff member opens a one-time link (/join-team/:token) and picks an email and password. After that they see only:
They can book customers in with themselves. Everything else is owner-only, and the server enforces it (see Security).
A typical week, end to end:
Customers are added to the client list automatically when they book. They're matched by phone number within each business.
| Path | Who | What |
|---|---|---|
/, /features, /solutions, /how-it-works, /about, /demo, /roadmap, /design | Everyone | Website, live demo, roadmap and design system |
/register, /login | Owners and staff | Sign up and sign in |
/book/:slug | Customers | Booking page with tabs |
/my-booking/:token | Customers | One booking: details, chat, cancel, rate |
/c/:token | Customers | All their appointments; book again |
/join/:slug | Customers | Join the client list |
/join-team/:token | Staff | Create a staff login from an invite |
/dashboard | Owner | Home |
/inbox | Owner, staff (own chats) | Messages and chat requests |
/bookings, /bookings/new, /bookings/:id | Owner, staff (own) | Bookings |
/calendar, /front-desk, /calendar/sync | Owner (staff: /calendar only) | Calendar views and sync |
/customers, /customers/new, /customers/:id, /customers/:id/edit | Owner (staff can view a customer) | Clients |
/services, /services/new, /services/:id, /services/:id/edit | Owner | Services |
/staff, /staff/new, /staff/:id, /staff/:id/edit | Owner | Team, hours, time off, app access |
/showcase, /showcase/new, /showcase/:id/edit | Owner | Portfolio and reviews |
public/sw.js) for push alerts.packages/types holds the request and response types shared by both apps, so the web and API can't drift apart.servicebook/
├── apps/
│ ├── web/ React app (Vercel)
│ │ ├── public/sw.js Service worker: push alerts when the site is closed
│ │ └── src/
│ │ ├── pages/ One file per page (marketing/ for the website)
│ │ ├── components/ Shared UI (ui/ for primitives)
│ │ └── lib/ Data hooks (one per API area) and helpers
│ └── api/ Express API (Render)
│ └── src/
│ ├── routes/ One router per area
│ ├── models/ Mongoose models
│ ├── lib/ Booking engine, push, ratings, iCalendar, demo…
│ ├── services/ Email, reminders, birthday notices
│ └── middleware/ Auth, business scoping, staff access list
└── packages/
├── types/ Shared TypeScript types
└── config/ Shared tsconfig
| Model | Holds |
|---|---|
User | Login: OWNER or STAFF |
Business | Profile, slug, time zone, currency, colour, settings, calendar-feed token |
BusinessHours, StaffAvailability | Weekly opening hours and each person's working hours |
Service, Staff | What's offered and who does it. Staff have optional userId (login) and invite token |
Customer | Per business, unique by phone. Optional birthday and a private page token |
Booking | Who, what, when, status, price at the time, private access token |
Message | Booking chat (customer ↔ business) |
TimeOff | Unavailable time for a person or the whole business |
WaitlistEntry | Someone waiting for a full day |
Review, WorkPost | Ratings after completed visits; portfolio photos |
Enquiry | Chat requests from social apps |
Notification, PushSubscription | The owner's bell and the devices that get push alerts |
Image | Uploaded photos (stored in MongoDB) |
Idea, AppSetting | Roadmap items and votes; server-wide values such as the push keys |
apps/api/src/lib/bookingEngine.ts is the single path for creating and moving bookings, for both owners and customers.
businessId. requireBusiness derives it on the server from the signed-in user and never trusts one sent by the client.STAFF login may only call the endpoints listed in middleware/staffAccess.ts. Those endpoints then limit data to that person: their bookings, chats and time off. Anything not on the list is owner-only./my-booking/:token and /c/:token use long random tokens. They're private links, the same model as Google's "secret address" calendar links.These run inside the API process:
/health every 10 minutes. The free plan sleeps after 15 idle minutes, which makes the next visitor wait about a minute and pauses the jobs above.You need: Node.js 20+, pnpm (corepack enable), and a MongoDB replica set. A free Atlas cluster works. Locally, a single-node replica set works too:
docker run -d --name mongo -p 27017:27017 mongo:7 --replSet rs0
docker exec mongo mongosh --eval 'rs.initiate()'
Then:
pnpm install
cp apps/api/.env.example apps/api/.env # works as-is against local Mongo
cp apps/web/.env.example apps/web/.env
pnpm dev # API on :4000, web on :5173
pnpm --filter api seed # demo salon with staff, services, clients, bookings and reviews
Demo logins (password password123 for both):
[email protected][email protected]Type checks: run pnpm typecheck from the repo root, or pnpm --filter api typecheck / pnpm --filter web typecheck for one app.
Tests: pnpm test runs both apps' tests (pnpm --filter api test / pnpm --filter web test for one). The API tests need MongoDB: each test file creates its own database from MONGODB_TEST_URI (default mongodb://127.0.0.1:27017/sbx_test, plus a random suffix) and drops it at the end. Against a replica set, also set MONGODB_REPLICA_SET=1 to run the transaction and simultaneous-booking tests:
MONGODB_REPLICA_SET=1 MONGODB_TEST_URI="mongodb://127.0.0.1:27017/sbx_test?directConnection=true" pnpm --filter api test
Without a replica set those tests are skipped and the rest run with DB_TRANSACTIONS=off, a switch the API accepts only when NODE_ENV=test. CI (.github/workflows/ci.yml) runs everything against a MongoDB 7 replica set, builds both apps, and imports the built API with node apps/api/scripts/smoke-dist.mjs.
The API runs on Render, the web app on Vercel. render.yaml describes the API as a Blueprint.
Releases, the frozen multi-business version and the order to deploy in are in docs/RELEASING.md; release notes are in CHANGELOG.md.
| Setting | Value |
|---|---|
| Root Directory | apps/api |
| Build Command | pnpm install --frozen-lockfile && pnpm run build |
| Start Command | pnpm start |
| Health Check Path | /health |
| Variable | Required | Notes |
|---|---|---|
NODE_ENV | yes | production |
MONGODB_URI | yes | Atlas string including the database name. Allow 0.0.0.0/0 in Atlas Network Access |
JWT_SECRET | yes | 32+ characters: openssl rand -base64 48 |
CORS_ORIGIN | yes | https://servicebook-drab.vercel.app. Comma-separate others; https://servicebook-*.vercel.app allows previews |
JWT_EXPIRES_IN | no | Default 7d |
EMAIL_PROVIDER | no | console (default, logs emails) or resend |
EMAIL_FROM, EMAIL_API_KEY | with resend | Sender and Resend API key |
ADMIN_EMAILS | no | Comma-separated emails that can change roadmap statuses |
SEED_DEMO | no | Default true: keeps the demo salon available and fresh. Set false to skip it |
VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT | no | Push keys. Generated and stored automatically if unset |
KEEP_AWAKE | no | Default on when Render's RENDER_EXTERNAL_URL is set. false turns it off. KEEP_AWAKE_URL sets the address on other hosts |
The API stops at start-up with a clear message if a required variable is missing.
Import the repo with Root Directory apps/web (framework: Vite).
apps/web/.env.production points at the Render API. Set VITE_API_URL (no trailing slash) to override it. It's baked in at build time, so redeploy after changing it.vercel.json makes links like /dashboard and /book/your-business work on refresh./time-off, /time-off/new | Owner, staff (own) | Time off |
/waitlist | Owner | Waitlist |
/settings, /poster | Owner | Settings and the printable poster |