Files
bruno 937ecfc2e0
FlowDeck CI / lint (push) Canceled after 0s
FlowDeck CI / test (push) Canceled after 0s
FlowDeck CI / docker (push) Canceled after 0s
fix: A30 + A37 + A39 + A40 + A41 — fin du P2/P3 XS/S (v7.4.0)
- A30 — `require_scope()` câblé : 69 sites stricts de api_v2.py passent par la
  factory (Bearer + scope en 1 appel, contrôle manuel supprimé) ; sémantique
  alignée sur celle des handlers (pas de default "read" → 0 changement de
  comportement) ; 12 top-level morts supprimés (0 ref app ET tests) :
  unsync_block, find_referring, _b64url, strip_markdown, format_number,
  get_auto_property_value, get_next_unique_id, local_date_in_tz,
  verify_device_token, _get_dynamic_groups, _require_user_gitea,
  validate_upload_request
- A37 — CORS sans `*` : origines = app_base_url + allow_origin_regex
  (localhost/dev, origines d'extension pour le Web Clipper), méthodes et
  entêtes minutées, allow_credentials explicite + test test_cors_no_star
- A39 — htmx : décision « rien » documentée (32 attributs hx-* réels sur 6
  templates, conversion = refonte du view-switching sans test E2E)
- A40 — version d'assets à source unique : ENV.globals["asset_version"] lu au
  boot depuis le fichier VERSION ; littéraux `?v=` de base.html éliminés ;
  test test_asset_version_single_source
- A41 — app.css : 91 règles mortes purgées (-10 274 octets, 121 618 → 111 344),
  scan templates/JS/CSS/Python à 0 référence

suite **1031/1031** · `ruff check app tests` OK · OpenAPI 511 chemins / 7.4.0
docs (ROADMAP/CHANGELOG/WORKLOAD/VERSION) à jour
2026-10-01 09:30:37 -04:00

76 lines
3.1 KiB
Python

"""FlowDeck — Pydantic request models for API validation."""
from __future__ import annotations
from pydantic import BaseModel, Field, model_validator
from app.middleware.security import ALLOWED_EXTENSIONS, _ext
# ── File Save ────────────────────────────────────────────────
class FileSaveRequest(BaseModel):
"""Request model for saving/updating a file via Gitea."""
path: str = Field(..., min_length=1, description="File path in the repository")
content: str = Field(..., description="File content (UTF-8 encoded)")
message: str = Field(default="Update via FlowDeck", description="Commit message")
sha: str | None = Field(default=None, description="SHA of the file being updated (required for updates)")
@model_validator(mode="after")
def validate_path_extension(self):
ext = _ext(self.path)
if ext and ext not in ALLOWED_EXTENSIONS:
raise ValueError(f"File extension '{ext}' is not allowed")
return self
# ── Upload ───────────────────────────────────────────────────
class UploadValidationResult(BaseModel):
"""Result of validating an upload."""
filename: str
size: int
extension: str
valid: bool
error: str | None = None
class IssueCreateRequest(BaseModel):
"""Request model for creating a Gitea issue."""
title: str = Field(..., min_length=1, max_length=500)
body: str = Field(default="")
labels: str = Field(default="", description="Comma-separated label IDs")
milestone: str = Field(default="", description="Milestone ID")
assignee: str = Field(default="")
class IssueUpdateRequest(BaseModel):
"""Request model for updating a Gitea issue (partial update)."""
title: str | None = Field(default=None, max_length=500)
body: str | None = Field(default=None)
state: str | None = Field(default=None, pattern=r"^(open|closed)$")
labels: str | None = Field(default=None, description="Comma-separated label IDs")
milestone: str | None = Field(default=None)
assignee: str | None = Field(default=None)
# ── Card Move ────────────────────────────────────────────────
class CardMoveRequest(BaseModel):
"""Request model for moving a card between columns."""
owner: str = Field(..., min_length=1)
repo: str = Field(..., min_length=1)
issue_id: int = Field(..., gt=0)
column: str = Field(..., min_length=1)
# ── Column Mapping ───────────────────────────────────────────
class ColMappingRequest(BaseModel):
"""Request model for column-to-label mapping."""
owner: str = Field(..., min_length=1)
repo: str = Field(..., min_length=1)
column: str = Field(..., min_length=1)
gitea_label: str = Field(..., min_length=1)
close_issue: bool = Field(default=False)