diff --git a/README.md b/README.md deleted file mode 100644 index cfc6dad..0000000 --- a/README.md +++ /dev/null @@ -1,456 +0,0 @@ -# Rabbi Gerzi - -Rabbi Gerzi is the public website and Torah media application for Rabbi -Yehoshua Gerzi. The site presents Rabbi Gerzi's work, projects, and Torah -media, with an authenticated admin area for managing media sets, nested -elements, rich text, YouTube links, icons, and PDF uploads. - -The application is split into: - -- `backend/`: Laravel 13, PHP 8.3, PostgreSQL, PHPUnit. -- `frontend/rabbi_gerzi/`: Vue 3, Vite, TypeScript, Pinia, Cypress. -- `nix/`: Nix packages, checks, upload limits, and the NixOS service module. - -Production assumptions: - -- Website: `https://rabbigerzi.com` -- API: `https://api.rabbigerzi.com` - -All local and deployment workflows are Nix-first. Run commands from -`nix develop`, or use direnv with the checked-in `.envrc`. - -## How the Application Works - -### Content and Public Browsing - -Torah media is organized into sets. Each set has a name, description, icon, -and one root element. Elements form an ordered tree: the root can have child -elements, and each child can have further children. - -Visitors browse the available sets at `/media`. Opening a set loads its root -element, where the visitor can move through parent, child, and sibling links. -An element can present an icon, formatted rich text, an embedded YouTube video, -and short or long PDFs. The PDF viewer supports page navigation, zoom, search, -download, printing, and opening the source document in a new tab. - -### Admin Editing - -Administrators sign in at `/login`. An authenticated session reveals the -management controls on `/media` and protects all content-changing API calls. - -From the media page, an administrator can: - -- Create a set with a name, description, and icon. This also creates its root - element and opens that element in edit mode. -- Edit an existing set's card details or replace its icon. -- Delete a set together with its complete element tree and managed uploads. - -The `Edit Element` action on a public element opens its admin editor. The -editor supports: - -- Editing the title, description, rich text, and YouTube URL. Rich text - includes headings, inline formatting, alignment, lists, links, and tables. -- Uploading or removing an icon, a short PDF, and a long PDF. -- Adding child elements, moving direct children up or down, and opening a - child directly in edit mode. -- Removing a child together with all of its descendants and managed uploads. - -Title, description, rich text, and YouTube changes are written by the `Save` -action. Icon and PDF changes are uploaded or removed immediately. The -`View Element` action returns to the public presentation, and `Logout` ends -the admin session. Development login credentials are listed under -[Local Development](#local-development). - -## Required Secrets and Environment - -Do not commit real secret values. Local examples can live in ignored `.env` -files, and production secrets should live outside the Nix store. - -### Required Production Secrets - -The NixOS module requires -`services.rabbi-gerzi.backend.environmentFile`. That file must contain: - -```dotenv -APP_KEY=base64:replace-with-laravel-app-key -RABBI_GERZI_INITIAL_ADMIN_EMAIL=admin@example.com -RABBI_GERZI_INITIAL_ADMIN_PASSWORD=replace-with-a-long-random-password -``` - -- `APP_KEY` encrypts and signs Laravel application data. -- `RABBI_GERZI_INITIAL_ADMIN_EMAIL` is the first admin login email. -- `RABBI_GERZI_INITIAL_ADMIN_PASSWORD` is used only to create that admin if - it does not already exist. - -Generate an app key inside the dev shell: - -```sh -cd backend -php artisan key:generate --show -``` - -### Public Frontend Config - -The frontend build needs the public API origin: - -```dotenv -VITE_API_BASE_URL=https://api.rabbigerzi.com -``` - -For NixOS deployment this is handled by -`services.rabbi-gerzi.frontend.apiBaseUrl`, which defaults to the backend -public URL. - -### NixOS Runtime Config - -The NixOS module supplies these non-secret runtime values: - -| Setting | Production value | -|---|---| -| `APP_ENV` | `production` | -| `APP_DEBUG` | `false` | -| `APP_URL` | `https://api.rabbigerzi.com` | -| `CORS_ALLOWED_ORIGINS` | `https://rabbigerzi.com` by default | -| `DB_CONNECTION` | `pgsql` | -| `DB_HOST` | `/run/postgresql` | -| `DB_DATABASE` | `rabbi-gerzi` by default | -| `DB_USERNAME` | `rabbi-gerzi` by default | -| `FILESYSTEM_DISK` | `public` | -| `LARAVEL_STORAGE_PATH` | `/var/lib/rabbi-gerzi/storage` by default | -| `SESSION_DRIVER` | `database` | -| `SESSION_DOMAIN` | empty by default, meaning host-only cookies | -| `SESSION_SAME_SITE` | `none` by default | -| `SESSION_SECURE_COOKIE` | `true` by default | -| `CACHE_STORE` | `file` | -| `QUEUE_CONNECTION` | `sync` | -| `MAIL_MAILER` | `log` | - -The module also manages PostgreSQL, php-fpm, nginx, writable storage/cache -directories, and the 6 MB upload limit for nginx and PHP. - -### Local Environment Files - -Backend local configuration starts from: - -```sh -cd backend -cp .env.example .env -php artisan key:generate -``` - -The checked-in example already targets the Nix dev PostgreSQL socket through -`PGHOST`, and uses the `postgres` local database/user. - -Frontend local configuration should point Vite at the local backend: - -```sh -cd frontend/rabbi_gerzi -printf '%s\n' 'VITE_API_BASE_URL=http://127.0.0.1:8000' > .env.local -``` - -## Local Development - -Enter the Nix dev shell before running local development commands: - -```sh -nix develop -``` - -If direnv is installed: - -```sh -direnv allow -``` - -The dev shell provides PHP, Composer, Node, PostgreSQL, Cypress, -process-compose, TypeScript tooling, formatters, linters, and Nix formatters. -It also initializes a local PostgreSQL cluster in `.postgres` when needed. - -Install dependencies: - -```sh -cd backend -composer install - -cd ../frontend/rabbi_gerzi -npm install --legacy-peer-deps -``` - -Create local environment files: - -```sh -cd ../../backend -cp .env.example .env -php artisan key:generate - -cd ../frontend/rabbi_gerzi -printf '%s\n' 'VITE_API_BASE_URL=http://127.0.0.1:8000' > .env.local -``` - -Start the local services from the repo root: - -```sh -process-compose up -``` - -This starts: - -- PostgreSQL on the local Unix socket under `.postgres`. -- Laravel at `http://127.0.0.1:8000`. -- Vite at `http://localhost:5173`. - -In another dev-shell terminal, prepare the database: - -```sh -cd backend -php artisan migrate:fresh --seed -``` - -The seed admin login is: - -```text -admin@rabbigerzi.test -password123!@# -``` - -## Testing - -Run local tests from `nix develop`. To run the entire local test suite, keep -the app stack running in one dev-shell terminal: - -```sh -nix develop -process-compose up -``` - -Then run backend, frontend, and Cypress checks from another dev-shell -terminal. `process-compose` provides the PostgreSQL database, Laravel backend, -and Vite frontend needed by the full suite. - -Backend tests live under `backend/tests`. - -- Unit tests exercise domain entities, use cases, middleware, controllers, and - fake repositories without requiring PostgreSQL. -- Feature tests exercise HTTP endpoints, Laravel routing, migrations, seed - data, and real framework integration. -- PHPUnit is configured to use an in-memory SQLite database for test runs. - -Run backend tests: - -```sh -cd backend -./vendor/bin/phpunit tests -``` - -Run backend style checks: - -```sh -cd backend -phpcs app tests routes database -``` - -Frontend quality checks live under `frontend/rabbi_gerzi`. - -- `npm run format` checks and formats Vue and TypeScript source with oxfmt. -- `npm run lint` runs oxlint and eslint. -- `npm run type-check` runs vue-tsc against the Vite application. - -Run frontend checks: - -```sh -cd frontend/rabbi_gerzi -npm run format -npm run lint -npm run type-check -``` - -Cypress E2E tests live under `frontend/rabbi_gerzi/cypress/e2e`. - -- Specs drive the UI through the Vite frontend at `http://localhost:5173`. -- The Laravel backend must be running at `http://127.0.0.1:8000`. -- The Cypress config creates a seeded PostgreSQL template database before the - run and resets the app database between specs with the `db:reset` task. -- Seeded media assets are copied into `backend/storage/app/public` for each - reset. - -Run E2E tests after `process-compose up` is healthy: - -```sh -cd frontend/rabbi_gerzi -npm run test:e2e -``` - -Open the Cypress runner: - -```sh -cd frontend/rabbi_gerzi -npm run test:e2e:open -``` - -## Verification - -Run the full local suite from a second dev-shell terminal while -`process-compose up` is running: - -```sh -cd backend -phpcs app tests routes database -./vendor/bin/phpunit tests - -cd ../frontend/rabbi_gerzi -npm run format -npm run lint -npm run type-check -npm run test:e2e -``` - -Run the full Nix package and module checks: - -```sh -nix flake check -``` - -## Deployment - -Deploy through the flake's NixOS module. The module builds the frontend with -`VITE_API_BASE_URL=https://api.rabbigerzi.com`, serves it from nginx, runs the -Laravel backend with php-fpm, provisions PostgreSQL, creates writable storage -and cache directories, runs migrations, and ensures the initial admin exists. - -Example host configuration: - -```nix -{ - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - rabbi-gerzi.url = "git+ssh://git@example.com/rabbi-gerzi.git"; - - sops-nix = { - url = "github:Mic92/sops-nix"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = - { - nixpkgs, - rabbi-gerzi, - sops-nix, - ... - }: - { - nixosConfigurations.rabbigerzi = nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - modules = [ - rabbi-gerzi.nixosModules.default - sops-nix.nixosModules.sops - ( - { config, ... }: - let - appKey = config.sops.placeholder."app-key"; - initialAdminEmail = - config.sops.placeholder."admin-email"; - initialAdminPassword = - config.sops.placeholder."admin-password"; - in - { - sops = { - defaultSopsFile = ./secrets/rabbi-gerzi.yaml; - age.sshKeyPaths = [ - "/etc/ssh/ssh_host_ed25519_key" - ]; - - secrets = { - "app-key" = { }; - "admin-email" = { }; - "admin-password" = { }; - }; - - templates."rabbi-gerzi.env".content = '' - APP_KEY=${appKey} - RABBI_GERZI_INITIAL_ADMIN_EMAIL=${initialAdminEmail} - RABBI_GERZI_INITIAL_ADMIN_PASSWORD=${initialAdminPassword} - ''; - }; - - services.rabbi-gerzi = { - enable = true; - - frontend.hostName = "rabbigerzi.com"; - - backend = { - hostName = "api.rabbigerzi.com"; - environmentFile = - config.sops.templates."rabbi-gerzi.env".path; - }; - }; - } - ) - ]; - }; - }; -} -``` - -If the API origin must be set explicitly, use: - -```nix -services.rabbi-gerzi.frontend.apiBaseUrl = - "https://api.rabbigerzi.com"; -``` - -Configure `.sops.yaml` with age recipients for each administrator and the -server's SSH Ed25519 host key. Then create the encrypted secrets file: - -```sh -sops secrets/rabbi-gerzi.yaml -``` - -Add these values in the editor opened by `sops`: - -```yaml -app-key: base64:replace-with-laravel-app-key -admin-email: admin@example.com -admin-password: replace-with-a-long-random-password -``` - -Commit only the encrypted file written by `sops`. At activation time, -`sops-nix` decrypts these values and renders the runtime environment file -outside the Nix store. - -Then rebuild the host with the deployment method used by the server, for -example: - -```sh -sudo nixos-rebuild switch --flake .#rabbigerzi -``` - -During activation, `rabbi-gerzi-setup` runs: - -```sh -php artisan migrate --force -php artisan rabbi-gerzi:ensure-initial-admin -``` - -The initial admin command is idempotent. If the configured admin email already -exists, it leaves the user unchanged. - -## Useful Nix Outputs - -Build the frontend package: - -```sh -nix build .#frontend -``` - -Build the backend package: - -```sh -nix build .#backend -``` - -Evaluate all checks: - -```sh -nix flake check -```