style: apply linter suggestions
This commit is contained in:
@@ -14,8 +14,8 @@ function t(key, params) {
|
||||
/** Escape a string for safe embedding inside a JS string literal within an HTML attribute. */
|
||||
function _escJs(s) {
|
||||
if (typeof s !== 'string') return '';
|
||||
return s.replace(/[^\w .\-]/g, function (c) {
|
||||
return '\\x' + c.charCodeAt(0).toString(16).padStart(2, '0');
|
||||
return s.replace(/[^\w .-]/g, (c) => {
|
||||
return `\\x${c.charCodeAt(0).toString(16).padStart(2, '0')}`;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ function formatBytes(bytes) {
|
||||
const k = 1024,
|
||||
sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
||||
return `${parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;
|
||||
}
|
||||
|
||||
function timeAgo(dateStr) {
|
||||
@@ -63,11 +63,11 @@ function timeAgo(dateStr) {
|
||||
|
||||
/* ── Custom confirm modal ── */
|
||||
function showConfirm(message) {
|
||||
return new Promise(function (resolve) {
|
||||
var overlay = document.getElementById('confirm-modal');
|
||||
var msgEl = document.getElementById('confirm-message');
|
||||
var yesBtn = document.getElementById('confirm-yes');
|
||||
var noBtn = document.getElementById('confirm-cancel');
|
||||
return new Promise((resolve) => {
|
||||
const overlay = document.getElementById('confirm-modal');
|
||||
const msgEl = document.getElementById('confirm-message');
|
||||
const yesBtn = document.getElementById('confirm-yes');
|
||||
const noBtn = document.getElementById('confirm-cancel');
|
||||
msgEl.textContent = message;
|
||||
overlay.classList.remove('hidden');
|
||||
overlay.classList.add('show-flex');
|
||||
@@ -100,10 +100,10 @@ let activeTabName = 'dashboard';
|
||||
|
||||
function switchTab(name, el) {
|
||||
if (name === activeTabName) return;
|
||||
var oldTab = document.getElementById('tab-' + activeTabName);
|
||||
var newTab = document.getElementById('tab-' + name);
|
||||
var oldTab = document.getElementById(`tab-${activeTabName}`);
|
||||
var newTab = document.getElementById(`tab-${name}`);
|
||||
|
||||
document.querySelectorAll('.admin-tab').forEach(function (b) {
|
||||
document.querySelectorAll('.admin-tab').forEach((b) => {
|
||||
b.classList.remove('active');
|
||||
});
|
||||
if (el) el.classList.add('active');
|
||||
@@ -138,7 +138,7 @@ function switchTab(name, el) {
|
||||
|
||||
async function loadDashboard() {
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/dashboard', {
|
||||
const resp = await fetch(`${API}/admin/dashboard`, {
|
||||
headers: headers(),
|
||||
credentials: 'same-origin'
|
||||
});
|
||||
@@ -147,13 +147,13 @@ async function loadDashboard() {
|
||||
document.getElementById('ds-total-users').textContent = d.total_users;
|
||||
document.getElementById('ds-active-users').textContent = d.active_users;
|
||||
document.getElementById('ds-admin-users').textContent = d.admin_users;
|
||||
document.getElementById('ds-version').textContent = 'v' + d.server_version;
|
||||
document.getElementById('ds-version').textContent = `v${d.server_version}`;
|
||||
document.getElementById('ds-used').textContent = formatBytes(d.total_used_bytes);
|
||||
document.getElementById('ds-quota').textContent = formatBytes(d.total_quota_bytes);
|
||||
document.getElementById('ds-usage-pct').textContent = d.storage_usage_percent.toFixed(1) + '%';
|
||||
document.getElementById('ds-usage-pct').textContent = `${d.storage_usage_percent.toFixed(1)}%`;
|
||||
const bar = document.getElementById('ds-bar');
|
||||
bar.style.width = Math.min(d.storage_usage_percent, 100) + '%';
|
||||
bar.className = 'progress-fill ' + (d.storage_usage_percent > 90 ? 'red' : d.storage_usage_percent > 70 ? 'orange' : 'green');
|
||||
bar.style.width = `${Math.min(d.storage_usage_percent, 100)}%`;
|
||||
bar.className = `progress-fill ${d.storage_usage_percent > 90 ? 'red' : d.storage_usage_percent > 70 ? 'orange' : 'green'}`;
|
||||
document.getElementById('ds-auth').textContent = d.auth_enabled ? t('admin.enabled') : t('admin.disabled');
|
||||
document.getElementById('ds-oidc').textContent = d.oidc_configured ? t('admin.active') : t('admin.off');
|
||||
document.getElementById('ds-quotas-flag').textContent = d.quotas_enabled ? t('admin.enabled') : t('admin.disabled');
|
||||
@@ -179,10 +179,9 @@ async function loadDashboard() {
|
||||
|
||||
async function loadUsers() {
|
||||
const tbody = document.getElementById('users-tbody');
|
||||
tbody.innerHTML =
|
||||
'<tr><td colspan="7" class="table-loading-cell"><i class="fas fa-spinner fa-spin"></i> ' + escapeHtml(t('admin.loading_users')) + '</td></tr>';
|
||||
tbody.innerHTML = `<tr><td colspan="7" class="table-loading-cell"><i class="fas fa-spinner fa-spin"></i> ${escapeHtml(t('admin.loading_users'))}</td></tr>`;
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/users?limit=' + PAGE_SIZE + '&offset=' + usersPage * PAGE_SIZE, {
|
||||
const resp = await fetch(`${API}/admin/users?limit=${PAGE_SIZE}&offset=${usersPage * PAGE_SIZE}`, {
|
||||
headers: headers(),
|
||||
credentials: 'same-origin'
|
||||
});
|
||||
@@ -197,7 +196,7 @@ async function loadUsers() {
|
||||
totalUsers = data.total;
|
||||
const users = data.users;
|
||||
if (users.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="7" class="table-status-empty">' + escapeHtml(t('admin.no_users_found')) + '</td></tr>';
|
||||
tbody.innerHTML = `<tr><td colspan="7" class="table-status-empty">${escapeHtml(t('admin.no_users_found'))}</td></tr>`;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -207,8 +206,8 @@ async function loadUsers() {
|
||||
const quotaColor = quotaPct > 90 ? 'red' : quotaPct > 70 ? 'orange' : 'green';
|
||||
const quotaText =
|
||||
u.storage_quota_bytes > 0
|
||||
? formatBytes(u.storage_used_bytes) + ' / ' + formatBytes(u.storage_quota_bytes)
|
||||
: formatBytes(u.storage_used_bytes) + ' / ∞';
|
||||
? `${formatBytes(u.storage_used_bytes)} / ${formatBytes(u.storage_quota_bytes)}`
|
||||
: `${formatBytes(u.storage_used_bytes)} / ∞`;
|
||||
const isSelf = u.id === currentAdminId;
|
||||
const isOidc = u.auth_provider && u.auth_provider !== 'local';
|
||||
const authBadge = isOidc
|
||||
@@ -217,12 +216,12 @@ async function loadUsers() {
|
||||
'"><i class="fas fa-key badge-admin-icon-small"></i> ' +
|
||||
escapeHtml(u.auth_provider) +
|
||||
'</span>'
|
||||
: '<span class="badge badge-local">' + escapeHtml(t('admin.local')) + '</span>';
|
||||
: `<span class="badge badge-local">${escapeHtml(t('admin.local'))}</span>`;
|
||||
return (
|
||||
'<tr>' +
|
||||
'<td><div class="user-info"><span class="user-name">' +
|
||||
escapeHtml(u.username) +
|
||||
(isSelf ? ' <span class="user-self-badge">' + escapeHtml(t('admin.you_badge')) + '</span>' : '') +
|
||||
(isSelf ? ` <span class="user-self-badge">${escapeHtml(t('admin.you_badge'))}</span>` : '') +
|
||||
'</span><span class="user-email">' +
|
||||
escapeHtml(u.email) +
|
||||
'</span></div></td>' +
|
||||
@@ -308,15 +307,15 @@ async function loadUsers() {
|
||||
.join('');
|
||||
|
||||
// Set dynamic progress bar widths (CSP-safe via JS property)
|
||||
document.querySelectorAll('.progress-fill[data-width]').forEach(function (el) {
|
||||
el.style.width = el.dataset.width + '%';
|
||||
document.querySelectorAll('.progress-fill[data-width]').forEach((el) => {
|
||||
el.style.width = `${el.dataset.width}%`;
|
||||
el.removeAttribute('data-width');
|
||||
});
|
||||
|
||||
// Wire up admin action buttons (replaces inline onclick handlers)
|
||||
document.querySelectorAll('.admin-action-btn').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
var action = btn.dataset.action;
|
||||
document.querySelectorAll('.admin-action-btn').forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
const action = btn.dataset.action;
|
||||
if (action === 'quota') openQuotaModal(btn.dataset.uid, btn.dataset.uname, Number(btn.dataset.quota));
|
||||
else if (action === 'reset-pw') openResetPasswordModal(btn.dataset.uid, btn.dataset.uname);
|
||||
else if (action === 'toggle-role') toggleRole(btn.dataset.uid, btn.dataset.role);
|
||||
@@ -356,7 +355,7 @@ async function toggleRole(userId, currentRole) {
|
||||
const ok = await showConfirm(t('admin.confirm_role_change', { role: newRole }));
|
||||
if (!ok) return;
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/users/' + userId + '/role', {
|
||||
const resp = await fetch(`${API}/admin/users/${userId}/role`, {
|
||||
method: 'PUT',
|
||||
headers: headers(),
|
||||
credentials: 'same-origin',
|
||||
@@ -377,7 +376,7 @@ async function toggleActive(userId, currentActive) {
|
||||
const ok = await showConfirm(msg);
|
||||
if (!ok) return;
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/users/' + userId + '/active', {
|
||||
const resp = await fetch(`${API}/admin/users/${userId}/active`, {
|
||||
method: 'PUT',
|
||||
headers: headers(),
|
||||
credentials: 'same-origin',
|
||||
@@ -397,7 +396,7 @@ async function deleteUser(userId, username) {
|
||||
const ok = await showConfirm(t('admin.confirm_delete_user', { name: username }));
|
||||
if (!ok) return;
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/users/' + userId, {
|
||||
const resp = await fetch(`${API}/admin/users/${userId}`, {
|
||||
method: 'DELETE',
|
||||
headers: headers(),
|
||||
credentials: 'same-origin'
|
||||
@@ -429,10 +428,10 @@ function closeQuotaModal() {
|
||||
|
||||
async function saveQuota() {
|
||||
const val = parseFloat(document.getElementById('qm-value').value) || 0;
|
||||
const unit = parseInt(document.getElementById('qm-unit').value);
|
||||
const unit = parseInt(document.getElementById('qm-unit').value, 10);
|
||||
const bytes = Math.round(val * unit);
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/users/' + quotaUserId + '/quota', {
|
||||
const resp = await fetch(`${API}/admin/users/${quotaUserId}/quota`, {
|
||||
method: 'PUT',
|
||||
headers: headers(),
|
||||
credentials: 'same-origin',
|
||||
@@ -473,7 +472,7 @@ async function submitCreateUser() {
|
||||
const email = document.getElementById('cu-email').value.trim() || null;
|
||||
const role = document.getElementById('cu-role').value;
|
||||
const quotaVal = parseFloat(document.getElementById('cu-quota-value').value) || 0;
|
||||
const quotaUnit = parseInt(document.getElementById('cu-quota-unit').value);
|
||||
const quotaUnit = parseInt(document.getElementById('cu-quota-unit').value, 10);
|
||||
const quotaBytes = Math.round(quotaVal * quotaUnit);
|
||||
|
||||
const errorEl = document.getElementById('cu-error');
|
||||
@@ -490,9 +489,9 @@ async function submitCreateUser() {
|
||||
|
||||
const btn = document.getElementById('cu-submit');
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + escapeHtml(t('admin.creating'));
|
||||
btn.innerHTML = `<i class="fas fa-spinner fa-spin"></i> ${escapeHtml(t('admin.creating'))}`;
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/users', {
|
||||
const resp = await fetch(`${API}/admin/users`, {
|
||||
method: 'POST',
|
||||
headers: headers(),
|
||||
credentials: 'same-origin',
|
||||
@@ -518,7 +517,7 @@ async function submitCreateUser() {
|
||||
errorEl.className = 'alert alert-error';
|
||||
}
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-user-plus"></i> ' + escapeHtml(t('admin.create_user'));
|
||||
btn.innerHTML = `<i class="fas fa-user-plus"></i> ${escapeHtml(t('admin.create_user'))}`;
|
||||
}
|
||||
|
||||
let resetPwUserId = '';
|
||||
@@ -546,9 +545,9 @@ async function submitResetPassword() {
|
||||
|
||||
const btn = document.getElementById('rp-submit');
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + escapeHtml(t('admin.resetting'));
|
||||
btn.innerHTML = `<i class="fas fa-spinner fa-spin"></i> ${escapeHtml(t('admin.resetting'))}`;
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/users/' + resetPwUserId + '/password', {
|
||||
const resp = await fetch(`${API}/admin/users/${resetPwUserId}/password`, {
|
||||
method: 'PUT',
|
||||
headers: headers(),
|
||||
credentials: 'same-origin',
|
||||
@@ -566,14 +565,14 @@ async function submitResetPassword() {
|
||||
errorEl.className = 'alert alert-error';
|
||||
}
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-save"></i> ' + escapeHtml(t('admin.reset_btn'));
|
||||
btn.innerHTML = `<i class="fas fa-save"></i> ${escapeHtml(t('admin.reset_btn'))}`;
|
||||
}
|
||||
|
||||
async function toggleRegistration(enabled) {
|
||||
if (enabled) hideElement('registration-warning');
|
||||
else showElement('registration-warning', 'flex');
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/settings/registration', {
|
||||
const resp = await fetch(`${API}/admin/settings/registration`, {
|
||||
method: 'PUT',
|
||||
headers: headers(),
|
||||
credentials: 'same-origin',
|
||||
@@ -606,7 +605,7 @@ document.getElementById('disable-password').addEventListener('change', function
|
||||
function showOidcStatus(msg, type) {
|
||||
const el = document.getElementById('oidc-status');
|
||||
el.textContent = msg;
|
||||
el.className = 'alert alert-' + type;
|
||||
el.className = `alert alert-${type}`;
|
||||
}
|
||||
|
||||
function copyCallback() {
|
||||
@@ -622,10 +621,10 @@ async function testConnection() {
|
||||
}
|
||||
const btn = document.getElementById('discover-btn');
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + escapeHtml(t('admin.discovering'));
|
||||
btn.innerHTML = `<i class="fas fa-spinner fa-spin"></i> ${escapeHtml(t('admin.discovering'))}`;
|
||||
const resultDiv = document.getElementById('discovery-result');
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/settings/oidc/test', {
|
||||
const resp = await fetch(`${API}/admin/settings/oidc/test`, {
|
||||
method: 'POST',
|
||||
headers: headers(),
|
||||
credentials: 'same-origin',
|
||||
@@ -644,19 +643,19 @@ async function testConnection() {
|
||||
if (!document.getElementById('provider-name').value && r.provider_name_suggestion)
|
||||
document.getElementById('provider-name').value = r.provider_name_suggestion;
|
||||
} else {
|
||||
resultDiv.innerHTML = '<div class="discovery-result fail"><strong><i class="fas fa-times-circle"></i> ' + escapeHtml(r.message) + '</strong></div>';
|
||||
resultDiv.innerHTML = `<div class="discovery-result fail"><strong><i class="fas fa-times-circle"></i> ${escapeHtml(r.message)}</strong></div>`;
|
||||
}
|
||||
} catch (e) {
|
||||
resultDiv.innerHTML = '<div class="discovery-result fail"><i class="fas fa-times-circle"></i> Error: ' + escapeHtml(e.message) + '</div>';
|
||||
resultDiv.innerHTML = `<div class="discovery-result fail"><i class="fas fa-times-circle"></i> Error: ${escapeHtml(e.message)}</div>`;
|
||||
}
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-search"></i> ' + escapeHtml(t('admin.auto_discover'));
|
||||
btn.innerHTML = `<i class="fas fa-search"></i> ${escapeHtml(t('admin.auto_discover'))}`;
|
||||
}
|
||||
|
||||
async function saveOidcSettings() {
|
||||
const btn = document.getElementById('save-btn');
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + escapeHtml(t('admin.saving'));
|
||||
btn.innerHTML = `<i class="fas fa-spinner fa-spin"></i> ${escapeHtml(t('admin.saving'))}`;
|
||||
const body = {
|
||||
enabled: document.getElementById('oidc-enabled').checked,
|
||||
issuer_url: document.getElementById('issuer-url').value.trim(),
|
||||
@@ -669,7 +668,7 @@ async function saveOidcSettings() {
|
||||
provider_name: document.getElementById('provider-name').value.trim() || null
|
||||
};
|
||||
try {
|
||||
const resp = await fetch(API + '/admin/settings/oidc', {
|
||||
const resp = await fetch(`${API}/admin/settings/oidc`, {
|
||||
method: 'PUT',
|
||||
headers: headers(),
|
||||
credentials: 'same-origin',
|
||||
@@ -681,18 +680,18 @@ async function saveOidcSettings() {
|
||||
loadDashboard();
|
||||
} else {
|
||||
const e = await resp.json().catch(() => ({}));
|
||||
showOidcStatus('Error: ' + (e.message || resp.statusText), 'error');
|
||||
showOidcStatus(`Error: ${e.message || resp.statusText}`, 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
showOidcStatus(t('admin.error_network', { message: e.message }), 'error');
|
||||
}
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-save"></i> ' + escapeHtml(t('admin.save_btn'));
|
||||
btn.innerHTML = `<i class="fas fa-save"></i> ${escapeHtml(t('admin.save_btn'))}`;
|
||||
}
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
const me = await fetch(API + '/auth/me', {
|
||||
const me = await fetch(`${API}/auth/me`, {
|
||||
headers: headers(),
|
||||
credentials: 'same-origin'
|
||||
});
|
||||
@@ -707,7 +706,7 @@ async function init() {
|
||||
}
|
||||
currentAdminId = user.id;
|
||||
|
||||
const oidcResp = await fetch(API + '/admin/settings/oidc', {
|
||||
const oidcResp = await fetch(`${API}/admin/settings/oidc`, {
|
||||
headers: headers(),
|
||||
credentials: 'same-origin'
|
||||
});
|
||||
@@ -728,7 +727,7 @@ async function init() {
|
||||
document.getElementById('callback-url').textContent = s.callback_url;
|
||||
if (s.client_secret_set) showElement('secret-hint');
|
||||
(s.env_overrides || []).forEach((field) => {
|
||||
const badge = document.getElementById('badge-' + field);
|
||||
const badge = document.getElementById(`badge-${field}`);
|
||||
if (badge) badge.innerHTML = '<span class="badge badge-env">ENV</span>';
|
||||
});
|
||||
}
|
||||
@@ -748,14 +747,14 @@ function showAccessDenied() {
|
||||
}
|
||||
|
||||
/* ── Apply i18n when translations load / change ── */
|
||||
document.addEventListener('translationsLoaded', function () {
|
||||
if (window.i18n && window.i18n.translatePage) window.i18n.translatePage();
|
||||
document.addEventListener('translationsLoaded', () => {
|
||||
if (window.i18n?.translatePage) window.i18n.translatePage();
|
||||
// Re-render dynamic content that uses t()
|
||||
loadDashboard();
|
||||
if (activeTabName === 'users') loadUsers();
|
||||
});
|
||||
document.addEventListener('localeChanged', function () {
|
||||
if (window.i18n && window.i18n.translatePage) window.i18n.translatePage();
|
||||
document.addEventListener('localeChanged', () => {
|
||||
if (window.i18n?.translatePage) window.i18n.translatePage();
|
||||
loadDashboard();
|
||||
if (activeTabName === 'users') loadUsers();
|
||||
});
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// device-verify.js — Extracted from inline <script> in device-verify.html
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
(() => {
|
||||
var API_BASE = window.location.origin;
|
||||
var codeInput = document.getElementById('user-code');
|
||||
var deviceInfo = document.getElementById('device-info');
|
||||
@@ -20,11 +18,11 @@
|
||||
}
|
||||
|
||||
// Auto-insert hyphen and lookup on input
|
||||
codeInput.addEventListener('input', function (e) {
|
||||
var val = e.target.value.toUpperCase().replace(/[^A-Z0-9\-]/g, '');
|
||||
codeInput.addEventListener('input', (e) => {
|
||||
var val = e.target.value.toUpperCase().replace(/[^A-Z0-9-]/g, '');
|
||||
// Auto-insert hyphen after 4 chars
|
||||
if (val.length === 4 && val.indexOf('-') === -1) {
|
||||
val = val + '-';
|
||||
val = `${val}-`;
|
||||
}
|
||||
e.target.value = val;
|
||||
errorText.classList.add('hidden');
|
||||
@@ -32,7 +30,7 @@
|
||||
// Debounce lookup
|
||||
clearTimeout(debounceTimer);
|
||||
if (val.length >= 9) {
|
||||
debounceTimer = setTimeout(function () {
|
||||
debounceTimer = setTimeout(() => {
|
||||
lookupCode(val);
|
||||
}, 300);
|
||||
} else {
|
||||
@@ -42,16 +40,16 @@
|
||||
});
|
||||
|
||||
// Wire up approve / deny buttons (replaces inline onclick)
|
||||
btnApprove.addEventListener('click', function () {
|
||||
btnApprove.addEventListener('click', () => {
|
||||
handleAction('approve');
|
||||
});
|
||||
btnDeny.addEventListener('click', function () {
|
||||
btnDeny.addEventListener('click', () => {
|
||||
handleAction('deny');
|
||||
});
|
||||
|
||||
async function lookupCode(code) {
|
||||
try {
|
||||
var resp = await fetch(API_BASE + '/api/auth/device/verify?code=' + encodeURIComponent(code), {
|
||||
const resp = await fetch(`${API_BASE}/api/auth/device/verify?code=${encodeURIComponent(code)}`, {
|
||||
credentials: 'same-origin'
|
||||
});
|
||||
if (resp.status === 401) {
|
||||
@@ -59,7 +57,7 @@
|
||||
return;
|
||||
}
|
||||
if (!resp.ok) throw new Error('Lookup failed');
|
||||
var data = await resp.json();
|
||||
const data = await resp.json();
|
||||
|
||||
if (data.valid) {
|
||||
currentCode = code;
|
||||
@@ -83,7 +81,7 @@
|
||||
btnDeny.disabled = true;
|
||||
|
||||
try {
|
||||
var resp = await fetch(API_BASE + '/api/auth/device/verify', {
|
||||
const resp = await fetch(`${API_BASE}/api/auth/device/verify`, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: Object.assign({ 'Content-Type': 'application/json' }, getCsrfHeaders()),
|
||||
@@ -91,7 +89,7 @@
|
||||
});
|
||||
|
||||
if (!resp.ok) {
|
||||
var err = await resp.json().catch(function () {
|
||||
const err = await resp.json().catch(() => {
|
||||
return {};
|
||||
});
|
||||
throw new Error(err.message || 'Action failed');
|
||||
|
||||
@@ -11,7 +11,7 @@ switch (errorType) {
|
||||
errorTitle.textContent = 'Login Failed';
|
||||
errorMessage.textContent = 'Invalid username or password. Please check your credentials and try again.';
|
||||
errorAction.textContent = 'Try Again';
|
||||
errorAction.addEventListener('click', function () {
|
||||
errorAction.addEventListener('click', () => {
|
||||
history.back();
|
||||
});
|
||||
break;
|
||||
@@ -19,7 +19,7 @@ switch (errorType) {
|
||||
errorTitle.textContent = 'Session Expired';
|
||||
errorMessage.textContent = 'Your session has expired. Please try again.';
|
||||
errorAction.textContent = 'Close Window';
|
||||
errorAction.addEventListener('click', function () {
|
||||
errorAction.addEventListener('click', () => {
|
||||
window.close();
|
||||
});
|
||||
break;
|
||||
@@ -27,7 +27,7 @@ switch (errorType) {
|
||||
errorTitle.textContent = 'Not Found';
|
||||
errorMessage.textContent = 'The requested page was not found.';
|
||||
errorAction.textContent = 'Close Window';
|
||||
errorAction.addEventListener('click', function () {
|
||||
errorAction.addEventListener('click', () => {
|
||||
window.close();
|
||||
});
|
||||
break;
|
||||
@@ -35,7 +35,7 @@ switch (errorType) {
|
||||
errorTitle.textContent = 'Error';
|
||||
errorMessage.textContent = 'An unexpected error occurred. Please try again.';
|
||||
errorAction.textContent = 'Close Window';
|
||||
errorAction.addEventListener('click', function () {
|
||||
errorAction.addEventListener('click', () => {
|
||||
window.close();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,22 +6,22 @@ if (!/^[0-9a-fA-F]+$/.test(token)) {
|
||||
document.body.innerHTML = '<p>Invalid session token.</p>';
|
||||
throw new Error('Invalid token format');
|
||||
}
|
||||
document.getElementById('login-flow-form').action = '/login/v2/flow/' + token;
|
||||
document.getElementById('login-flow-form').action = `/login/v2/flow/${token}`;
|
||||
|
||||
// Check if OIDC is available and configure SSO button
|
||||
(async function () {
|
||||
(async () => {
|
||||
try {
|
||||
var resp = await fetch('/api/auth/oidc/providers');
|
||||
const resp = await fetch('/api/auth/oidc/providers');
|
||||
if (!resp.ok) return;
|
||||
var info = await resp.json();
|
||||
const info = await resp.json();
|
||||
if (!info.enabled) return;
|
||||
|
||||
// Show OIDC section
|
||||
document.getElementById('oidc-section').classList.remove('hidden');
|
||||
|
||||
// Update button text with provider name
|
||||
var btn = document.getElementById('oidc-button');
|
||||
btn.textContent = 'Sign in with ' + (info.provider_name || 'SSO');
|
||||
const btn = document.getElementById('oidc-button');
|
||||
btn.textContent = `Sign in with ${info.provider_name || 'SSO'}`;
|
||||
|
||||
// If password login is disabled, hide the password form
|
||||
if (!info.password_login_enabled) {
|
||||
@@ -29,10 +29,10 @@ document.getElementById('login-flow-form').action = '/login/v2/flow/' + token;
|
||||
}
|
||||
|
||||
// SSO button redirects to the OIDC flow for this NC token
|
||||
btn.addEventListener('click', function () {
|
||||
window.location.href = '/login/v2/flow/' + token + '/oidc';
|
||||
btn.addEventListener('click', () => {
|
||||
window.location.href = `/login/v2/flow/${token}/oidc`;
|
||||
});
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
// OIDC not available — silently keep password-only mode
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
document.getElementById('close-window-btn').addEventListener('click', function () {
|
||||
document.getElementById('close-window-btn').addEventListener('click', () => {
|
||||
window.close();
|
||||
});
|
||||
// Auto-close after 3 seconds
|
||||
setTimeout(function () {
|
||||
setTimeout(() => {
|
||||
window.close();
|
||||
}, 3000);
|
||||
|
||||
@@ -15,7 +15,7 @@ function formatBytes(bytes) {
|
||||
const k = 1024,
|
||||
sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
||||
return `${parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;
|
||||
}
|
||||
|
||||
function timeAgo(dateStr) {
|
||||
@@ -32,7 +32,7 @@ function timeAgo(dateStr) {
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
const resp = await fetch(API + '/auth/me', {
|
||||
const resp = await fetch(`${API}/auth/me`, {
|
||||
headers: headers(),
|
||||
credentials: 'same-origin'
|
||||
});
|
||||
@@ -50,10 +50,10 @@ async function init() {
|
||||
const badge = document.getElementById('p-role-badge');
|
||||
if (user.role === 'admin') {
|
||||
badge.className = 'role-badge role-badge-admin';
|
||||
badge.innerHTML = '<i class="fas fa-shield-alt"></i> ' + t('profile.role_admin');
|
||||
badge.innerHTML = `<i class="fas fa-shield-alt"></i> ${t('profile.role_admin')}`;
|
||||
} else {
|
||||
badge.className = 'role-badge role-badge-user';
|
||||
badge.innerHTML = '<i class="fas fa-user"></i> ' + t('profile.role_user');
|
||||
badge.innerHTML = `<i class="fas fa-user"></i> ${t('profile.role_user')}`;
|
||||
}
|
||||
|
||||
document.getElementById('p-detail-username').textContent = user.username;
|
||||
@@ -67,12 +67,12 @@ async function init() {
|
||||
|
||||
document.getElementById('p-storage-used').textContent = formatBytes(used);
|
||||
document.getElementById('p-storage-quota').textContent = quota > 0 ? formatBytes(quota) : '∞';
|
||||
document.getElementById('p-storage-pct').textContent = quota > 0 ? pct + '%' : '—';
|
||||
document.getElementById('p-storage-pct').textContent = quota > 0 ? `${pct}%` : '—';
|
||||
|
||||
const bar = document.getElementById('p-storage-bar');
|
||||
bar.style.width = pct + '%';
|
||||
bar.className = 'storage-fill ' + (pct > 90 ? 'red' : pct > 70 ? 'orange' : 'green');
|
||||
document.getElementById('p-storage-text').textContent = formatBytes(used) + ' / ' + (quota > 0 ? formatBytes(quota) : t('profile.unlimited'));
|
||||
bar.style.width = `${pct}%`;
|
||||
bar.className = `storage-fill ${pct > 90 ? 'red' : pct > 70 ? 'orange' : 'green'}`;
|
||||
document.getElementById('p-storage-text').textContent = `${formatBytes(used)} / ${quota > 0 ? formatBytes(quota) : t('profile.unlimited')}`;
|
||||
|
||||
if (user.auth_provider && user.auth_provider !== 'local') {
|
||||
document.getElementById('password-section').classList.add('hidden');
|
||||
@@ -81,7 +81,7 @@ async function init() {
|
||||
loadAppPasswords();
|
||||
|
||||
try {
|
||||
const oidcResp = await fetch(API + '/auth/oidc/providers', {
|
||||
const oidcResp = await fetch(`${API}/auth/oidc/providers`, {
|
||||
credentials: 'same-origin'
|
||||
});
|
||||
if (oidcResp.ok) {
|
||||
@@ -90,7 +90,7 @@ async function init() {
|
||||
document.getElementById('password-section').classList.add('hidden');
|
||||
}
|
||||
}
|
||||
} catch (oidcErr) {}
|
||||
} catch (_oidcErr) {}
|
||||
|
||||
document.getElementById('loading').classList.add('hidden');
|
||||
document.getElementById('main-content').classList.remove('hidden');
|
||||
@@ -113,23 +113,21 @@ async function changePassword(e) {
|
||||
const statusEl = document.getElementById('pw-status');
|
||||
|
||||
if (newPw !== confirmPw) {
|
||||
statusEl.innerHTML =
|
||||
'<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ' + escapeHtml(t('profile.passwords_no_match')) + '</div>';
|
||||
statusEl.innerHTML = `<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ${escapeHtml(t('profile.passwords_no_match'))}</div>`;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newPw.length < 8) {
|
||||
statusEl.innerHTML =
|
||||
'<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ' + escapeHtml(t('profile.password_too_short')) + '</div>';
|
||||
statusEl.innerHTML = `<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ${escapeHtml(t('profile.password_too_short'))}</div>`;
|
||||
return false;
|
||||
}
|
||||
|
||||
const btn = document.getElementById('pw-submit');
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + escapeHtml(t('profile.updating'));
|
||||
btn.innerHTML = `<i class="fas fa-spinner fa-spin"></i> ${escapeHtml(t('profile.updating'))}`;
|
||||
|
||||
try {
|
||||
const resp = await fetch(API + '/auth/change-password', {
|
||||
const resp = await fetch(`${API}/auth/change-password`, {
|
||||
method: 'PUT',
|
||||
headers: headers(),
|
||||
credentials: 'same-origin',
|
||||
@@ -140,7 +138,7 @@ async function changePassword(e) {
|
||||
});
|
||||
|
||||
if (resp.ok) {
|
||||
statusEl.innerHTML = '<div class="alert alert-success"><i class="fas fa-check-circle"></i> ' + escapeHtml(t('profile.password_updated')) + '</div>';
|
||||
statusEl.innerHTML = `<div class="alert alert-success"><i class="fas fa-check-circle"></i> ${escapeHtml(t('profile.password_updated'))}</div>`;
|
||||
document.getElementById('password-form').reset();
|
||||
} else {
|
||||
const err = await resp.json().catch(() => ({}));
|
||||
@@ -157,7 +155,7 @@ async function changePassword(e) {
|
||||
}
|
||||
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-save"></i> ' + escapeHtml(t('profile.update_password'));
|
||||
btn.innerHTML = `<i class="fas fa-save"></i> ${escapeHtml(t('profile.update_password'))}`;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -193,7 +191,7 @@ function renderPwRow(pw) {
|
||||
btn.className = 'btn btn-danger-sm';
|
||||
btn.innerHTML = '<i class="fas fa-trash"></i>';
|
||||
btn.title = t('profile.revoke_title');
|
||||
btn.addEventListener('click', function () {
|
||||
btn.addEventListener('click', () => {
|
||||
revokeAppPassword(pw.id, pw.label);
|
||||
});
|
||||
actions.appendChild(btn);
|
||||
@@ -204,7 +202,7 @@ function renderPwRow(pw) {
|
||||
|
||||
async function loadAppPasswords() {
|
||||
try {
|
||||
const resp = await fetch(API + '/auth/app-passwords', {
|
||||
const resp = await fetch(`${API}/auth/app-passwords`, {
|
||||
headers: headers(),
|
||||
credentials: 'same-origin'
|
||||
});
|
||||
@@ -214,7 +212,7 @@ async function loadAppPasswords() {
|
||||
}
|
||||
const data = await resp.json();
|
||||
const passwords = data.app_passwords || data;
|
||||
const userPws = passwords.filter(function (pw) {
|
||||
const userPws = passwords.filter((pw) => {
|
||||
return !isAutoPassword(pw);
|
||||
});
|
||||
const autoPws = passwords.filter(isAutoPassword);
|
||||
@@ -264,17 +262,16 @@ async function createAppPassword() {
|
||||
const btn = document.getElementById('app-pw-generate');
|
||||
|
||||
if (!label) {
|
||||
statusEl.innerHTML =
|
||||
'<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ' + escapeHtml(t('profile.error_label_required')) + '</div>';
|
||||
statusEl.innerHTML = `<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ${escapeHtml(t('profile.error_label_required'))}</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + escapeHtml(t('profile.generating'));
|
||||
btn.innerHTML = `<i class="fas fa-spinner fa-spin"></i> ${escapeHtml(t('profile.generating'))}`;
|
||||
statusEl.innerHTML = '';
|
||||
|
||||
try {
|
||||
const resp = await fetch(API + '/auth/app-passwords', {
|
||||
const resp = await fetch(`${API}/auth/app-passwords`, {
|
||||
method: 'POST',
|
||||
headers: headers(),
|
||||
credentials: 'same-origin',
|
||||
@@ -295,19 +292,19 @@ async function createAppPassword() {
|
||||
labelInput.value = '';
|
||||
loadAppPasswords();
|
||||
} catch (err) {
|
||||
statusEl.innerHTML = '<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ' + err.message + '</div>';
|
||||
statusEl.innerHTML = `<div class="alert alert-error"><i class="fas fa-exclamation-circle"></i> ${err.message}</div>`;
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-plus"></i> ' + escapeHtml(t('profile.generate'));
|
||||
btn.innerHTML = `<i class="fas fa-plus"></i> ${escapeHtml(t('profile.generate'))}`;
|
||||
}
|
||||
}
|
||||
|
||||
function copyAppPassword() {
|
||||
const pw = document.getElementById('app-pw-created-password').textContent;
|
||||
navigator.clipboard.writeText(pw).then(function () {
|
||||
navigator.clipboard.writeText(pw).then(() => {
|
||||
const btn = document.getElementById('app-pw-copy-btn');
|
||||
btn.innerHTML = '<i class="fas fa-check"></i>';
|
||||
setTimeout(function () {
|
||||
setTimeout(() => {
|
||||
btn.innerHTML = '<i class="fas fa-copy"></i>';
|
||||
}, 1500);
|
||||
});
|
||||
@@ -316,7 +313,7 @@ function copyAppPassword() {
|
||||
async function revokeAppPassword(id, label) {
|
||||
if (!confirm(t('profile.confirm_revoke', { label: label }))) return;
|
||||
try {
|
||||
const resp = await fetch(API + '/auth/app-passwords/' + encodeURIComponent(id), {
|
||||
const resp = await fetch(`${API}/auth/app-passwords/${encodeURIComponent(id)}`, {
|
||||
method: 'DELETE',
|
||||
headers: headers(),
|
||||
credentials: 'same-origin'
|
||||
@@ -348,9 +345,9 @@ document.getElementById('app-pw-copy-btn').addEventListener('click', copyAppPass
|
||||
document.getElementById('app-pw-auto-toggle').addEventListener('click', toggleAutoPasswords);
|
||||
|
||||
/* Re-render when language changes */
|
||||
window.addEventListener('translationsLoaded', function () {
|
||||
window.addEventListener('translationsLoaded', () => {
|
||||
init();
|
||||
});
|
||||
window.addEventListener('localeChanged', function () {
|
||||
window.addEventListener('localeChanged', () => {
|
||||
init();
|
||||
});
|
||||
|
||||
@@ -181,7 +181,7 @@ const sharedView = {
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (window.i18n && window.i18n.translateElement) {
|
||||
if (window.i18n?.translateElement) {
|
||||
window.i18n.translateElement(container);
|
||||
}
|
||||
},
|
||||
@@ -271,7 +271,9 @@ const sharedView = {
|
||||
option.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
// Update active state
|
||||
dropdown.querySelectorAll('.shared-select-option').forEach((o) => o.classList.remove('active'));
|
||||
dropdown.querySelectorAll('.shared-select-option').forEach((o) => {
|
||||
o.classList.remove('active');
|
||||
});
|
||||
option.classList.add('active');
|
||||
// Update label
|
||||
const label = toggle.querySelector('.shared-select-label');
|
||||
@@ -529,8 +531,8 @@ const sharedView = {
|
||||
write: permWrite ? permWrite.checked : false,
|
||||
reshare: permReshare ? permReshare.checked : false
|
||||
},
|
||||
password: enablePw && enablePw.checked && pwField && pwField.value ? pwField.value : null,
|
||||
expires_at: enableExp && enableExp.checked && expField && expField.value ? Math.floor(new Date(expField.value).getTime() / 1000) : null
|
||||
password: enablePw?.checked && pwField?.value ? pwField.value : null,
|
||||
expires_at: enableExp?.checked && expField?.value ? Math.floor(new Date(expField.value).getTime() / 1000) : null
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -588,7 +590,7 @@ const sharedView = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.fileSharing && window.fileSharing.sendShareNotification) {
|
||||
if (window.fileSharing?.sendShareNotification) {
|
||||
window.fileSharing
|
||||
.sendShareNotification(this.currentItem.url, email, message)
|
||||
.then(() => {
|
||||
@@ -600,7 +602,7 @@ const sharedView = {
|
||||
},
|
||||
|
||||
showNotification(message, type = 'success') {
|
||||
if (window.ui && window.ui.showNotification) {
|
||||
if (window.ui?.showNotification) {
|
||||
window.ui.showNotification(message, type);
|
||||
} else {
|
||||
alert(message);
|
||||
@@ -616,7 +618,7 @@ const sharedView = {
|
||||
},
|
||||
|
||||
translate(key, defaultText) {
|
||||
if (window.i18n && window.i18n.t) return window.i18n.t(key, defaultText);
|
||||
if (window.i18n?.t) return window.i18n.t(key, defaultText);
|
||||
return defaultText;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user