MMD b31124e1e4 وبلاگ و سئو: پیش‌نویس/انتشار مقاله + کلمه کلیدی سئو در دیتابیس + OG/canonical/JSON-LD و متای سمت سرور + تغییر رمز و محدودسازی ورود + تست‌ها
- مقاله‌ها ستون status (draft/published) گرفتند — GET /api/site و همه انتهای عمومی فقط منتشرشده را برمی‌گردانند؛ autosave دیگر پیش‌نویس نیمه‌کاره را زنده نمی‌کند
- کلمه کلیدی هدف چک‌لیست سئو فیلد seo_keyphrase شد (قبلاً localStorage بود)
- robots.txt و sitemap.xml از بک‌اند + سرو /blog و /projects با تزریق title/description/OG/JSON-LD از دیتابیس برای خزنده‌های بدون JS (app/seo_pages.py)
- فرانت: applySeo در seo.ts (OG/توییتر/canonical/JSON-LD)، ادغام پیش‌نویس برای ادمین، برچسب پیش‌نویس در وبلاگ، حالت خالی وبلاگ، دکمه انتشار در صفحه نوشتن
- تغییر رمز ادمین POST /api/auth/change-password + دیالوگ «رمز» در نوار مدیریت
- محدودسازی ورود: ۵ تلاش ناموفق در ۱۵ دقیقه از هر IP → 429
- ساخت دکمه + برای ردیف سئوی صفحه‌های بدون ردیف (SingletonPill)
- تست‌های pytest (۱۵ مورد) + پین‌کردن requirements + حذف package.json سرگردان فرانت
2026-09-07 04:26:18 +03:30

گروه ساختمانی آرامش — پروژه کامل (فرانت‌اند + بک‌اند)

مونوریپوی سایت گروه ساختمانی آرامش شامل:

  • frontend/ — سایت React + Vite + Tailwind (تمام متن‌ها و تصاویر از API خوانده می‌شود)
  • backend/ — بک‌اند FastAPI + SQLAlchemy 2 + PostgreSQL + JWT + MinIO

معماری

Traefik (پورت ۵۱۷۳ — تنها نقطه ورود)
├── /         →  Caddy (فایل‌های استاتیک SPA)
├── /api      →  FastAPI (بک‌اند)
└── /media    →  MinIO (باکت aramesh-media — تصاویر آپلودی)
  • همه محتوای متنی سایت در PostgreSQL است — هیچ متنی در کد فرانت‌اند هاردکد نشده؛ با یک درخواست GET /api/site بارگذاری و از پنل مدیریت ویرایش می‌شود
  • تصاویر آپلودی در MinIO (Object Storage سازگار با S3) ذخیره می‌شوند و با مسیر نسبی /media/... برگردانده می‌شوند — بک‌اند ذخیره‌سازی بدون تغییر محتوای دیتابیس قابل تعویض است
  • مسیریابی همه‌چیز روی Traefik است (با Docker labels) — nginx در پروژه وجود ندارد

ساختار

├── frontend/             # سایت React
│   ├── src/              # کد منبع (api.tsx = لایه اتصال به بک‌اند)
│   ├── Dockerfile        # بیلد Node + سرو استاتیک با Caddy
│   └── Caddyfile         # فقط سرو فایل‌های استاتیک (مسیریابی روی Traefik است)
├── backend/              # بک‌اند FastAPI
│   ├── app/              # کد منبع (آپلود تصویر: app/api/admin.py)
│   ├── requirements.txt
│   ├── Dockerfile
│   └── .env.example
└── docker-compose.yml    # اجرای کامل با یک دستور

اجرا با Docker (ساده‌ترین راه)

docker compose up --build
  • سایت: http://localhost:5173 (از طریق Traefik)
  • مستندات API: http://localhost:8001/docs (با docker-compose.override.yml محلی)
  • کنسول MinIO: http://localhost:9001 — کاربر/رمز از متغیرهای MINIO_ROOT_* در compose
  • دیتابیس PostgreSQL و آبجکت‌استور MinIO هر دو در کانتینر جدا با volume دائمی
  • بک‌اند هنگام شروع، خودش جداول را می‌سازد و فقط جدول‌های خالی را seed می‌کند — ارتقای نسخه داده‌های فعلی را از بین نمی‌برد
  • در اولین اجرا، سرویس minio-setup باکت aramesh-media را می‌سازد و خواندن عمومی را فعال می‌کند

اجرای محلی (توسعه)

بک‌اند

cd backend
python -m venv .venv
.venv\Scripts\activate        # ویندوز — در لینوکس: source .venv/bin/activate
pip install -r requirements.txt

# فایل .env از روی .env.example بسازید و DATABASE_URL را تنظیم کنید
python -m app.seed            # ساخت جداول + محتوای اولیه
uvicorn app.main:app --reload

فرانت‌اند

cd frontend
npm install
npm run dev

در حالت توسعه، Vite درخواست‌های /api و /media را به Traefik (پورت ۵۱۷۳) پروکسی می‌کند؛ پس استک Docker باید در حال اجرا باشد. آدرس بک‌اند در frontend/src/config.ts از VITE_API_URL خوانده می‌شود — پیش‌فرض خالی یعنی درخواست‌های هم‌مبدأ.

امکانات بک‌اند

  • API عمومی: برند، منو، هیرو، خدمات، پروژه‌ها، مقالات، نظرات، سوالات متداول، تیم، تایم‌لاین، آمار، دفاتر، مدل‌های سرمایه‌گذاری و... — همه در دیتابیس
  • فرم تماس POST /api/contact و خبرنامه POST /api/newsletter
  • پنل مدیریت با JWT: ورود POST /api/auth/login (پیش‌فرض: admin / aramesh123) و سپس CRUD کامل همه محتواها در /api/admin/...
  • آپلود تصویر POST /api/admin/upload (فقط توکن ادمین، حداکثر ۱۰MB) — فایل در MinIO ذخیره و URL نسبی /media/... برمی‌گردد
  • ویرایش جزئی PATCH /api/admin/{resource}/{id} — فقط فیلدهای ارسال‌شده عوض می‌شوند
  • ترتیب‌بندی POST /api/admin/{resource}/reorder — با {"ids": [...]}
  • همه محتوا در یک درخواست: GET /api/site

در محیط واقعی حتماً SECRET_KEY، ADMIN_PASSWORD و MINIO_ROOT_PASSWORD را تغییر دهید.

ویرایش زنده سایت (دوقلوی دیجیتال)

ادمین‌ها نیازی به داشبورد جدا ندارند — خود سایت ویرایشگر است:

  1. به /admin/login بروید و وارد شوید
  2. نوار شناور پایین صفحه ظاهر می‌شود؛ حالت ویرایش را روشن کنید
  3. روی هر متن کلیک کنید و ویرایش کنید — خروج از کادر، بلافاصله ذخیره می‌شود (Enter ذخیره، Esc لغو)؛ وضعیت ذخیره در نوار پایین دیده می‌شود
  4. آیتم‌های مجموعه‌ها (خدمات، پروژه‌ها، مقالات، نظرات، تیم و...) با هاور، دکمه‌های ویرایش / حذف / جابه‌جایی ترتیب نشان می‌دهند؛ دکمه «افزودن» هم انتهای هر لیست هست
  5. تصویرها از همان کشوها به MinIO آپلود می‌شوند
  • در حالت ویرایش، لینک‌ها کلیک نمی‌شوند تا ویرایش متن ممکن باشد
  • بازدیدکننده‌های عادی هیچ اثری از این لایه نمی‌بینند (بدون توکن، همه درخواست‌های مدیریتی 401 برمی‌گردند)
  • تمام تغییرها بلافاصله در PostgreSQL ثبت می‌شوند — با Ctrl+F5 هم می‌مانند

وبلاگ — نوشتن با ویرایشگر WYSIWYG

مقالات با ویرایشگر غنی TipTap نوشته می‌شوند (همان تجربه Medium):

  • در حالت ویرایش، روی هر مقاله در صفحه وبلاگ دکمه ویرایش را بزنید — کشوی نوشتن باز می‌شود
  • تیتر (H2/H3)، بولد/ایتالیک/زیرخط، لیست نقطه‌ای و شماره‌ای، نقل‌قول، لینک و واگرد
  • درج تصویر داخل متن: دکمه تصویر، یا چسباندن/کشیدن تصویر داخل نوشته — فایل مستقیم در MinIO ذخیره و در متن قرار می‌گیرد
  • راست‌به‌چپ کامل با فونت و تم سایت
  • ذخیره خودکار: هر مکث کوتاه در تایپ، تغییرها را ذخیره می‌کند؛ بستن کشو هم ذخیره می‌کند

فرمت ذخیره: بدنه مقاله یک سند ساختاریافته TipTap (JSON) در PostgreSQL است — نه HTML خام. صفحه مقاله آن را با رندر استاتیک TipTap به HTML تبدیل می‌کند، پس فقط تگ‌های شناخته‌شده (پاراگراف، تیتر، لیست، نقل‌قول، تصویر، لینک) تولید می‌شوند. مقالات قدیمی (لیست پاراگراف) در اولین اجرا به‌صورت خودکار به این فرمت تبدیل می‌شوند.

سئو

  • بررسی سئو در صفحه نوشتن: فهرست سبز/نارنجی/قرمز به سبک یوست ولی سازگار با فارسی — طول عنوان، کلمه کلیدی هدف (در عنوان/پاراگراف اول/تیترها)، طول چکیده، تعداد واژه، تیتربندی H2، متن جایگزین تصاویر، پیوندها و نشانی (slug)
  • سئوی هر صفحه: در حالت ویرایش، دکمه سئو روی هیرو هر صفحه (و کنار هیرو صفحه اصلی) — عنوان تب و توضیح متای هر صفحه از دیتابیس خوانده و اعمال می‌شود
  • صفحه مقاله و پروژه به‌طور خودکار عنوان و توضیح متا را از محتوای خودشان می‌گیرند

نکات امنیتی

  • فایل .env هرگز commit نمی‌شود (در .gitignore است)
  • رمز عبور ادمین با bcrypt هش می‌شود
  • توکن‌های JWT امضا دارند و مدت اعتبارشان قابل تنظیم است
  • تصاویر باکت MinIO عمومی (فقط خواندن) هستند؛ آپلود فقط با توکن ادمین ممکن است
S
Description
No description provided
Readme
61 MiB
Languages
TypeScript 53%
Python 37.6%
JavaScript 5.2%
Shell 2%
CSS 1.5%
Other 0.6%