debug:调试恢复功能和登录功能

This commit is contained in:
初衷
2025-08-13 21:33:06 +08:00
parent bf4596acf2
commit 5d4b1edb94
8 changed files with 444 additions and 11 deletions

View File

@@ -1,3 +1,3 @@
import { errorHandling, telemetryData, checkKVConfig } from './utils/middleware';
import { errorHandling, telemetryData, checkDatabaseConfig } from './utils/middleware';
export const onRequest = [checkKVConfig, errorHandling, telemetryData];
export const onRequest = [checkDatabaseConfig, errorHandling, telemetryData];

View File

@@ -0,0 +1,114 @@
/**
* 备份功能测试工具
*/
import { getDatabase } from '../../utils/databaseAdapter.js';
export async function onRequest(context) {
var env = context.env;
try {
var db = getDatabase(env);
var results = {
databaseType: db.constructor.name || 'Unknown',
settings: {
all: [],
manage: [],
sysConfig: []
},
files: {
count: 0,
sample: []
}
};
// 测试列出所有设置
try {
var allSettings = await db.listSettings({});
results.settings.all = allSettings.keys.map(function(item) {
return {
key: item.name,
hasValue: !!item.value,
valueLength: item.value ? item.value.length : 0
};
});
} catch (error) {
results.settings.allError = error.message;
}
// 测试列出manage@开头的设置
try {
var manageSettings = await db.listSettings({ prefix: 'manage@' });
results.settings.manage = manageSettings.keys.map(function(item) {
return {
key: item.name,
hasValue: !!item.value,
valueLength: item.value ? item.value.length : 0
};
});
} catch (error) {
results.settings.manageError = error.message;
}
// 测试列出sysConfig设置
try {
var sysConfigSettings = await db.listSettings({ prefix: 'manage@sysConfig@' });
results.settings.sysConfig = sysConfigSettings.keys.map(function(item) {
return {
key: item.name,
hasValue: !!item.value,
valueLength: item.value ? item.value.length : 0,
valuePreview: item.value ? item.value.substring(0, 100) + '...' : null
};
});
} catch (error) {
results.settings.sysConfigError = error.message;
}
// 测试列出文件
try {
var filesList = await db.listFiles({ limit: 5 });
results.files.count = filesList.keys.length;
results.files.sample = filesList.keys.map(function(item) {
return {
id: item.name,
hasMetadata: !!item.metadata
};
});
} catch (error) {
results.files.error = error.message;
}
// 测试特定设置的读取
try {
var pageConfig = await db.get('manage@sysConfig@page');
results.specificTests = {
pageConfig: {
exists: !!pageConfig,
length: pageConfig ? pageConfig.length : 0,
preview: pageConfig ? pageConfig.substring(0, 200) + '...' : null
}
};
} catch (error) {
results.specificTests = { error: error.message };
}
return new Response(JSON.stringify(results, null, 2), {
headers: {
'Content-Type': 'application/json'
}
});
} catch (error) {
return new Response(JSON.stringify({
error: error.message,
stack: error.stack
}), {
status: 500,
headers: {
'Content-Type': 'application/json'
}
});
}
}

View File

@@ -0,0 +1,104 @@
/**
* 恢复样本数据测试
*/
import { getDatabase } from '../../utils/databaseAdapter.js';
export async function onRequest(context) {
var env = context.env;
try {
var db = getDatabase(env);
// 使用您提供的样本数据
var sampleSettings = {
"manage@sysConfig@page": "{\"config\":[{\"id\":\"siteTitle\",\"label\":\"网站标题\",\"placeholder\":\"Sanyue ImgHub\",\"category\":\"全局设置\",\"value\":\"ChuZhong ImgHub\"},{\"id\":\"siteIcon\",\"label\":\"网站图标\",\"category\":\"全局设置\"},{\"id\":\"ownerName\",\"label\":\"图床名称\",\"placeholder\":\"Sanyue ImgHub\",\"category\":\"全局设置\",\"value\":\"ChuZhong ImgHub\"},{\"id\":\"logoUrl\",\"label\":\"图床Logo\",\"category\":\"全局设置\"},{\"id\":\"bkInterval\",\"label\":\"背景切换间隔\",\"placeholder\":\"3000\",\"tooltip\":\"单位:毫秒 ms\",\"category\":\"全局设置\"},{\"id\":\"bkOpacity\",\"label\":\"背景图透明度\",\"placeholder\":\"1\",\"tooltip\":\"0-1 之间的小数\",\"category\":\"全局设置\"},{\"id\":\"urlPrefix\",\"label\":\"默认URL前缀\",\"tooltip\":\"自定义URL前缀https://img.a.com/file/,留空则使用当前域名 <br/> 设置后将应用于客户端和管理端\",\"category\":\"全局设置\"},{\"id\":\"announcement\",\"label\":\"公告\",\"tooltip\":\"支持HTML标签\",\"category\":\"客户端设置\"},{\"id\":\"defaultUploadChannel\",\"label\":\"默认上传渠道\",\"type\":\"select\",\"options\":[{\"label\":\"Telegram\",\"value\":\"telegram\"},{\"label\":\"Cloudflare R2\",\"value\":\"cfr2\"},{\"label\":\"S3\",\"value\":\"s3\"}],\"placeholder\":\"telegram\",\"category\":\"客户端设置\"},{\"id\":\"defaultUploadFolder\",\"label\":\"默认上传目录\",\"placeholder\":\"/ 开头的合法目录,不能包含特殊字符, 默认为根目录\",\"category\":\"客户端设置\"},{\"id\":\"defaultUploadNameType\",\"label\":\"默认命名方式\",\"type\":\"select\",\"options\":[{\"label\":\"默认\",\"value\":\"default\"},{\"label\":\"仅前缀\",\"value\":\"index\"},{\"label\":\"仅原名\",\"value\":\"origin\"},{\"label\":\"短链接\",\"value\":\"short\"}],\"placeholder\":\"default\",\"category\":\"客户端设置\"},{\"id\":\"loginBkImg\",\"label\":\"登录页背景图\",\"tooltip\":\"1.填写 bing 使用必应壁纸轮播 <br/> 2.填写 [\\\"url1\\\",\\\"url2\\\"] 使用多张图片轮播 <br/> 3.填写 [\\\"url\\\"] 使用单张图片\",\"category\":\"客户端设置\"},{\"id\":\"uploadBkImg\",\"label\":\"上传页背景图\",\"tooltip\":\"1.填写 bing 使用必应壁纸轮播 <br/> 2.填写 [\\\"url1\\\",\\\"url2\\\"] 使用多张图片轮播 <br/> 3.填写 [\\\"url\\\"] 使用单张图片\",\"category\":\"客户端设置\"},{\"id\":\"footerLink\",\"label\":\"页脚传送门链接\",\"category\":\"客户端设置\"},{\"id\":\"disableFooter\",\"label\":\"隐藏页脚\",\"type\":\"boolean\",\"default\":false,\"category\":\"客户端设置\",\"value\":false},{\"id\":\"adminLoginBkImg\",\"label\":\"登录页背景图\",\"tooltip\":\"1.填写 bing 使用必应壁纸轮播 <br/> 2.填写 [\\\"url1\\\",\\\"url2\\\"] 使用多张图片轮播 <br/> 3.填写 [\\\"url\\\"] 使用单张图片\",\"category\":\"管理端设置\"}]}",
"manage@sysConfig@security": "{\"auth\":{\"user\":{\"authCode\":\"ccxy211008\"},\"admin\":{\"adminUsername\":\"chuzhong\",\"adminPassword\":\"ccxy211008\"}},\"upload\":{\"moderate\":{\"enabled\":false,\"channel\":\"default\",\"moderateContentApiKey\":\"\",\"nsfwApiPath\":\"\"}},\"access\":{\"allowedDomains\":\"\",\"whiteListMode\":false}}"
};
var results = {
beforeRestore: {},
afterRestore: {},
restoreResults: [],
errors: []
};
// 检查恢复前的状态
for (var key in sampleSettings) {
try {
var beforeValue = await db.get(key);
results.beforeRestore[key] = {
exists: !!beforeValue,
length: beforeValue ? beforeValue.length : 0
};
} catch (error) {
results.beforeRestore[key] = { error: error.message };
}
}
// 执行恢复
for (var key in sampleSettings) {
try {
var value = sampleSettings[key];
console.log('恢复设置:', key, '长度:', value.length);
await db.put(key, value);
// 立即验证
var retrieved = await db.get(key);
var success = retrieved === value;
results.restoreResults.push({
key: key,
success: success,
originalLength: value.length,
retrievedLength: retrieved ? retrieved.length : 0,
matches: success
});
if (!success) {
console.error('恢复验证失败:', key);
console.error('原始长度:', value.length);
console.error('检索长度:', retrieved ? retrieved.length : 0);
}
} catch (error) {
results.errors.push({
key: key,
error: error.message
});
console.error('恢复失败:', key, error);
}
}
// 检查恢复后的状态
for (var key in sampleSettings) {
try {
var afterValue = await db.get(key);
results.afterRestore[key] = {
exists: !!afterValue,
length: afterValue ? afterValue.length : 0
};
} catch (error) {
results.afterRestore[key] = { error: error.message };
}
}
return new Response(JSON.stringify(results, null, 2), {
headers: {
'Content-Type': 'application/json'
}
});
} catch (error) {
return new Response(JSON.stringify({
error: error.message,
stack: error.stack
}), {
status: 500,
headers: {
'Content-Type': 'application/json'
}
});
}
}

View File

@@ -0,0 +1,103 @@
/**
* 恢复功能测试工具
*/
import { getDatabase } from '../../utils/databaseAdapter.js';
export async function onRequest(context) {
var env = context.env;
var request = context.request;
try {
if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 });
}
var testData = await request.json();
var db = getDatabase(env);
var results = {
settings: [],
files: [],
errors: []
};
// 测试恢复设置
if (testData.settings) {
for (var key in testData.settings) {
try {
var value = testData.settings[key];
await db.put(key, value);
// 验证是否成功保存
var retrieved = await db.get(key);
results.settings.push({
key: key,
saved: true,
retrieved: retrieved !== null,
valueMatch: retrieved === value,
originalLength: value.length,
retrievedLength: retrieved ? retrieved.length : 0
});
} catch (error) {
results.errors.push({
type: 'setting',
key: key,
error: error.message
});
}
}
}
// 测试恢复文件
if (testData.files) {
for (var fileId in testData.files) {
try {
var fileData = testData.files[fileId];
if (fileData.value) {
await db.put(fileId, fileData.value, {
metadata: fileData.metadata
});
} else if (fileData.metadata) {
await db.put(fileId, '', {
metadata: fileData.metadata
});
}
// 验证是否成功保存
var retrieved = await db.getWithMetadata(fileId);
results.files.push({
fileId: fileId,
saved: true,
retrieved: retrieved !== null,
hasMetadata: retrieved && retrieved.metadata !== null
});
} catch (error) {
results.errors.push({
type: 'file',
fileId: fileId,
error: error.message
});
}
}
}
return new Response(JSON.stringify(results, null, 2), {
headers: {
'Content-Type': 'application/json'
}
});
} catch (error) {
return new Response(JSON.stringify({
error: error.message,
stack: error.stack
}), {
status: 500,
headers: {
'Content-Type': 'application/json'
}
});
}
}

View File

@@ -0,0 +1,87 @@
/**
* 设置检查工具
*/
import { getDatabase } from '../../utils/databaseAdapter.js';
export async function onRequest(context) {
var env = context.env;
try {
var db = getDatabase(env);
var results = {
allSettings: [],
expectedSettings: [
'manage@sysConfig@page',
'manage@sysConfig@security',
'manage@sysConfig@upload',
'manage@sysConfig@others'
],
missingSettings: [],
existingSettings: {}
};
// 列出所有设置
var allSettings = await db.listSettings({});
results.allSettings = allSettings.keys.map(function(item) {
return {
key: item.name,
hasValue: !!item.value,
valueLength: item.value ? item.value.length : 0
};
});
// 检查每个预期的设置
for (var i = 0; i < results.expectedSettings.length; i++) {
var settingKey = results.expectedSettings[i];
try {
var value = await db.get(settingKey);
if (value) {
results.existingSettings[settingKey] = {
exists: true,
length: value.length,
preview: value.substring(0, 200) + (value.length > 200 ? '...' : '')
};
} else {
results.missingSettings.push(settingKey);
results.existingSettings[settingKey] = {
exists: false
};
}
} catch (error) {
results.existingSettings[settingKey] = {
exists: false,
error: error.message
};
}
}
// 检查是否有其他manage@开头的设置
var manageSettings = await db.listSettings({ prefix: 'manage@' });
results.manageSettings = manageSettings.keys.map(function(item) {
return {
key: item.name,
hasValue: !!item.value,
valueLength: item.value ? item.value.length : 0
};
});
return new Response(JSON.stringify(results, null, 2), {
headers: {
'Content-Type': 'application/json'
}
});
} catch (error) {
return new Response(JSON.stringify({
error: error.message,
stack: error.stack
}), {
status: 500,
headers: {
'Content-Type': 'application/json'
}
});
}
}

View File

@@ -1,6 +1,7 @@
import { fetchSecurityConfig } from "../../utils/sysConfig";
import { checkKVConfig, errorHandling } from "../../utils/middleware";
import { checkDatabaseConfig, errorHandling } from "../../utils/middleware";
import { validateApiToken } from "../../utils/tokenValidator";
import { getDatabase } from "../../utils/databaseAdapter.js";
let securityConfig = {}
let basicUser = ""
@@ -108,7 +109,8 @@ async function authentication(context) {
const pathname = new URL(context.request.url).pathname;
const requiredPermission = extractRequiredPermission(pathname);
const tokenValidation = await validateApiToken(context.request, context.env.img_url, requiredPermission);
const db = getDatabase(context.env);
const tokenValidation = await validateApiToken(context.request, db, requiredPermission);
if (tokenValidation.valid) {
// Token验证通过继续处理请求
return context.next();
@@ -138,4 +140,4 @@ async function authentication(context) {
}
export const onRequest = [checkKVConfig, errorHandling, authentication];
export const onRequest = [checkDatabaseConfig, errorHandling, authentication];

View File

@@ -83,8 +83,10 @@ async function handleBackup(context) {
// 备份系统设置
const db = getDatabase(env);
const settingsList = await db.listSettings({ prefix: 'manage@' });
for (const key of settingsList.keys) {
// 备份所有设置不仅仅是manage@开头的
const allSettingsList = await db.listSettings({});
for (const key of allSettingsList.keys) {
// 忽略索引文件
if (key.name.startsWith('manage@index')) continue;
@@ -94,6 +96,18 @@ async function handleBackup(context) {
}
}
// 额外确保备份manage@开头的设置
const manageSettingsList = await db.listSettings({ prefix: 'manage@' });
for (const key of manageSettingsList.keys) {
// 忽略索引文件
if (key.name.startsWith('manage@index')) continue;
const setting = key.value;
if (setting && !backupData.data.settings[key.name]) {
backupData.data.settings[key.name] = setting;
}
}
const backupJson = JSON.stringify(backupData, null, 2);
return new Response(backupJson, {
@@ -157,8 +171,17 @@ async function handleRestore(request, env) {
// 恢复系统设置
for (const [key, value] of Object.entries(backupData.data.settings)) {
try {
console.log(`恢复设置: ${key}, 长度: ${value.length}`);
await db.put(key, value);
restoredSettings++;
// 验证是否成功保存
const retrieved = await db.get(key);
if (retrieved === value) {
restoredSettings++;
console.log(`设置 ${key} 恢复成功`);
} else {
console.error(`设置 ${key} 恢复后验证失败`);
}
} catch (error) {
console.error(`恢复设置 ${key} 失败:`, error);
}

View File

@@ -4,11 +4,11 @@ import { getTokenPermissions } from '../api/manage/apiTokens.js';
/**
* 验证API Token权限
* @param {Request} request - 请求对象
* @param {KVNamespace} kv - KV存储
* @param {Object} db - 数据库适配器
* @param {string} requiredPermission - 需要的权限 ('upload', 'delete', 'list')
* @returns {Promise<{valid: boolean, error?: string}>}
*/
export async function validateApiToken(request, kv, requiredPermission) {
export async function validateApiToken(request, db, requiredPermission) {
const authHeader = request.headers.get('Authorization');
if (!authHeader) {
@@ -29,7 +29,7 @@ export async function validateApiToken(request, kv, requiredPermission) {
}
// 获取Token权限
const permissions = await getTokenPermissions(kv, token);
const permissions = await getTokenPermissions(db, token);
if (!permissions) {
return { valid: false, error: '无效的Token' };