관리자 이메일 확장 및 폴백 동작 정리
- 기본 관리자 이메일 목록에 추가 계정 반영 - script.js 로드 실패 시 폴백에서도 /api/config/auth hydrate 및 /api/auth/sync 호출 - 폴백에서 관리자 전용 기능 잠금 및 로그인 전 내보내기 비활성화 적용 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -14,7 +14,7 @@ AUTH0_CLIENT_ID=g5RDfax7FZkKzXYvXOgku3Ll8CxuA4IM
|
|||||||
# Google connection name (usually google-oauth2)
|
# Google connection name (usually google-oauth2)
|
||||||
AUTH0_GOOGLE_CONNECTION=google-oauth2
|
AUTH0_GOOGLE_CONNECTION=google-oauth2
|
||||||
# Admin emails (comma-separated)
|
# Admin emails (comma-separated)
|
||||||
ADMIN_EMAILS=dosangyoon@gmail.com,dsyoon@ncue.net
|
ADMIN_EMAILS=dsyoon@ncue.net,dosangyoon@gmail.com,dosangyoon2@gmail.com,dosangyoon3@gmail.com
|
||||||
|
|
||||||
## Optional
|
## Optional
|
||||||
# Server port
|
# Server port
|
||||||
|
|||||||
87
index.html
87
index.html
@@ -433,11 +433,15 @@
|
|||||||
"link-ncue-net",
|
"link-ncue-net",
|
||||||
"dreamgirl-ncue-net",
|
"dreamgirl-ncue-net",
|
||||||
]);
|
]);
|
||||||
const ACCESS_ADMIN_EMAILS = new Set(["dosangyoon@gmail.com", "dsyoon@ncue.net"]);
|
|
||||||
let sessionEmail = "";
|
let sessionEmail = "";
|
||||||
|
|
||||||
function isAdminEmail(email) {
|
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) {
|
function canAccessLink(link) {
|
||||||
const id = String(link && link.id ? link.id : "");
|
const id = String(link && link.id ? link.id : "");
|
||||||
@@ -663,6 +667,50 @@
|
|||||||
ready: false,
|
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() {
|
function loadAuthOverride() {
|
||||||
const raw = localStorage.getItem(AUTH_OVERRIDE_KEY);
|
const raw = localStorage.getItem(AUTH_OVERRIDE_KEY);
|
||||||
const data = raw ? safeJsonParse(raw, null) : null;
|
const data = raw ? safeJsonParse(raw, null) : null;
|
||||||
@@ -736,6 +784,7 @@
|
|||||||
|
|
||||||
async function ensureAuthClient() {
|
async function ensureAuthClient() {
|
||||||
if (auth.client) return auth.client;
|
if (auth.client) return auth.client;
|
||||||
|
await hydrateAuthConfigFromServerIfNeeded();
|
||||||
const cfg = getAuthConfig();
|
const cfg = getAuthConfig();
|
||||||
if (!cfg.auth0.domain || !cfg.auth0.clientId) return null;
|
if (!cfg.auth0.domain || !cfg.auth0.clientId) return null;
|
||||||
if (typeof window.createAuth0Client !== "function") return null;
|
if (typeof window.createAuth0Client !== "function") return null;
|
||||||
@@ -756,6 +805,7 @@
|
|||||||
const client = await ensureAuthClient();
|
const client = await ensureAuthClient();
|
||||||
if (!client) {
|
if (!client) {
|
||||||
// No config: keep buttons visible but disabled style
|
// No config: keep buttons visible but disabled style
|
||||||
|
applyManageLock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const u = new URL(location.href);
|
const u = new URL(location.href);
|
||||||
@@ -774,6 +824,29 @@
|
|||||||
if (el.snsLogin) el.snsLogin.hidden = Boolean(auth.user);
|
if (el.snsLogin) el.snsLogin.hidden = Boolean(auth.user);
|
||||||
if (el.user) el.user.hidden = !auth.user;
|
if (el.user) el.user.hidden = !auth.user;
|
||||||
if (el.userText && auth.user) el.userText.textContent = auth.user.email || auth.user.name || "로그인됨";
|
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();
|
render();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -876,6 +949,13 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return;
|
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 act = btn.getAttribute("data-act");
|
||||||
const link = id ? getById(id) : null;
|
const link = id ? getById(id) : null;
|
||||||
if (!link) return;
|
if (!link) return;
|
||||||
@@ -899,6 +979,8 @@
|
|||||||
if (el.btnExport) el.btnExport.addEventListener("click", exportJson);
|
if (el.btnExport) el.btnExport.addEventListener("click", exportJson);
|
||||||
if (el.btnImport)
|
if (el.btnImport)
|
||||||
el.btnImport.addEventListener("click", () => {
|
el.btnImport.addEventListener("click", () => {
|
||||||
|
const canManage = Boolean(auth.user) && isAdminEmail(sessionEmail);
|
||||||
|
if (!canManage) return toast("관리 기능은 로그인(관리자 이메일) 후 사용 가능합니다.");
|
||||||
if (el.file) el.file.click();
|
if (el.file) el.file.click();
|
||||||
});
|
});
|
||||||
if (el.file)
|
if (el.file)
|
||||||
@@ -926,6 +1008,7 @@
|
|||||||
|
|
||||||
render();
|
render();
|
||||||
initAuth().catch(() => {});
|
initAuth().catch(() => {});
|
||||||
|
applyManageLock();
|
||||||
toast("스크립트 로딩 문제로 폴백 모드로 실행 중입니다.");
|
toast("스크립트 로딩 문제로 폴백 모드로 실행 중입니다.");
|
||||||
}, 200);
|
}, 200);
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -70,7 +70,12 @@
|
|||||||
"link-ncue-net",
|
"link-ncue-net",
|
||||||
"dreamgirl-ncue-net",
|
"dreamgirl-ncue-net",
|
||||||
]);
|
]);
|
||||||
const DEFAULT_ADMIN_EMAILS = new Set(["dosangyoon@gmail.com", "dsyoon@ncue.net"]);
|
const DEFAULT_ADMIN_EMAILS = new Set([
|
||||||
|
"dsyoon@ncue.net",
|
||||||
|
"dosangyoon@gmail.com",
|
||||||
|
"dosangyoon2@gmail.com",
|
||||||
|
"dosangyoon3@gmail.com",
|
||||||
|
]);
|
||||||
|
|
||||||
function getUserEmail() {
|
function getUserEmail() {
|
||||||
const e = auth && auth.user && auth.user.email ? String(auth.user.email) : "";
|
const e = auth && auth.user && auth.user.email ? String(auth.user.email) : "";
|
||||||
|
|||||||
Reference in New Issue
Block a user