پروژه کامل: فرانت متصل به API + داکرایز

- اتصال کامل فرانت به API: همه محتوای سایت (پروژه‌ها، مقالات، خدمات، برند و...) از بک‌اند خوانده می‌شود (SiteProvider + useSite)
- فرم تماس و خبرنامه متصل به بک‌اند و تست‌شده در مرورگر
- ساختار مونوریپو: frontend/ + backend/
- Dockerfile برای هر دو بخش + docker-compose (PostgreSQL + FastAPI + nginx)
- nginx با پروکسی /api — بدون نیاز به CORS در حالت داکر
- README کامل فارسی
This commit is contained in:
Behrad
2026-08-29 13:26:58 +03:30
parent ed14435485
commit ad75f7a7bd
78 changed files with 8242 additions and 133 deletions
+246
View File
@@ -0,0 +1,246 @@
from datetime import datetime
from typing import Literal
from pydantic import BaseModel, ConfigDict, EmailStr, Field
# ---------- پایه ----------
class ORMModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
# ---------- خروجی‌ها ----------
class BrandOut(ORMModel):
id: int
name: str
logo: str
hero: str
contact_bg: str
phone: str
phone_href: str
email: str
address: str
class ServiceOut(ORMModel):
id: int
title: str
desc: str
icon: str
class ProjectOut(ORMModel):
id: int
slug: str
title: str
location: str
year: str
status: str
image: str
gallery: list[str]
area: str
floors: str
units: str
client: str
summary: str
body: list[str]
class ArticleOut(ORMModel):
id: int
slug: str
title: str
date: str
category: str
image: str
read: str
excerpt: str
body: list[str]
class TestimonialOut(ORMModel):
id: int
name: str
role: str
quote: str
class FaqOut(ORMModel):
id: int
question: str
answer: str
class TeamMemberOut(ORMModel):
id: int
name: str
role: str
bio: str
class TimelineItemOut(ORMModel):
id: int
year: str
title: str
desc: str
image: str
class StatOut(ORMModel):
id: int
value: int
suffix: str
label: str
class WhyUsItemOut(ORMModel):
id: int
title: str
desc: str
class BeforeAfterOut(ORMModel):
id: int
title: str
lead: str
before: str
after: str
before_label: str
after_label: str
class ContactMessageOut(ORMModel):
id: int
name: str
phone: str
note: str
is_read: bool
created_at: datetime
class SubscriberOut(ORMModel):
id: int
email: str
is_active: bool
created_at: datetime
# ---------- ورودی فرم‌ها ----------
class ContactIn(BaseModel):
name: str = Field(min_length=3, max_length=128)
phone: str = Field(pattern=r"^[\d۰-۹+\-\s]{8,15}$")
note: str = Field(default="", max_length=2000)
class NewsletterIn(BaseModel):
email: EmailStr
# ---------- ورودی‌های پنل مدیریت ----------
class LoginIn(BaseModel):
username: str
password: str
class TokenOut(BaseModel):
access_token: str
token_type: str = "bearer"
class BrandIn(BaseModel):
name: str = ""
logo: str = ""
hero: str = ""
contact_bg: str = ""
phone: str = ""
phone_href: str = ""
email: str = ""
address: str = ""
class ServiceIn(BaseModel):
title: str = Field(min_length=1, max_length=256)
desc: str = ""
icon: str = "draft"
class ProjectIn(BaseModel):
slug: str = Field(min_length=1, max_length=128)
title: str = Field(min_length=1, max_length=256)
location: str = ""
year: str = ""
status: Literal["done", "progress"] = "done"
image: str = ""
gallery: list[str] = []
area: str = ""
floors: str = ""
units: str = ""
client: str = ""
summary: str = ""
body: list[str] = []
class ArticleIn(BaseModel):
slug: str = Field(min_length=1, max_length=128)
title: str = Field(min_length=1, max_length=256)
date: str = ""
category: str = ""
image: str = ""
read: str = ""
excerpt: str = ""
body: list[str] = []
class TestimonialIn(BaseModel):
name: str = Field(min_length=1, max_length=128)
role: str = ""
quote: str = ""
class FaqIn(BaseModel):
question: str = Field(min_length=1)
answer: str = ""
class TeamMemberIn(BaseModel):
name: str = Field(min_length=1, max_length=128)
role: str = ""
bio: str = ""
class TimelineItemIn(BaseModel):
year: str = Field(min_length=1, max_length=32)
title: str = Field(min_length=1, max_length=256)
desc: str = ""
image: str = ""
class StatIn(BaseModel):
value: int
suffix: str = ""
label: str = Field(min_length=1, max_length=128)
class WhyUsItemIn(BaseModel):
title: str = Field(min_length=1, max_length=256)
desc: str = ""
class BeforeAfterIn(BaseModel):
title: str = ""
lead: str = ""
before: str = ""
after: str = ""
before_label: str = ""
after_label: str = ""
class MessageReadIn(BaseModel):
is_read: bool = True