DB 기반 관리자 권한으로 전환

- ncue_user에 is_admin 추가 및 can_manage 호환 유지
- /api/auth/sync 및 관리 API를 DB is_admin 기반으로 변경
- index.html 폴백에서 관리자 이메일 하드코딩 제거
- .env로 관리자 부트스트랩(ADMIN_EMAILS) 지원

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dsyoon
2026-02-25 21:09:02 +09:00
parent 19c6814d2f
commit c21a7b3739
7 changed files with 190 additions and 114 deletions

View File

@@ -435,18 +435,11 @@
]);
let sessionEmail = "";
function isAdminEmail(email) {
const cfg = getAuthConfig();
const admins = cfg && Array.isArray(cfg.adminEmails) ? cfg.adminEmails : [];
const e = String(email || "").trim().toLowerCase();
if (admins.length) return admins.includes(e);
// fallback default
return ["dsyoon@ncue.net", "dosangyoon@gmail.com", "dosangyoon2@gmail.com", "dosangyoon3@gmail.com"].includes(e);
}
function canAccessLink(link) {
// Admin access is determined by server (DB: ncue_user.is_admin) via /api/auth/sync.
if (auth && auth.authorized) return true;
const id = String(link && link.id ? link.id : "");
const email = String(sessionEmail || "").trim().toLowerCase();
if (email && isAdminEmail(email)) return true;
if (email) return ACCESS_USER_IDS.has(id);
return ACCESS_ANON_IDS.has(id);
}
@@ -751,6 +744,7 @@
const auth = {
client: null,
user: null,
authorized: false,
ready: false,
};
@@ -771,7 +765,6 @@
const v = data.value;
const auth0 = v.auth0 || {};
const connections = v.connections || {};
const adminEmails = Array.isArray(v.adminEmails) ? v.adminEmails : Array.isArray(v.allowedEmails) ? v.allowedEmails : [];
const domain = String(auth0.domain || "").trim();
const clientId = String(auth0.clientId || "").trim();
const google = String(connections.google || "").trim();
@@ -781,7 +774,6 @@
JSON.stringify({
auth0: { domain, clientId },
connections: { google },
adminEmails,
})
);
return true;
@@ -791,7 +783,7 @@
}
function applyManageLock() {
const canManage = Boolean(auth.user) && isAdminEmail(sessionEmail);
const canManage = Boolean(auth.user) && Boolean(auth.authorized);
if (el.btnAdd) el.btnAdd.disabled = !canManage;
if (el.btnImport) el.btnImport.disabled = !canManage;
// 요청: 로그인 전 내보내기 비활성화
@@ -919,14 +911,18 @@
const raw = claims && claims.__raw ? String(claims.__raw) : "";
if (raw) {
const cfg = getAuthConfig();
await fetch(apiUrl("/api/auth/sync"), {
const r = await fetch(apiUrl("/api/auth/sync"), {
method: "POST",
headers: {
Authorization: `Bearer ${raw}`,
"X-Auth0-Issuer": `https://${cfg.auth0.domain}/`,
"X-Auth0-ClientId": cfg.auth0.clientId,
},
}).catch(() => {});
}).catch(() => null);
if (r && r.ok) {
const data = await r.json().catch(() => null);
if (data && data.ok) auth.authorized = Boolean(data.canManage);
}
}
} catch {
// ignore
@@ -1036,9 +1032,9 @@
e.preventDefault();
return;
}
// block manage actions unless admin
// block manage actions unless admin (server: ncue_user.is_admin)
const act0 = btn.getAttribute("data-act");
const canManage = Boolean(auth.user) && isAdminEmail(sessionEmail);
const canManage = Boolean(auth.user) && Boolean(auth.authorized);
if (!canManage && (act0 === "fav" || act0 === "edit" || act0 === "del")) {
toast("관리 기능은 로그인(관리자 이메일) 후 사용 가능합니다.");
return;
@@ -1066,7 +1062,7 @@
if (el.btnExport) el.btnExport.addEventListener("click", exportJson);
if (el.btnImport)
el.btnImport.addEventListener("click", () => {
const canManage = Boolean(auth.user) && isAdminEmail(sessionEmail);
const canManage = Boolean(auth.user) && Boolean(auth.authorized);
if (!canManage) return toast("관리 기능은 로그인(관리자 이메일) 후 사용 가능합니다.");
if (el.file) el.file.click();
});