diff --git a/PRODUCTION.md b/PRODUCTION.md new file mode 100644 index 0000000..da51c6a --- /dev/null +++ b/PRODUCTION.md @@ -0,0 +1,66 @@ +# Production Notes + +## Build + +Frontend: + +```sh +cd cshare-app +npm ci +npm run check +``` + +`npm run build` also creates `.gz` and `.br` siblings for compressible files in `dist/`. + +Backend: + +```sh +cd backend +npm ci +npm run check +NODE_ENV=production npm start +``` + +## Required Environment + +Set these before starting the backend in production: + +```sh +NODE_ENV=production +PORT=4001 +FRONTEND_URL=https://cshare.app +PUBLIC_APP_URL=https://cshare.app +JWT_ACCESS_SECRET=<64+ random chars> +JWT_REFRESH_SECRET=<64+ random chars> +ADMIN_API_KEY=<64+ random chars> +PREVIEW_MAX_FILE_SIZE_BYTES=25000000 +DATA_DIR=/var/lib/cshare +``` + +Set `VITE_API_BASE_URL` for the frontend only when the API is not served from the same origin. If omitted in production, the frontend calls `window.location.origin`, which works with an Nginx reverse proxy that forwards `/api` to the backend. + +## Runtime + +- Serve `cshare-app/dist/` with Nginx or another static host. +- Cache hashed files under `/assets/` aggressively. +- Serve precompressed `.br` first and `.gz` second when the client supports them. +- Do not cache `/index.html`; it must be revalidated so deploys pick up new asset hashes. +- Proxy `/api`, `/health`, and file download routes to the backend. +- Keep `backend/data/` outside the repo and backed up if using the local disk adapter. +- Protect every `/api/admin/*` request with `x-admin-key: $ADMIN_API_KEY`. +- Small safe file previews are served from `/api/v1/links/:linkId/preview`; files over `PREVIEW_MAX_FILE_SIZE_BYTES` remain download-only. +- The admin API is disabled unless `ADMIN_API_KEY` is set. + +Example Nginx compression settings: + +```nginx +brotli on; +brotli_static on; +gzip on; +gzip_static on; +gzip_types text/css application/javascript application/json image/svg+xml; +``` + +`brotli_static` requires an Nginx build with the Brotli module. If that module is unavailable, keep `gzip_static on` and serve the generated `.gz` files. + +The current backend is production-shaped but still uses local JSON and disk storage. For durable production use, replace it with the PostgreSQL, Redis, and R2 adapters described in `CONTINUE.md`.