fix: alot
CI / build-and-test (push) Canceled after 0s

This commit is contained in:
2026-10-19 21:41:07 +02:00
parent df90cf8a18
commit fa38ac7617
70 changed files with 1389 additions and 1023 deletions
+6 -6
View File
@@ -24,17 +24,17 @@
**Rationale:** Provides security (tokens can be revoked) and aligns with OAuth2 best practices for refresh token management.
## 5. SQLite for Development
## 5. PostgreSQL Everywhere
**Decision:** SQLite is the default development database.
**Decision:** PostgreSQL is used in development, CI and production.
**Rationale:** Zero configuration, fast for local development, and Prisma abstracts most differences. PostgreSQL migration path is documented in production.md.
**Rationale:** One database means one set of migrations; the schema uses JSON columns and serializable transactions that SQLite doesn't support the same way.
## 6. No WebSockets
## 6. Invalidation-Only Live Updates
**Decision:** Dashboard uses polling (TanStack Query) instead of WebSockets.
**Decision:** The API pushes topic names (`tabs`, `commands`, ...) over Socket.IO; the dashboard refetches that data over the normal REST API. It falls back to polling while the socket is down.
**Rationale:** Simpler implementation, adequate for the expected update frequency (5-30 second intervals), and avoids WebSocket server infrastructure.
**Rationale:** Events never carry data, so a socket can't see anything its owner couldn't fetch, and the REST API stays the single source of truth.
## 7. In-Memory Rate Limiting
+3 -2
View File
@@ -10,7 +10,7 @@ KoolTap is a modular monolith designed for remote tab control in venues.
- REST API with versioned endpoints (`/api/v1`)
- JWT authentication for dashboard users
- Bearer token authentication for devices
- SQLite database via Prisma ORM
- PostgreSQL database via Prisma ORM
- Scheduled command expiry job
- In-memory rate limiting
@@ -19,7 +19,8 @@ KoolTap is a modular monolith designed for remote tab control in venues.
- TanStack Query for API data fetching
- shadcn/ui component library
- Tailwind CSS styling
- Secure HttpOnly cookie-based auth
- Tokens kept in cookies (see security.md)
- Socket.IO connection for live updates, polling as fallback
### Shared Package
- TypeScript types and constants
+1 -2
View File
@@ -54,9 +54,8 @@ pnpm db:reset # Reset database and re-seed
## Environment Variables
### API (`apps/api/.env`)
- `DATABASE_URL` - SQLite path (default: `file:./dev.db`)
- `DATABASE_URL` - PostgreSQL connection URL (`docker compose up postgres` starts one)
- `JWT_SECRET` - JWT signing secret
- `JWT_REFRESH_SECRET` - Refresh token secret
- `PORT` - API port (default: 3001)
- `CORS_ORIGIN` - Allowed CORS origin (default: http://localhost:3000)
+8 -33
View File
@@ -12,9 +12,8 @@ Create `.env` in the project root:
# Database
DB_PASSWORD=<generate-a-strong-password>
# JWT secrets — generate with: openssl rand -hex 64
# JWT secret — generate with: openssl rand -hex 64
JWT_SECRET=<64-char-hex-string>
JWT_REFRESH_SECRET=<64-char-hex-string>
# URLs — set your actual domain/port
DASHBOARD_URL=https://dashboard.yourdomain.com
@@ -29,16 +28,16 @@ API_PORT=3001
docker compose -f docker-compose.prod.yml up --build -d
```
### 3. Seed the database (first time only)
### 3. Create the venue and admin (first time only)
Migrations run automatically when the API container starts.
```bash
# Generate Prisma client and run migrations
docker compose -f docker-compose.prod.yml exec api npx prisma migrate deploy
# Seed demo data (or create your own venue/users)
docker compose -f docker-compose.prod.yml exec api npx ts-node prisma/seed.ts
docker compose -f docker-compose.prod.yml exec api node prisma/setup.js "My Venue" admin@myvenue.com '<strong password>'
```
`setup.js` does nothing when a venue already exists. `--force` wipes ALL data and starts over.
### 4. Set up a reverse proxy (nginx/Caddy)
Example Caddyfile:
@@ -70,22 +69,11 @@ GRANT ALL PRIVILEGES ON DATABASE kooltap TO kooltap;
```env
DATABASE_URL="postgresql://kooltap:your-password@localhost:5432/kooltap"
JWT_SECRET="<random-64-char-string>"
JWT_REFRESH_SECRET="<random-64-char-string>"
PORT=3001
CORS_ORIGIN="https://dashboard.yourdomain.com"
```
### 3. Update Prisma schema for PostgreSQL
In `apps/api/prisma/schema.prisma`, change:
```prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
```
### 4. Build and run
### 3. Build and run
```bash
cd apps/api
@@ -98,19 +86,6 @@ node dist/main
---
## Migration Steps: SQLite → PostgreSQL
1. Update `schema.prisma` to use `postgresql` provider
2. Update `DATABASE_URL` environment variable
3. Run `prisma migrate dev` to create initial PostgreSQL migration
4. Export SQLite data and import to PostgreSQL (using `prisma db pull` + manual script, or insert via seed)
5. Run `prisma migrate deploy` on production
Key differences to handle:
- SQLite uses `Int` for booleans → use `Boolean` in PostgreSQL
- SQLite uses `String` for JSON → use `Json` type in PostgreSQL
- UUID generation is handled by Prisma on both
## Security Checklist
- [ ] JWT secrets are 64+ random hex characters
+8 -7
View File
@@ -11,15 +11,16 @@
### Client Authentication
- Passwords hashed with bcrypt (10 rounds)
- JWT access tokens (24h expiration)
- Refresh tokens with rotation and revocation
- Tokens stored in HttpOnly cookies on dashboard
- JWT access tokens (15 minute expiration)
- Refresh tokens (30 days) with rotation and revocation; a refresh token works only once
- The dashboard keeps both tokens in `SameSite=Lax` cookies (`Secure` over HTTPS). They are readable by
the dashboard's JavaScript, so an XSS bug would expose them; the short access token lifetime limits the damage
## Authorization
### Role-Based Access
- `admin`: Full access including device management and revocation
- `viewer`: Read-only access to tabs, devices, and commands
- `admin`: Full access, including users, device pairing, planned customers and sending reminders
- `viewer` (shown as staff): views tabs and commands and can close tabs
### Venue Isolation
- Every user and device is scoped to exactly one venue
@@ -34,9 +35,9 @@
- Body size limits enforced
### Rate Limiting
- Device sync: 1 request per 30 seconds per device
- Command creation: 10 requests per minute per user
- Login and device registration: 10 requests per minute per client
- General: 100 requests per minute per client
- Device sync and command acks are not rate limited (they are authenticated by the device token)
### Error Handling
- Consistent error format never exposes internals