Files
digitalcatalog/docs/PROJECT.md
T
MahdiandCursor c236b0f621 Add display frames and improve create-page page management.
Store phone/tablet/photo-frame shells with catalogs, append pages from extra images or PDFs, and reorder drafts with drag-and-drop.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-20 14:20:16 +03:30

20 KiB
Raw Blame History

کاتالوگ دیجیتال (Digital Catalog)

سند مرجع پروژه — ۱۸ اوت ۲۰۲۶، به‌روز تا ۱۹ اوت ۲۰۲۶
قبل از پیاده‌سازی نوشته شده است. اگر رفتار محصول عوض شد، همین فایل باید به‌روز شود.


۱. محصول چیست

کاربر یک PDF چندصفحه‌ای یا چند تصویر می‌دهد. سیستم یک کاتالوگ دیجیتال می‌سازد: صفحات با افکت ورق‌زدن نمایش داده می‌شوند، یک لینک مشاهده و یک QR تحویل می‌شود.

هدف دو کار با هم است:

  1. ساخت و دیدن کاتالوگ همان لحظه
  2. اشتراک‌گذاری همان کاتالوگ با لینک و QR، بدون ثبت‌نام

کاتالوگ‌ها در سایت لیست نمی‌شوند. فقط کسی که لینک یا QR را دارد می‌تواند ببیند.


۲. تصمیم‌های قفل‌شده

موضوع تصمیم
ثبت‌نام / ورود ندارد. هر بازدیدکننده دامنه می‌تواند بسازد
گالری عمومی ندارد
خروجی ساخت لینک مشاهده + تصویر QR + لینک حذف
ویرایش بعدی ندارد (صفحات، PDF، افکت و زمان ورق بعد از ساخت عوض نمی‌شود)
انقضای خودکار ندارد
زبان رابط فارسی، راست‌به‌چپ
نسخه اول ساده؛ بدون حساب کاربری، بدون پنل ادمین، بدون SEO کاتالوگ
دامنهٔ زنده https://ktg.montzar.ir
دیتابیس نسخه اول SQLite روی دیسک همین پروژه؛ به Postgres مونتایم/سوپرادمین وصل نمی‌شود

۳. چه چیزهایی نسخه اول نیست

این‌ها عمداً بیرون هستند تا پروژه ساده بماند:

  • حساب کاربری، نقش، پنل مدیریت
  • گالری، جستجو، نقشه سایت از کاتالوگ‌ها
  • ویرایش کاتالوگ بعد از ساخت
  • نقطه کلیک‌پذیر روی صفحه، فهرست مطالب، ویدیو داخل صفحه
  • موسیقی پس‌زمینه، پخش خودکار اجباری
  • اپ دسکتاپ / موبایل بومی
  • پرداخت، چندزبانه بودن رابط، تم‌های زیاد
  • Three.js و شبیه‌سازی فیزیک کاغذ از صفر

اگر فایل اشتباه آپلود شد: کاتالوگ را با لینک حذف پاک می‌کند و یکی جدید می‌سازد.


۴. مسیر کاربر

ورود به دامنه
    → صفحه ساخت (آپلود PDF یا چند تصویر + انتخاب افکت)
    → پردازش و ذخیره
    → صفحه نتیجه:
         • لینک مشاهده (کپی)
         • QR (نمایش + دانلود PNG)
         • باز کردن کاتالوگ
         • لینک حذف (یک‌بار نشان داده می‌شود)
    → بیننده با لینک یا اسکن QR کاتالوگ را ورق می‌زند

لینک حذف را باید همان لحظه ذخیره کند. اگر گم شود، راه بازیابی نیست (چون حساب کاربری نداریم). این محدودیت باید روی صفحه نتیجه نوشته شود.


۵. امکانات نسخه اول

ساخت

  • Drag & drop یا انتخاب فایل
  • هنگام خواندن PDF/تصویر، آماده‌سازی صفحات و ساخت کاتالوگ، لودینگ با متن پیشرفت نشان داده می‌شود
  • یک PDF یا چند تصویر (نه هر دو با هم در یک کاتالوگ)
  • ترتیب تصاویر = ترتیب انتخاب / ترتیب نام فایل؛ امکان جابه‌جایی قبل از ساخت
  • انتخاب یکی از افکت‌های ورق‌زدن
  • انتخاب قالب نمایش (بدون قالب، گوشی، تبلت، قاب مدرن/کودکانه/کلاسیک). قالب دور کل کاتالوگ است و صفحات داخل همان صفحه/شیشه ورق می‌خورند. بعد از ساخت عوض نمی‌شود
  • تعیین زمان ورق‌خوردن (۴۰۰ تا ۵۰۰۰ میلی‌ثانیه؛ پیش‌فرض ۱ ثانیه). بعد از ساخت عوض نمی‌شود. روی مجله، اسلاید، محو و کارت اعمال می‌شود؛ اسکرول عمودی فقط فاصلهٔ پخش خودکار را از همین زمان می‌گیرد
  • عنوان اختیاری (اگر خالی باشد: «کاتالوگ»)

مشاهده

  • نمایش صفحات با افکت انتخاب‌شده (اسلاید، محو و کارت با انتقال ورودی/خروجی واقعی)
  • ۳ ثانیه پس از باز شدن، پخش خودکار شروع می‌شود؛ فاصلهٔ بین ورق‌ها برابر زمان ورق‌خوردن ذخیره‌شده به‌علاوهٔ کمی مکث است
  • قبلی / بعدی، کلیدهای جهت، کلیک/اسوایپ روی صفحه
  • بندانگشتی صفحات
  • زوم و تمام‌صفحه
  • دسکتاپ: فقط افکت magazine دو صفحه روبه‌رو نشان می‌دهد؛ بقیه همیشه یک صفحه
  • موبایل: همیشه یک صفحه؛ نوار عنوان و دکمه‌ها جمع می‌شوند و هنگام پخش خودکار مخفی می‌شوند (دکمهٔ کوچک توقف می‌ماند). اگر ورق سه‌بعدی روی دستگاه ضعیف بود، همان کاتالوگ با اسلاید افقی دیده می‌شود (تنظیم ذخیره‌شده عوض نمی‌شود)

اشتراک

  • آدرس ثابت: {BASE_URL}/c/{id}
  • QR همان آدرس را کد می‌کند
  • دانلود QR به صورت PNG

حذف

  • لینک حذف فقط روی صفحهٔ نتیجه نشان داده می‌شود؛ باید همان لحظه کپی و ذخیره شود
  • بعداً فقط وقتی به کار می‌آید که سازنده بخواهد همین کاتالوگ را پاک کند (باز کردن لینک در مرورگر و تأیید)
  • در برنامه ذخیره یا بازیابی نمی‌شود؛ اگر گم شود راهی نیست
  • لینک مشاهده برای اشتراک است؛ لینک حذف را نباید پخش کرد
  • بعد از حذف، لینک مشاهده و QR دیگر کار نمی‌کنند و فایل صفحات از دیسک پاک می‌شوند

۶. افکت‌های ورق‌زدن

موقع ساخت، یکی انتخاب می‌شود و برای همیشه روی همان کاتالوگ می‌ماند.

شناسه نام نمایشی رفتار
magazine ورق مجله سه‌بعدی واقعی؛ افکت اصلی
slide اسلاید افقی صفحه بعدی از بغل می‌آید
fade محو شدن صفحه فعلی محو، بعدی ظاهر می‌شود
card کارت ورق دوبعدی مثل کارت
scroll اسکرول عمودی صفحات زیر هم؛ مناسب موبایل

پیاده‌سازی: برای magazine از کتابخانه StPageFlip استفاده می‌شود. بقیه با Vue + CSS/transition. فیزیک کاغذ از صفر نوشته نمی‌شود.


۷. معماری

یک اپلیکیشن با دو بخش در یک مخزن:

  • فرانت‌اند: Vue 3 — ساخت، نتیجه، نمایش کاتالوگ
  • بک‌اند: API سبک Node — آپلود، ذخیره، سرو کردن صفحات، QR، حذف

کاتالوگ‌ها روی سرور ذخیره می‌شوند تا لینک برای همه باز شود. تبدیل PDF به تصویر در مرورگر با pdf.js انجام می‌شود. سرور PDF خام نمی‌گیرد؛ فقط JPEG صفحات را ذخیره می‌کند. به ابزار native مثل Poppler نیاز نیست.

مرورگر                         سرور
──────                         ────
انتخاب PDF/تصویر
pdf.js → تصویر هر صفحه
آپلود تصاویر + افکت + عنوان  →  ذخیره فایل‌ها روی دیسک
                             →  ردیف در SQLite
                             →  ساخت QR
صفحه نتیجه ← لینک/QR/حذف
بیننده /c/:id                →  متادیتا + آدرس تصاویر
نمایش با افکت انتخاب‌شده

۸. استک قفل‌شده

لایه انتخاب دلیل
UI Vue 3 + Vite + TypeScript کامپوننت و state برای آپلود و نمایش
استایل CSS ساده + RTL بدون UI kit سنگین
PDF در مرورگر pdf.js تبدیل صفحه به تصویر بدون سرور خاص
ورق مجله StPageFlip افکت آماده و قابل‌نگهداری
QR بسته qrcode در سرور PNG قابل دانلود
API Hono روی Node سبک، TypeScript
داده SQLite از طریق sql.js + Drizzle بدون کامپایل native (روی ویندوز بدون Visual Studio هم نصب می‌شود)
فایل صفحات پوشه محلی دیسک ساده برای یک سرور
شناسه nanoid کوتاه و غیرقابل حدس نسبی

HTML/CSS خالی رد شد: آپلود، تبدیل PDF، چند افکت، موبایل و صفحه نتیجه خیلی زود شلوغ می‌شود.

Nuxt و فریم‌ورک سنگین بک‌اند رد شد: نمایش کاتالوگ تقریباً همه در کلاینت است؛ یک API کوچک کافی است.


۹. مدل داده

جدول catalogs:

فیلد نوع توضیح
id متن، حدود ۱۰ کاراکتر شناسه عمومی در لینک
delete_token متن، حدود ۲۴ کاراکتر فقط برای حذف؛ در URL مشاهده نیست
title متن عنوان
effect متن یکی از شناسه‌های بخش ۶
frame متن قالب پوسته: none، phone، tablet، frame-modern، frame-kids، frame-classic
page_count عدد تعداد صفحات ذخیره‌شده
flip_ms عدد مدت انیمیشن ورق‌خوردن به میلی‌ثانیه (۴۰۰–۵۰۰۰)
created_at زمان

فایل‌ها:

storage/catalogs/{id}/
    page-001.jpg
    page-002.jpg
    ...
    qr.png

صفحات با عرض حداکثر ۱۶۰۰ پیکسل، JPEG با کیفیت حدود ۸۵ ذخیره می‌شوند.


۱۰. API

پایه: /api

متد مسیر کار
POST /catalogs ساخت. بدنه: تصاویر JPEG صفحات (نه خود PDF) + title + effect + flipMs + frame
GET /catalogs/:id متادیتا برای بیننده (بدون delete_token)؛ شامل flipMs و frame
GET /catalogs/:id/pages/:n فایل تصویر صفحه
GET /catalogs/:id/qr PNG کد QR. عمومی است چون فقط همان لینک مشاهده را کد می‌کند
DELETE /catalogs/:id حذف؛ کوئری token باید با delete_token یکی باشد

پاسخ ساخت:

{
  "id": "abc12xyz90",
  "viewUrl": "https://example.com/c/abc12xyz90",
  "deleteUrl": "https://example.com/c/abc12xyz90/delete?token=...",
  "qrUrl": "https://example.com/api/catalogs/abc12xyz90/qr"
}

deleteUrl فقط همین یک‌بار به سازنده داده می‌شود. از GET عمومی برنمی‌گردد.

صفحات فرانت:

مسیر صفحه
/ ساخت کاتالوگ
/c/:id نمایش کاتالوگ
/c/:id/done?token=... صفحه نتیجه (لینک، QR، حذف) — توکن فقط برای نشان دادن دکمه حذف و جلوگیری از دیدن این صفحه توسط بیننده معمولی
/c/:id/delete?token=... تأیید حذف

بیننده /c/:id را می‌بیند. صفحه نتیجه بدون توکن باز نشود.


۱۱. محدودیت‌ها (ضد سوءاستفاده)

چون ثبت‌نام نیست، این سقف‌ها اجباری‌اند:

مورد سقف نسخه اول
حجم هر فایل ورودی ۱۰۰ مگابایت
تعداد صفحه حداکثر ۲۰۰
فرمت PDF application/pdf
فرمت تصویر jpeg, png, webp, gif
ساخت از یک IP ۱۰ کاتالوگ در ساعت (شمارنده در حافظه همان پروسه Node؛ یک سرور)
عرض تصویر ذخیره‌شده حداکثر ۱۶۰۰px
زمان ورق‌خوردن ۴۰۰ تا ۵۰۰۰ میلی‌ثانیه

اگر سقف رد شود، پیام فارسی واضح نشان داده می‌شود و چیزی ذخیره نمی‌شود.


۱۲. امنیت و حریم

  • کاتالوگ unlisted است: endpoint لیست وجود ندارد
  • id تصادفی است؛ نباید پشت سر هم و قابل حدس باشد
  • حذف فقط با delete_token
  • نوع فایل واقعی بررسی شود، نه فقط پسوند
  • فایل آپلودی هرگز به عنوان اسکریپت سرو نشود
  • برای QR باید PUBLIC_BASE_URL در محیط سرور تنظیم شود تا لینک داخل QR درست باشد

این مدل «امنیت از طریق مخفی بودن لینک» است، نه قفل رمز روی کاتالوگ. هر کس لینک را داشته باشد محتوا را می‌بیند.


۱۳. رابط کاربری (خلاصه)

سه صفحه اصلی، فارسی و RTL:

  1. ساخت: ناحیه آپلود، لودینگ هنگام خواندن فایل و ساخت، لیست صفحات، انتخاب افکت، اسلایدر زمان ورق‌خوردن، دکمه ساخت
  2. نتیجه: لینک مشاهده، QR، هشدار «لینک حذف را ذخیره کن»، فیلد لینک حذف، دکمه حذف
  3. نمایش: استیج کاتالوگ، کنترل‌ها، بندانگشتی

ظاهر نسخه اول تمیز و ساده باشد، نه داشبورد شلوغ.


۱۴. اجرا، دامنه و استقرار

یک سرور Node کافی است. SQLite و پوشه storage/ کنار اپ می‌مانند.

توسعه محلی: npm run dev — فرانت http://127.0.0.1:3000، API روی 3010 و از طریق /api پروکسی می‌شود.

دامنهٔ زنده (۱۹ اوت ۲۰۲۶)

  • آدرس عمومی: https://ktg.montzar.ir
  • کد: http://get.feep.ir/Mahdi/digitalcatalog.git (شاخه main)
  • مسیر روی سرور: /var/www/ktg
  • PM2: نام ktg-catalog، یک اینستنس، fork (نه cluster؛ sql.js با چند پروسه خراب می‌شود)
  • پورت داخلی: 3020 — تداخل نکند با مونتایم (3000)، فوری‌سند (3001)، سوپرادمین (3010)
  • Nginx: فقط server_name ktg.montzar.ir؛ همهٔ مسیرها به 127.0.0.1:3020 پروکسی می‌شوند (client_max_body_size 100M فقط در همین vhost)
  • راهنمای قدم‌به‌قدم: deploy/DEPLOY.md

این اپ روی همان هاست مشترک مونتایم است (185.50.38.53) ولی جدا می‌ماند: پوشه، پورت، پروسهٔ PM2، فایل SQLite و دامنهٔ خودش. دیتابیس Postgres و JWT سوپرادمین لمس نمی‌شود. اگر بعداً لاگین/پروفایل آمد، باز هم داخل همین دامنه و دیتابیس همین پروژه است؛ کوکی نباید روی .montzar.ir باشد.

متغیرهای محیط:

# توسعه
PORT=3000
API_PORT=3010
PUBLIC_BASE_URL=http://localhost:3000
STORAGE_DIR=./storage
DATABASE_PATH=./data/catalogs.sqlite

# سرور ktg.montzar.ir
NODE_ENV=production
PORT=3020
PUBLIC_BASE_URL=https://ktg.montzar.ir
STORAGE_DIR=/var/www/ktg/storage
DATABASE_PATH=/var/www/ktg/data/catalogs.sqlite

PUBLIC_BASE_URL همان دامنه‌ای است که داخل QR نوشته می‌شود. کاتالوگ‌های ساخته‌شده با localhost روی دامنهٔ زنده معتبر نیستند؛ باید روی سرور دوباره ساخته شوند.

آپدیت کد روی سرور: git pull سپس npm ci && npm run build && pm2 restart ktg-catalog. پوشه‌های storage و data و فایل .env پاک نشوند.


۱۵. گزینه‌هایی که بررسی شد و انتخاب نشد

  1. فقط HTML/CSS — برای دمو کافی است؛ برای آپلود، PDF، چند افکت و لینک عمومی کافی نیست.
  2. فقط Vue در مرورگر، بدون سرور — کاتالوگ بعد از بستن تب از بین می‌رود؛ لینک و QR برای دیگری کار نمی‌کند.
  3. Vue + بک‌اند + حساب کاربری / گالری — برای نسخه اول اضافه است.

انتخاب نهایی: Vue 3 + API سبک Node + SQLite + فایل روی دیسک.


۱۶. معیار تمام شدن نسخه اول

نسخه اول وقتی تمام است که:

  • با چند تصویر کاتالوگ ساخته شود (تست API با JPEG)
  • با PDF چندصفحه‌ای در مرورگر (بررسی دستی روی دامنهٔ زنده)
  • پنج افکت در UI پیاده شده‌اند (بررسی ظاهری باقی است)
  • لینک /c/:id از API متادیتا می‌گیرد
  • QR همان لینک را کد می‌کند
  • حذف با توکن کاتالوگ را از بین ببرد
  • بدون توکن نشود حذف کرد
  • گالری یا لیست عمومی وجود نداشته باشد
  • روی موبایل بررسی دستی
  • سقف تعداد صفحه پیام خطا بدهد
  • روی https://ktg.montzar.ir زنده است؛ ساخت کاتالوگ و لینک /c/:id کار می‌کند

۱۷. کارهای بعدی (عمداً بعد از نسخه اول)

فقط اگر بعداً لازم شد، نه الان:

  • ذخیره لینک‌های همین مرورگر در localStorage
  • عوض کردن افکت بدون آپلود دوباره
  • انقضای اختیاری
  • رمز روی کاتالوگ
  • هاست چند سروره / S3
  • حساب کاربری، پروفایل و «کاتالوگ‌های من» — جدا از سوپرادمین/مونتایم؛ دیتابیس و کوکی مال ktg.montzar.ir

تاریخچه تصمیم

تاریخ تصمیم
۲۰۲۶-۰۸-۱۸ محصول: کاتالوگ دیجیتال از PDF/تصویر با افکت و اشتراک
۲۰۲۶-۰۸-۱۸ از اول هم مشاهده همان‌جا، هم لینک عمومی + QR
۲۰۲۶-۰۸-۱۸ بدون ورود؛ هر بازدیدکننده می‌تواند بسازد
۲۰۲۶-۰۸-۱۸ بدون گالری؛ فقط لینک و QR
۲۰۲۶-۰۸-۱۸ بعد از ساخت: لینک + QR + لینک حذف؛ بدون ویرایش و بدون انقضا
۲۰۲۶-۰۸-۱۸ استک: Vue 3 + Hono + SQLite + pdf.js + StPageFlip
۲۰۲۶-۰۸-۱۸ زمان ورق‌خوردن هنگام ساخت (۴۰۰–۵۰۰۰ms)؛ روی مجله/اسلاید/محو/کارت
۲۰۲۶-۰۸-۱۸ لودینگ هنگام خواندن فایل، آماده‌سازی صفحات و ساخت کاتالوگ
۲۰۲۶-۰۸-۱۹ استقرار روی هاست مشترک؛ دامنه ktg.montzar.ir؛ پورت ۳۰۲۰؛ SQLite جدا
۲۰۲۶-۰۸-۱۹ مخزن Gitea: http://get.feep.ir/Mahdi/digitalcatalog.git
۲۰۲۶-۰۸-۲۰ قالب پوسته دور کاتالوگ: گوشی، تبلت، سه قاب عکس؛ SVG/CSS