Commit Graph

15 Commits

Author SHA1 Message Date
Anas Rashid
19af91c594 Moderation: versions of works and the L2 -> L1 -> admin pipeline (#31, #51)
- revisions (a work's Divan text, summary, status, author/reviewer/publisher, the text it started from)
  and revision_events (who did what, with comments): the review thread and the moderation log.
- API (api/src/moderation.ts): what a moderator may do on a work, queue, drafts (one open draft per
  person and work), save, submit, approve, return/reject (reason required), publish; history; log.
  Rules come from the IAM grants: L2 drafts, an L1 covering the work reviews, an admin publishes; L1
  drafts go to the admin, admin drafts publish directly. Publishing numbers the version, writes it
  to divan-data (owned.ts) and updates the site; a draft based on an older version is refused (409).
- Line diff (api/src/diff.ts) against the text a draft started from.
- Site: ترمیم کریں / تاریخچہ on works, /mod (queue, activity log), /mod/rev/<id> (editor with the Urdu
  keyboard and Roman typing, diff, preview, actions, timeline), /mod/work/<id> (versions).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:31:47 +02:00
Anas Rashid
98585962e2 Bookmarks with 🔖 for poets, books/chapters and poems; word book on its own page
Owner direction: the word collection is separate from bookmarks, and bookmarks use the bookmark
glyph, not hearts.
- 🔖 نشان buttons on poet, book/chapter (new) and poem pages; contents lists and cards show 🔖.
- /library (نشانات): bookmarked poets, books and chapters, poems, couplets, phrases.
- /words (ذخیرۂ الفاظ): the word book on its own page; tabs between the two; header links.
- Word button: 'ذخیرۂ الفاظ میں ڈالیں' (+), 'ذخیرۂ الفاظ میں ہے' (✓) once added.
- API: library kind 'category' (books and chapters) in toggle, state, marks and the list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:58:59 +02:00
Anas Rashid
55cc602815 Library: bookmarked phrases, and glyphs where the reader's marks are
- Select any part of a couplet or paragraph and press نشان (selection menu): the phrase is
  bookmarked and shown underlined with a small 🔖, also across the two misras; clicking it removes
  the bookmark (with a confirmation). Listed in the library as نشان زدہ عبارتیں.
- Contents lists show ♥ on favourite works and 🔖 on works holding bookmarks; book/section cards and
  poet cards (home) show 🔖 when they hold any. API: GET /api/library/marks; phrases in state and
  library; phrase kind validated against the couplet's text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:48:42 +02:00
Anas Rashid
8f4a71530d Personal library and word collection (#24, #25)
- Save (♡) poets and works, bookmark (🔖) couplets and paragraphs, save words from the word sidebar
  or the selection menu (with the couplet they came from); notes on items.
- /library (میری لائبریری): the reader's name and bio, then favourite poets, works, bookmarked
  couplets and saved words, each with its full path (poet » book » section » work) and a link to
  the couplet (#cN anchors on poem pages); saved words open the word sidebar.
- API (api/src/library.ts), JSON + Bearer tokens so mobile apps (#44) can use it; deleting an
  account deletes its library. Signed-out readers are sent to sign-in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:35:34 +02:00
Anas Rashid
be0238521f Profile: full name and bio (#42)
Readers write their full name and a short bio (Urdu or any text) on /account; the name heads the
account page and the header shows the first name. API: POST /api/auth/profile (trimmed, control
characters dropped, 100 and 1,000 characters).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:31:19 +02:00
Anas Rashid
d72c14f8bd Permissions (IAM): L2/L1 moderators and scoped grants (#29)
- Roles: reader, mod-l2, mod-l1, admin. Grants: scope (all, poet, book/section with everything in
  it, one work) x content (poets, books, works, dictionary) x actions (create, edit, delete,
  arrange); dictionary grants are site-wide. can() in api/src/permissions.ts is the one check.
- Admin API and pages: role dropdown on /admin; /admin/user/:id lists a moderator's grants, adds
  them (target by poet id or page link) and revokes them; demoting a moderator clears their grants;
  grant and revoke go to the audit log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:13:06 +02: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
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
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
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
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
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
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