diff --git a/.gitignore b/.gitignore index fce1a429..9f1276da 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -node_modules/ +# node_modules as a folder or a link (links were once committed by mistake) +node_modules dist/ .astro/ .env diff --git a/api/node_modules b/api/node_modules deleted file mode 120000 index 1ee83cb3..00000000 --- a/api/node_modules +++ /dev/null @@ -1 +0,0 @@ -/Users/anasrashid/divan/api/node_modules \ No newline at end of file diff --git a/api/src/server.ts b/api/src/server.ts index 29155658..445742c9 100644 --- a/api/src/server.ts +++ b/api/src/server.ts @@ -25,6 +25,25 @@ import { ebookRoutes } from './ebooks.ts'; import { feedRoutes } from './feeds.ts'; const app = Fastify({ logger: { level: process.env.LOG_LEVEL ?? 'info' } }); + +// Errors: readers get a short message, never internal details; the log gets the whole error (one JSON line each, +// with the method and address), which is what the server's error report reads (divan-deploy scripts/errors.sh). +app.setErrorHandler((err: any, req, reply) => { + const status = err.statusCode >= 400 && err.statusCode < 500 ? err.statusCode : 500; + if (status === 500) req.log.error({ err, method: req.method, url: req.url }, 'request failed'); + reply.code(status).send({ error: status === 500 ? 'سرور میں خرابی۔ تھوڑی دیر بعد کوشش کریں۔' : err.message }); +}); + +// for the site's /health (uptime monitoring): the API answers and the database too +app.get('/api/health', async (req, reply) => { + try { + await pool.query('SELECT 1'); + return { ok: true, db: 'ok' }; + } catch (err) { + req.log.error({ err }, 'health: database unreachable'); + return reply.code(503).send({ ok: false, db: 'down' }); + } +}); const PAGE_SIZE = 20; app.get('/health', async () => { diff --git a/web/node_modules b/web/node_modules deleted file mode 120000 index 0fd07bfe..00000000 --- a/web/node_modules +++ /dev/null @@ -1 +0,0 @@ -/Users/anasrashid/divan/web/node_modules \ No newline at end of file diff --git a/web/src/components/Icon.astro b/web/src/components/Icon.astro index 3a2915ef..d950292e 100644 --- a/web/src/components/Icon.astro +++ b/web/src/components/Icon.astro @@ -56,6 +56,9 @@ const PATHS = { chevron: '', // footer rss: '', + home: '', + lock: '', + scale: '', info: '', globe: '', database: '', diff --git a/web/src/layouts/Base.astro b/web/src/layouts/Base.astro index b6bb27a0..f20dba90 100644 --- a/web/src/layouts/Base.astro +++ b/web/src/layouts/Base.astro @@ -136,6 +136,8 @@ import { brand } from '../lib/brand';
  • {brand.name} کے بارے میںمقصد اور طریقۂ کار
  • ٹیگموضوعات سے کلام
  • نئی تبدیلیاںRSS: نئے ورژن اور ای بکس
  • +
  • رازداریہم کیا محفوظ رکھتے ہیں
  • +
  • استعمال کی شرائطلائسنس اور ذمہ داریاں
  • ماخذ

    • ویکی ماخذمتن · عوامی ملکیت
    • diff --git a/web/src/lib/brand.ts b/web/src/lib/brand.ts index 38c207ae..18a98abd 100644 --- a/web/src/lib/brand.ts +++ b/web/src/lib/brand.ts @@ -4,6 +4,8 @@ export const brand = { name: 'دیوان', tagline: 'اردو کا کلاسیکی ادب', madeBy: 'ایک پاکستانی کی طرف سے، پاکستان کے لیے', // footer, beside Pakistan's flag + // where readers write: questions, corrections, copyright claims (privacy and terms pages) + contact: { text: 'ہم سے رابطہ کریں', url: 'https://git.anasrashid.net/anas/divan/issues' }, // the attribution forks keep (NOTICE, GPL v3 section 7(b)) basedOn: { text: 'Divan by Anas Rashid', url: 'https://git.anasrashid.net/anas/divan' }, }; diff --git a/web/src/middleware.ts b/web/src/middleware.ts index d0d91be4..a24b0bad 100644 --- a/web/src/middleware.ts +++ b/web/src/middleware.ts @@ -1,6 +1,6 @@ // The signed-in reader (or null) for every page, from the session cookie; and the reader's numerals: pages are // written with Eastern Arabic (Urdu) digits, and a reader who chose Western digits (cookie divan-digits=latn, set in -// the display settings) gets them in the page as sent, so nothing flashes +// the display settings) gets them in the page as sent, so nothing flashes. Errors are logged as one JSON line each. import { defineMiddleware } from 'astro:middleware'; import { auth, COOKIE } from './lib/auth'; @@ -12,7 +12,15 @@ export const onRequest = defineMiddleware(async (ctx, next) => { if (r.ok) ctx.locals.user = r.data.user; else if (r.status === 401) ctx.cookies.delete(COOKIE, { path: '/' }); // expired or signed out elsewhere } - const res = await next(); + let res: Response; + try { + res = await next(); + } catch (err: any) { + // one JSON line per error (the server's error report reads these); the reader sees the Urdu error page (500.astro) + console.error(JSON.stringify({ level: 'error', time: new Date().toISOString(), method: ctx.request.method, path: ctx.url.pathname, + msg: String(err?.message ?? err), stack: String(err?.stack ?? '').split('\n').slice(0, 8).join('\n') })); + throw err; + } if (ctx.cookies.get('divan-digits')?.value !== 'latn' || !res.headers.get('content-type')?.includes('text/html')) return res; // text only: scripts and styles are left alone (the page's own scripts look for Eastern digits) const html = (await res.text()).split(/(|)/) diff --git a/web/src/pages/404.astro b/web/src/pages/404.astro new file mode 100644 index 00000000..e65fb231 --- /dev/null +++ b/web/src/pages/404.astro @@ -0,0 +1,18 @@ +--- +// صفحہ نہیں ملا: any address that is not a page, and pages that answer "not found" (a missing poet, work or book) +import Base from '../layouts/Base.astro'; +import Icon from '../components/Icon.astro'; +Astro.response.status = 404; +--- + +
      + +

      یہ صفحہ نہیں ملا

      +

      ہو سکتا ہے پتہ بدل گیا ہو یا اس میں کوئی غلطی ہو۔ اوپر کے خانے میں تلاش کیجیے، یا یہاں سے شروع کیجیے:

      + +
      + diff --git a/web/src/pages/500.astro b/web/src/pages/500.astro new file mode 100644 index 00000000..ed440ca3 --- /dev/null +++ b/web/src/pages/500.astro @@ -0,0 +1,16 @@ +--- +// کچھ غلط ہو گیا: an error in the site; nothing internal is shown (the error is in the server's log, see /health) +import Base from '../layouts/Base.astro'; +import Icon from '../components/Icon.astro'; +--- + +
      + +

      معذرت، کچھ غلط ہو گیا

      +

      یہ ہماری طرف کی خرابی ہے اور درج ہو گئی ہے۔ تھوڑی دیر بعد صفحہ دوبارہ کھولیے۔

      + +
      + diff --git a/web/src/pages/health.ts b/web/src/pages/health.ts new file mode 100644 index 00000000..1b3bb5d0 --- /dev/null +++ b/web/src/pages/health.ts @@ -0,0 +1,20 @@ +// /health for an uptime monitor (divan-deploy README): 200 when the site, the API and the database all answer, +// 503 otherwise, with which part is down. Never cached. +import type { APIRoute } from 'astro'; + +const API = process.env.API_URL ?? 'http://127.0.0.1:4100'; + +export const GET: APIRoute = async () => { + const started = Date.now(); + let api = 'down', db = 'down'; + try { + const r = await fetch(`${API}/api/health`, { signal: AbortSignal.timeout(3000) }); + api = 'ok'; + db = (await r.json().catch(() => ({}))).db ?? 'down'; + } catch {} + const ok = api === 'ok' && db === 'ok'; + if (!ok) console.error(JSON.stringify({ level: 'error', time: new Date().toISOString(), msg: 'health check failed', api, db })); + return new Response(JSON.stringify({ ok, site: 'ok', api, db, ms: Date.now() - started }), { + status: ok ? 200 : 503, headers: { 'content-type': 'application/json', 'cache-control': 'no-store' }, + }); +}; diff --git a/web/src/pages/privacy.astro b/web/src/pages/privacy.astro new file mode 100644 index 00000000..ef347f71 --- /dev/null +++ b/web/src/pages/privacy.astro @@ -0,0 +1,51 @@ +--- +// رازداری (privacy): what Divan keeps about readers and moderators, why, what it does not do, and how to remove it +import Base from '../layouts/Base.astro'; +import { brand } from '../lib/brand'; +--- + +
      +

      رازداری

      +

      آخری تبدیلی: ۹ اکتوبر ۲۰۲۶ء

      +

      + {brand.name} پڑھنے کے لیے کسی اکاؤنٹ کی ضرورت نہیں۔ ہم صرف وہی محفوظ رکھتے ہیں جو سائٹ کے چلنے کے لیے ضروری ہے، اور اسے + کسی کو بیچتے یا دیتے نہیں۔ نہ اشتہار ہیں، نہ اشتہاری یا جاسوس کوکیز، نہ کسی اور کمپنی کے ٹریکر۔ +

      + +

      بغیر اکاؤنٹ کے پڑھتے ہوئے

      +
        +
      • آپ کی پسند (خط، رنگ، روشنی، پڑھنے کا انداز) آپ کے اپنے براؤزر میں رہتی ہے، ہمارے سرور پر نہیں۔ ہندسوں کی پسند ایک چھوٹی کوکی میں رہتی ہے تاکہ صفحہ انھی ہندسوں کے ساتھ آئے۔
      • +
      • لاگ ان اور اکاؤنٹ بنانے کی کوششوں کی حد کے لیے سرور آپ کا انٹرنیٹ پتہ (IP) تھوڑی دیر کے لیے یاد رکھتا ہے، اور غلطیوں کے ریکارڈ میں صفحے کا پتہ اور وقت درج ہوتا ہے۔ یہ صرف سائٹ کی حفاظت اور خرابیاں ٹھیک کرنے کے لیے ہیں اور کچھ دن بعد مٹ جاتے ہیں۔
      • +
      + +

      اکاؤنٹ کے ساتھ

      +
        +
      • اکاؤنٹ: ای میل پتہ، اور پاس ورڈ کی ایک نہ پلٹنے والی شکل (اصل پاس ورڈ کہیں محفوظ نہیں ہوتا)۔ اگر آپ لکھیں تو پورا نام اور تعارف۔
      • +
      • آپ کا ذخیرہ: محفوظ شدہ شعرا، کتابیں، کلام، اشعار، عبارتیں اور کتابوں کے صفحات؛ نوٹ؛ ذخیرۂ الفاظ۔ یہ صرف آپ کو نظر آتے ہیں۔
      • +
      • لاگ ان کی کوکی: ایک کوکی جو ۳۰ دن آپ کو لاگ ان رکھتی ہے۔ سرور پر اس کی صرف ایک نہ پلٹنے والی شکل رہتی ہے۔
      • +
      + +

      موڈریٹروں کا کام عوامی ہے

      +

      + جو تبدیلی شائع ہوتی ہے وہ {brand.name} کے کھلے ڈیٹا کے ذخیرے میں درج ہوتی ہے: کس نے لکھی، کس نے منظور کی، کب۔ وہاں آپ کا + عوامی نام لکھا جاتا ہے (پورا نام، ورنہ «موڈریٹر» اور ایک نمبر)، ای میل کبھی نہیں۔ یہ تاریخ کھلی اور مستقل ہے، + اکاؤنٹ ختم کرنے کے بعد بھی شائع شدہ کام کے ساتھ رہتی ہے۔ مسودے اور جائزے کے تبصرے صرف موڈریٹر دیکھتے ہیں۔ +

      + +

      دوسری ویب سائٹیں

      +

      + خط اور تمام فائلیں ہمارے اپنے سرور سے آتی ہیں۔ دوسری سائٹ سے صرف تب رابطہ ہوتا ہے جب آپ خود چنیں: لغت میں تلفظ کی آواز + ویکی میڈیا سے چلتی ہے، archive.org کی کتاب archive.org کا ریڈر کھولتی ہے، اور «گوگل» نئی ٹیب میں گوگل کھولتا ہے۔ ان سائٹوں + کی اپنی رازداری کی پالیسیاں ہیں۔ +

      + +

      آپ کا اختیار

      +
        +
      • اکاؤنٹ میں نام اور تعارف بدل سکتے ہیں اور اکاؤنٹ اپنے تمام ذخیرے سمیت مستقل طور پر ختم کر سکتے ہیں۔
      • +
      • محفوظ کی ہوئی ہر چیز اور ہر نوٹ اپنی جگہ سے ہٹایا جا سکتا ہے۔
      • +
      • اپنے ڈیٹا کی نقل یا کسی سوال کے لیے {brand.contact.text}۔
      • +
      + +

      یہ پالیسی بدلے تو یہی صفحہ بدلے گا، تاریخ کے ساتھ۔ دیکھیے: استعمال کی شرائط۔

      +
      + diff --git a/web/src/pages/signup.astro b/web/src/pages/signup.astro index fd89fe84..0df23553 100644 --- a/web/src/pages/signup.astro +++ b/web/src/pages/signup.astro @@ -28,6 +28,7 @@ if (Astro.request.method === 'POST') { +

      اکاؤنٹ بنا کر آپ استعمال کی شرائط مانتے ہیں؛ ہم کیا محفوظ رکھتے ہیں، یہ رازداری میں ہے۔

      پہلے سے اکاؤنٹ ہے؟ لاگ ان کریں

      diff --git a/web/src/pages/terms.astro b/web/src/pages/terms.astro new file mode 100644 index 00000000..a7748a49 --- /dev/null +++ b/web/src/pages/terms.astro @@ -0,0 +1,45 @@ +--- +// استعمال کی شرائط (terms): licences of the content and the code, accounts, moderators' and uploaders' responsibilities, +// reporting problems and copyright claims +import Base from '../layouts/Base.astro'; +import { brand } from '../lib/brand'; +--- + +
      +

      استعمال کی شرائط

      +

      آخری تبدیلی: ۹ اکتوبر ۲۰۲۶ء

      +

      {brand.name} مفت ہے اور سب کے لیے کھلا ہے۔ سائٹ استعمال کر کے آپ ان شرائط کو مانتے ہیں۔ ساتھ رازداری کا صفحہ بھی دیکھیے۔

      + +

      مواد

      +
        +
      • کلاسیکی متن عوامی ملکیت میں ہیں: انھیں پڑھنا، نقل کرنا اور آگے بانٹنا سب کے لیے آزاد ہے۔
      • +
      • {brand.name} کا ڈیٹا (متن کی ترتیب، تعارف، تبدیلیاں، ٹیگ) CC BY-SA 4.0 کے تحت ہے: استعمال کریں تو ویکی ماخذ، ویکیپیڈیا اور {brand.name} کا حوالہ دیں اور اسی لائسنس کے ساتھ بانٹیں۔
      • +
      • سائٹ کا کوڈ GNU GPL v3 کے تحت کھلا ہے۔ نام «{brand.name}»، اس کا لوگو اور شناخت اس لائسنس میں شامل نہیں: اپنی سائٹ بنائیں تو اپنا نام اور لوگو رکھیں (کوڈ کے ساتھ NOTICE اور TRADEMARKS.md)۔
      • +
      • ای بکس اپنے ناشر یا مصنف کے حقوق کے ساتھ ہیں جیسا کہ ہر کتاب پر لکھا ہے۔ archive.org کی کتابیں وہیں سے دکھائی جاتی ہیں۔
      • +
      + +

      اکاؤنٹ

      +
        +
      • اپنا پاس ورڈ محفوظ رکھیے؛ اکاؤنٹ سے ہونے والا کام آپ کی ذمہ داری ہے۔
      • +
      • سائٹ کو نقصان پہنچانا، بے جا بوجھ ڈالنا، دوسروں کے اکاؤنٹ میں دخل دینا یا خودکار طریقے سے اکاؤنٹ بنانا منع ہے۔ ایسی صورت میں اکاؤنٹ بند کیا جا سکتا ہے۔
      • +
      + +

      موڈریٹر اور اپ لوڈ

      +
        +
      • موڈریٹر کی ہر تبدیلی جائزے کے مراحل سے گزرتی ہے اور شائع ہونے پر کھلے ڈیٹا میں ان کے عوامی نام کے ساتھ درج ہوتی ہے۔
      • +
      • ای بک اپ لوڈ کرنے والا تصدیق کرتا ہے کہ اسے یہ کتاب بانٹنے کا حق ہے (عوامی ملکیت یا اجازت) اور ماخذ و حقوق درست لکھتا ہے۔ جائزہ لینے والے اس کی تصدیق کرتے ہیں؛ شک ہو تو کتاب شائع نہیں ہوتی۔
      • +
      + +

      شکایت اور حقوق کا دعویٰ

      +

      + کسی متن میں غلطی، کسی کتاب کے حقوق پر اعتراض، یا کسی اور مسئلے کے لیے {brand.contact.text}۔ حقوق کے + دعوے میں کتاب یا صفحے کا پتہ اور اپنی ملکیت کی تفصیل لکھیے؛ دعویٰ درست ہو تو مواد جلد ہٹا دیا جاتا ہے۔ +

      + +

      ذمہ داری کی حد

      +

      + {brand.name} جیسا ہے ویسا مہیا کیا جاتا ہے۔ ہم متن کو درست رکھنے کی پوری کوشش کرتے ہیں، مگر کسی غلطی یا سائٹ کے بند رہنے سے + ہونے والے نقصان کی ذمہ داری نہیں لیتے۔ یہ شرائط بدلیں تو یہی صفحہ تاریخ کے ساتھ بدلے گا۔ +

      +
      + diff --git a/web/src/styles/global.css b/web/src/styles/global.css index d0f01a6a..4245498a 100644 --- a/web/src/styles/global.css +++ b/web/src/styles/global.css @@ -833,3 +833,7 @@ p, li { text-wrap: pretty; } .byline { display: block; text-align: center; } .poem { background: none; border: 0; } } +/* 404 and error pages */ +.error-page { text-align: center; padding: 32px 0 48px; } +.error-code { font: 700 4.5rem/1 'Noto Naskh Arabic', serif; color: var(--accent, var(--brand)); opacity: .35; margin: 0 0 8px; } +.error-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-top: 18px; }