import { showToast, toggleVisibility } from './domUtils.js'; import { sendRequest } from './networkUtils.js'; const version = "v1.0.6"; const adminTitle = `Admin Panel ${version}`; let lastLoginData = null; export function setLastLoginData(data) { lastLoginData = data; } export function openTOTPLoginModal() { let totpLoginModal = document.getElementById("totpLoginModal"); const isDarkMode = document.body.classList.contains("dark-mode"); const modalBg = isDarkMode ? "#2c2c2c" : "#fff"; const textColor = isDarkMode ? "#e0e0e0" : "#000"; if (!totpLoginModal) { totpLoginModal = document.createElement("div"); totpLoginModal.id = "totpLoginModal"; totpLoginModal.style.cssText = ` position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; z-index: 3200; `; totpLoginModal.innerHTML = `
×

Enter TOTP Code

`; document.body.appendChild(totpLoginModal); document.getElementById("closeTOTPLoginModal").addEventListener("click", () => { totpLoginModal.style.display = "none"; }); const totpInput = document.getElementById("totpLoginInput"); totpInput.focus(); totpInput.addEventListener("input", function () { if (this.value.trim().length === 6 && lastLoginData) { lastLoginData.totp_code = this.value.trim(); totpLoginModal.style.display = "none"; if (typeof window.submitLogin === "function") { window.submitLogin(lastLoginData); } } }); } else { totpLoginModal.style.display = "flex"; const modalContent = totpLoginModal.firstElementChild; modalContent.style.background = modalBg; modalContent.style.color = textColor; } } export function openUserPanel() { const username = localStorage.getItem("username") || "User"; let userPanelModal = document.getElementById("userPanelModal"); const isDarkMode = document.body.classList.contains("dark-mode"); const overlayBackground = isDarkMode ? "rgba(0,0,0,0.7)" : "rgba(0,0,0,0.3)"; const modalContentStyles = ` background: ${isDarkMode ? "#2c2c2c" : "#fff"}; color: ${isDarkMode ? "#e0e0e0" : "#000"}; padding: 20px; max-width: 600px; width: 90%; border-radius: 8px; position: relative; overflow-y: auto; max-height: 90vh; border: ${isDarkMode ? "1px solid #444" : "1px solid #ccc"}; transform: none; transition: none; `; if (!userPanelModal) { userPanelModal = document.createElement("div"); userPanelModal.id = "userPanelModal"; userPanelModal.style.cssText = ` position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: ${overlayBackground}; display: flex; justify-content: center; align-items: center; z-index: 3000; `; userPanelModal.innerHTML = ` `; document.body.appendChild(userPanelModal); document.getElementById("closeUserPanel").addEventListener("click", () => { userPanelModal.style.display = "none"; }); document.getElementById("openChangePasswordModalBtn").addEventListener("click", () => { document.getElementById("changePasswordModal").style.display = "block"; }); const totpCheckbox = document.getElementById("userTOTPEnabled"); totpCheckbox.checked = localStorage.getItem("userTOTPEnabled") === "true"; totpCheckbox.addEventListener("change", function () { localStorage.setItem("userTOTPEnabled", this.checked ? "true" : "false"); const enabled = this.checked; fetch("updateUserPanel.php", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", "X-CSRF-Token": window.csrfToken }, body: JSON.stringify({ totp_enabled: enabled }) }) .then(r => r.json()) .then(result => { if (!result.success) { showToast("Error updating TOTP setting: " + result.error); } else if (enabled) { openTOTPModal(); } }) .catch(() => { showToast("Error updating TOTP setting."); }); }); } else { userPanelModal.style.backgroundColor = overlayBackground; const modalContent = userPanelModal.querySelector(".modal-content"); modalContent.style.background = isDarkMode ? "#2c2c2c" : "#fff"; modalContent.style.color = isDarkMode ? "#e0e0e0" : "#000"; modalContent.style.border = isDarkMode ? "1px solid #444" : "1px solid #ccc"; } userPanelModal.style.display = "flex"; } export function openTOTPModal() { let totpModal = document.getElementById("totpModal"); const isDarkMode = document.body.classList.contains("dark-mode"); const overlayBackground = isDarkMode ? "rgba(0,0,0,0.7)" : "rgba(0,0,0,0.3)"; const modalContentStyles = ` background: ${isDarkMode ? "#2c2c2c" : "#fff"}; color: ${isDarkMode ? "#e0e0e0" : "#000"}; padding: 20px; max-width: 400px; width: 90%; border-radius: 8px; position: relative; `; if (!totpModal) { totpModal = document.createElement("div"); totpModal.id = "totpModal"; totpModal.style.cssText = ` position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: ${overlayBackground}; display: flex; justify-content: center; align-items: center; z-index: 3100; `; totpModal.innerHTML = ` `; document.body.appendChild(totpModal); // Bind the X button to call closeTOTPModal with disable=true document.getElementById("closeTOTPModal").addEventListener("click", () => { closeTOTPModal(true); }); // Add event listener for TOTP confirmation document.getElementById("confirmTOTPBtn").addEventListener("click", function () { const code = document.getElementById("totpConfirmInput").value.trim(); if (code.length !== 6) { showToast("Please enter a valid 6-digit code."); return; } // Call the endpoint to verify the TOTP code fetch("totp_verify.php", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", "X-CSRF-Token": window.csrfToken }, body: JSON.stringify({ totp_code: code }) }) .then(r => r.json()) .then(result => { if (result.success) { showToast("TOTP successfully enabled."); // On success, close the modal without disabling closeTOTPModal(false); } else { showToast("TOTP verification failed: " + (result.error || "Invalid code.")); } }) .catch(() => { showToast("Error verifying TOTP code."); }); }); } else { totpModal.style.display = "flex"; totpModal.style.backgroundColor = overlayBackground; const modalContent = totpModal.querySelector(".modal-content"); modalContent.style.background = isDarkMode ? "#2c2c2c" : "#fff"; modalContent.style.color = isDarkMode ? "#e0e0e0" : "#000"; } } // Updated closeTOTPModal function with a disable parameter export function closeTOTPModal(disable = true) { const totpModal = document.getElementById("totpModal"); if (totpModal) totpModal.style.display = "none"; if (disable) { // Uncheck the Enable TOTP checkbox const totpCheckbox = document.getElementById("userTOTPEnabled"); if (totpCheckbox) { totpCheckbox.checked = false; localStorage.setItem("userTOTPEnabled", "false"); } // Call endpoint to remove the TOTP secret from the user's record fetch("totp_disable.php", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", "X-CSRF-Token": window.csrfToken } }) .then(r => r.json()) .then(result => { if (!result.success) { showToast("Error disabling TOTP setting: " + result.error); } }) .catch(() => { showToast("Error disabling TOTP setting."); }); } } export function openAdminPanel() { fetch("getConfig.php", { credentials: "include" }) .then(response => response.json()) .then(config => { if (config.oidc) Object.assign(window.currentOIDCConfig, config.oidc); if (config.globalOtpauthUrl) window.currentOIDCConfig.globalOtpauthUrl = config.globalOtpauthUrl; const isDarkMode = document.body.classList.contains("dark-mode"); const overlayBackground = isDarkMode ? "rgba(0,0,0,0.7)" : "rgba(0,0,0,0.3)"; const modalContentStyles = ` background: ${isDarkMode ? "#2c2c2c" : "#fff"}; color: ${isDarkMode ? "#e0e0e0" : "#000"}; padding: 20px; max-width: 600px; width: 90%; border-radius: 8px; position: relative; overflow-y: auto; max-height: 90vh; border: ${isDarkMode ? "1px solid #444" : "1px solid #ccc"}; `; let adminModal = document.getElementById("adminPanelModal"); if (!adminModal) { adminModal = document.createElement("div"); adminModal.id = "adminPanelModal"; adminModal.style.cssText = ` position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: ${overlayBackground}; display: flex; justify-content: center; align-items: center; z-index: 3000; `; // Added a version number next to "Admin Panel" adminModal.innerHTML = ` `; document.body.appendChild(adminModal); document.getElementById("closeAdminPanel").addEventListener("click", closeAdminPanel); adminModal.addEventListener("click", (e) => { if (e.target === adminModal) closeAdminPanel(); }); document.getElementById("cancelAdminSettings").addEventListener("click", closeAdminPanel); document.getElementById("adminOpenAddUser").addEventListener("click", () => { toggleVisibility("addUserModal", true); document.getElementById("newUsername").focus(); }); document.getElementById("adminOpenRemoveUser").addEventListener("click", () => { if (typeof window.loadUserList === "function") { window.loadUserList(); } toggleVisibility("removeUserModal", true); }); // New event binding for the User Permissions button: document.getElementById("adminOpenUserPermissions").addEventListener("click", () => { openUserPermissionsModal(); }); document.getElementById("saveAdminSettings").addEventListener("click", () => { const disableFormLoginCheckbox = document.getElementById("disableFormLogin"); const disableBasicAuthCheckbox = document.getElementById("disableBasicAuth"); const disableOIDCLoginCheckbox = document.getElementById("disableOIDCLogin"); const totalDisabled = [disableFormLoginCheckbox, disableBasicAuthCheckbox, disableOIDCLoginCheckbox].filter(cb => cb.checked).length; if (totalDisabled === 3) { showToast("At least one login method must remain enabled."); disableOIDCLoginCheckbox.checked = false; localStorage.setItem("disableOIDCLogin", "false"); if (typeof window.updateLoginOptionsUI === "function") { window.updateLoginOptionsUI({ disableFormLogin: disableFormLoginCheckbox.checked, disableBasicAuth: disableBasicAuthCheckbox.checked, disableOIDCLogin: disableOIDCLoginCheckbox.checked }); } return; } const newOIDCConfig = { providerUrl: document.getElementById("oidcProviderUrl").value.trim(), clientId: document.getElementById("oidcClientId").value.trim(), clientSecret: document.getElementById("oidcClientSecret").value.trim(), redirectUri: document.getElementById("oidcRedirectUri").value.trim() }; const disableFormLogin = disableFormLoginCheckbox.checked; const disableBasicAuth = disableBasicAuthCheckbox.checked; const disableOIDCLogin = disableOIDCLoginCheckbox.checked; const globalOtpauthUrl = document.getElementById("globalOtpauthUrl").value.trim(); sendRequest("updateConfig.php", "POST", { oidc: newOIDCConfig, disableFormLogin, disableBasicAuth, disableOIDCLogin, globalOtpauthUrl }, { "X-CSRF-Token": window.csrfToken }) .then(response => { if (response.success) { showToast("Settings updated successfully."); localStorage.setItem("disableFormLogin", disableFormLogin); localStorage.setItem("disableBasicAuth", disableBasicAuth); localStorage.setItem("disableOIDCLogin", disableOIDCLogin); if (typeof window.updateLoginOptionsUI === "function") { window.updateLoginOptionsUI({ disableFormLogin, disableBasicAuth, disableOIDCLogin }); } closeAdminPanel(); } else { showToast("Error updating settings: " + (response.error || "Unknown error")); } }) .catch(() => { }); }); const disableFormLoginCheckbox = document.getElementById("disableFormLogin"); const disableBasicAuthCheckbox = document.getElementById("disableBasicAuth"); const disableOIDCLoginCheckbox = document.getElementById("disableOIDCLogin"); function enforceLoginOptionConstraint(changedCheckbox) { const totalDisabled = [disableFormLoginCheckbox, disableBasicAuthCheckbox, disableOIDCLoginCheckbox].filter(cb => cb.checked).length; if (changedCheckbox.checked && totalDisabled === 3) { showToast("At least one login method must remain enabled."); changedCheckbox.checked = false; } } disableFormLoginCheckbox.addEventListener("change", function () { enforceLoginOptionConstraint(this); }); disableBasicAuthCheckbox.addEventListener("change", function () { enforceLoginOptionConstraint(this); }); disableOIDCLoginCheckbox.addEventListener("change", function () { enforceLoginOptionConstraint(this); }); document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true; document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true; document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true; } else { adminModal.style.backgroundColor = overlayBackground; const modalContent = adminModal.querySelector(".modal-content"); if (modalContent) { modalContent.style.background = isDarkMode ? "#2c2c2c" : "#fff"; modalContent.style.color = isDarkMode ? "#e0e0e0" : "#000"; modalContent.style.border = isDarkMode ? "1px solid #444" : "1px solid #ccc"; } document.getElementById("oidcProviderUrl").value = window.currentOIDCConfig.providerUrl; document.getElementById("oidcClientId").value = window.currentOIDCConfig.clientId; document.getElementById("oidcClientSecret").value = window.currentOIDCConfig.clientSecret; document.getElementById("oidcRedirectUri").value = window.currentOIDCConfig.redirectUri; document.getElementById("globalOtpauthUrl").value = window.currentOIDCConfig.globalOtpauthUrl || 'otpauth://totp/FileRise?issuer=FileRise'; document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true; document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true; document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true; adminModal.style.display = "flex"; } }) .catch(() => { let adminModal = document.getElementById("adminPanelModal"); if (adminModal) { adminModal.style.backgroundColor = "rgba(0,0,0,0.5)"; const modalContent = adminModal.querySelector(".modal-content"); if (modalContent) { modalContent.style.background = "#fff"; modalContent.style.color = "#000"; modalContent.style.border = "1px solid #ccc"; } document.getElementById("oidcProviderUrl").value = window.currentOIDCConfig.providerUrl; document.getElementById("oidcClientId").value = window.currentOIDCConfig.clientId; document.getElementById("oidcClientSecret").value = window.currentOIDCConfig.clientSecret; document.getElementById("oidcRedirectUri").value = window.currentOIDCConfig.redirectUri; document.getElementById("globalOtpauthUrl").value = window.currentOIDCConfig.globalOtpauthUrl || 'otpauth://totp/FileRise?issuer=FileRise'; document.getElementById("disableFormLogin").checked = localStorage.getItem("disableFormLogin") === "true"; document.getElementById("disableBasicAuth").checked = localStorage.getItem("disableBasicAuth") === "true"; document.getElementById("disableOIDCLogin").checked = localStorage.getItem("disableOIDCLogin") === "true"; adminModal.style.display = "flex"; } else { openAdminPanel(); } }); } export function closeAdminPanel() { const adminModal = document.getElementById("adminPanelModal"); if (adminModal) adminModal.style.display = "none"; } // --- New: User Permissions Modal --- export function openUserPermissionsModal() { let userPermissionsModal = document.getElementById("userPermissionsModal"); const isDarkMode = document.body.classList.contains("dark-mode"); const overlayBackground = isDarkMode ? "rgba(0,0,0,0.7)" : "rgba(0,0,0,0.3)"; const modalContentStyles = ` background: ${isDarkMode ? "#2c2c2c" : "#fff"}; color: ${isDarkMode ? "#e0e0e0" : "#000"}; padding: 20px; max-width: 500px; width: 90%; border-radius: 8px; position: relative; `; if (!userPermissionsModal) { userPermissionsModal = document.createElement("div"); userPermissionsModal.id = "userPermissionsModal"; userPermissionsModal.style.cssText = ` position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: ${overlayBackground}; display: flex; justify-content: center; align-items: center; z-index: 3500; `; userPermissionsModal.innerHTML = ` `; document.body.appendChild(userPermissionsModal); document.getElementById("closeUserPermissionsModal").addEventListener("click", () => { userPermissionsModal.style.display = "none"; }); document.getElementById("cancelUserPermissionsBtn").addEventListener("click", () => { userPermissionsModal.style.display = "none"; }); document.getElementById("saveUserPermissionsBtn").addEventListener("click", () => { // Collect permissions data from each user row. const rows = userPermissionsModal.querySelectorAll(".user-permission-row"); const permissionsData = []; rows.forEach(row => { const username = row.getAttribute("data-username"); const folderOnlyCheckbox = row.querySelector("input[data-permission='folderOnly']"); const readOnlyCheckbox = row.querySelector("input[data-permission='readOnly']"); const disableUploadCheckbox = row.querySelector("input[data-permission='disableUpload']"); permissionsData.push({ username, folderOnly: folderOnlyCheckbox.checked, readOnly: readOnlyCheckbox.checked, disableUpload: disableUploadCheckbox.checked }); }); // Send the permissionsData to the server. sendRequest("updateUserPermissions.php", "POST", { permissions: permissionsData }, { "X-CSRF-Token": window.csrfToken }) .then(response => { if (response.success) { showToast("User permissions updated successfully."); userPermissionsModal.style.display = "none"; } else { showToast("Error updating permissions: " + (response.error || "Unknown error")); } }) .catch(() => { showToast("Error updating permissions."); }); }); } else { userPermissionsModal.style.display = "flex"; } // Load the list of users into the modal. loadUserPermissionsList(); } function loadUserPermissionsList() { const listContainer = document.getElementById("userPermissionsList"); if (!listContainer) return; listContainer.innerHTML = ""; // First, fetch the current permissions from the server. fetch("getUserPermissions.php", { credentials: "include" }) .then(response => response.json()) .then(permissionsData => { // Then, fetch the list of users. return fetch("getUsers.php", { credentials: "include" }) .then(response => response.json()) .then(usersData => { const users = Array.isArray(usersData) ? usersData : (usersData.users || []); if (users.length === 0) { listContainer.innerHTML = "

No users found.

"; return; } users.forEach(user => { // Skip admin users. if ((user.role && user.role === "1") || user.username.toLowerCase() === "admin") return; // Use stored permissions if available; otherwise fall back to localStorage defaults. const defaultPerm = { folderOnly: localStorage.getItem("folderOnly") === "true", readOnly: localStorage.getItem("readOnly") === "true", disableUpload: localStorage.getItem("disableUpload") === "true" }; const userPerm = (permissionsData && typeof permissionsData === "object" && permissionsData[user.username]) || defaultPerm; // Create a row for the user. const row = document.createElement("div"); row.classList.add("user-permission-row"); row.setAttribute("data-username", user.username); row.style.padding = "10px 0"; row.innerHTML = `
${user.username}

`; listContainer.appendChild(row); }); }); }) .catch(() => { listContainer.innerHTML = "

Error loading users.

"; }); }