اولین نسخه بک‌اند آرامش پلازا

This commit is contained in:
Behrad
2026-08-27 15:15:12 +03:30
commit 854309f976
26 changed files with 1861 additions and 0 deletions
View File
+70
View File
@@ -0,0 +1,70 @@
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.auth import get_current_admin
from app.database import get_db
from app import models, schemas
router = APIRouter(prefix="/api/articles", tags=["Articles"])
@router.get("", response_model=list[schemas.ArticleOut])
def list_articles(category: str | None = None, db: Session = Depends(get_db)):
query = db.query(models.Article)
if category:
query = query.filter(models.Article.category == category)
return query.order_by(models.Article.order).all()
@router.get("/{slug}", response_model=schemas.ArticleOut)
def get_article(slug: str, db: Session = Depends(get_db)):
article = db.query(models.Article).filter(models.Article.slug == slug).first()
if not article:
raise HTTPException(status.HTTP_404_NOT_FOUND, "مقاله پیدا نشد")
return article
@router.post("", response_model=schemas.ArticleOut, status_code=status.HTTP_201_CREATED)
def create_article(
payload: schemas.ArticleCreate,
db: Session = Depends(get_db),
_admin=Depends(get_current_admin),
):
if db.query(models.Article).filter(models.Article.slug == payload.slug).first():
raise HTTPException(status.HTTP_400_BAD_REQUEST, "این slug قبلاً استفاده شده است")
article = models.Article(**payload.model_dump())
db.add(article)
db.commit()
db.refresh(article)
return article
@router.put("/{article_id}", response_model=schemas.ArticleOut)
def update_article(
article_id: int,
payload: schemas.ArticleUpdate,
db: Session = Depends(get_db),
_admin=Depends(get_current_admin),
):
article = db.get(models.Article, article_id)
if not article:
raise HTTPException(status.HTTP_404_NOT_FOUND, "مقاله پیدا نشد")
for field, value in payload.model_dump(exclude_unset=True).items():
setattr(article, field, value)
db.commit()
db.refresh(article)
return article
@router.delete("/{article_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_article(
article_id: int,
db: Session = Depends(get_db),
_admin=Depends(get_current_admin),
):
article = db.get(models.Article, article_id)
if not article:
raise HTTPException(status.HTTP_404_NOT_FOUND, "مقاله پیدا نشد")
db.delete(article)
db.commit()
return None
+25
View File
@@ -0,0 +1,25 @@
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from app.auth import authenticate_admin, create_access_token
from app.database import get_db
from app.schemas import Token
router = APIRouter(prefix="/api/auth", tags=["Auth"])
@router.post("/login", response_model=Token)
def login(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
"""
ورود ادمین. با فرم استاندارد OAuth2 (username, password) کار می‌کند —
یعنی از Swagger UI (/docs) هم مستقیماً قابل تست است.
"""
user = authenticate_admin(db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="نام کاربری یا رمز عبور اشتباه است",
)
token = create_access_token({"sub": user.username})
return Token(access_token=token)
+38
View File
@@ -0,0 +1,38 @@
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from app.auth import get_current_admin
from app.database import get_db
from app import models, schemas
router = APIRouter(prefix="/api/brand", tags=["Brand"])
def _get_or_create_brand(db: Session) -> models.Brand:
brand = db.get(models.Brand, 1)
if not brand:
brand = models.Brand(id=1)
db.add(brand)
db.commit()
db.refresh(brand)
return brand
@router.get("", response_model=schemas.BrandOut)
def get_brand(db: Session = Depends(get_db)):
"""اطلاعات کلی برند (نام، لوگو، تلفن، ایمیل، آدرس و...) — یک رکورد ثابت."""
return _get_or_create_brand(db)
@router.put("", response_model=schemas.BrandOut)
def update_brand(
payload: schemas.BrandUpdate,
db: Session = Depends(get_db),
_admin=Depends(get_current_admin),
):
brand = _get_or_create_brand(db)
for field, value in payload.model_dump(exclude_unset=True).items():
setattr(brand, field, value)
db.commit()
db.refresh(brand)
return brand
+59
View File
@@ -0,0 +1,59 @@
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.auth import get_current_admin
from app.database import get_db
from app import models, schemas
router = APIRouter(prefix="/api/contact", tags=["Contact"])
@router.post("", response_model=schemas.ContactOut, status_code=status.HTTP_201_CREATED)
def submit_contact(payload: schemas.ContactCreate, db: Session = Depends(get_db)):
"""این اندپوینت عمومی است — فرم تماس سایت آن را صدا می‌زند."""
lead = models.ContactLead(**payload.model_dump())
db.add(lead)
db.commit()
db.refresh(lead)
return lead
@router.get("", response_model=list[schemas.ContactOut])
def list_contacts(
db: Session = Depends(get_db),
_admin=Depends(get_current_admin),
):
"""فقط ادمین: لیست همه درخواست‌های تماس، جدیدترین اول."""
return db.query(models.ContactLead).order_by(models.ContactLead.created_at.desc()).all()
@router.put("/{lead_id}", response_model=schemas.ContactOut)
def update_contact(
lead_id: int,
payload: schemas.ContactUpdate,
db: Session = Depends(get_db),
_admin=Depends(get_current_admin),
):
"""فقط ادمین: مثلاً علامت زدن به‌عنوان خوانده‌شده."""
lead = db.get(models.ContactLead, lead_id)
if not lead:
raise HTTPException(status.HTTP_404_NOT_FOUND, "درخواست پیدا نشد")
for field, value in payload.model_dump(exclude_unset=True).items():
setattr(lead, field, value)
db.commit()
db.refresh(lead)
return lead
@router.delete("/{lead_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_contact(
lead_id: int,
db: Session = Depends(get_db),
_admin=Depends(get_current_admin),
):
lead = db.get(models.ContactLead, lead_id)
if not lead:
raise HTTPException(status.HTTP_404_NOT_FOUND, "درخواست پیدا نشد")
db.delete(lead)
db.commit()
return None
+48
View File
@@ -0,0 +1,48 @@
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.auth import get_current_admin
from app.database import get_db
from app import models, schemas
router = APIRouter(prefix="/api/newsletter", tags=["Newsletter"])
@router.post("", response_model=schemas.NewsletterOut, status_code=status.HTTP_201_CREATED)
def subscribe(payload: schemas.NewsletterCreate, db: Session = Depends(get_db)):
"""اندپوینت عمومی — فرم خبرنامه سایت این را صدا می‌زند."""
existing = db.query(models.NewsletterSubscriber).filter(
models.NewsletterSubscriber.email == payload.email
).first()
if existing:
raise HTTPException(status.HTTP_400_BAD_REQUEST, "این ایمیل قبلاً ثبت شده است")
sub = models.NewsletterSubscriber(email=payload.email)
db.add(sub)
db.commit()
db.refresh(sub)
return sub
@router.get("", response_model=list[schemas.NewsletterOut])
def list_subscribers(
db: Session = Depends(get_db),
_admin=Depends(get_current_admin),
):
"""فقط ادمین: لیست همه مشترکین خبرنامه."""
return db.query(models.NewsletterSubscriber).order_by(
models.NewsletterSubscriber.created_at.desc()
).all()
@router.delete("/{sub_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_subscriber(
sub_id: int,
db: Session = Depends(get_db),
_admin=Depends(get_current_admin),
):
sub = db.get(models.NewsletterSubscriber, sub_id)
if not sub:
raise HTTPException(status.HTTP_404_NOT_FOUND, "مشترک پیدا نشد")
db.delete(sub)
db.commit()
return None
+70
View File
@@ -0,0 +1,70 @@
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.auth import get_current_admin
from app.database import get_db
from app import models, schemas
router = APIRouter(prefix="/api/projects", tags=["Projects"])
@router.get("", response_model=list[schemas.ProjectOut])
def list_projects(status_filter: str | None = None, db: Session = Depends(get_db)):
query = db.query(models.Project)
if status_filter:
query = query.filter(models.Project.status == status_filter)
return query.order_by(models.Project.order).all()
@router.get("/{slug}", response_model=schemas.ProjectOut)
def get_project(slug: str, db: Session = Depends(get_db)):
project = db.query(models.Project).filter(models.Project.slug == slug).first()
if not project:
raise HTTPException(status.HTTP_404_NOT_FOUND, "پروژه پیدا نشد")
return project
@router.post("", response_model=schemas.ProjectOut, status_code=status.HTTP_201_CREATED)
def create_project(
payload: schemas.ProjectCreate,
db: Session = Depends(get_db),
_admin=Depends(get_current_admin),
):
if db.query(models.Project).filter(models.Project.slug == payload.slug).first():
raise HTTPException(status.HTTP_400_BAD_REQUEST, "این slug قبلاً استفاده شده است")
project = models.Project(**payload.model_dump())
db.add(project)
db.commit()
db.refresh(project)
return project
@router.put("/{project_id}", response_model=schemas.ProjectOut)
def update_project(
project_id: int,
payload: schemas.ProjectUpdate,
db: Session = Depends(get_db),
_admin=Depends(get_current_admin),
):
project = db.get(models.Project, project_id)
if not project:
raise HTTPException(status.HTTP_404_NOT_FOUND, "پروژه پیدا نشد")
for field, value in payload.model_dump(exclude_unset=True).items():
setattr(project, field, value)
db.commit()
db.refresh(project)
return project
@router.delete("/{project_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_project(
project_id: int,
db: Session = Depends(get_db),
_admin=Depends(get_current_admin),
):
project = db.get(models.Project, project_id)
if not project:
raise HTTPException(status.HTTP_404_NOT_FOUND, "پروژه پیدا نشد")
db.delete(project)
db.commit()
return None
+65
View File
@@ -0,0 +1,65 @@
from app.crud_router import build_crud_router
from app import models, schemas
services_router = build_crud_router(
prefix="/api/services",
tag="Services",
model=models.Service,
create_schema=schemas.ServiceCreate,
update_schema=schemas.ServiceUpdate,
out_schema=schemas.ServiceOut,
)
stats_router = build_crud_router(
prefix="/api/stats",
tag="Stats",
model=models.Stat,
create_schema=schemas.StatCreate,
update_schema=schemas.StatUpdate,
out_schema=schemas.StatOut,
)
timeline_router = build_crud_router(
prefix="/api/timeline",
tag="Timeline",
model=models.TimelineItem,
create_schema=schemas.TimelineCreate,
update_schema=schemas.TimelineUpdate,
out_schema=schemas.TimelineOut,
)
why_us_router = build_crud_router(
prefix="/api/why-us",
tag="WhyUs",
model=models.WhyUsItem,
create_schema=schemas.WhyUsCreate,
update_schema=schemas.WhyUsUpdate,
out_schema=schemas.WhyUsOut,
)
testimonials_router = build_crud_router(
prefix="/api/testimonials",
tag="Testimonials",
model=models.Testimonial,
create_schema=schemas.TestimonialCreate,
update_schema=schemas.TestimonialUpdate,
out_schema=schemas.TestimonialOut,
)
team_router = build_crud_router(
prefix="/api/team",
tag="Team",
model=models.TeamMember,
create_schema=schemas.TeamMemberCreate,
update_schema=schemas.TeamMemberUpdate,
out_schema=schemas.TeamMemberOut,
)
faqs_router = build_crud_router(
prefix="/api/faqs",
tag="FAQs",
model=models.Faq,
create_schema=schemas.FaqCreate,
update_schema=schemas.FaqUpdate,
out_schema=schemas.FaqOut,
)