设置登录

进一步完善日志
This commit is contained in:
CN-JS-HuiBai
2026-04-03 22:23:47 +08:00
parent 84c5b8f858
commit e30736c3e7
2 changed files with 123 additions and 13 deletions

View File

@@ -72,6 +72,8 @@ document.addEventListener('DOMContentLoaded', () => {
let s3Username = ''; let s3Username = '';
let s3Password = ''; let s3Password = '';
let s3AuthHeaders = {}; let s3AuthHeaders = {};
const SAVED_AUTH_STORAGE_KEY = 'media-coding-web:s3-auth';
const SAVED_BUCKET_STORAGE_KEY = 'media-coding-web:selected-bucket';
// Seek state // Seek state
let videoDuration = 0; let videoDuration = 0;
@@ -768,6 +770,62 @@ document.addEventListener('DOMContentLoaded', () => {
if (s3Password) s3AuthHeaders['X-S3-Password'] = s3Password; if (s3Password) s3AuthHeaders['X-S3-Password'] = s3Password;
}; };
const saveAuthState = (username, password) => {
try {
window.localStorage.setItem(SAVED_AUTH_STORAGE_KEY, JSON.stringify({
username,
password
}));
} catch (error) {
console.warn('Failed to persist auth state:', error);
}
};
const loadAuthState = () => {
try {
const rawValue = window.localStorage.getItem(SAVED_AUTH_STORAGE_KEY);
if (!rawValue) return null;
const parsed = JSON.parse(rawValue);
if (!parsed || typeof parsed.username !== 'string' || typeof parsed.password !== 'string') {
return null;
}
return parsed;
} catch (error) {
console.warn('Failed to read auth state:', error);
return null;
}
};
const clearAuthState = () => {
try {
window.localStorage.removeItem(SAVED_AUTH_STORAGE_KEY);
window.localStorage.removeItem(SAVED_BUCKET_STORAGE_KEY);
} catch (error) {
console.warn('Failed to clear auth state:', error);
}
};
const saveSelectedBucket = (bucketName) => {
try {
if (bucketName) {
window.localStorage.setItem(SAVED_BUCKET_STORAGE_KEY, bucketName);
} else {
window.localStorage.removeItem(SAVED_BUCKET_STORAGE_KEY);
}
} catch (error) {
console.warn('Failed to persist selected bucket:', error);
}
};
const loadSelectedBucket = () => {
try {
return window.localStorage.getItem(SAVED_BUCKET_STORAGE_KEY) || '';
} catch (error) {
console.warn('Failed to read selected bucket:', error);
return '';
}
};
const showLogin = () => { const showLogin = () => {
if (loginScreen) loginScreen.classList.remove('hidden'); if (loginScreen) loginScreen.classList.remove('hidden');
if (appContainer) appContainer.classList.add('hidden'); if (appContainer) appContainer.classList.add('hidden');
@@ -801,16 +859,19 @@ document.addEventListener('DOMContentLoaded', () => {
loginError.classList.add('hidden'); loginError.classList.add('hidden');
}; };
const login = async () => { const login = async (options = {}) => {
if (!loginUsernameInput || !loginPasswordInput) return; if (!loginUsernameInput || !loginPasswordInput) return false;
const username = loginUsernameInput.value.trim(); const username = typeof options.username === 'string' ? options.username.trim() : loginUsernameInput.value.trim();
const password = loginPasswordInput.value; const password = typeof options.password === 'string' ? options.password : loginPasswordInput.value;
const skipErrorUi = options.skipErrorUi === true;
if (!username || !password) { if (!username || !password) {
showLoginError('Please enter both access key and secret key.'); if (!skipErrorUi) {
return; showLoginError('Please enter both access key and secret key.');
}
return false;
} }
loginBtn.disabled = true; loginBtn.disabled = true;
loginBtn.textContent = 'Logging in...'; loginBtn.textContent = options.isRestoring ? 'Restoring...' : 'Logging in...';
clearLoginError(); clearLoginError();
try { try {
@@ -824,17 +885,31 @@ document.addEventListener('DOMContentLoaded', () => {
if (!Array.isArray(data.buckets) || data.buckets.length === 0) { if (!Array.isArray(data.buckets) || data.buckets.length === 0) {
throw new Error('No buckets available for this account'); throw new Error('No buckets available for this account');
} }
saveAuthState(username, password);
if (loginUsernameInput) loginUsernameInput.value = username;
if (loginPasswordInput) loginPasswordInput.value = password;
renderBuckets(data.buckets); renderBuckets(data.buckets);
showApp(); showApp();
selectedBucket = data.buckets[0].Name; const savedBucket = loadSelectedBucket();
selectedBucket = data.buckets.some((bucket) => bucket.Name === savedBucket)
? savedBucket
: data.buckets[0].Name;
if (bucketSelect) bucketSelect.value = selectedBucket; if (bucketSelect) bucketSelect.value = selectedBucket;
saveSelectedBucket(selectedBucket);
loadConfig(); loadConfig();
connectWebSocket(); if (!ws) {
connectWebSocket();
}
await fetchVideos(selectedBucket); await fetchVideos(selectedBucket);
return true;
} catch (err) { } catch (err) {
console.error('Login error:', err); console.error('Login error:', err);
showLoginError(err.message); clearAuthState();
if (!skipErrorUi) {
showLoginError(err.message);
}
setAuthHeaders('', ''); setAuthHeaders('', '');
return false;
} finally { } finally {
if (loginBtn) { if (loginBtn) {
loginBtn.disabled = false; loginBtn.disabled = false;
@@ -843,6 +918,22 @@ document.addEventListener('DOMContentLoaded', () => {
} }
}; };
const restoreSavedSession = async () => {
const savedAuth = loadAuthState();
if (!savedAuth) {
return false;
}
if (loginUsernameInput) loginUsernameInput.value = savedAuth.username;
if (loginPasswordInput) loginPasswordInput.value = savedAuth.password;
return login({
username: savedAuth.username,
password: savedAuth.password,
isRestoring: true,
skipErrorUi: true
});
};
const clearDownloadCache = async () => { const clearDownloadCache = async () => {
if (!clearDownloadCacheBtn) return; if (!clearDownloadCacheBtn) return;
clearDownloadCacheBtn.disabled = true; clearDownloadCacheBtn.disabled = true;
@@ -1282,10 +1373,19 @@ document.addEventListener('DOMContentLoaded', () => {
if (bucketSelect) { if (bucketSelect) {
bucketSelect.addEventListener('change', async (event) => { bucketSelect.addEventListener('change', async (event) => {
selectedBucket = event.target.value; selectedBucket = event.target.value;
saveSelectedBucket(selectedBucket);
await fetchVideos(selectedBucket); await fetchVideos(selectedBucket);
}); });
} }
// Initial state: require login before loading data // Initial state: require login before loading data
showLogin(); showLogin();
restoreSavedSession().then((restored) => {
if (!restored) {
showLogin();
}
}).catch((error) => {
console.error('Session restore failed:', error);
showLogin();
});
}); });

View File

@@ -451,13 +451,15 @@ const clearConvertCache = () => {
app.get('/api/buckets', async (req, res) => { app.get('/api/buckets', async (req, res) => {
try { try {
const auth = extractS3Credentials(req); const auth = extractS3Credentials(req);
console.log(`[s3] list buckets start endpoint=${defaultS3ClientConfig.endpoint || 'aws-default'} region=${defaultS3ClientConfig.region} authProvided=${Boolean(auth.username)}`);
const s3Client = createS3Client(auth); const s3Client = createS3Client(auth);
const command = new ListBucketsCommand({}); const command = new ListBucketsCommand({});
const response = await s3Client.send(command); const response = await s3Client.send(command);
const buckets = response.Buckets || []; const buckets = response.Buckets || [];
console.log(`[s3] list buckets complete count=${buckets.length}`);
res.json({ buckets }); res.json({ buckets });
} catch (error) { } catch (error) {
console.error('Error listing buckets:', error); console.error('[s3] list buckets failed:', error);
res.status(500).json({ error: 'Failed to list buckets', detail: error.message }); res.status(500).json({ error: 'Failed to list buckets', detail: error.message });
} }
}); });
@@ -473,14 +475,21 @@ app.get('/api/videos', async (req, res) => {
const auth = extractS3Credentials(req); const auth = extractS3Credentials(req);
const s3Client = createS3Client(auth); const s3Client = createS3Client(auth);
let continuationToken; let continuationToken;
let pageNumber = 0;
console.log(`[s3] scan bucket start bucket=${bucket} endpoint=${defaultS3ClientConfig.endpoint || 'aws-default'} authProvided=${Boolean(auth.username)}`);
do { do {
pageNumber += 1;
console.log(`[s3] scan bucket page request bucket=${bucket} page=${pageNumber} continuationToken=${continuationToken || 'none'}`);
const command = new ListObjectsV2Command({ const command = new ListObjectsV2Command({
Bucket: bucket, Bucket: bucket,
ContinuationToken: continuationToken, ContinuationToken: continuationToken,
}); });
const response = await s3Client.send(command); const response = await s3Client.send(command);
allObjects.push(...(response.Contents || [])); const pageItems = response.Contents || [];
allObjects.push(...pageItems);
console.log(`[s3] scan bucket page result bucket=${bucket} page=${pageNumber} objects=${pageItems.length} truncated=${Boolean(response.IsTruncated)} nextToken=${response.NextContinuationToken || 'none'} scannedTotal=${allObjects.length}`);
continuationToken = response.IsTruncated ? response.NextContinuationToken : undefined; continuationToken = response.IsTruncated ? response.NextContinuationToken : undefined;
} while (continuationToken); } while (continuationToken);
@@ -499,9 +508,10 @@ app.get('/api/videos', async (req, res) => {
return videoExtensions.some(ext => lowerKey.endsWith(ext)); return videoExtensions.some(ext => lowerKey.endsWith(ext));
}); });
console.log(`[s3] scan bucket complete bucket=${bucket} pages=${pageNumber} objects=${allObjects.length} videos=${videos.length}`);
res.json({ videos }); res.json({ videos });
} catch (error) { } catch (error) {
console.error('Error fetching videos:', error); console.error(`[s3] scan bucket failed bucket=${req.query.bucket || BUCKET_NAME || 'unknown'}:`, error);
res.status(500).json({ error: 'Failed to fetch videos from S3', detail: error.message }); res.status(500).json({ error: 'Failed to fetch videos from S3', detail: error.message });
} }
}); });