memleketmeselesi/mm_api/routers/issues.py
Mukan Erkin 2498e75594 init: memleketmeselesi platform — API + migrations
FastAPI + PostgreSQL 16. KYC, issue sistemi, permission/group yönetimi,
session yönetimi, API client auth (kışla kapısı), officials/persons CRUD.
Migration 0001–0013 dahil.
2026-04-27 23:06:59 +03:00

193 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from typing import Literal
from fastapi import APIRouter, Depends, HTTPException, Query
from psycopg import AsyncConnection
from pydantic import BaseModel, Field
from mm_api.db import get_conn
from mm_api.dependencies import current_user, verified_user
import mm_api.services.issue as svc
import mm_api.services.permission as perm_svc
router = APIRouter(prefix="/issues", tags=["issues"])
class IssueCreate(BaseModel):
title: str = Field(..., min_length=5, max_length=300)
body: str = Field(..., min_length=20)
category_id: int
location_id: int
class IssueUpdate(BaseModel):
title: str | None = Field(None, min_length=5, max_length=300)
body: str | None = Field(None, min_length=20)
class StatusUpdate(BaseModel):
status: Literal["open", "in_progress", "resolved", "rejected", "duplicate"]
class VoteRequest(BaseModel):
vote: Literal["resolved", "ongoing"]
class CommentCreate(BaseModel):
body: str = Field(..., min_length=1, max_length=5000)
parent_id: int | None = None
@router.get("/categories")
async def list_categories(
parent_id: int | None = Query(None),
conn: AsyncConnection = Depends(get_conn),
):
return await svc.list_categories(conn, parent_id)
@router.get("/categories/{category_id}")
async def get_category(
category_id: int,
conn: AsyncConnection = Depends(get_conn),
):
cat = await svc.get_category(conn, category_id)
if not cat:
raise HTTPException(404, "Kategori bulunamadı")
return cat
@router.get("")
async def list_issues(
location_id: int | None = Query(None),
category_id: int | None = Query(None),
status: str | None = Query(None),
limit: int = Query(20, ge=1, le=100),
offset: int = Query(0, ge=0),
conn: AsyncConnection = Depends(get_conn),
):
return await svc.list_issues(conn, location_id, category_id, status, limit=limit, offset=offset)
@router.post("", status_code=201)
async def create_issue(
data: IssueCreate,
user: dict = Depends(verified_user),
conn: AsyncConnection = Depends(get_conn),
):
if not await perm_svc.can(conn, user["id"], "create", "issue"):
raise HTTPException(403, "Sorun bildirme yetkiniz yok")
try:
return await svc.create_issue(conn, user["id"], data.title, data.body, data.category_id, data.location_id)
except ValueError as e:
raise HTTPException(400, str(e))
@router.get("/{issue_id}")
async def get_issue(
issue_id: int,
user: dict = Depends(current_user),
conn: AsyncConnection = Depends(get_conn),
):
issue = await svc.get_issue(conn, issue_id)
if not issue:
raise HTTPException(404, "Sorun bulunamadı")
return issue
@router.patch("/{issue_id}")
async def update_issue(
issue_id: int,
data: IssueUpdate,
user: dict = Depends(verified_user),
conn: AsyncConnection = Depends(get_conn),
):
is_admin = await perm_svc.can(conn, user["id"], "update", "issue", is_admin=True)
if not is_admin and not await perm_svc.can(conn, user["id"], "update", "issue"):
raise HTTPException(403, "Yetersiz yetki")
try:
return await svc.update_issue(conn, issue_id, user["id"], is_admin, data.title, data.body)
except PermissionError as e:
raise HTTPException(403, str(e))
except ValueError as e:
raise HTTPException(400, str(e))
@router.put("/{issue_id}/status")
async def update_status(
issue_id: int,
data: StatusUpdate,
user: dict = Depends(current_user),
conn: AsyncConnection = Depends(get_conn),
):
if not await perm_svc.can(conn, user["id"], "status", "issue", is_admin=True):
raise HTTPException(403, "Yetersiz yetki")
try:
return await svc.update_status(conn, issue_id, data.status)
except ValueError as e:
raise HTTPException(400, str(e))
@router.delete("/{issue_id}", status_code=204)
async def delete_issue(
issue_id: int,
user: dict = Depends(current_user),
conn: AsyncConnection = Depends(get_conn),
):
if not await perm_svc.can(conn, user["id"], "delete", "issue", is_admin=True):
raise HTTPException(403, "Yetersiz yetki")
try:
await svc.delete_issue(conn, issue_id)
except ValueError as e:
raise HTTPException(400, str(e))
@router.post("/{issue_id}/vote")
async def vote(
issue_id: int,
data: VoteRequest,
user: dict = Depends(verified_user),
conn: AsyncConnection = Depends(get_conn),
):
if not await perm_svc.can(conn, user["id"], "create", "vote"):
raise HTTPException(403, "Oy kullanma yetkiniz yok")
try:
return await svc.cast_vote(conn, issue_id, user["id"], data.vote)
except ValueError as e:
raise HTTPException(400, str(e))
@router.get("/{issue_id}/comments")
async def list_comments(
issue_id: int,
user: dict = Depends(current_user),
conn: AsyncConnection = Depends(get_conn),
):
return await svc.list_comments(conn, issue_id)
@router.post("/{issue_id}/comments", status_code=201)
async def add_comment(
issue_id: int,
data: CommentCreate,
user: dict = Depends(verified_user),
conn: AsyncConnection = Depends(get_conn),
):
if not await perm_svc.can(conn, user["id"], "create", "comment"):
raise HTTPException(403, "Yorum yapma yetkiniz yok")
try:
return await svc.add_comment(conn, issue_id, user["id"], data.body, data.parent_id)
except ValueError as e:
raise HTTPException(400, str(e))
@router.delete("/comments/{comment_id}", status_code=204)
async def delete_comment(
comment_id: int,
user: dict = Depends(current_user),
conn: AsyncConnection = Depends(get_conn),
):
is_admin = await perm_svc.can(conn, user["id"], "delete", "comment", is_admin=True)
try:
await svc.delete_comment(conn, comment_id, user["id"], is_admin)
except PermissionError as e:
raise HTTPException(403, str(e))
except ValueError as e:
raise HTTPException(404, str(e))