- 대시보드 지움 = 늘 휴지통(deleted_at) · PROJECT_DELETE_HARD_ENABLED 스위치 걷음 → TRASH_RETENTION_DAYS(7) - 영구 삭제(hard_delete_project) = 휴지통 것만 · FOR UPDATE · 이미 지운 것 False 로 건너뜀 · 감사 기록 동작 이름 받음 - 정리 루프에 purge_expired_trash — GET_LOCK 이름 잠금(두 PC 서버 중 하나만) · 기한 지난 것 PROJECT_PURGE - API /api/dashboard/trash/projects — 보기 · 되살리기 = 시스템 관리자(전부) · 회사 관리자(자기 회사) · 영구 삭제 = 시스템 관리자만 - 화면 휴지통 — 시스템 관리 › 휴지통 · 회사 관리 › 휴지통(회사 관리자 · 되살리기만) · 지운 사람 = 감사 기록 - 시험 test_592_1_trash 새로 · test_546 메뉴 기대값에 휴지통 · test_573 복제본 지움을 휴지통 거쳐 영구 삭제로(영구 삭제 = 휴지통 것만) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
90 lines
3.4 KiB
Python
90 lines
3.4 KiB
Python
"""임시 보관함 만료 정리.
|
|
|
|
라이다 원본은 수십 GB라 방치하면 디스크를 금방 채운다. 파일이 다 올라온 시각 기준
|
|
`TEMP_UPLOAD_RETENTION_DAYS`가 지난 묶음을 주기적으로 지운다(2026-08-08 사용자 지시).
|
|
보관 기간·주기는 `config/config_system.py`에서 조정한다.
|
|
"""
|
|
|
|
import asyncio
|
|
import logging
|
|
import os
|
|
import shutil
|
|
|
|
from B03_FileInput.B03_FileInput_Repository_Temp import (
|
|
delete_temp_batch,
|
|
list_expired_temp_batches,
|
|
)
|
|
from common_util.common_util_audit import purge_expired_audit_logs
|
|
from common_util.common_util_project_delete import purge_expired_trash
|
|
from common_util.common_util_storage import resolve_temp_batch_path, temp_upload_root
|
|
from config.config_db import get_db_pool
|
|
from config.config_system import (
|
|
TEMP_UPLOAD_CLEANUP_INTERVAL_HOURS,
|
|
TEMP_UPLOAD_RETENTION_DAYS,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def cleanup_expired_temp_uploads() -> int:
|
|
"""만료된 보관함 묶음을 폴더째 지우고 DB 행도 제거한다. 지운 개수를 돌려준다."""
|
|
pool = get_db_pool()
|
|
removed = 0
|
|
try:
|
|
async with pool.acquire() as connection:
|
|
expired = await list_expired_temp_batches(connection)
|
|
for batch in expired:
|
|
batch_id = str(batch["id"])
|
|
user_id = int(batch["user_id"])
|
|
try:
|
|
batch_root = resolve_temp_batch_path(user_id, batch_id, create=False)
|
|
shutil.rmtree(batch_root, ignore_errors=True)
|
|
except ValueError:
|
|
logger.warning("보관함 경로 해석 실패(행만 삭제): batch_id=%s", batch_id)
|
|
await delete_temp_batch(connection, batch_id=batch_id, user_id=user_id)
|
|
removed += 1
|
|
logger.info(
|
|
"임시 보관함 만료 삭제: batch_id=%s name=%s expires_at=%s",
|
|
batch_id,
|
|
batch.get("name"),
|
|
batch.get("expires_at"),
|
|
)
|
|
if removed:
|
|
await connection.commit()
|
|
_remove_empty_user_dirs()
|
|
except Exception:
|
|
logger.exception("임시 보관함 만료 정리 실패")
|
|
return removed
|
|
|
|
|
|
def _remove_empty_user_dirs() -> None:
|
|
"""묶음이 모두 사라진 사용자 폴더는 함께 정리한다."""
|
|
root = temp_upload_root()
|
|
if not os.path.isdir(root):
|
|
return
|
|
for entry in os.listdir(root):
|
|
path = os.path.join(root, entry)
|
|
if os.path.isdir(path) and not os.listdir(path):
|
|
try:
|
|
os.rmdir(path)
|
|
except OSError:
|
|
pass
|
|
|
|
|
|
async def cleanup_expired_temp_uploads_loop() -> None:
|
|
"""서버 시작 직후 한 번, 이후 설정된 주기마다 만료분을 정리한다."""
|
|
interval_seconds = max(1, TEMP_UPLOAD_CLEANUP_INTERVAL_HOURS) * 3600
|
|
logger.info(
|
|
"임시 보관함 정리 루프 시작: 보관 %d일, 주기 %d시간",
|
|
TEMP_UPLOAD_RETENTION_DAYS,
|
|
TEMP_UPLOAD_CLEANUP_INTERVAL_HOURS,
|
|
)
|
|
while True:
|
|
removed = await cleanup_expired_temp_uploads()
|
|
if removed:
|
|
logger.info("임시 보관함 정리 완료: %d건 삭제", removed)
|
|
# 시스템 로그 보관 기간 · 휴지통 정리도 같은 주기에 얹는다 — 루프를 따로 두지 않는다.
|
|
await purge_expired_audit_logs()
|
|
await purge_expired_trash()
|
|
await asyncio.sleep(interval_seconds)
|