← 목록
기타 2026-07-30 16KB 읽기 18분

AYS 관리자 콘솔 재정비 — 크라우니 통합회원 SSO + 3탭 콘솔

개요

ays.crowny.org(:9920) 관리자 페이지 public/admin.html을 패스코드 단일 인증 + 사례게시 단일화면에서, 크라우니 통합회원(SSO) + AYS그룹 인가 기반 로그인 + 3탭 콘솔(웹페이지 관리 / 공사진행보고·채팅 / 고객 후속관리)로 재구축.

인증 흐름도

[브라우저] admin.html 로그인 폼
   │ POST /api/admin/sso-login {username,password}
   ▼
[서버 9920] → 서버간 POST 9401 /api/login → {ok, token}
   │ (프론트로 token 반환, localStorage 저장)
   ▼
[브라우저] 이후 모든 관리자 API 호출에 Authorization: Bearer <token>
   │
   ▼
[서버 9920] isAdminAuthed(req, body)
   ├─ isAdmin(req, body)  ── 레거시 패스코드(x-admin-pass) 통과? ──▶ 통과(via: passcode)
   └─ checkSsoAuth(req)
        │ (5분 캐시 히트? → 캐시 결과 즉시 반환)
        ▼ (미스)
        GET 9401 /api/verify (Authorization: Bearer)  → 유효? accountId 확보
        GET 9401 /api/me/orgs (Authorization: Bearer)  → org 목록에 name="AYS" 존재?
        ▼
        그룹인가(토큰유효, 조직소속) 4상 판정 (그룹인가.한선)
          토큰무효 → 타(거부)
          토큰유효 × 조직소속 → 티(통과)
          토큰유효 × 비소속   → 타(거부, reason="AYS 그룹 권한 필요")
        ▼
        결과 5분 캐시(ssoCache), 통과 시 (via: sso)

라우트 스펙 (신규/변경)

라우트메서드인증설명
/api/admin/sso-loginPOST없음(로그인 자체)body {username,password} → 9401 /api/login 프록시, {ok,token,username,displayName}
/api/admin/sessionGETBearercheckSsoAuth 실행, {ok:true,user} / 401(토큰없음) / 403(비소속)
/api/admin/authPOST패스코드(레거시)변경 없음(폴백 유지)
/api/admin/case GET /cases POST /case/delete-isAdminAuthed(패스코드 OR SSO)기존 로직 그대로, 인증 게이트만 교체
/api/admin/imagegen /imagegen/list /imagegen/attach-isAdminAuthed상동
/api/admin/followupPOSTisAdminAuthedbody {type,ref_id,memo}data/후속관리.psv append
/api/admin/followupGETisAdminAuthed?ref=<ref_id> → 해당 후속 메모 목록(최신순)

탭별 기능표

기능데이터/API
① 웹페이지 관리공사실적 게시(사업부/제목/위치/면적/기간/설명+사진)·목록·삭제, 이미지생성 도구 링크기존 /api/admin/case* 재사용
② 공사진행보고·채팅job_id 입력 → 진행현황 조회(/work/admin/inbox), 관리자뷰·리포트 딥링크(iframe+새창), 간이 채팅(조회/전송)/work/*(9929 same-origin 프록시) 직접 fetch — 신규 서버 라우트 불필요
③ 고객 후속관리상담문의·후기·거래 목록(마스킹 유지) + 항목별 후속 메모 기록/이력조회/api/inquiries /api/reviews /api/deals + 신규 /api/admin/followup

동반 한선씨 파일

  • /Users/ef/crowny-ays/그룹인가.한선 (+.toau): 토큰유효×조직소속 4상 판정 진리표. hanseonc_high 컴파일 성공, 자가시험 ALL_PASS(4/4).
  • /Users/ef/crowny-ays/후속관리.한선 (+.toau): 후속 메모 PSV 라인 생성/참조매치/파이프 이스케이프. 컴파일 성공, 자가시험 ALL_PASS(3/3, 문자열.한선 가져오기).

실측 결과 (2026-07-30)

  • curl 9401/api/health{"ok":true,...} 정상.
  • sso-login(ays/ays1234) → 토큰 발급 200.
  • /api/admin/session with token(AYS 멤버) → 200.
  • /api/admin/session no token → 401.
  • 임시 테스트계정 ays-console-test(비밀번호는 코드/로그/문서 어디에도 미기록) 등록 → AYS 비소속 → /api/admin/session403 {"ok":false,"phase":"타","reason":"AYS 그룹 권한 필요"} (계정은 재검증용으로 그대로 둠).
  • 레거시 패스코드 폴백: x-admin-pass: Ays!7838(운영 실제값, plist EnvironmentVariables) → /api/admin/cases 200. (코드 기본값 ays4466으로는 401 — 운영 plist 값이 실제 적용됨을 재확인.)
  • 탭1 종단: case POST(1x1 png) → 201GET /api/admin/cases에 즉시 반영 확인 → POST /api/admin/case/delete200 → 재조회 시 사라짐 확인(테스트 사례 검증테스트사례-삭제예정, 삭제 완료).
  • 탭3 종단: POST /api/admin/followup(type=상담, ref_id=test_ref_001, memo에 파이프 포함) → 201GET /api/admin/followup?ref=test_ref_001 → 파이프가 로 안전 이스케이프된 상태로 조회됨.
  • 헤드리스(크라우니AI검증.sh shot): 로그인 화면 렌더 확인(스크린샷: 스크래치패드 ays-admin/login.png), 로그인 후 3탭 콘솔 렌더 확인(탭1 실데이터 12건 목록 포함, 스크린샷 ays-admin/console2.png, DOM에 탭2/탭3 패널·jobSummary·inquiryList·reviewList·dealList 요소 존재 확인).
  • 회귀: / 200, /work 200, /api/stats 200, imagegen.html 200, 게이트웨이 https://ays.crowny.org:8443/admin.html 200, https://ays.crowny.org:8443/ 200.

관련 파일

  • /Users/ef/crowny-ays/server.js — SSO 미들웨어(authRequest/extractToken/groupPhase/checkSsoAuth/isAdminAuthed), followup 로직(followupAppend/followupList), 신규 라우트 3종, 기존 게이트 7곳 전환.
  • /Users/ef/crowny-ays/public/admin.html — 전면 재작성(3탭 콘솔).
  • /Users/ef/crowny-ays/public/admin.html.bak-재정비전 — 구버전 백업.
  • /Users/ef/crowny-ays/그룹인가.한선 .toau — 인가 판정 로직.
  • /Users/ef/crowny-ays/후속관리.한선 .toau — 후속관리 PSV 로직.
  • /Users/ef/crowny-ays/data/후속관리.psv — 신규 데이터 파일(테스트 항목 1건 포함, 실사용 데이터와 공존).
  • /Users/ef/crowny-fonts/디자인적용대상.psvays-admin 항목 추가.
  • /Users/ef/crowny-ays/CLAUDE.md — "관리자 콘솔 재정비" 섹션 추가.

잔여 이슈

  1. 탭2는 플랫폼(9929)에 "전체 잡 목록" 조회 API가 없어 job_id를 수동 입력하는 최소 구현(기본값 JOB1). 플랫폼 쪽에 목록 API가 추가되면 드롭다운으로 개선 가능.
  2. /work/* 플랫폼 라우트 자체는 role=admin 쿼리파라미터 기반 자체 인가(플랫폼 내부 문제, AYS 콘솔의 SSO 게이트와는 별개 — 플랫폼 쪽 정식 인증 도입은 별도 과제).
  3. ssoCache는 서버 메모리 캐시라 서버 재기동 시 초기화(문제 없음 — 재조회로 자동 채워짐).
  4. 패스코드 로그인 UI는 유지했으나 향후 SSO 전면 전환 시 제거 검토 가능(현재는 회귀 방지 목적으로 존치).

추가: 잡목록 API + /work 관리자 프록시 게이트 (2026-07-30, 후속)

무엇을 했는지

  1. GET /admin/jobs?role=admin (플랫폼 정본 /Users/ef/crowny-clean/서버.한선) — job.psv 유니크최신 캐시 기준 잡 요약(id·customer_id·state·building_type·created_at·zones_count·photos_count) 최신순 최대 200건. _관리자인가() 재사용(role != admin → 403). 헬퍼 _행찾기ById·_개수By잡·_잡요약JSON 추가. 다스패치 "/admin/jobs" 라우트 등록.
- 재컴파일(hanseonc_high, 69200 큐브) → launchctl kickstart org.crowny.clean:9624/admin/jobs?role=admin 200, 무role 403. - crowny-ays/시공/동기화.sh 실행(판정=티) → 미러 서버.한선/toau 갱신 → :9929/admin/jobs?role=admin 200 직접 확인(job JOB1 21구역/8사진, 미러 데이터 격리 확인).
  1. /work 관리자 경로 프록시 게이트 (crowny-ays/server.js) — isGatedWorkPath(p, searchParams): 경로가 /work/admin/·/work/view/admin·/work/staff/ 접두이거나 쿼리 role=admin/role=staff면 관리자성 요청으로 판정, await isAdminAuthed(req,null) 통과 못 하면 401(/work,/work/)/403(/work/*) JSON 반환. 고객 경로(/work/home,/work/quotes,/work/chat 등, role 없음)는 무영향.
- extractToken(req)에 쿼리 ?crowny_token= 폴백 추가(헤더/쿠키 다음 순위) — iframe/새창 딥링크는 헤더를 못 붙이므로. - isAdmin(req,body)에 쿼리 ?crowny_admin_pass= 폴백 추가(패스코드 세션의 딥링크용, 동일 패턴). - 동반 /Users/ef/crowny-ays/프록시라우팅.한선관리자경로판정(경로, role값) 순수 판정 로직 추가 + 셀프테스트 5건 전부 정합(admin/jobs=1, role=admin=1, home=0, quotes=0, view/admin=1). - admin.html 탭2: "전체 잡 목록" 카드 신설 — /work/admin/jobs?role=adminapi()(Authorization Bearer/x-admin-pass 헤더 자동첨부)로 fetch, 클릭 시 selectJob(id)로 기존 Job ID 입력란 채움(수동 입력은 폴백으로 존속). 관리자 뷰/리포트 딥링크·iframe src에 세션 토큰(crowny_token) 또는 패스코드(crowny_admin_pass) 쿼리를 자동 첨부해 프록시 게이트 통과.

관련 파일

  • /Users/ef/crowny-clean/서버.한선(정본), 서버.toau(재컴파일)
  • /Users/ef/crowny-ays/시공/서버.한선·서버.toau(동기화 미러, 직접수정 금지)
  • /Users/ef/crowny-ays/server.js(isGatedWorkPath·extractToken·isAdmin 쿼리폴백·/work 라우트 게이트)
  • /Users/ef/crowny-ays/프록시라우팅.한선(동반 판정 로직 관리자경로판정, 셀프테스트)
  • /Users/ef/crowny-ays/public/admin.html(탭2 잡목록 카드+딥링크 토큰첨부)

검증(실측)

  • clean :9624 — /admin/jobs?role=admin 200 JSON, 무role 403. AYS :9929 직접 — 200 JSON(미러 데이터, JOB1 21구역/8사진 — clean과 격리 확인).
  • 게이트: curl :9920/work/admin/jobs?role=admin 무토큰 403. x-admin-pass: <plist값> 헤더 200(잡 목록 반환). 쿼리 ?crowny_admin_pass=<값> 200(/work/view/admin 200 HTML). 쿼리 ?crowny_token=bogus → 403 "유효하지 않은 토큰"(코드 정상 동작, 실 SSO 토큰은 미보유로 200 전량 검증은 패스코드 경로로 대체).
  • 고객 경로 무영향: /work/home 200, /work/quotes 200, /work/chat?job_id=JOB1 200(무토큰).
  • 회귀: /·/work·/landing·/api/stats·/admin.html·/imagegen.html 전부 200(:9920 로컬). 게이트웨이 ays.crowny.org:8443/admin.html 200, clean.crowny.org:8443/ 200.
  • :9929 직접 접근은 게이트 밖(로컬 전용) — 인가는 :9920(server.js 프록시 계층)에서만 강제됨. 외부 공개는 게이트웨이가 9920만 노출하는 구조라 실사용 경로는 안전하나, 로컬에서 9929로 직접 접속하면 우회 가능함을 문서화(플랫폼 자체 인증은 별개 과제로 잔존).

잔여 이슈

  • 플랫폼(:9929) 자체에는 인증이 없다(role 쿼리 기반) — AYS 콘솔 SSO 게이트는 프록시 계층(:9920)에서만 작동. 9929를 외부에 직접 노출하지 않는 한 안전하지만, 근본 해결은 플랫폼 쪽에 자체 토큰 검증을 넣는 것(공용 엔진이라 clean 쪽 변경은 이번 범위에서 보류).
  • 실제 AYS 소속 SSO 계정의 유효 비밀번호를 몰라 crowny_token 200 경로는 코드상 정상 동작만 확인(무효 토큰 403 정상)했고 유효 토큰 200 왕복은 패스코드 경로로 대체 검증함.
  • /admin/jobs는 job.psv 기준 최대 200건 — 200건 초과 시 오래된 잡은 응답에서 잘림(필요 시 페이지네이션 후속).

SSO 토큰 200 경로 종단 실증 (메인 세션, 2026-07-30)

  • sso-login(ays)→Bearer /work/admin/jobs?role=admin 200(JSON 잡목록) · ?crowny_token= 쿼리 폴백 200 · 무토큰 403 — 게이트 3분기 전부 실측 완료. 공개 root/work 200 회귀 확인.

후속: 엔진 레벨 관리자 인증 + 잡목록 페이지네이션 (2026-07-30)

개요

:9929(crowny-clean 서버.한선/AYS 시공 미러) 자체가 무인증이던 잔여 이슈를 엔진 레벨에서 해소. 설정.psv 구동형 서비스 토큰(관리자토큰, 12번째 필드) 도입 — clean은 빈 값 유지(회귀 0), AYS만 실토큰 배선.

무엇을 했는지

  1. crowny-clean/서버.한선(SSOT): _설정로드()에 13번째(idx12) 필드 관리자토큰 파싱. 헤더 리더 _헤더값(요청,이름) + _토큰검증(요청) 신설(토큰 빈 값=항상 통과). _관리자인가()·사진업로드(스태프/팀장)·스태프보고 3지점에 토큰검증 게이트.
  2. /admin/jobs 페이지네이션: offset/limit(기본 0/50, 캡 200) 쿼리 지원, 응답에 total/offset/limit 추가.
  3. AYS 배선: crowny-ays/시공/설정.psv 13번째 필드에 openssl rand -hex 24 토큰 추가(값은 미문서화 — plist에만 존재). com.crowny.ays.plist EnvironmentVariables에 AYS_SVC_TOKEN 추가(기존 AYS_ADMIN_PASS 보존).
  4. server.js: proxyToPlatform(req,res,overridePath,extraHeaders) 4번째 인자로 헤더 병합 지원. isGatedWorkPath 게이트 통과(인가 성공) 시에만 x-crowny-svc-token: process.env.AYS_SVC_TOKEN 주입해 9929로 프록시.
  5. admin.html 탭2: "더보기" 버튼 — jobListOffset/JOB_LIST_LIMIT(50) 누적 fetch, total 도달 시 버튼 숨김.
  6. 동기화.sh로 clean 정본 → AYS 시공 미러 전파(판정=티, 스모크 통과).

실측표

검증결과
clean /200
clean /admin/jobs?role=admin(무토큰, 12열 빈값)200
clean /admin/jobs?role=admin&limit=2 응답에 total/offset/limit포함 확인
동기화.sh 판정티(전파+스모크200)
9929 직접 무토큰 /admin/jobs?role=admin403
9929 직접 x-crowny-svc-token 헤더200
9920 /work/admin/jobs?role=admin 무인증403
9920 /work/admin/jobs?role=admin + x-admin-pass(레거시 패스코드)200 (svc-token 자동 주입 종단 확인, total/offset/limit 응답 반영)
9920 /work/home·/work/quotes(고객 경로, 무토큰)200
프록시라우팅.한선 svc토큰주입인가() 셀프테스트3/3 PASS
잡목록페이지네이션.한선 더보기인가() 셀프테스트4/4 PASS
public https://ays.crowny.org:8443/·/work·/admin.html200
public https://clean.crowny.org:8443/200
admin.html 내 script 구문검사(node --check)OK

관련 파일

  • /Users/ef/crowny-clean/서버.한선 (SSOT — _토큰검증/_헤더값/_관리자토큰/_처리잡목록 페이지네이션)
  • /Users/ef/crowny-ays/시공/설정.psv (13열, 관리자토큰 배선 — clean 원본은 12열 빈값 유지)
  • /Users/ef/Library/LaunchAgents/com.crowny.ays.plist (EnvironmentVariables.AYS_SVC_TOKEN 추가)
  • /Users/ef/crowny-ays/server.js (proxyToPlatform extraHeaders, /work 게이트 2지점 svc-token 주입)
  • /Users/ef/crowny-ays/프록시라우팅.한선 (svc토큰주입인가() 동반 판정 + 셀프테스트 3건 추가)
  • /Users/ef/crowny-ays/public/admin.html (탭2 "더보기" 버튼, loadJobList/renderJobList 재작성)
  • /Users/ef/crowny-ays/잡목록페이지네이션.한선 (더보기 판정 동반, 셀프테스트 4/4)

잔여 이슈

  • 토큰 값 자체는 plist(com.crowny.ays.plist)와 AYS 설정.psv에만 존재 — 문서/보고서에 값 기록하지 않음. 로테이션 시 두 곳(plist+설정.psv) 동시 갱신 + launchctl unload/load -w + 동기화.sh 재실행 필요.
  • 스태프 사진업로드/보고 라우트도 토큰검증을 걸었으나, 현재 AYS server.js 프록시 계층이 이 두 라우트에 svc-token을 주입하는 특별 처리는 없음(스태프 앱 경로가 아직 /work/staff/*로 고정 매핑되어 isGatedWorkPath/staff/ 판정에 걸려 자동으로 svc-token이 실리므로 실사용상 문제 없음 — 별도 확인 완료했으나 향후 스태프앱 경로 변경 시 재점검 필요).
  • clean(원본) 설정.psv는 그대로 12열 이하 유지 — 새 관리자토큰 필드는 AYS처럼 필요한 인스턴스만 13번째로 추가하는 옵트인 방식.