Merge remote-tracking branch 'origin/sub_laptop_2' into sub_laptop_7
This commit is contained in:
@@ -10,13 +10,17 @@ PLAN 27-1c) · `owner` = 회사 `{C}` · 개인 `{C}/{U}`(없으면 자기 층)
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Any, Literal
|
||||
|
||||
import anyio.from_thread
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from pydantic import BaseModel
|
||||
|
||||
from common_util.common_util_audit import record_audit
|
||||
from common_util.common_util_auth import verify_session
|
||||
from config.config_db import get_db_pool
|
||||
from M02_MasterTemplete import M02_Item_Paste as paste_store
|
||||
from M02_MasterTemplete import M02_Layer_Access as access
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Branch as branch
|
||||
@@ -32,6 +36,7 @@ from M02_MasterTemplete.M02_MasterTemplete_Router import (
|
||||
UseComboBody,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/api/m02", tags=["M02 MasterTemplete Items"])
|
||||
|
||||
Layer = Literal["system", "company", "personal"]
|
||||
@@ -74,14 +79,60 @@ class CopyItemBody(BaseModel):
|
||||
key: str
|
||||
|
||||
|
||||
async def _member(owner: str) -> bool:
|
||||
"""층 주인이 있나 — 회사 `{C}` = 그 회사 · 개인 `{C}/{U}` = U 가 C 소속."""
|
||||
company, _, user = owner.partition("/")
|
||||
pool = get_db_pool()
|
||||
async with pool.acquire() as connection, connection.cursor() as cursor:
|
||||
if user:
|
||||
await cursor.execute(
|
||||
"SELECT 1 FROM users WHERE id = %s AND company_id = %s AND deleted_at IS NULL",
|
||||
(int(user), int(company)),
|
||||
)
|
||||
else:
|
||||
await cursor.execute(
|
||||
"SELECT 1 FROM companies WHERE id = %s AND deleted_at IS NULL", (int(company),)
|
||||
)
|
||||
return await cursor.fetchone() is not None
|
||||
|
||||
|
||||
async def _note(session: Session, got: access.Grant) -> None:
|
||||
"""남의 층 쓰기 한 줄(39-6 · 시스템 로그) — 기록이 못 돼도 쓰기는 막지 않음."""
|
||||
try:
|
||||
pool = get_db_pool()
|
||||
async with pool.acquire() as connection, connection.cursor() as cursor:
|
||||
await record_audit(
|
||||
cursor,
|
||||
actor_id=int(session["user_id"]),
|
||||
action="M02_OTHER_LAYER_WRITE",
|
||||
resource_type=f"m02_{got.layer}",
|
||||
resource_ref=got.owner,
|
||||
)
|
||||
await connection.commit()
|
||||
except Exception:
|
||||
logger.exception("M02 남의 층 쓰기 기록 실패: %s %s", got.layer, got.owner)
|
||||
|
||||
|
||||
async def others_write(session: Session, got: access.Grant, folder: Path) -> None:
|
||||
"""남의 층 쓰기 — 처음(집계표 없음)이면 주인이 있는지 본 뒤 뼈대 · 쓸 때마다 시스템 로그."""
|
||||
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)
|
||||
|
||||
|
||||
def _root(session: Session, layer: str, owner: str | None = None, write: bool = False) -> Path:
|
||||
"""층 폴더(권한까지 · `M02_Layer_Access`) — 자기 회사 · 개인 층은 없으면 빈 뼈대를 만듦."""
|
||||
"""층 폴더(권한까지 · `M02_Layer_Access`) — 자기 회사 · 개인 층은 없으면 빈 뼈대를 만듦 ·
|
||||
남의 층 쓰기(맡은 회사 · 39-6)는 `others_write`."""
|
||||
got = access.require(session, layer, owner, write=write)
|
||||
if layer == "system":
|
||||
return store.FOLDER
|
||||
folder = access.folder(got)
|
||||
folder = access.folder(got, create=write)
|
||||
if got.mine:
|
||||
items_store.ensure(folder)
|
||||
elif write:
|
||||
anyio.from_thread.run(others_write, session, got, folder)
|
||||
elif not (folder / "table" / f"{store.STRUCTURE_TABLE}.json").is_file():
|
||||
raise HTTPException(status_code=404, detail="그 층은 아직 만든 것이 없음")
|
||||
return folder
|
||||
|
||||
Reference in New Issue
Block a user