1 file changed
+309
-2
@@ -1,3 +1,310 @@
|
||||
# NextChair
|
||||
+# NextChair
|
||||
|
||||
Booking app as an alternative to Booksy
|
||||
NextChair is a white-label, mobile-first booking platform for barbershops. It combines customer booking, staff scheduling, owner administration, notifications, loyalty, media, reviews, and optional Google integrations in one Next.js application.
|
||||
|
||||
## Technology
|
||||
|
||||
- Next.js 16 with the App Router and React 19
|
||||
- TypeScript 6
|
||||
- MySQL 8.0, accessed through Prisma 7 and the MariaDB driver adapter
|
||||
- Auth.js / NextAuth v5 for authentication
|
||||
- Tailwind CSS 4, Radix UI, Lucide, and Font Awesome
|
||||
- Vitest for unit and integration tests
|
||||
- Playwright for browser tests
|
||||
- Docker Compose for a production-style local deployment
|
||||
|
||||
The application is intentionally configured with discrete database settings: `DATABASE_HOST`, `DATABASE_PORT`, `DATABASE_NAME`, `DATABASE_USER`, and `DATABASE_PASSWORD`. It does not use `DATABASE_URL` at runtime.
|
||||
|
||||
## Deployment Model
|
||||
|
||||
The intended deployment model is a shared multi-business VPS:
|
||||
|
||||
- One Traefik reverse proxy handles HTTPS and routes each business domain.
|
||||
- One shared MySQL 8 server hosts a separate database and database user for each business.
|
||||
- Each business runs its own application container and has its own media volume.
|
||||
- Each business is installed independently through `/install`.
|
||||
|
||||
`.env.business.example` is the primary application configuration template. The local Docker files are development tooling and should not be treated as the production deployment model.
|
||||
|
||||
## Requirements
|
||||
|
||||
For local Node development:
|
||||
|
||||
- Node.js 22 or later
|
||||
- npm
|
||||
- A reachable MySQL 8.0 database
|
||||
|
||||
For the primary VPS deployment:
|
||||
|
||||
- A Linux VPS with Docker Engine and Docker Compose
|
||||
- A domain per business, with DNS pointing at the VPS
|
||||
- Ports 80 and 443 available for Traefik and Let's Encrypt
|
||||
|
||||
For local development:
|
||||
|
||||
- Docker Desktop with Docker Compose
|
||||
|
||||
## Local Docker Desktop (Isolated Business Instance)
|
||||
|
||||
This is a disposable local approximation of one business from the VPS deployment. It starts one MySQL container and one NextChair container, applies migrations automatically, and leaves the database empty until you either complete the installer or explicitly run the demo seed. It is not the shared multi-business topology; use the VPS setup above for the canonical deployment.
|
||||
|
||||
1. Install dependencies if you also plan to run commands on the host:
|
||||
|
||||
```bash
|
||||
npm ci
|
||||
```
|
||||
|
||||
2. Create the Docker environment file:
|
||||
|
||||
```bash
|
||||
cp .env.local.example .env.local
|
||||
```
|
||||
|
||||
3. Replace the placeholder values in `.env.local`. At minimum, set strong values for:
|
||||
|
||||
- `MYSQL_PASSWORD`
|
||||
- `MYSQL_ROOT_PASSWORD`
|
||||
- `DATABASE_PASSWORD`
|
||||
- `AUTH_SECRET`
|
||||
- `INSTALLATION_TOKEN`
|
||||
- `CRON_SECRET`
|
||||
|
||||
`DATABASE_NAME`, `DATABASE_USER`, and `DATABASE_PASSWORD` must match the corresponding MySQL container settings. Inside Docker, `DATABASE_HOST` must remain `next-chair-mysql`, not `localhost`.
|
||||
|
||||
Generate suitable secrets with:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
npx auth secret
|
||||
```
|
||||
|
||||
4. Build and start the stack:
|
||||
|
||||
```bash
|
||||
docker compose -p next-chair -f docker-compose.local.yaml --env-file .env.local up --build -d
|
||||
```
|
||||
|
||||
5. Open [http://localhost:3000](http://localhost:3000). If the database has not been installed, the application redirects to `/install`. Enter the `INSTALLATION_TOKEN` and complete the wizard to create the first business and owner account.
|
||||
|
||||
6. Follow the application logs when needed:
|
||||
|
||||
```bash
|
||||
docker compose -p next-chair -f docker-compose.local.yaml --env-file .env.local logs -f next-chair
|
||||
```
|
||||
|
||||
The application health endpoint is [http://localhost:3000/api/health](http://localhost:3000/api/health). It returns HTTP 200 when the database is reachable, even before the installation wizard has been completed.
|
||||
|
||||
### Docker lifecycle commands
|
||||
|
||||
```bash
|
||||
# Stop containers but keep database and media volumes
|
||||
docker compose -p next-chair -f docker-compose.local.yaml --env-file .env.local down
|
||||
|
||||
# Start again without rebuilding
|
||||
docker compose -p next-chair -f docker-compose.local.yaml --env-file .env.local up -d
|
||||
|
||||
# Run the development/demo fixture explicitly
|
||||
docker compose -p next-chair -f docker-compose.local.yaml --env-file .env.local run --rm next-chair-seed
|
||||
|
||||
# Remove containers and all local database/media data
|
||||
docker compose -p next-chair -f docker-compose.local.yaml --env-file .env.local down -v
|
||||
```
|
||||
|
||||
The seed service is opt-in. Normal `up` never seeds the database. `down -v` is destructive for this local deployment: it removes the named MySQL and media volumes.
|
||||
|
||||
## Local Node Development
|
||||
|
||||
Use this path when you already have MySQL running outside Docker or want fast Next.js source reloads.
|
||||
|
||||
1. Install dependencies:
|
||||
|
||||
```bash
|
||||
npm ci
|
||||
```
|
||||
|
||||
2. Create `.env.local` from `.env.example` and configure a local MySQL 8 database. Set `DATABASE_HOST=localhost` and make sure the database and user already exist.
|
||||
|
||||
3. Generate the Prisma client and apply development migrations:
|
||||
|
||||
```bash
|
||||
npm run prisma:generate
|
||||
npm run prisma:migrate
|
||||
```
|
||||
|
||||
4. Start the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000).
|
||||
|
||||
5. Choose one database bootstrap path:
|
||||
|
||||
- For a real, empty installation, do not seed. Use `/install` with `INSTALLATION_TOKEN`.
|
||||
- For development fixtures, run:
|
||||
|
||||
```bash
|
||||
npm run prisma:seed
|
||||
```
|
||||
|
||||
The seed is idempotent and creates demo business, staff, services, customers, appointments, and related records. It marks the database as installed so the application can be used immediately. Do not run it alongside a real first-run installation.
|
||||
|
||||
The direct Node path and the local Docker path should use different `.env.local` values: Docker connects to `next-chair-mysql`, while a host-run Next.js process normally connects to `localhost`. For the business-specific VPS environment, use `.env.business.<slug>` and the matching per-business compose file instead.
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
The available templates are:
|
||||
|
||||
- `.env.example`: host-run development configuration
|
||||
- `.env.local.example`: Docker Desktop configuration
|
||||
- `.env.business.example`: one business in the shared VPS deployment
|
||||
|
||||
Required for a basic running installation:
|
||||
|
||||
- MySQL settings: `DATABASE_*`
|
||||
- `AUTH_SECRET`
|
||||
- `NEXT_PUBLIC_APP_URL`
|
||||
- `INSTALLATION_TOKEN`
|
||||
- `CRON_SECRET`
|
||||
- `AUTH_TRUST_HOST=true` when serving through a non-localhost host or reverse proxy
|
||||
|
||||
Optional integrations are disabled safely when left unconfigured:
|
||||
|
||||
- Google sign-in: `AUTH_GOOGLE_ID`, `AUTH_GOOGLE_SECRET`
|
||||
- Google Calendar sync: `GOOGLE_CALENDAR_*`, `CALENDAR_TOKEN_ENCRYPTION_KEY`
|
||||
- Google Reviews: `GOOGLE_PLACES_API_KEY`
|
||||
- SMTP email: set `EMAIL_PROVIDER=brevo` and provide the `EMAIL_SMTP_*` and `EMAIL_FROM_ADDRESS` values. The default `console` provider logs notifications instead of sending them.
|
||||
- Web Push: set `PUSH_PROVIDER=web-push` and provide the VAPID keys. Generate them with `npx web-push generate-vapid-keys`.
|
||||
- Platform super administrator: `SUPER_ADMIN_EMAIL`, `SUPER_ADMIN_PASSWORD`, `SUPER_ADMIN_FIRST_NAME`, and `SUPER_ADMIN_LAST_NAME`
|
||||
|
||||
Do not commit `.env.local`, production environment files, OAuth secrets, SMTP credentials, VAPID private keys, or encryption keys.
|
||||
|
||||
## Application Flow
|
||||
|
||||
The application has a deliberate first-run gate:
|
||||
|
||||
1. A fresh database is migrated by Prisma.
|
||||
2. Requests are redirected to `/install`, except for the installer and required static/PWA assets.
|
||||
3. The installer validates `INSTALLATION_TOKEN`, then creates the business, primary location, settings, hours, booking rules, loyalty configuration, starter services, owner role, and first administrator in one transaction.
|
||||
4. After installation, `/staff` and `/owner` require authentication and permissions. Customer browsing and booking routes remain available according to the business booking rules.
|
||||
|
||||
The installation token is checked server-side and rate-limited. It is never stored in the database. The installer is one-way for a deployment: to start over locally, remove the local database volume or use a separate database rather than attempting to reinstall over existing data.
|
||||
|
||||
## Database and Prisma
|
||||
|
||||
Useful commands:
|
||||
|
||||
```bash
|
||||
npm run prisma:generate # Generate the client in generated/prisma
|
||||
npm run prisma:migrate # Create/apply a development migration
|
||||
npm run prisma:studio # Open Prisma Studio
|
||||
npm run prisma:seed # Load idempotent development fixtures
|
||||
```
|
||||
|
||||
For a deployed database, the container entrypoint runs:
|
||||
|
||||
```bash
|
||||
npx prisma migrate deploy
|
||||
npx tsx scripts/ensure-super-admin.ts
|
||||
```
|
||||
|
||||
Migrations are applied on every application container start and are not destructive. The optional seed is separate because demo data is not an appropriate production bootstrap mechanism.
|
||||
|
||||
## Quality Checks and Tests
|
||||
|
||||
Run the fast checks first:
|
||||
|
||||
```bash
|
||||
npm run typecheck
|
||||
npm run lint
|
||||
npm test
|
||||
```
|
||||
|
||||
Additional commands:
|
||||
|
||||
```bash
|
||||
npm run test:watch
|
||||
npm run test:integration
|
||||
npm run test:e2e
|
||||
npm run build
|
||||
```
|
||||
|
||||
Unit tests do not require a database. Integration tests require a disposable MySQL 8 database configured through the `DATABASE_*` variables. They apply migrations but the caller is responsible for providing the database.
|
||||
|
||||
The Playwright suite starts a development server on port `3100`, applies migrations, loads the demo seed, and provisions test credentials. Point it at a throwaway database, never a database containing real business data:
|
||||
|
||||
```bash
|
||||
DATABASE_HOST=localhost \
|
||||
DATABASE_PORT=3306 \
|
||||
DATABASE_NAME=nextchair_e2e \
|
||||
DATABASE_USER=nextchair \
|
||||
DATABASE_PASSWORD='replace-me' \
|
||||
npm run test:e2e
|
||||
```
|
||||
|
||||
Playwright uses one Chromium project, one worker, a 45-second test timeout, screenshots on failure, and retains traces on failure. The database should be reset or recreated between isolated test runs when fixtures from previous runs could affect results.
|
||||
|
||||
## VPS Deployment Reference
|
||||
|
||||
The repository also contains a shared multi-business deployment model:
|
||||
|
||||
- `docker-compose.infra.yaml` runs one Traefik reverse proxy and one shared MySQL 8 server.
|
||||
- Each business gets its own database and MySQL user, not a shared schema.
|
||||
- `docker-compose.blake-heritage.yaml` and `docker-compose.marios.yaml` are per-business application examples.
|
||||
- `.env.business.example` is the template for each business environment file.
|
||||
|
||||
The high-level setup is:
|
||||
|
||||
1. Create the external network and configure `.env.infra`.
|
||||
2. Start the shared infrastructure:
|
||||
|
||||
```bash
|
||||
docker network create next-chair-edge
|
||||
docker compose -f docker-compose.infra.yaml --env-file .env.infra up -d
|
||||
```
|
||||
|
||||
3. Create a separate database and user for the business on the shared MySQL server.
|
||||
4. Copy `.env.business.example` to `.env.business.<slug>` and set the domain, database credentials, secrets, and `NEXT_PUBLIC_APP_URL`.
|
||||
5. Start the business compose file:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.blake-heritage.yaml \
|
||||
--env-file .env.business.blake-heritage up -d --build
|
||||
```
|
||||
|
||||
Traefik terminates HTTPS with Let's Encrypt and routes by the configured `DOMAIN`. Read the comments in [docker-compose.infra.yaml](./docker-compose.infra.yaml) for the exact database provisioning sequence and operational assumptions.
|
||||
|
||||
## Repository Layout
|
||||
|
||||
```text
|
||||
src/app/ Next.js routes, layouts, and API handlers
|
||||
src/features/ Feature workflows, server actions, schemas, components
|
||||
src/services/ Domain services and external-provider integrations
|
||||
src/lib/ Shared infrastructure, auth, database, security, and utilities
|
||||
prisma/ Schema, migrations, and development seed
|
||||
e2e/ Playwright browser journeys and test setup
|
||||
docker/ Container entrypoint
|
||||
docs/ Phase implementation notes and historical decisions
|
||||
templates/ UI reference templates
|
||||
```
|
||||
|
||||
The phase documents in `docs/` contain implementation history and deeper design rationale. They are useful when changing installation, scheduling, notification, calendar, media, or deployment behavior, but this README is the intended starting point for running the project.
|
||||
|
||||
## Common Troubleshooting
|
||||
|
||||
**The app redirects to `/install`.** This is expected for a fresh or unseeded database. Set `INSTALLATION_TOKEN` and complete the wizard, or explicitly run the development seed.
|
||||
|
||||
**The app cannot connect to MySQL in Docker.** Use `DATABASE_HOST=next-chair-mysql`, not `localhost`, and ensure the `DATABASE_*` credentials match `MYSQL_DATABASE`, `MYSQL_USER`, and `MYSQL_PASSWORD`.
|
||||
|
||||
**The app cannot connect to MySQL when run with `npm run dev`.** Use `DATABASE_HOST=localhost` or the hostname of your external MySQL server, and verify that the database and user already exist.
|
||||
|
||||
**Port 3000 is already in use.** Set `APP_HOST_PORT` in `.env.local` for Docker, or start Next.js with `npm run dev -- --port 3001` for host development. Update `NEXT_PUBLIC_APP_URL` when callback URLs need to match the new port.
|
||||
|
||||
**Emails or push notifications are not delivered.** The default providers are console-only. Configure the relevant provider and credentials before expecting external delivery.
|
||||
|
||||
**A reset is needed during local development.** Use a separate database or `docker compose ... down -v` for the local Docker stack. This removes local database and media data and should never be used against production volumes.
|
||||
|
||||
## License
|
||||
|
||||
The package currently declares the ISC license in `package.json`. Confirm the intended distribution license before publishing the application or its assets.
|
||||
Reference in new issue
Block a user