관리자 이메일 확장 및 폴백 동작 정리
- 기본 관리자 이메일 목록에 추가 계정 반영 - script.js 로드 실패 시 폴백에서도 /api/config/auth hydrate 및 /api/auth/sync 호출 - 폴백에서 관리자 전용 기능 잠금 및 로그인 전 내보내기 비활성화 적용 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
87
index.html
87
index.html
@@ -433,11 +433,15 @@
|
||||
"link-ncue-net",
|
||||
"dreamgirl-ncue-net",
|
||||
]);
|
||||
const ACCESS_ADMIN_EMAILS = new Set(["dosangyoon@gmail.com", "dsyoon@ncue.net"]);
|
||||
let sessionEmail = "";
|
||||
|
||||
function isAdminEmail(email) {
|
||||
return ACCESS_ADMIN_EMAILS.has(String(email || "").trim().toLowerCase());
|
||||
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) {
|
||||
const id = String(link && link.id ? link.id : "");
|
||||
@@ -663,6 +667,50 @@
|
||||
ready: false,
|
||||
};
|
||||
|
||||
function apiUrl(pathname) {
|
||||
// same-origin only in fallback
|
||||
return pathname;
|
||||
}
|
||||
|
||||
async function hydrateAuthConfigFromServerIfNeeded() {
|
||||
const cfg = getAuthConfig();
|
||||
const hasLocal = Boolean(cfg.auth0.domain && cfg.auth0.clientId && cfg.connections.google);
|
||||
if (hasLocal) return true;
|
||||
try {
|
||||
const r = await fetch(apiUrl("/api/config/auth"), { cache: "no-store" });
|
||||
if (!r.ok) return false;
|
||||
const data = await r.json();
|
||||
if (!data || !data.ok || !data.value) return false;
|
||||
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();
|
||||
if (!domain || !clientId || !google) return false;
|
||||
localStorage.setItem(
|
||||
AUTH_OVERRIDE_KEY,
|
||||
JSON.stringify({
|
||||
auth0: { domain, clientId },
|
||||
connections: { google },
|
||||
adminEmails,
|
||||
})
|
||||
);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function applyManageLock() {
|
||||
const canManage = Boolean(auth.user) && isAdminEmail(sessionEmail);
|
||||
if (el.btnAdd) el.btnAdd.disabled = !canManage;
|
||||
if (el.btnImport) el.btnImport.disabled = !canManage;
|
||||
// 요청: 로그인 전 내보내기 비활성화
|
||||
if (el.btnExport) el.btnExport.disabled = !auth.user;
|
||||
}
|
||||
|
||||
function loadAuthOverride() {
|
||||
const raw = localStorage.getItem(AUTH_OVERRIDE_KEY);
|
||||
const data = raw ? safeJsonParse(raw, null) : null;
|
||||
@@ -736,6 +784,7 @@
|
||||
|
||||
async function ensureAuthClient() {
|
||||
if (auth.client) return auth.client;
|
||||
await hydrateAuthConfigFromServerIfNeeded();
|
||||
const cfg = getAuthConfig();
|
||||
if (!cfg.auth0.domain || !cfg.auth0.clientId) return null;
|
||||
if (typeof window.createAuth0Client !== "function") return null;
|
||||
@@ -756,6 +805,7 @@
|
||||
const client = await ensureAuthClient();
|
||||
if (!client) {
|
||||
// No config: keep buttons visible but disabled style
|
||||
applyManageLock();
|
||||
return;
|
||||
}
|
||||
const u = new URL(location.href);
|
||||
@@ -774,6 +824,29 @@
|
||||
if (el.snsLogin) el.snsLogin.hidden = Boolean(auth.user);
|
||||
if (el.user) el.user.hidden = !auth.user;
|
||||
if (el.userText && auth.user) el.userText.textContent = auth.user.email || auth.user.name || "로그인됨";
|
||||
|
||||
// sync user to server (upsert ncue_user)
|
||||
if (auth.user) {
|
||||
try {
|
||||
const claims = await client.getIdTokenClaims();
|
||||
const raw = claims && claims.__raw ? String(claims.__raw) : "";
|
||||
if (raw) {
|
||||
const cfg = getAuthConfig();
|
||||
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 {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
applyManageLock();
|
||||
render();
|
||||
}
|
||||
|
||||
@@ -876,6 +949,13 @@
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
// block manage actions unless admin
|
||||
const act0 = btn.getAttribute("data-act");
|
||||
const canManage = Boolean(auth.user) && isAdminEmail(sessionEmail);
|
||||
if (!canManage && (act0 === "fav" || act0 === "edit" || act0 === "del")) {
|
||||
toast("관리 기능은 로그인(관리자 이메일) 후 사용 가능합니다.");
|
||||
return;
|
||||
}
|
||||
const act = btn.getAttribute("data-act");
|
||||
const link = id ? getById(id) : null;
|
||||
if (!link) return;
|
||||
@@ -899,6 +979,8 @@
|
||||
if (el.btnExport) el.btnExport.addEventListener("click", exportJson);
|
||||
if (el.btnImport)
|
||||
el.btnImport.addEventListener("click", () => {
|
||||
const canManage = Boolean(auth.user) && isAdminEmail(sessionEmail);
|
||||
if (!canManage) return toast("관리 기능은 로그인(관리자 이메일) 후 사용 가능합니다.");
|
||||
if (el.file) el.file.click();
|
||||
});
|
||||
if (el.file)
|
||||
@@ -926,6 +1008,7 @@
|
||||
|
||||
render();
|
||||
initAuth().catch(() => {});
|
||||
applyManageLock();
|
||||
toast("스크립트 로딩 문제로 폴백 모드로 실행 중입니다.");
|
||||
}, 200);
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user