4.4 KiB
4.4 KiB
NGU-Web
Website for NGU (Next Generation of Unity), a Unity movement organization with regional chapters in the US and internationally. Full-stack app with a public site and a role-based admin panel.
Stack
- Frontend: React, TypeScript, Tailwind CSS, React Router, Vite (in
src/) - Backend: Hono on Node.js, SQLite (WAL mode, STRICT tables) via better-sqlite3 / node:sqlite (in
server/) - Package manager: pnpm only (never npm or yarn)
Deployment (production)
- Ubuntu VPS, nginx reverse proxy, systemd service
ngu-api - App deployed to
/srv/ngu-api; database at/var/lib/ngu/ngu.db - Debugging: check
journalctl -u ngu-api -n 40 --no-pagerfirst. Make sure rsync ran from the repo (not the deployed copy) before restarting the service. - Never run commands against the production server or database unless explicitly asked.
Git workflow
- Remote is a self-hosted Forgejo server, not GitHub. Do not use
gh. - Open pull requests with
tea:tea pr create --base main --head <branch> --title "..." --description "..." - Never commit directly to main. Create a branch per change, push it, open a PR.
- Versions are marked with annotated tags (v1.0, v1.3...). Don't create or move tags unless asked.
server/dev.dband other*.dbfiles are local only and never committed.
How to work in this repo
- Read the relevant existing files before writing anything. Follow existing patterns exactly: descriptors, field syntax, extension shape, import conventions.
- Ask questions up front before implementing non-trivial features.
- Prefer targeted edits when surrounding code is stable; full rewrites only when a component is being substantially reworked.
- Fix root causes. No redirect shims or workarounds.
- Keep data logic in the database and presentation logic in code. Make things configurable via constants, not hardcoded in components.
- Name components for what they do, not what they currently filter.
Project layout
- All pages use
PageShell.tsxas the wrapper unless explicitly noted otherwise. - Pages live in
src/pages/; section-level components go insrc/pages/sections/. src/data/holds only hardcoded data shared across multiple section files (e.g.historyDecades.ts, map grid). Everything else comes from SQLite.navConfig.jsis the single source of truth for navigation, routes, and actions (header, footer, pages).api.jsis the shared caching client used by frontend data hooks.- Logos: org logos in
/org-logos/, event logos inpublic/event-logos/. The<Logo>component hides itself on load error.
Rules and gotchas
- Role checks must use ladder comparisons, never equality. Roles rank viewer → editor → admin → superadmin. Use
roleCanWrite(user)/roleCanDelete(user)fromlib/rolesand the minimum-rank helpers (canWrite,canDelete,isSuper,atLeast).role === "admin"silently excludes higher roles and has caused repeated bugs. - Imports need explicit extensions (
.ts,.tsx,.js) everywhere. - Vite resolves
.jsbefore.ts, so a.jsand.tsfile with the same base name will import the wrong one. Give new hooks distinct names. - Don't use
fallback: EMPTYin api.js hooks. It silently returns empty arrays and hides server errors; let the error state surface.
Admin CRUD engine
Descriptor-driven: server/admin-crud.js and admin-schema.js (server) and adminSchema.js (client) generate SQL and form fields from declarative entity configs. Adding an entity should mean adding a descriptor, not new CRUD code.
- Child collections are deleted and reinserted wholesale. Unsafe for entities referenced by foreign keys elsewhere.
reindex: falseprevents cross-entity sort order collisions.- The
OMITsentinel distinguishes unsent fields from deliberate clears. admin-schema-sync.jsruns at boot and throws if descriptors don't match livePRAGMA table_info. If boot fails after a schema change, update the descriptor or migration so they agree.admin-cli.jsimportsROLESanddestroyAllSessionsForfromauth.js. Keep it that way to prevent drift.
Migrations
- Sequential files:
001_,002_, ... - The runner may drop statements after a
BEGIN...ENDtrigger body. Put eachCREATE VIEWin its own migration file with noBEGIN...ENDblock. PRAGMA foreign_keys = OFFmust be set outside transactions when cascading constraints are involved.
Integrations
- Church Center (ngu.churchcenteronline.com): Planning Center embeds for giving and the calendar.