테스트 메일 수신자 제한

This commit is contained in:
Yun Chan 2026-06-29 17:13:34 +09:00
parent 3bf38c50df
commit 1274ba9ccc
5 changed files with 35 additions and 8 deletions

View file

@ -532,7 +532,7 @@
<p class="dg-note">96차 적용(2026-06-29): M2 digest worker를 운영자가 명시 실행할 수 있도록 <code>scripts/run-session-digest-worker.py</code> dry-run/apply runner를 추가했다. 기본은 metadata-only dry-run이고, <code>--apply</code>를 줘야 accepted 후보를 DB에 반영한다. runner는 load → DB release → engine generate → short apply 순서로 동작해 LLM 호출 동안 DB transaction을 잡지 않는다. digest 본문은 민감할 수 있어 <code>--show-digest</code>를 명시할 때만 출력한다. 이는 실 provider 장시간 운영 완료가 아니다. 검증: <code>py -3.11 -X utf8 -B -m py_compile scripts\run-session-digest-worker.py apps\api\app\services\session_digest_worker.py apps\api\app\test_session_digest_worker.py</code>, <code>py -3.11 -X utf8 scripts\run-session-digest-worker.py --help</code>, M2 focused 25 passed.</p>
<p class="dg-note">97차 적용(2026-06-29): <code>SESSION_DIGEST_WORKER_ENABLED=false</code> 기본값을 두고, opt-in일 때만 세션 종료 성공 뒤 digest worker를 background task로 예약한다. scheduler는 persisted fallback row를 load한 뒤 DB connection을 놓고 engine을 호출하며, accepted plan만 짧은 apply transaction으로 반영한다. 이미 <code>compressed_by</code>가 있는 세션은 loader에서 제외하고, apply도 <code>compressed_by IS NULL</code> CAS로 보호하며, fallback summary 재작성 시 compression metadata를 초기화한다. 이는 bounded/default-off scheduler 골격이며, 실 provider 장시간 운영·임상 골든셋 품질평가·재압축 정책 완료가 아니다. 검증: <code>py_compile</code>, runner <code>--help</code>, M2 focused 30 passed.</p>
<p class="dg-note">99차 적용(2026-06-29): 대시보드 SSOT drift gate를 보강했다. <code>scripts/check-dev-dashboard-ssot.py</code>는 현재 카드 상태(<code>done=25</code>, <code>planned=0</code>, <code>doing=0</code>), CHECK 잔여 0, owner board counts(<code>block=2</code>, <code>decide=7</code>, <code>ext=2</code>), 외부 <code>GATE</code> 상세 6행, 결정 탭 7행, 자유연습 F-10 결정 문구, M2 30/87 검증 수치, default-off scheduler/CAS 문구, 실 provider·임상 골든셋·재압축 GATE 보존, stale 82/25/26 passed 및 낡은 결정 8건 문구 제거를 함께 확인한다. 검증: <code>py_compile</code>, <code>py -3.11 -X utf8 scripts\check-dev-dashboard-ssot.py --json</code> PASS. 아래 적용 노트 일부는 historical snapshot이라 당시 통과 수치를 그대로 보존한다.</p>
<p class="dg-note">100차 적용(2026-06-29): 관리자·교수자 메일링 시스템 1차를 추가했다. 신규 외부 로그인 pending 계정은 <code>account_pending_approval:{user_id}</code>, 회기 종료 후 평가/error record 저장 완료 세션은 <code>session_review_ready:{session_id}</code>, 명시 테스트 메일은 <code>admin_test_email:{uuid}</code> idempotency key로 <code>app.notification_event</code>/<code>app.notification_delivery</code>에 큐잉된다. 메일 HTML은 Vignette 톤앤매너(종이 배경, 세이지 CTA, 8px 카드)를 inline style로 유지하고, 본문에는 축어록·평가 전문을 넣지 않고 <code>/admin/users</code>, <code>/teach/session/:sessionId/review</code>, <code>/admin</code> 딥링크만 제공한다. <code>NOTIFICATION_EMAIL_PROVIDER=smtp</code><code>SMTP_*</code>가 설정된 경우 실제 발송하며, <code>GET /admin/notifications</code>, <code>POST /admin/notifications/process</code>, <code>POST /admin/notifications/test</code>, <code>scripts/run-notification-worker.py</code>로 delivery 상태와 재시도를 운영한다.</p>
<p class="dg-note">100차 적용(2026-06-29): 관리자·교수자 메일링 시스템 1차를 추가했다. 신규 외부 로그인 pending 계정은 <code>account_pending_approval:{user_id}</code>, 회기 종료 후 평가/error record 저장 완료 세션은 <code>session_review_ready:{session_id}</code>, 명시 테스트 메일은 <code>admin_test_email:{uuid}</code> idempotency key로 <code>app.notification_event</code>/<code>app.notification_delivery</code>에 큐잉된다. 테스트 메일은 대량 오발송을 피하기 위해 <code>AUTH_SUPER_ADMIN_EMAILS</code> 대상에게만 큐잉한다. 메일 HTML은 Vignette 톤앤매너(종이 배경, 세이지 CTA, 8px 카드)를 inline style로 유지하고, 본문에는 축어록·평가 전문을 넣지 않고 <code>/admin/users</code>, <code>/teach/session/:sessionId/review</code>, <code>/admin</code> 딥링크만 제공한다. <code>NOTIFICATION_EMAIL_PROVIDER=smtp</code><code>SMTP_*</code>가 설정된 경우 실제 발송하며, <code>GET /admin/notifications</code>, <code>POST /admin/notifications/process</code>, <code>POST /admin/notifications/test</code>, <code>scripts/run-notification-worker.py</code>로 delivery 상태와 재시도를 운영한다.</p>
<p class="dg-note">20차 적용(2026-06-28): 신규 Google/SAML 사용자는 <code>account_status=pending</code>으로 시작하고 승인 전에는 <code>/pending</code> 안내 화면만 본다. <code>yunchan@twentyoz.kr</code>는 슈퍼 관리자 allowlist로 admin+approved를 받으며, <code>/admin/users</code>는 가입 승인 탭에서 pending 계정을 승인 또는 보류 처리한다.</p>
<p class="dg-note">21차 적용(2026-06-28): 관리자 페이지 진입권을 기본 역할과 분리해 <code>app_user.admin_access</code>로 저장한다. <code>AUTH_SUPER_ADMIN_EMAILS</code> 기본값은 <code>yunchan@twentyoz.kr</code>, <code>hoonjungkoo@hs.ac.kr</code>이며, 슈퍼 관리자는 학습자·교수자·관리자 공간 전환과 관리자 권한 부여/회수를 할 수 있다. 학생·교수 계정도 <code>admin_access=true</code>면 우측 상단 관리자 진입이 노출된다. 구성 슈퍼 관리자의 권한 회수와 계정 비활성화는 차단한다.</p>
<p class="dg-note">25차 적용(2026-06-28): <code>/admin/users</code>에서 허용 도메인 밖 이메일도 정확한 계정 단위로 강제 등록할 수 있다. Google/SAML/dev-login은 미리 등록된 이메일만 도메인 게이트 예외로 통과시키고, provider 로그인 시 기존 관리 row의 역할·코호트·승인 상태를 이어받는다. 미등록 외부 도메인 로그인은 계속 차단한다.</p>