Compare commits

..

4 commits

Author SHA1 Message Date
3e02a83dd4
Merge branch 'docs/readme' 2026-07-17 16:20:54 +03:00
bb2f745024
document application mechanics 2026-07-17 16:19:20 +03:00
b36da46543
document sops deployment 2026-07-17 12:27:34 +03:00
092dd2ad72
add project readme 2026-07-06 22:25:13 +03:00

456
README.md Normal file
View file

@ -0,0 +1,456 @@
# 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
```