# gerbeur A small invite-only social platform for sharing links and files. Users can post URLs and media (YouTube, SoundCloud, Bandcamp, images, audio, video, …), vote, comment, follow each other, build playlists, and search content. A real-time WebSocket layer handles live presence, vote counts, and notifications. The UI is localized (English and French) and ships multiple visual themes. ## Stack | Layer | Technology | | -------- | ------------------------------------------------- | | Runtime | [Deno](https://deno.com) 2.x | | API | [Oak](https://jsr.io/@oak/oak) (HTTP + WebSocket) | | Database | SQLite via `node:sqlite` | | Frontend | React 19 + Vite 8 | ## Development ### Prerequisites - Deno 2.x ### Setup ```sh cp .env.example .env # Edit .env: set GERBEUR_JWT_SECRET to the output of: openssl rand -hex 32 ``` ### Run ```sh deno task dev ``` This starts both the API server (port 8000, with file watching) and the Vite dev server (port 3000) concurrently. Open [http://localhost:3000](http://localhost:3000). On first run a default `admin` / `admin` account is created — change the password immediately. ### Environment variables See [`.env.example`](.env.example) for the full list with descriptions. Key variables: | Variable | Description | Default | | ---------------------------- | ----------------------------------------------------------------------------------------------------- | ----------------------- | | `GERBEUR_JWT_SECRET` | JWT signing secret — **required**, generate with `openssl rand -hex 32` | — | | `GERBEUR_PROTOCOL` | Protocol the API server listens on (`http` or `https`) | `http` | | `GERBEUR_HOSTNAME` | Public hostname used in generated URLs (e.g. OG image tags) | `localhost` | | `GERBEUR_PORT` | API server port | `8000` | | `GERBEUR_LISTEN_HOST` | Network interface Oak binds to; use `127.0.0.1` to restrict to loopback | `0.0.0.0` | | `GERBEUR_ALLOWED_ORIGINS` | Comma-separated list of extra allowed frontend origins; the server's own `BASE_URL` is always allowed | `http://localhost:3000` | | `GERBEUR_FRONTEND_URL` | Frontend base URL used in email links (e.g. password reset); defaults to the API's own `BASE_URL` | `BASE_URL` | | `GERBEUR_SITE_NAME` | Site name used in OG meta tags and emails | `gerbeur` | | `GERBEUR_SMTPS_URL` | SMTPS connection URL for outgoing email (`smtps://user:pass@host:465`) | unset | | `GERBEUR_FROM_EMAIL` | Sender address for outgoing emails — required when `GERBEUR_SMTPS_URL` is set | unset | | `GERBEUR_WELCOME_EMAIL_BODY` | Markdown body for the account-creation welcome email; supports `{{username}}` and `{{site_name}}` | built-in template | | `VITE_API_PROTOCOL` | API protocol baked into the frontend bundle (see [Production](#production)) | `http` | | `VITE_API_HOSTNAME` | API hostname baked into the frontend bundle | `localhost` | | `VITE_API_PORT` | API port baked into the frontend bundle | `8000` | ## Production ### Docker (recommended) The standard deployment runs API and frontend in a single container. The API server (Oak) serves the compiled frontend as static files, so both share the same origin — no `VITE_API_*` build args needed. The server's own `BASE_URL` is always allowed for HTTP/WebSocket requests automatically. ```sh docker build -t gerbeur . docker run -d \ -p 8000:8000 \ -v gerbeur-db:/app/api/sql \ -v gerbeur-uploads:/app/api/uploads \ -e GERBEUR_JWT_SECRET=$(openssl rand -hex 32) \ -e GERBEUR_PROTOCOL=https \ -e GERBEUR_HOSTNAME=example.com \ -e GERBEUR_PORT=8000 \ --name gerbeur \ gerbeur ``` The two volumes are required for persistence: - `gerbeur-db` — SQLite database (`api/sql/gerbeur.db`), initialized automatically on first run - `gerbeur-uploads` — user-uploaded files (`api/uploads/`) #### Separate API and frontend (optional) If you need to run the API on a different host than the frontend, pass the API location as build args so it gets baked into the frontend bundle. In that setup, add the frontend origin to `GERBEUR_ALLOWED_ORIGINS` so cross-origin HTTP/WebSocket requests are accepted: ```sh docker build \ --build-arg VITE_API_PROTOCOL=https \ --build-arg VITE_API_HOSTNAME=api.example.com \ --build-arg VITE_API_PORT=443 \ -t gerbeur . ``` ### Reverse proxy Put a reverse proxy (nginx, Caddy, …) in front of the container to handle TLS. Forward everything to port 8000. Example Caddyfile: ``` example.com { reverse_proxy localhost:8000 } ``` ## Project structure ``` api/ main.ts # Entry point — Oak application, middleware, routes config.ts # Environment variables middleware/ # errorMiddleware, authMiddleware routes/ # HTTP routes + WebSocket services/ # Business logic model/ # Database schema, row types, type guards lib/ # JWT, pagination, upload helpers, … sql/ schema.sql # Database schema init.ts # First-run database initialisation gerbeur.db # SQLite database (not committed) uploads/ # User-uploaded files (not committed) src/ # React frontend (Vite) config/api.ts # API base URL, validation constants pages/ # Route-level components components/ # Shared UI components contexts/ # Auth, WebSocket, player, follows, theme hooks/ # Data fetching and UI hooks locales/ # Lingui message catalogues (en, fr) themes/ # Per-theme CSS files i18n.ts # Lingui runtime setup public/ # Static assets (favicon, icon sprite) ```