Files
aramesh-backend/backend/app/api/admin.py
T
Behrad ad75f7a7bd پروژه کامل: فرانت متصل به API + داکرایز
- اتصال کامل فرانت به API: همه محتوای سایت (پروژه‌ها، مقالات، خدمات، برند و...) از بک‌اند خوانده می‌شود (SiteProvider + useSite)
- فرم تماس و خبرنامه متصل به بک‌اند و تست‌شده در مرورگر
- ساختار مونوریپو: frontend/ + backend/
- Dockerfile برای هر دو بخش + docker-compose (PostgreSQL + FastAPI + nginx)
- nginx با پروکسی /api — بدون نیاز به CORS در حالت داکر
- README کامل فارسی
2026-08-29 13:26:58 +03:30

164 lines
6.3 KiB
Python

"""انتهای پنل مدیریت — همه مسیرها نیازمند توکن JWT معتبر هستند."""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
from .. import models, schemas
from ..auth import create_access_token, get_current_admin, verify_password
from ..database import get_db
from ..schemas import (
ArticleIn,
ArticleOut,
BeforeAfterIn,
BeforeAfterOut,
BrandIn,
BrandOut,
ContactMessageOut,
FaqIn,
FaqOut,
LoginIn,
MessageReadIn,
ProjectIn,
ProjectOut,
ServiceIn,
ServiceOut,
StatIn,
StatOut,
SubscriberOut,
TeamMemberIn,
TeamMemberOut,
TestimonialIn,
TestimonialOut,
TimelineItemIn,
TimelineItemOut,
TokenOut,
WhyUsItemIn,
WhyUsItemOut,
)
# روتر عمومی برای ورود — بدون نیاز به توکن
auth_router = APIRouter()
# روتر محافظت‌شده — همه مسیرهای آن فقط با توکن معتبر قابل دسترسی هستند
router = APIRouter(dependencies=[Depends(get_current_admin)])
@auth_router.post("/auth/login", response_model=TokenOut)
def login(payload: LoginIn, db: Session = Depends(get_db)):
user = db.query(models.AdminUser).filter(models.AdminUser.username == payload.username).first()
if user is None or not verify_password(payload.password, user.password_hash):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="نام کاربری یا رمز عبور اشتباه است",
)
return TokenOut(access_token=create_access_token(user.username))
def register_crud(path: str, model, out_schema, create_schema) -> None:
"""ثبت پنج مسیر CRUD استاندارد (لیست، ساخت، دریافت، ویرایش، حذف) روی روتر مدیریت."""
@router.get(f"/{path}", response_model=list[out_schema])
def list_items(db: Session = Depends(get_db)):
return db.query(model).order_by(model.id).all()
@router.post(f"/{path}", response_model=out_schema, status_code=201)
def create_item(payload: create_schema, db: Session = Depends(get_db)):
item = model(**payload.model_dump())
db.add(item)
try:
db.commit()
except IntegrityError:
db.rollback()
raise HTTPException(status_code=409, detail="رکوردی با این مقدار تکراری وجود دارد (مثلاً slug یا ایمیل)")
db.refresh(item)
return item
@router.get(f"/{path}/{{item_id}}", response_model=out_schema)
def get_item(item_id: int, db: Session = Depends(get_db)):
item = db.get(model, item_id)
if item is None:
raise HTTPException(status_code=404, detail="رکورد یافت نشد")
return item
@router.put(f"/{path}/{{item_id}}", response_model=out_schema)
def update_item(item_id: int, payload: create_schema, db: Session = Depends(get_db)):
item = db.get(model, item_id)
if item is None:
raise HTTPException(status_code=404, detail="رکورد یافت نشد")
for key, value in payload.model_dump().items():
setattr(item, key, value)
try:
db.commit()
except IntegrityError:
db.rollback()
raise HTTPException(status_code=409, detail="رکوردی با این مقدار تکراری وجود دارد (مثلاً slug یا ایمیل)")
db.refresh(item)
return item
@router.delete(f"/{path}/{{item_id}}", status_code=204)
def delete_item(item_id: int, db: Session = Depends(get_db)):
item = db.get(model, item_id)
if item is None:
raise HTTPException(status_code=404, detail="رکورد یافت نشد")
db.delete(item)
db.commit()
# CRUD محتوا
register_crud("brand", models.Brand, BrandOut, BrandIn)
register_crud("services", models.Service, ServiceOut, ServiceIn)
register_crud("projects", models.Project, ProjectOut, ProjectIn)
register_crud("articles", models.Article, ArticleOut, ArticleIn)
register_crud("testimonials", models.Testimonial, TestimonialOut, TestimonialIn)
register_crud("faqs", models.Faq, FaqOut, FaqIn)
register_crud("team", models.TeamMember, TeamMemberOut, TeamMemberIn)
register_crud("timeline", models.TimelineItem, TimelineItemOut, TimelineItemIn)
register_crud("stats", models.Stat, StatOut, StatIn)
register_crud("why-us", models.WhyUsItem, WhyUsItemOut, WhyUsItemIn)
register_crud("before-after", models.BeforeAfter, BeforeAfterOut, BeforeAfterIn)
# پیام‌های تماس
@router.get("/messages", response_model=list[ContactMessageOut])
def list_messages(unread_only: bool = False, db: Session = Depends(get_db)):
query = db.query(models.ContactMessage)
if unread_only:
query = query.filter(models.ContactMessage.is_read.is_(False))
return query.order_by(models.ContactMessage.created_at.desc()).all()
@router.patch("/messages/{message_id}", response_model=ContactMessageOut)
def update_message(message_id: int, payload: MessageReadIn, db: Session = Depends(get_db)):
message = db.get(models.ContactMessage, message_id)
if message is None:
raise HTTPException(status_code=404, detail="پیام یافت نشد")
message.is_read = payload.is_read
db.commit()
db.refresh(message)
return message
@router.delete("/messages/{message_id}", status_code=204)
def delete_message(message_id: int, db: Session = Depends(get_db)):
message = db.get(models.ContactMessage, message_id)
if message is None:
raise HTTPException(status_code=404, detail="پیام یافت نشد")
db.delete(message)
db.commit()
# مشترکین خبرنامه
@router.get("/subscribers", response_model=list[SubscriberOut])
def list_subscribers(db: Session = Depends(get_db)):
return db.query(models.Subscriber).order_by(models.Subscriber.created_at.desc()).all()
@router.delete("/subscribers/{subscriber_id}", status_code=204)
def delete_subscriber(subscriber_id: int, db: Session = Depends(get_db)):
subscriber = db.get(models.Subscriber, subscriber_id)
if subscriber is None:
raise HTTPException(status_code=404, detail="مشترک یافت نشد")
db.delete(subscriber)
db.commit()