init commit

This commit is contained in:
2025-08-17 11:44:54 +09:00
commit 5592014530
59 changed files with 3175 additions and 0 deletions

0
app/api/__init__.py Normal file
View File

10
app/api/main.py Normal file
View File

@@ -0,0 +1,10 @@
from fastapi import FastAPI
from app.core.config import settings
from app.api.routes import templates as templates_router
app = FastAPI(title="TG Autoposting API", version="0.1.0")
app.include_router(templates_router.router)
@app.get("/health")
async def health():
return {"status": "ok", "env": settings.env}

142
app/api/routes/templates.py Normal file
View File

@@ -0,0 +1,142 @@
# # app/api/routes/templates.py
# from __future__ import annotations
# from fastapi import APIRouter, Depends, HTTPException, Query
# from sqlalchemy import select, or_
# from sqlalchemy.ext.asyncio import AsyncSession
# from app.db.session import get_async_session
# from app.models.templates import Template
# from app.api.schemas.template import TemplateIn, TemplateOut
# from app.services.templates import count_templates
# router = APIRouter(prefix="/templates", tags=["templates"])
# # Заглушка аутентификации
# async def get_owner_id(x_user_id: int | None = None):
# return x_user_id or 0
# @router.post("/", response_model=TemplateOut)
# async def create_tpl(
# data: TemplateIn,
# owner_id: int = Depends(get_owner_id),
# s: AsyncSession = Depends(get_async_session),
# ):
# tpl = Template(owner_id=owner_id, **data.model_dump())
# s.add(tpl)
# await s.commit()
# await s.refresh(tpl)
# return tpl
# @router.get("/", response_model=list[TemplateOut])
# async def list_tpls(
# owner_id: int = Depends(get_owner_id),
# limit: int = Query(20, ge=1, le=100),
# offset: int = Query(0, ge=0),
# q: str | None = Query(default=None),
# s: AsyncSession = Depends(get_async_session),
# ):
# stmt = select(Template).where(
# Template.owner_id == owner_id,
# Template.is_archived.is_(False),
# )
# if q:
# like = f"%{q}%"
# stmt = stmt.where(or_(Template.name.ilike(like), Template.title.ilike(like)))
# stmt = stmt.order_by(Template.updated_at.desc()).limit(limit).offset(offset)
# res = await s.execute(stmt)
# return list(res.scalars())
# @router.get("/count")
# async def count_tpls(
# owner_id: int = Depends(get_owner_id),
# q: str | None = None,
# ):
# total = await count_templates(owner_id, q)
# return {"total": total}
# @router.delete("/{tpl_id}")
# async def delete_tpl(
# tpl_id: int,
# owner_id: int = Depends(get_owner_id),
# s: AsyncSession = Depends(get_async_session),
# ):
# res = await s.execute(select(Template).where(
# Template.id == tpl_id,
# Template.owner_id == owner_id
# ))
# tpl = res.scalars().first()
# if not tpl:
# raise HTTPException(404, "not found")
# await s.delete(tpl)
# await s.commit()
# return {"ok": True}
# app/api/routes/templates.py
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy import select, or_
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.session import get_async_session
from app.models.templates import Template
from app.api.schemas.template import TemplateIn, TemplateOut
from app.services.templates import count_templates
router = APIRouter(prefix="/templates", tags=["templates"])
async def get_owner_id(x_user_id: int | None = None):
return x_user_id or 0
@router.post("/", response_model=TemplateOut)
async def create_tpl(
data: TemplateIn,
owner_id: int = Depends(get_owner_id),
s: AsyncSession = Depends(get_async_session),
):
tpl = Template(owner_id=owner_id, **data.model_dump())
s.add(tpl)
await s.commit()
await s.refresh(tpl)
return tpl
@router.get("/", response_model=list[TemplateOut])
async def list_tpls(
owner_id: int = Depends(get_owner_id),
limit: int = Query(20, ge=1, le=100),
offset: int = Query(0, ge=0),
q: str | None = Query(default=None),
s: AsyncSession = Depends(get_async_session),
):
stmt = select(Template).where(
Template.owner_id == owner_id,
Template.is_archived.is_(False),
)
if q:
like = f"%{q}%"
stmt = stmt.where(or_(Template.name.ilike(like), Template.title.ilike(like)))
stmt = stmt.order_by(Template.updated_at.desc()).limit(limit).offset(offset)
res = await s.execute(stmt)
return list(res.scalars())
@router.get("/count")
async def count_tpls(owner_id: int = Depends(get_owner_id), q: str | None = None):
total = await count_templates(owner_id, q)
return {"total": total}
@router.delete("/{tpl_id}")
async def delete_tpl(
tpl_id: int,
owner_id: int = Depends(get_owner_id),
s: AsyncSession = Depends(get_async_session),
):
res = await s.execute(select(Template).where(
Template.id == tpl_id,
Template.owner_id == owner_id
))
tpl = res.scalars().first()
if not tpl:
raise HTTPException(404, "not found")
await s.delete(tpl)
await s.commit()
return {"ok": True}

View File

0
app/api/schemas/base.py Normal file
View File

View File

0
app/api/schemas/post.py Normal file
View File

View File

@@ -0,0 +1,17 @@
from __future__ import annotations
from typing import Optional, Literal
from pydantic import BaseModel, Field, ConfigDict
class TemplateIn(BaseModel):
name: str = Field(min_length=1, max_length=64)
title: Optional[str] = None
type: Literal["text","photo","video","animation"] = "text"
content: str
keyboard_tpl: Optional[list[dict]] = None
parse_mode: Optional[str] = "HTML"
visibility: Literal["private","org","public"] = "private"
class TemplateOut(TemplateIn):
id: int
is_archived: bool
model_config = ConfigDict(from_attributes=True)