divan/README.md
Anas Rashid 3df43e4979 Backups, restore and rollback runbook (#10, #12)
- deploy/backup.sh / deploy/restore.sh (compose service or local container; side-by-side test restore)
- restore: chown copied backup for sqlservr, fail loudly on unreadable file list
- README: update, backup, test-restore and rollback steps

Validated locally: backup 194 MB, restored into a test DB with identical counts, test DB dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:27:31 +02:00

111 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# دیوان · Divan site
Divan is a standalone project built on a fork of [GanjoorService](https://github.com/ganjoor/GanjoorService) (GPL-3.0), the software behind ganjoor.net, adapted to serve **classical Urdu poetry and prose** from [divan-data](https://github.com/anas-rashid/divan-data).
## Changes from upstream
- **Renamed Ganjoor → Divan throughout:** projects (`DivanRazor`, `DivanService.sln`), files, folders, classes, settings (`Divan:` section), API routes (`/api/divan/...`) and database tables (`Divan*`). Real external addresses (ganjoor.net, github.com/ganjoor) are unchanged. Because table names changed, Divan needs a fresh database; it can't reuse a Ganjoor one.
- **Removed Ganjoor/Persian-specific features:** music (Spotify, Golha, Beeptunes, music index, song suggestions; DB models kept for a future Urdu version), Ganjoor's visit analytics, Turkish/Kurdish page options, abjad, Persian dictionary links. Random verse now picks from Divan's own data.
- **Tajik removed:** the TajikGanjoor site, Tajik API endpoints, services, export and transliteration are gone. Migration `DivanRemoveTajik` drops their tables.
- **Branding:** "گنجور" becomes "دیوان" throughout the site. Upstream is credited in the footer.
- **Urdu basics:** pages are `lang="ur-PK"`. The home page, footer and century groups (Hijri centuries, e.g. "تیرہویں صدی ہجری") are in Urdu. Deeper pages, such as admin and account pages, are still Persian.
- **Fonts:** Noto Nastaliq Urdu by default, with a **نستعلیق / نسخ** switch (Noto Naskh Arabic) at the bottom left. The choice is remembered per browser.
- **Footer:** links to Divan-only services (Hafez divination, music index, etc.) are removed. Links to the Wikisource source, the data and the code are added.
- **Linux/Docker:** `Dockerfile` + `docker-compose.yml` (SQL Server 2022, API, site, Caddy for HTTPS).
- **Config fixes so env vars work:** the four places that read `appsettings.json` directly now also read environment variables. The JWT issuer follows `RSecurityBackend:ApplicationName` instead of the hard-coded "Divan". `deploy/entrypoint.sh` copies the settings that RSecurityBackend reads only from `appsettings.json` (connection string, secret, app name, admin email) into the file at container start.
- **Links:** `ganjoor.net` links to the site's own pages are now relative. Links to Divan's other services (blog, audio, etc.) are left as they are.
- **Locale:** `ur-PK`.
- **Search without full-text:** the SQL Server Linux image has no full-text search, so poem, similar-poem and comment search use `LIKE` patterns (`LanguageUtils.SearchLikePatterns`). The normaliser handles Urdu letter variants (Arabic ي/ك/ه → ی/ک/ہ, ۂ/ۓ), the Urdu full stop and Urdu diacritics. After changing normalisation rules, rebuild stored search text with `POST /api/divan/regenplaintext/0` (admin).
## Static reader (no server)
`reader/index.html` is a single-file reader: poets, intros, books and poems, with Nastaliq/Naskh switching. It reads the [divan-data](https://github.com/anas-rashid/divan-data) static API directly in the browser, so it needs no API, database or build step.
```sh
# against the public CDN: just open reader/index.html in a browser, or host it anywhere (e.g. GitHub Pages)
# against a local divan-data checkout:
mkdir -p www && ln -s "$PWD/reader/index.html" www/ && ln -s /path/to/divan-data www/data
python3 -m http.server 5300 -d www # open http://localhost:5300/?data=data/
```
## Deploy (Ubuntu/Debian x86-64, e.g. Vultr)
```sh
# 1. Docker
curl -fsSL https://get.docker.com | sh
# 2. Code + config
git clone https://github.com/anas-rashid/divan.git && cd divan
cp .env.example .env && nano .env # domains, passwords, admin email
# 3. DNS: point SITE_DOMAIN and API_DOMAIN (A records) at the server, then:
docker compose up -d --build # first build takes a few minutes
docker compose logs -f api # wait for "Application started"
```
SQL Server needs about 2 GB of RAM. Use a plan with at least 4 GB in total.
Security defaults: the API refuses to start outside Development without `JWT_SECRET`; browsers may call the API only from `https://SITE_DOMAIN` (`Cors:AllowedOrigins`); public sign-up is off (`SIGNUP_ENABLED=False`) until SMTP (`SmptConfig__*`) is configured.
### Load the data
1. Open `https://SITE_DOMAIN/login` and sign in with `ADMIN_EMAIL` and the password **`Test!123`**. The first login creates the admin account with that fixed password (RSecurityBackend's default; upstream's guide is wrong about this). **Change it right away** in the user panel.
2. On the import page that opens (or **Admin → مالی و سایت → درون‌ریزی دادهٔ عمومی**), choose **Internet URL** and enter:
```
https://cdn.jsdelivr.net/gh/anas-rashid/divan-data@main/
```
3. The import runs in the background (about 11k poems). Century groups are rebuilt automatically when it finishes.
Re-running the import adds new poems and leaves existing ones untouched, so it can be repeated after divan-data's daily sync.
### Update
```sh
deploy/backup.sh # always back up first
git pull && docker compose up -d --build
```
Database migrations run automatically when the API starts.
### Backups
```sh
deploy/backup.sh # -> backups/divan-<UTC stamp>.bak (copy it off the server)
TARGET_DB=divan_restoretest deploy/restore.sh backups/divan-<stamp>.bak # test a backup side by side
```
Schedule `deploy/backup.sh` with cron (e.g. daily) and copy `backups/` off the server. A backup counts only once a test restore succeeds.
### Rollback
1. `docker compose stop api site`
2. `git checkout <previous commit or tag>`
3. If the failed version applied database migrations: `deploy/restore.sh backups/<backup taken before the update>.bak`
4. `docker compose up -d --build`
Restoring replaces the `divan` database, so anything written after that backup (comments, edits) is lost.
## Run locally (macOS/Linux)
```sh
./run-local.sh import # SQL Server container + API + site, then imports divan-data (~1 h, background)
./run-local.sh # later runs: rebuild + start
./run-local.sh stop
```
Site: http://localhost:5200 · API: http://localhost:5100/swagger · admin `admin@divan.local` / `Test!123`. On Apple Silicon, start Docker via `colima start --vm-type vz --vz-rosetta --memory 6` first (SQL Server is x86-64 only).
## Build locally (macOS/Linux)
```sh
cd RMuseum # its global.json pins SDK 10.0.302; newer SDKs fail on some upstream Razor views
dotnet build RMuseum.csproj -p:EnableWindowsTargeting=true
dotnet build ../DivanRazor/DivanRazor.csproj -p:EnableWindowsTargeting=true
```
Running it needs SQL Server, so use the Docker setup above. SQL Server's image is x86-64 only.
## License
GPL-3.0, same as upstream (see `LICENSE`). Data: see [divan-data](https://github.com/anas-rashid/divan-data).