(function () { "use strict"; const storageKey = "teraclone.workspaceTabs.v2"; function allowedPath(path, role) { // Restore only local page paths, never executable/external URLs or APIs. if (typeof path !== "string" || !/^\/(?!\/)[A-Za-z0-9/_-]*$/.test(path) || /^\/(?:api|static|login|logout)(?:\/|$)/.test(path)) { return false; } if (role !== "port_operator") { return true; } return ["/", "/port-status", "/password"].includes(path) || /^\/ports\/(?:[1-9]|[1-3][0-9]|4[0-8])\/(?:parameters|operation-1|operation-2)$/.test(path); } function sanitizeTabs(tabs, role) { if (!Array.isArray(tabs)) { return []; } const seen = new Set(); return tabs.reduce(function (result, tab) { if (!tab || typeof tab.key !== "string" || !tab.key || typeof tab.label !== "string" || !tab.label || !allowedPath(tab.path, role)) { return result; } const match = tab.path.match(/^\/ports\/(\d+)\/(?:parameters|operation-1|operation-2|data-log|advanced)$/); const normalized = match ? { key: "port-settings", path: tab.path, label: "포트 " + Number(match[1]) + " 설정" } : { key: tab.key, path: tab.path, label: tab.label }; if (!seen.has(normalized.key)) { result.push(normalized); seen.add(normalized.key); } return result; }, []); } function createStore(getStorage, getScope) { let memory = null; function matches(record, scope) { return Boolean(scope.id && record && record.id === scope.id && record.user === scope.user && record.role === scope.role); } return { read: function () { const scope = getScope(); let record = memory; try { record = JSON.parse(getStorage().getItem(storageKey) || "null"); } catch (_) { /* Storage can be disabled; keep this page usable in memory. */ } return matches(record, scope) ? sanitizeTabs(record.tabs, scope.role) : []; }, write: function (tabs) { const scope = getScope(); memory = { id: scope.id, user: scope.user, role: scope.role, tabs: sanitizeTabs(tabs, scope.role) }; try { getStorage().setItem(storageKey, JSON.stringify(memory)); } catch (_) { /* In-memory fallback. */ } }, clear: function () { memory = null; try { getStorage().removeItem(storageKey); } catch (_) { /* Storage unavailable. */ } } }; } const api = { storageKey: storageKey, allowedPath: allowedPath, sanitizeTabs: sanitizeTabs, createStore: createStore }; if (typeof module !== "undefined" && module.exports) { module.exports = api; } if (typeof window !== "undefined") { window.teraWorkspaceTabs = api; } }());