- API (api/src/auth.ts): scrypt password hashes; random session tokens stored only as SHA-256; 30-day sliding sessions; rate limits on sign-in (per IP and per email) and sign-up; changing the password signs out other devices; deleting the account removes its data. - Site: /signup, /signin (returns to the page the reader came from), /account; header link; plain forms, no JavaScript needed. Session in an HTTP-only, SameSite=Lax cookie (Secure over HTTPS). - CSRF: Astro's origin check, with the site's hostnames listed (SITE_HOSTS) so its own form posts pass and other sites' are refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
68 lines
4.4 KiB
Markdown
68 lines
4.4 KiB
Markdown
# دیوان · Divan
|
||
|
||
An open-source site for reading and searching **classical Urdu poetry and prose**, in Urdu script (`ur-PK`). Content comes from [divan-data](https://github.com/anas-rashid/divan-data): public-domain texts from Urdu Wikisource, with short poet introductions from Urdu Wikipedia.
|
||
|
||
Divan follows the features of [GanjoorService](https://github.com/ganjoor/GanjoorService) (the software behind ganjoor.net), rebuilt in Node.js, TypeScript and PostgreSQL with its own UI. The first version was a fork of GanjoorService (.NET + SQL Server); that code is kept at the tag [`dotnet-final`](https://github.com/anas-rashid/divan/tree/dotnet-final).
|
||
|
||
## Layout
|
||
|
||
| Path | What |
|
||
|---|---|
|
||
| `db/schema.sql` | PostgreSQL schema: poets, categories, poems, verses; trigram index for Urdu substring search |
|
||
| `api/` | Node.js API (Fastify + pg). TypeScript runs natively on Node 24+, no build step |
|
||
| `web/` | The site (Astro, server-rendered). Naskh by default with a Nastaliq option, light/dark, RTL, mobile-first |
|
||
| `reader/index.html` | A single-file static reader over the divan-data CDN (no server) |
|
||
|
||
## Run locally
|
||
|
||
```sh
|
||
# PostgreSQL 17 (any Postgres 14+ works; port 5433 avoids clashing with a local one)
|
||
docker run -d --name divan-pg -e POSTGRES_USER=divan -e POSTGRES_PASSWORD=divan_local -e POSTGRES_DB=divan \
|
||
-p 5433:5432 -v divan-pg:/var/lib/postgresql/data postgres:17-alpine
|
||
|
||
cd api && npm install
|
||
npm run import -- ../../divan-data # a local divan-data checkout, or the CDN:
|
||
# npm run import -- https://cdn.jsdelivr.net/gh/anas-rashid/divan-data@main/
|
||
npm start # API on http://127.0.0.1:4100
|
||
npm test # Urdu normaliser tests
|
||
|
||
cd ../web && npm install && npm run build && npm start # site on http://127.0.0.1:4200
|
||
```
|
||
|
||
Settings: `DATABASE_URL` (API, default `postgres://divan:divan_local@localhost:5433/divan`), `PORT`/`HOST`; `API_URL` (web, default `http://127.0.0.1:4100`); `SITE_HOSTS` (web, at build time: the site's hostnames, comma-separated, default `127.0.0.1,localhost`; form posts from other origins are refused).
|
||
|
||
The import upserts, so re-running it after a divan-data sync applies the changes.
|
||
|
||
## Daily content sync (server)
|
||
|
||
`deploy/sync.sh` keeps a server current: it updates a divan-data checkout, fetches new and edited works from Wikisource (incremental, about a minute), rebuilds the export and upserts it into PostgreSQL. The site shows new content immediately. Runs are locked so they never overlap.
|
||
|
||
```sh
|
||
# crontab -e (daily at 03:15; DATABASE_URL as for the API)
|
||
15 3 * * * DATABASE_URL=postgres://divan:...@localhost:5432/divan /opt/divan/deploy/sync.sh >> /var/log/divan-sync.log 2>&1
|
||
```
|
||
|
||
The same script keeps the word dictionary current (`npm run dict-sync` in `api/`): the full Wiktionary data for Urdu, Persian and Arabic (English Wiktionary via [kaikki.org](https://kaikki.org), and the Urdu, Persian and Arabic Wiktionary dumps) is re-imported when upstream publishes new files, and each day's Wiktionary edits are applied from recent changes. The first run downloads about 700 MB.
|
||
|
||
Settings: `DIVAN_DATA_DIR` (default `/opt/divan-data`, cloned on first run), `DIVAN_APP_DIR` (default: this repo), `DIVAN_DATA_PUSH=1` to also commit and push data changes (needs git push access). Needs git, python3 and Node 24+.
|
||
|
||
## API
|
||
|
||
| Endpoint | Returns |
|
||
|---|---|
|
||
| `GET /api/poets` | all poets |
|
||
| `GET /api/page?url=/p238/...` | the poet, category or poem at a site URL (breadcrumbs, children, verses, prev/next) |
|
||
| `GET /api/search?q=&poet=&page=` | poems containing all words (or a `"quoted phrase"`), Urdu-normalised; exact phrase first; each with the best-matching couplet or paragraph (`snippet`) |
|
||
| `GET /api/word?w=` | one word's meanings and pronunciation from the local Wiktionary data (Urdu, Persian, Arabic in that order; English meanings; Urdu equivalents via English when Urdu Wiktionary has none) |
|
||
| `GET /health` | database check |
|
||
|
||
Search normalises both stored text and queries: Arabic ي/ك/ه → Urdu ی/ک/ہ, ۂ/ۓ, diacritics and the Urdu full stop removed; do-chashmi ھ stays distinct.
|
||
|
||
## Roadmap
|
||
|
||
v2 is reaching parity with Ganjoor's features in phases: reading, accounts, community (comments, bookmarks), editorial tools, recitations, then operations and deployment.
|
||
|
||
## License
|
||
|
||
GPL-3.0 (see `LICENSE`). Texts are public domain; the divan-data compilation is CC BY-SA 4.0 (Urdu Wikisource and Wikipedia contributors).
|