Commit Graph

4201 Commits

Author SHA1 Message Date
48b106857d Merge pull request 'Admin panel: users, password resets, roles, audit log' (#39) from feature/admin-panel into main
Reviewed-on: #39
2026-10-08 21:10:04 +00:00
Anas Rashid
acf7767495 Admin panel: users, password reset on request, disable, roles, delete, audit log (#27)
- API (api/src/admin.ts, admins only): user list and search; password reset generates a temporary
  password shown once and ends the user's sessions; disable/enable (ends sessions, blocks sign-in);
  roles reader/admin; delete; an admin cannot disable, demote or delete themself. Every action is
  written to audit_log (kept when users are deleted).
- First admin from the server: npm run make-admin -- <email> (after signing up).
- Site: /admin (users) and /admin/audit; 'ایڈمن' link in the header for admins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:08:19 +02:00
6610ed0f71 Merge pull request 'Accounts: email and password' (#37) from feature/accounts into main
Reviewed-on: #37
2026-10-08 21:05:05 +00:00
Anas Rashid
aa264aa6c3 Merge main (selection menu) into feature/accounts 2026-10-08 22:58:08 +02:00
c177b45561 Merge pull request 'Text selection menu like Ganjoor's' (#26) from feature/selection-menu into main
Reviewed-on: #26
2026-10-08 20:57:38 +00:00
Anas Rashid
e12f6eeb2a Selection menu: remove the قرآن option (owner request)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:56:46 +02:00
Anas Rashid
e409adc668 Accounts: email and password sign-up, sign-in/out, change password, delete account (#18)
- 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>
2026-10-08 22:53:51 +02:00
Anas Rashid
c558960ddc Text selection menu like Ganjoor's (#23)
Selecting reading text shows a menu by the selection: معنی (word sidebar, single words),
تلاش (search Divan, phrases quoted), کاپی (text with poet, title and link), شیئر (link to the
page with the selection highlighted via ?hl=; native share sheet where available), قرآن
(tanzil.net), گوگل. Escape or clearing the selection closes it; no layout shift.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:36:52 +02:00
Anas Rashid
3201a1c478 Word dictionary in full: all senses with usage examples, every IPA with dialect labels, every recording, full etymologies, synonyms, derived and related words
Panel folds the extras (more meanings, examples, long etymologies, long word lists). 175 MB in PostgreSQL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:27:37 +02:00
Anas Rashid
19b68425f6 Word sidebar: readings by vowels, vowel variants, similar words; Persian/Arabic explained in Urdu
- readings: one spelling, different short vowels (ملک: مَلَک، مَلِک، مِلک، مُلک), each with form,
  transliteration, IPA, audio and meanings
- always: same consonants with other long vowels (consonant-skeleton regex)
- not found: inflection stems (آنکھوں -> آنکھ, جاتے -> جانا), prefix regex, trigram similarity
- Persian/Arabic words with no Urdu entry: Urdu translations via English first, then English, then
  their own language; filler glosses (especially, usually…) dropped
- punctuation, dashes and spaces dropped from selected words and lookup keys; clean titles

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:07:10 +02:00
Anas Rashid
7fcc0ee7fa Word dictionary: full Wiktionary data for Urdu, Persian and Arabic in PostgreSQL, kept in sync
- en.wiktionary entries via kaikki.org (meanings, IPA, transliteration, audio, etymology, synonyms)
- ur./fa./ar.wiktionary dumps for meanings in each language (incl. more Urdu entry formats)
- English->Urdu pivot table from Urdu entries' glosses and ur.wiktionary's English entries
- dict-sync.ts: re-imports sources when upstream publishes new files, applies daily recent changes;
  run from deploy/sync.sh
- lookups now read the database (2-7 ms) instead of calling Wikimedia live

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:48:56 +02:00
Anas Rashid
ef618094d5 Word sidebar: select a word for its meanings and pronunciation from Wiktionary
Urdu, Persian and Arabic Wiktionaries in that order, each labelled; English meanings, IPA,
transliteration and audio from en.wiktionary; Urdu equivalents pivoted through English when Urdu
Wiktionary has no entry. Cached in PostgreSQL for 30 days. Left panel on wide screens, bottom
sheet on phones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:38:10 +02:00
Anas Rashid
efb7aacde3 Search: highlighted matches, matching couplet or prose excerpt, exact phrase first; opened pages keep the highlight (?hl=)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:30:42 +02:00
Anas Rashid
9a2db49eab Radif per ghazal, matla/maqta labels, pen name set apart; contents grouped by radif letter
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:27:43 +02:00
Anas Rashid
4c7e0559a7 Search: separate Roman-to-Urdu toggle and on-screen keyboard toggle; keys hidden until asked
The header's flex rule overrode [hidden], so the keys showed on every page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:17:14 +02:00
Anas Rashid
647b9fa369 Keyboard in the Gboard/Windows Urdu layout with a shift layer; physical keys by position
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:15:58 +02:00
Anas Rashid
3e0e3a625d Keyboard rows stacked and centred (not the header's flex row)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:14:40 +02:00
Anas Rashid
4ef7de256c Keyboard toggle as an icon inside the search box
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:13:50 +02:00
Anas Rashid
f82df122b9 Urdu on-screen keyboard and phonetic typing in search
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:11:57 +02:00
Anas Rashid
87cea30f28 Self-host Naskh and Nastaliq fonts (fixes font switch in Brave); ?font= URL option
- @fontsource/noto-naskh-arabic and noto-nastaliq-urdu bundled with the site; no Google
  Fonts request (Brave Shields blocked it, so the switch had nothing to switch to)
- ?font=nastaliq|naskh sets the reading font (shareable links)
Validated: both fonts render from local files in headless Chromium; no googleapis requests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:09:26 +02:00
Anas Rashid
13610569cc deploy/sync.sh: daily Wikisource -> divan-data -> PostgreSQL sync for the server
- updates a divan-data checkout, fetches new/edited works (incremental), rebuilds, upserts
  into Postgres; optional commit/push of data (DIVAN_DATA_PUSH=1); flock against overlap
- import prints progress only on a terminal
Validated locally: full run 1m46s, 382 poets / 11,087 poems imported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:00:15 +02:00
Anas Rashid
17200fc9aa Show Gregorian (عیسوی) years alongside Hijri for poets
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:53:53 +02:00
Anas Rashid
74f3973191 Header controls keep Naskh at normal height in Nastaliq mode
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:52:41 +02:00
Anas Rashid
d70e0a1fed Pinned poets on the home page (مقبول شعرا): Iqbal, Ghalib, Mir Taqi Mir, Mir Dard, Dagh
- poets.pin_order; editorial list in db/featured.sql, applied after every import
- home page shows the pinned row first, like Ganjoor
- Faiz is not included: his work is under copyright until 2035, so not in divan-data

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:42:33 +02:00
Anas Rashid
6b414c3d2d v2 UI: Ganjoor's look (palette, gold pills, centred couplets with ✤), leaner CSS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:32:06 +02:00
Anas Rashid
2f4344dcdf Remove the .NET stack; Divan is now Node.js + TypeScript + PostgreSQL
- delete RMuseum (API), DivanRazor (site), solution, Dockerfile/compose, SQL Server
  deploy/backup scripts, run-local.sh, archived upstream docs
- the .NET code stays available at tag dotnet-final
- README rewritten for v2; .gitignore for Node

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:30:03 +02:00
Anas Rashid
60f0791405 Divan v2: Astro site with Divan's own design (proof of concept)
- web/: server-rendered Astro over the Node API: home (poets by Hijri century), poet
  (intro + sections), category (numbered contents), poem (couplets, prev/next, source),
  search (paged); Naskh default with Nastaliq toggle, light/dark, Urdu digits, RTL, mobile
- api: section counts include nested categories
- README: how to run v2

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:26:54 +02:00
Anas Rashid
c450ec010a Divan v2: Node.js API + PostgreSQL (proof of concept)
- db/schema.sql: poets, categories, poems, verses; normalised search_text with pg_trgm index
- api/: Fastify + pg, Node native TypeScript (no build step)
  - import.ts: loads divan-data (folder or CDN) with upserts; full import in ~36 s
  - server.ts: /api/poets, /api/page?url=, /api/search (Urdu-normalised ILIKE), /health
  - urdu.ts (+ test): normaliser ported from the .NET version

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:24:20 +02:00
Anas Rashid
4a1d2e0642 Poet/category tables of contents after import; Divan wordmark instead of Ganjoor logo
- public data import ran with empty HtmlText for poet/category pages (upstream fills it
  with a separate admin TOC job), so poet pages showed no works; the import now
  regenerates tables of contents for the importing user
- replace Ganjoor's logo image (gdap/gdapk) with the Divan wordmark on all pages; delete it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:17:24 +02:00
Anas Rashid
5f2b779e89 Public home page, Naskh default font, no poet photos
- home page is public: no redirect to login/admin import; flat poet list until century
  groups exist (upstream needed a pinned 'popular poets' group, which Divan never has, so
  the home page always redirected); public 'being prepared' notice when there are no poets
- Naskh is the default font, Nastaliq the option (site and static reader)
- poet/writer photos hidden everywhere (CSS on /poet/image/), poet page photos tab removed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:59:20 +02:00
Anas Rashid
b186ed3336 run-local.sh: default data path ../divan-data
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:47:44 +02:00
Anas Rashid
37fe04ac79 Rename to Divan (Roman script) throughout
Projects (DivanRazor, DivanService.sln), files, folders, classes, settings (Divan:),
API routes (/api/divan), DB name and tables (divan, Divan*), local container/paths,
repo links (anas-rashid/divan, divan-data). Urdu دیوان unchanged.
Migrations renamed consistently (no pending model changes); requires a fresh database.
Stop tracking the generated RMuseum/RMuseum.xml (was upstream's stale doc).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:46:27 +02:00
Anas Rashid
a5f0f1b74d Fix poet image placeholder: null RImageId cast threw 500 (#4); run-local waits via HTTP
- GetPoetImageIdByUrl: missing portrait -> Guid.Empty (placeholder) instead of a null cast
- run-local.sh: wait for SQL Server with a query and for API/site via HTTP
  (pipefail+grep -q never matched; API log level hides 'Now listening' outside Development)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:03:44 +02:00
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
Anas Rashid
e78fc9bd99 Security pass (#11): no default JWT secret, CORS allow-list, Divan system emails
- appsettings: drop upstream's public JWT secret; API refuses to start outside Development without one
- Cors:AllowedOrigins (compose: https://SITE_DOMAIN); unset = any origin for local dev
- default admin/system/bot emails @divan.local instead of @ganjoor.net

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:26:01 +02:00
Anas Rashid
e8cedaab8f Museum subsystem: remove public UI, keep backend dormant (#5)
- poem page: drop manuscripts/images tab and Pinterest/museum image suggestion form
- remove image recommendation (Pin) page and route
- museum API, services and tables stay dormant (no schema change)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:24:30 +02:00
Anas Rashid
c97bb18c3a Urdu public pages, batch 3 (#2): browser JS messages and labels
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:23:00 +02:00
Anas Rashid
e3e3819c78 Urdu public pages, batch 2 (#2): auth, errors, map, FAQ, similar poems, people pages, page titles
- translate remaining public views and page-model strings (titles, messages, relation names)
- Urdu stopword list for word-frequency stats (replaces Persian list)
- error pages: drop list of alternative Ganjoor services

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:22:09 +02:00
Anas Rashid
4fd832dbcb Urdu public pages (#2); hide unconfigured semantic search; safe defaults
- translate reading-path views (poet, category, poem, search, header, login, partials)
  from Persian to Urdu (exact-run dictionary, 262 strings; AI-summary data marker kept)
- semantic search widget only when SemanticSearchAPIRoot is configured
- appsettings defaults: no Ganjoor tracking script, no Ganjoor semantic search,
  API/site roots default to the local Divan stack instead of api.ganjoor.net

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:18:00 +02:00
Anas Rashid
9bd445614e Urdu-aware search without SQL Server full-text (#3)
- MakeTextSearchable: Urdu letter variants, Urdu full stop, Urdu diacritics, zwj/marks
- SearchLikePatterns: phrase or per-word LIKE patterns (escaped), normalised like stored text
- poem, similar-poem and comment search use LIKE (Linux SQL Server image has no full-text)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:09:09 +02:00
Anas Rashid
8bb26a0296 Poet images: placeholder instead of broken images (#4)
- poet ImageUrl always points at the API image endpoint
- endpoint serves a neutral SVG placeholder when a poet has no portrait
- public data export: poet image URLs use our API (WebServiceUrl), not ganjoor.net

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:06:28 +02:00
Anas Rashid
90f07bf10e run-local.sh: one-command local stack (SQL Server container, API, site, optional import)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:57:40 +02:00
Anas Rashid
0ce63b2bc6 Static reader: single-file browser reader over the divan-data static API
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:53:00 +02:00
Anas Rashid
5063608d5d Move archived upstream docs to docs/upstream/
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:51:29 +02:00
Anas Rashid
ac41c6115f Database name: museum -> divan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:49:29 +02:00
Anas Rashid
532d005cdb Rename Ganjoor -> Divan throughout the code
Projects (GanjooRazor -> DivanRazor, GanjoorService.sln -> DivanService.sln), files,
folders, classes, namespaces, settings, API routes (/api/divan) and DB tables (Divan*).
External addresses (ganjoor.net, github.com/ganjoor) unchanged. Migrations renamed
consistently (no pending model changes); requires a fresh database.
Upstream RUNNING_LOCALLY/SEMANTIC_SEARCH docs archived unmodified under docs/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:49:01 +02:00
Anas Rashid
8a981bf993 Remove Ganjoor-specific services; Urdu defaults
- drop 7-day/top visits tabs (Ganjoor's kntr analytics)
- page language options: Urdu (default), Persian, Arabic; drop Turkish/Kurdish
- fa-IR -> ur-PK as default language/culture (site + API); JS digits ur-PK-u-nu-arabext
- unlink blog/museum/ava/... .ganjoor.net help links (text kept)
- random verse: data-driven random poem from our DB/API instead of c.ganjoor.net widget
  and hard-coded Persian poet id ranges
- word tooltip: Urdu Wiktionary instead of vajehyab; drop abjad.ganjoor.net

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:45:08 +02:00
Anas Rashid
91a7517d31 Remove music features (Spotify, Golha, Beeptunes, music index, song suggestions)
Pages, API endpoints, services and assets removed; music DB models/tables kept
so the feature can be rebuilt for Urdu later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:41:02 +02:00
Anas Rashid
ecad9e9311 Remove Tajik: TajikGanjoor site, API, services, export, transliteration
- delete TajikGanjoor project (removed from solution)
- drop Tajik endpoints/services/DTOs/entities; migration DivanRemoveTajik drops their tables
- remove Tajik admin buttons/handlers and appsettings sections
- README is now Divan's; upstream README kept in docs/GANJOOR-README.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:37:39 +02:00
Anas Rashid
223166ef5e Divan: make env config work, ur-PK locale, own-site links relative
- AddEnvironmentVariables() where appsettings.json is read directly
- JWT ValidIssuer follows RSecurityBackend:ApplicationName (was hard-coded Ganjoor)
- deploy/entrypoint.sh: copy settings RSecurityBackend reads only from appsettings.json
- Dockerfile: pin SDK 10.0.302 (newer Razor rejects some upstream views)
- lang=ur-PK; ganjoor.net own-site links -> relative; api.ganjoor.net -> APIRoot; search.xml
- DIVAN.md: first admin password is Test!123 (upstream doc is wrong)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 23:32:16 +02:00