设置登录
进一步完善日志
This commit is contained in:
@@ -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) {
|
||||||
|
if (!skipErrorUi) {
|
||||||
showLoginError('Please enter both access key and secret key.');
|
showLoginError('Please enter both access key and secret key.');
|
||||||
return;
|
}
|
||||||
|
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();
|
||||||
|
if (!ws) {
|
||||||
connectWebSocket();
|
connectWebSocket();
|
||||||
|
}
|
||||||
await fetchVideos(selectedBucket);
|
await fetchVideos(selectedBucket);
|
||||||
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Login error:', err);
|
console.error('Login error:', err);
|
||||||
|
clearAuthState();
|
||||||
|
if (!skipErrorUi) {
|
||||||
showLoginError(err.message);
|
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();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
16
server.js
16
server.js
@@ -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 });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user