여러 줄 주석이 설명보다 경위(예전·실측·지적)를 적고 있어 읽는 사람이 결론을 찾기 어려웠다. - ts·tsx·js·mjs·css·py 478개: 여러 줄 주석은 첫 문장 한 줄로, 과거형·날짜 문장은 삭제 - 주석 위치는 TypeScript 파서·파이썬 tokenize/ast 로 찾는다 — 문자열 안의 # · /* 는 건드리지 않는다 - eslint·ts·noqa·type: ignore 같은 지시 주석은 그대로 둔다 파이썬 275개 정리 전후 AST 동일, TS 298개 주석 뺀 토큰 동일(빈 JSX 주석 10곳만 차이). site·frontend·admin tsc, site vitest 105 passed Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
87 lines
3.8 KiB
Python
87 lines
3.8 KiB
Python
from fastapi import Depends
|
|
|
|
from common.database.db_session_manager import DB_SESSION_MNG
|
|
from common.database.model.models import places, users
|
|
from common.enums import DBWRType, ErrorType, UserRole
|
|
from common.models.gmodel import PageParams
|
|
from crud.site_crud import ISiteCRUD, SiteCRUD
|
|
from crud.user_crud import IUserCRUD, UserCRUD
|
|
from router.v1.ops.protocol import OpsSiteData, OpsUserData, Res_OpsSites, Res_OpsUsers
|
|
|
|
|
|
class OpsService:
|
|
"""내부 운영(개발자) 전용 조회 — 회사 스코프 없는 전 계정 사이트·유저 목록."""
|
|
|
|
def __init__(self, site_crud: ISiteCRUD = Depends(SiteCRUD), user_crud: IUserCRUD = Depends(UserCRUD)):
|
|
self.site_crud = site_crud
|
|
self.user_crud = user_crud
|
|
|
|
async def list_sites(self, pg: PageParams) -> Res_OpsSites:
|
|
res = Res_OpsSites(page=pg.page, size=pg.size)
|
|
err_type, rows, total = await DB_SESSION_MNG.execute_lambda(
|
|
places.DBType(), DBWRType.DB_READ.value,
|
|
lambda s: self.site_crud.list_all_sites(s, pg.skip, pg.size),
|
|
)
|
|
if err_type != ErrorType.SUCCESS:
|
|
res.result.SetResult(err_type)
|
|
return res
|
|
res.sites = [
|
|
self._site_row(place, site, built_at, photo, login_id, email, name)
|
|
for place, site, built_at, photo, login_id, email, name in rows
|
|
]
|
|
res.total = total
|
|
return res
|
|
|
|
@staticmethod
|
|
def _site_row(place, site, built_at, primary_photo_url, owner_login_id, owner_email, owner_name) -> OpsSiteData:
|
|
# 재빌드 판별은 site_service._my_site_row 와 같은 규칙이어야 한다 — 다르면 화면마다 다른 답을 한다.
|
|
changed = place.content_updated_at
|
|
ever_published = site is not None and getattr(site, "published_at", None) is not None
|
|
thumbnail_url = getattr(site, "thumbnail_url", None) or (primary_photo_url if ever_published else None)
|
|
return OpsSiteData(
|
|
place_id=place.place_id,
|
|
name=place.name,
|
|
category=place.category,
|
|
place_status=place.status,
|
|
created_at=place.created_at,
|
|
site_id=getattr(site, "site_id", None),
|
|
status=getattr(site, "status", None),
|
|
domain=getattr(site, "domain", None),
|
|
published_at=getattr(site, "published_at", None),
|
|
thumbnail_url=thumbnail_url,
|
|
needs_rebuild=bool(site is not None and changed and (built_at is None or changed > built_at)),
|
|
owner_login_id=owner_login_id,
|
|
owner_email=owner_email,
|
|
owner_name=owner_name,
|
|
)
|
|
|
|
async def list_users(self, pg: PageParams, search: str | None = None) -> Res_OpsUsers:
|
|
"""USER/OWNER 만 — 개발자 계정은 목록에서 뺀다(UserRole 주석: 고객사에 존재를 노출하지 않는다)."""
|
|
res = Res_OpsUsers(page=pg.page, size=pg.size)
|
|
roles = [UserRole.USER.value, UserRole.OWNER.value]
|
|
err_type, rows, total = await DB_SESSION_MNG.execute_lambda(
|
|
users.DBType(), DBWRType.DB_READ.value,
|
|
lambda s: self.user_crud.list_users(s, roles, pg.skip, pg.size, search),
|
|
)
|
|
if err_type != ErrorType.SUCCESS:
|
|
res.result.SetResult(err_type)
|
|
return res
|
|
res.users = [
|
|
OpsUserData(
|
|
user_id=user.user_id,
|
|
login_id=user.id,
|
|
name=user.name,
|
|
email=user.email,
|
|
contact_number=user.contact_number,
|
|
role=user.role,
|
|
status=user.status,
|
|
provider=user.provider,
|
|
created_at=user.created_at,
|
|
last_accessed_at=user.last_accessed_at,
|
|
place_count=place_count,
|
|
)
|
|
for user, place_count in rows
|
|
]
|
|
res.total = total
|
|
return res
|