Merge remote-tracking branch 'origin/sub_laptop_4' into sub_laptop_2

This commit is contained in:
2026-10-04 18:28:32 +09:00
7 changed files with 119 additions and 24 deletions
@@ -138,18 +138,6 @@ def post_combo(
return _call(folder, combos.create, body.combo, body.판 or None)
@router.put("/layers/{layer}/combos/{key}")
def put_combo(
layer: Layer,
key: str,
body: ComboBody,
owner: str | None = None,
session: Session = Depends(verify_session),
):
folder = _root(session, layer, owner, write=True)
return _call(folder, combos.edit, key, body.combo, body.판 or "")
@router.delete("/layers/{layer}/combos/{key}")
def delete_combo(
layer: Layer,
@@ -11,11 +11,14 @@ PLAN 27-1c) · `owner` = 회사 `{C}` · 개인 `{C}/{U}`(없으면 자기 층)
from __future__ import annotations
import logging
from collections.abc import Callable, Coroutine
from contextvars import ContextVar
from pathlib import Path
from typing import Any, Literal
import anyio.from_thread
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi import APIRouter, Depends, HTTPException, Query, Request, Response
from fastapi.routing import APIRoute
from pydantic import BaseModel
from common_util.common_util_audit import record_audit
@@ -37,7 +40,32 @@ from M02_MasterTemplete.M02_MasterTemplete_Router import (
)
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/m02", tags=["M02 MasterTemplete Items"])
#: 이 요청이 쓴 남의 층(`others_write`) — 길이 성공해 답한 뒤에만 시스템 로그(`NotedRoute`)
_WRITES: ContextVar[list[tuple[dict, access.Grant]] | None] = ContextVar("m02_writes", default=None)
class NotedRoute(APIRoute):
"""남의 층 쓰기 기록 = 성공한 쓰기만(39-1d) — 막히거나 실패한 부름(예외 · 4xx · 5xx)은 뺌."""
def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]:
handler = super().get_route_handler()
async def run(request: Request) -> Response:
notes: list[tuple[dict, access.Grant]] = []
token = _WRITES.set(notes)
try:
response = await handler(request)
finally:
_WRITES.reset(token)
if response.status_code < 400:
for session, got in notes:
await _note(session, got)
return response
return run
router = APIRouter(prefix="/api/m02", tags=["M02 MasterTemplete Items"], route_class=NotedRoute)
Layer = Literal["system", "company", "personal"]
Session = dict[str, Any]
@@ -114,12 +142,17 @@ async def _note(session: Session, got: access.Grant) -> None:
async def others_write(session: Session, got: access.Grant, folder: Path) -> None:
"""남의 층 쓰기 — 처음(집계표 없음)이면 주인이 있는지 본 뒤 뼈대 · 쓸 때마다 시스템 로그."""
"""남의 층 쓰기 — 처음(집계표 없음)이면 주인이 있는지 본 뒤 뼈대 · 시스템 로그는 길이 성공한
뒤(`NotedRoute` · 그 밖의 길은 바로)."""
if not (folder / "table" / f"{store.STRUCTURE_TABLE}.json").is_file():
if not await _member(str(got.owner)):
raise HTTPException(status_code=403, detail="그 회사 · 그 회사 사람이 아님")
items_store.ensure(folder)
await _note(session, got)
pending = _WRITES.get()
if pending is None:
await _note(session, got)
else:
pending.append((session, got))
def _root(session: Session, layer: str, owner: str | None = None, write: bool = False) -> Path:
@@ -34,7 +34,9 @@ from M02_MasterTemplete import M02_Template_Layers as layers
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/m02", tags=["M02 MasterTemplete Layers"])
router = APIRouter(
prefix="/api/m02", tags=["M02 MasterTemplete Layers"], route_class=items_router.NotedRoute
)
Layer = Literal["system", "company", "personal", "project"]