Documents gerbeur, a small invite-only social platform for sharing links and files. The repository holds three parts: the backend in api/, the frontend in src/, and in shared/ whatever crosses the wire between them. Every module is listed by path, so the list groups them by part and layer.

Backend, in api/

An Oak server over SQLite answers the API and the WebSocket, and serves the built frontend. api/main.ts builds the application and listens. A request goes to a route, which parses it, calls services and answers. The services hold the logic and every SQL query, read rows through the model, and reach other sites only through remote/. Each folder holds one kind of module, and each file is named after its domain.

  • routes/ holds the HTTP API, one router per resource. Each router's page lists its endpoints, with their method and path, who may call them, and what they do; API.md at the root sums them up, with the rule they all follow.
  • middleware/ holds what runs around the routes, which is auth, errors and the Open Graph tags.
  • services/ holds the logic and every SQL query, one module per domain.
  • model/ holds each table's row type, guard and mappers.
  • validators/ holds the guards of the request bodies.
  • remote/ is the only place that fetches from other sites, and its providers/ build the previews of each kind of site.
  • mail/ renders each email, one template per email on a shared layout that takes every site style, its colors and fonts read from the themes' stylesheets, and services/email-service.ts sends them.
  • lib/ holds the helpers that belong to no layer.
  • db/ opens, creates and migrates the database, and cleans it up at startup. db/migrations/ are applied in order, each once.
  • config.ts reads the settings from the environment, and scripts/ holds the icon generator.

Shared, in shared/

The types and constants both halves use sit here, one module per domain. They are the API envelope, each domain's types and request bodies, the socket protocol, the validation limits, the role permissions, and the page paths and slugs (paths.ts, slug.ts) the client links with and the server matches. shared/ imports nothing from either half. The server sends Dates, and the client reads the same types through Wire<T>, where every Date is an ISO string.

Beside it, locales/ holds one Lingui catalogue per language for the whole app: the interface's messages, and the emails' that api/mail/i18n.ts loads compiled. The interface writes its English in place (t, Trans) and Lingui keys the message by it. A message with a named id, which every email's is, along with the labels the interface shares with them, has its English in locales/en.po alone and the code names only the id; the build fails when a language lacks one.

Frontend, in src/

The React app is built with Vite and translated with Lingui. main.tsx mounts it, App.tsx holds the providers and the route table, and i18n.ts sets the language. A page reads the API and the socket through contexts and hooks, and draws with components, while utils hold what isn't React at all.

  • pages/ holds one component per route, each naming its route, and the two guards that keep routes to signed-in or signed-out visitors.
  • config/ holds the settings read at startup.
  • contexts/ holds, for each domain, a FooContext with its state and the FooProvider that fills it.
  • hooks/ holds every hook, and components/ only components.
  • utils/ holds every module that isn't React.

Every layer but pages/ and config/ is split into the same domains (auth, category, chat, comment, dump, editor, feed, form, layout, player, playlist, profile, social, theme, ws, and shared for what belongs to no one domain), so the chat lives in components/chat/, contexts/chat/, hooks/chat/ and utils/chat/.