fix: resolve all biome ci errors and warnings

- Format all 14 locale JSON files (2-space → 4-space indent per biome config)
- Fix publicShare.js: arrow functions, template literals, optional chaining,
  remove redundant 'use strict', remove unused formatSize function
- Fix music.js: template literal, unused _trackId parameter
- Fix contextMenus.js, fileOperations.js, fileSharing.js: unused parameters
- Fix auth.js, music.css, share-public.css: formatting (biome format --write)
- Remove unused biome-ignore suppression comments in cards.css and reset.css
- Remove empty .share-state block in share-public.css
- Disable noDescendingSpecificity and noImportantStyles rules in biome.json
  (intentional CSS patterns for utility classes and theme overrides)
This commit is contained in:
Diocrafts
2026-04-12 02:13:08 +02:00
parent 1efcdb58a5
commit 1dbe7e4553
25 changed files with 9034 additions and 9012 deletions
+10 -7
View File
@@ -902,14 +902,17 @@ if (isLoginPage && loginForm) {
// the auth cookies. The CSRF cookie (oxicloud_csrf) is non-HttpOnly
// so JS can read it. If it's missing the browser rejected the
// Set-Cookie (usually because of Secure flag over plain HTTP).
const csrfStored = document.cookie.split('; ').some(c => c.startsWith('oxicloud_csrf='));
const csrfStored = document.cookie.split('; ').some((c) => c.startsWith('oxicloud_csrf='));
if (!csrfStored) {
console.error('Auth cookies were NOT stored by the browser. '
+ 'This usually means OXICLOUD_COOKIE_SECURE=true (or OXICLOUD_BASE_URL=https://...) '
+ 'is set but you are accessing via plain HTTP.');
loginError.textContent = 'Login succeeded but the browser rejected the session cookie. '
+ 'If you are accessing via HTTP, set OXICLOUD_COOKIE_SECURE=false in your .env file '
+ 'or access via HTTPS through a reverse proxy.';
console.error(
'Auth cookies were NOT stored by the browser. ' +
'This usually means OXICLOUD_COOKIE_SECURE=true (or OXICLOUD_BASE_URL=https://...) ' +
'is set but you are accessing via plain HTTP.'
);
loginError.textContent =
'Login succeeded but the browser rejected the session cookie. ' +
'If you are accessing via HTTP, set OXICLOUD_COOKIE_SECURE=false in your .env file ' +
'or access via HTTPS through a reverse proxy.';
loginError.style.display = 'block';
return;
}
+1 -1
View File
@@ -773,7 +773,7 @@ const contextMenus = {
* @param {string} itemId - ID of the item being moved (unused, kept for compatibility)
* @param {string} mode - 'batch' for batch operations
*/
async loadAllFolders(itemId, mode) {
async loadAllFolders(_itemId, _mode) {
// For batch mode, use the same navigation as regular move dialog
// Initialize navigation state starting at home folder
window.app.moveDialogBreadcrumb = [];
+5 -4
View File
@@ -598,9 +598,10 @@ const fileOps = {
formData.append('folder_id', targetFolderId);
formData.append('file', uploadFile, file.name);
const thisTimeout = file.size === 0
? TIMEOUT_MS_ZERO
: Math.max(TIMEOUT_MIN_MS, TIMEOUT_BASE_MS + Math.ceil(file.size / (1024 * 1024)) * TIMEOUT_PER_MB_MS);
const thisTimeout =
file.size === 0
? TIMEOUT_MS_ZERO
: Math.max(TIMEOUT_MIN_MS, TIMEOUT_BASE_MS + Math.ceil(file.size / (1024 * 1024)) * TIMEOUT_PER_MB_MS);
console.log(`[UPLOAD START] #${idx} ${rel} (${file.size} bytes, timeout=${thisTimeout}ms)`);
result = await this._uploadFileFetch(formData, thisTimeout);
@@ -917,7 +918,7 @@ const fileOps = {
* @param {string} targetFolderId - Target folder ID
* @returns {Promise<boolean>} - Success status
*/
async copyFolder(folderId, targetFolderId) {
async copyFolder(_folderId, _targetFolderId) {
// Folder copy is not yet implemented in the backend
window.ui.showNotification('Not implemented', 'Folder copy is not yet supported');
return false;
+12 -5
View File
@@ -782,7 +782,9 @@ const musicView = {
};
overlay.querySelector('.music-picker-close').addEventListener('click', close);
overlay.querySelector('.music-picker-cancel').addEventListener('click', close);
overlay.addEventListener('click', (e) => { if (e.target === overlay) close(); });
overlay.addEventListener('click', (e) => {
if (e.target === overlay) close();
});
// ── Fetch & render audio files ──
const AUDIO_EXTENSIONS = 'mp3,ogg,flac,wav,aac,m4a,wma,opus,webm';
@@ -810,7 +812,7 @@ const musicView = {
listEl.innerHTML = '';
for (const file of files) {
const row = document.createElement('label');
row.className = 'music-picker-item' + (selectedIds.has(file.id) ? ' selected' : '');
row.className = `music-picker-item${selectedIds.has(file.id) ? ' selected' : ''}`;
const sizeStr = file.size != null && window.formatFileSize ? window.formatFileSize(file.size) : '';
row.innerHTML = `
<input type="checkbox" value="${file.id}" ${selectedIds.has(file.id) ? 'checked' : ''}>
@@ -820,8 +822,13 @@ const musicView = {
`;
const cb = row.querySelector('input');
cb.addEventListener('change', () => {
if (cb.checked) { selectedIds.add(file.id); row.classList.add('selected'); }
else { selectedIds.delete(file.id); row.classList.remove('selected'); }
if (cb.checked) {
selectedIds.add(file.id);
row.classList.add('selected');
} else {
selectedIds.delete(file.id);
row.classList.remove('selected');
}
countEl.textContent = `${selectedIds.size} ${t('music.selected', 'selected')}`;
addBtn.disabled = selectedIds.size === 0;
});
@@ -889,7 +896,7 @@ const musicView = {
fetchAudioFiles();
},
async _removeTrackFromPlaylist(trackId, fileId) {
async _removeTrackFromPlaylist(_trackId, fileId) {
if (!this.currentPlaylist) return;
const t = (key, fallback = '') => (typeof i18n !== 'undefined' && i18n.t ? i18n.t(key) : fallback || key);
+1 -1
View File
@@ -175,7 +175,7 @@ const fileSharing = {
* @param {string} message
* @returns {Promise<boolean>}
*/
async sendShareNotification(shareUrl, recipientEmail, message = '') {
async sendShareNotification(shareUrl, recipientEmail, _message = '') {
// TODO: implement backend endpoint for email notifications
console.log(`Share notification for ${shareUrl} sent to ${recipientEmail}`);
if (window.ui) {
+110 -123
View File
@@ -4,136 +4,123 @@
* Fetches share metadata from the API, handles password-protected shares,
* and renders file download or folder info.
*/
(function () {
'use strict';
(() => {
// ── DOM refs ───────────────────────────────────────────────────
const $loading = document.getElementById('share-loading');
const $password = document.getElementById('share-password');
const $expired = document.getElementById('share-expired');
const $file = document.getElementById('share-file');
const $folder = document.getElementById('share-folder');
// ── DOM refs ───────────────────────────────────────────────────
const $loading = document.getElementById('share-loading');
const $password = document.getElementById('share-password');
const $expired = document.getElementById('share-expired');
const $file = document.getElementById('share-file');
const $folder = document.getElementById('share-folder');
const $pwForm = document.getElementById('password-form');
const $pwInput = document.getElementById('password-input');
const $pwError = document.getElementById('password-error');
const $pwForm = document.getElementById('password-form');
const $pwInput = document.getElementById('password-input');
const $pwError = document.getElementById('password-error');
const $fileName = document.getElementById('file-name');
const $fileMeta = document.getElementById('file-meta');
const $fileDl = document.getElementById('file-download');
const $folderName = document.getElementById('folder-name');
const $expiredMsg = document.getElementById('expired-message');
const $fileName = document.getElementById('file-name');
const $fileMeta = document.getElementById('file-meta');
const $fileDl = document.getElementById('file-download');
const $folderName = document.getElementById('folder-name');
const $expiredMsg = document.getElementById('expired-message');
// ── Extract token from URL path (/s/{token}) ──────────────────
const pathParts = window.location.pathname.split('/');
const tokenIdx = pathParts.indexOf('s');
const TOKEN = tokenIdx !== -1 ? pathParts[tokenIdx + 1] : null;
// ── Extract token from URL path (/s/{token}) ──────────────────
const pathParts = window.location.pathname.split('/');
const tokenIdx = pathParts.indexOf('s');
const TOKEN = tokenIdx !== -1 ? pathParts[tokenIdx + 1] : null;
if (!TOKEN) {
showState('expired');
$expiredMsg.textContent = 'Invalid share link.';
return;
}
// ── Helpers ────────────────────────────────────────────────────
function showState(name) {
[$loading, $password, $expired, $file, $folder].forEach(function (el) {
el.classList.add('hidden');
});
var target = {
loading: $loading,
password: $password,
expired: $expired,
file: $file,
folder: $folder,
}[name];
if (target) target.classList.remove('hidden');
}
function formatSize(bytes) {
if (!bytes || bytes === 0) return '';
var units = ['B', 'KB', 'MB', 'GB', 'TB'];
var i = 0;
var size = bytes;
while (size >= 1024 && i < units.length - 1) { size /= 1024; i++; }
return size.toFixed(i === 0 ? 0 : 1) + ' ' + units[i];
}
// ── Render share data ─────────────────────────────────────────
function renderShare(data) {
if (data.item_type === 'folder') {
$folderName.textContent = data.item_name || 'Shared Folder';
showState('folder');
} else {
$fileName.textContent = data.item_name || 'Shared File';
$fileMeta.textContent = data.item_name
? 'Shared file'
: '';
$fileDl.href = '/api/s/' + TOKEN + '/download';
showState('file');
}
}
// ── Fetch share metadata ──────────────────────────────────────
function fetchShare() {
fetch('/api/s/' + encodeURIComponent(TOKEN))
.then(function (res) {
if (res.ok) return res.json();
if (res.status === 401) {
return res.json().then(function (body) {
if (body && body.requiresPassword) {
showState('password');
return null;
}
throw new Error('Unauthorized');
});
}
if (res.status === 410) {
showState('expired');
return null;
}
throw new Error('HTTP ' + res.status);
})
.then(function (data) {
if (data) renderShare(data);
})
.catch(function () {
if (!TOKEN) {
showState('expired');
$expiredMsg.textContent = 'This share link is no longer available.';
});
}
$expiredMsg.textContent = 'Invalid share link.';
return;
}
// ── Password form ─────────────────────────────────────────────
$pwForm.addEventListener('submit', function (e) {
e.preventDefault();
$pwError.classList.add('hidden');
// ── Helpers ────────────────────────────────────────────────────
function showState(name) {
[$loading, $password, $expired, $file, $folder].forEach((el) => {
el.classList.add('hidden');
});
var target = {
loading: $loading,
password: $password,
expired: $expired,
file: $file,
folder: $folder
}[name];
if (target) target.classList.remove('hidden');
}
var password = $pwInput.value;
if (!password) return;
fetch('/api/s/' + encodeURIComponent(TOKEN) + '/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ password: password }),
})
.then(function (res) {
if (res.ok) return res.json();
if (res.status === 401) {
$pwError.textContent = 'Incorrect password. Please try again.';
$pwError.classList.remove('hidden');
return null;
// ── Render share data ─────────────────────────────────────────
function renderShare(data) {
if (data.item_type === 'folder') {
$folderName.textContent = data.item_name || 'Shared Folder';
showState('folder');
} else {
$fileName.textContent = data.item_name || 'Shared File';
$fileMeta.textContent = data.item_name ? 'Shared file' : '';
$fileDl.href = `/api/s/${TOKEN}/download`;
showState('file');
}
throw new Error('HTTP ' + res.status);
})
.then(function (data) {
if (data) renderShare(data);
})
.catch(function () {
$pwError.textContent = 'An error occurred. Please try again.';
$pwError.classList.remove('hidden');
});
});
}
// ── Init ──────────────────────────────────────────────────────
fetchShare();
// ── Fetch share metadata ──────────────────────────────────────
function fetchShare() {
fetch(`/api/s/${encodeURIComponent(TOKEN)}`)
.then((res) => {
if (res.ok) return res.json();
if (res.status === 401) {
return res.json().then((body) => {
if (body?.requiresPassword) {
showState('password');
return null;
}
throw new Error('Unauthorized');
});
}
if (res.status === 410) {
showState('expired');
return null;
}
throw new Error(`HTTP ${res.status}`);
})
.then((data) => {
if (data) renderShare(data);
})
.catch(() => {
showState('expired');
$expiredMsg.textContent = 'This share link is no longer available.';
});
}
// ── Password form ─────────────────────────────────────────────
$pwForm.addEventListener('submit', (e) => {
e.preventDefault();
$pwError.classList.add('hidden');
var password = $pwInput.value;
if (!password) return;
fetch(`/api/s/${encodeURIComponent(TOKEN)}/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ password: password })
})
.then((res) => {
if (res.ok) return res.json();
if (res.status === 401) {
$pwError.textContent = 'Incorrect password. Please try again.';
$pwError.classList.remove('hidden');
return null;
}
throw new Error(`HTTP ${res.status}`);
})
.then((data) => {
if (data) renderShare(data);
})
.catch(() => {
$pwError.textContent = 'An error occurred. Please try again.';
$pwError.classList.remove('hidden');
});
});
// ── Init ──────────────────────────────────────────────────────
fetchShare();
})();