From 48817203045a7332635e3ce01cc74b541f11a289 Mon Sep 17 00:00:00 2001 From: rob thijssen Date: Wed, 22 Apr 2026 12:23:47 +0300 Subject: [PATCH] docs(generic): clarify frontend directory naming is not fixed to "web/" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "web/" folder name in §4 was being read as a required convention, but projects routinely use ui/, dashboard/, or admin/ instead — and may have more than one frontend in the same repo. Document the common names, note that each frontend is an independent Vite app, and add guidance on sharing types across multiple frontends. Co-Authored-By: Claude Opus 4.7 (1M context) --- generic.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/generic.md b/generic.md index 89cdff6..0137412 100644 --- a/generic.md +++ b/generic.md @@ -22,7 +22,7 @@ Projects are Rust cargo workspaces. The repository root contains: │ ├── -api/ # binary: REST / JSON / WebSocket daemon │ ├── -worker/ # binary: long-running processor / queue consumer │ └── -cli/ # binary: operator / admin CLI -├── web/ # Vite + React + SWC + TS frontend (when applicable) +├── / # Vite + React + SWC + TS frontend(s) — see §4 ├── asset/ # deployment artifacts (see §6) ├── script/ # deploy.sh and related operational scripts └── README.md @@ -130,10 +130,19 @@ API and worker binaries are managed by systemd unit files shipped from `asset/sy ## 4. Frontends ### Web (default) -Vite + React + SWC + TypeScript, in `/web/`: +Vite + React + SWC + TypeScript. The frontend lives in a top-level directory named for what it *is* to the user, not a generic `web/`. Common names: + +- `web/` — the primary public-facing app, when there's only one frontend +- `ui/` — equivalent, when the project prefers this naming +- `dashboard/` — a user-facing dashboard UI +- `admin/` — an operator/admin console distinct from the public UI + +A project may have more than one of these (e.g., both `dashboard/` and `admin/`). Each is an independent Vite app with its own `package.json`, built and deployed separately, typically served from its own nginx `server_name` or path prefix. Pick names that describe the audience or purpose; don't invent a generic wrapper directory to hold them. + +Whatever the directory is called, the internal structure is the same: ``` -web/ +/ ├── package.json ├── vite.config.ts ├── tsconfig.json @@ -150,6 +159,7 @@ web/ - Build output is static. Deployed to an nginx CDN endpoint — no Node.js in production. - API base URL is configured at build time (Vite `import.meta.env.VITE_API_BASE_URL`) and stamped per environment during deploy. - Prefer React Query or equivalent for server state. Keep business logic server-side; the frontend is a rendering and interaction layer. +- When a project has multiple frontends, they may share types via a local package (e.g., `packages/shared-types/`) or via generated TypeScript bindings from the Rust `entities` crate. Don't duplicate API clients across frontends — factor the shared bits out. ### Web (Rust framework exception) Use a Rust web framework (Axum + templating, or a fullstack framework) **only when** the deployment model requires a single self-contained binary with no external web server — e.g., distributed orchestration nodes that each serve their own UI over TLS. The Cichlid pattern. Default is still Vite + nginx. @@ -516,7 +526,7 @@ When scaffolding or extending a project: 5. Any new deployable component gets an entry in `asset/manifest.yml`, a systemd unit in `asset/systemd/`, a sysusers drop-in, a firewalld service XML, and any required SELinux assets — in the same change. 6. Config templates go in `asset/config/` with `{{PLACEHOLDER}}` secrets. Never commit a rendered config. 7. Postgres connections are mTLS, passwordless. If writing connection code that accepts a password, stop and ask. -8. Frontend is Vite + React + SWC + TS, served as static assets from nginx. Rust web frameworks require a stated reason. +8. Frontends are Vite + React + SWC + TS, served as static assets from nginx. Name the directory after its audience (`web/`, `ui/`, `dashboard/`, `admin/`) — `web/` is not a mandated convention. Rust web frameworks require a stated reason. 9. Services run as dedicated non-root users with hardened systemd units per §8. Root requires explicit justification. 10. Every listening port gets a named firewalld service per §9. No bare `--add-port` calls. 11. SELinux stays enforcing. Work with the default policy first; ship a custom module only when necessary (§10). Never suggest `setenforce 0`.