mirror of
https://github.com/MarSeventh/CloudFlare-ImgBed.git
synced 2026-04-25 06:35:21 +00:00
debug:调试恢复功能和登录功能
This commit is contained in:
@@ -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];
|
||||
114
functions/api/debug/backup-test.js
Normal file
114
functions/api/debug/backup-test.js
Normal 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'
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
104
functions/api/debug/restore-sample.js
Normal file
104
functions/api/debug/restore-sample.js
Normal 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'
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
103
functions/api/debug/restore-test.js
Normal file
103
functions/api/debug/restore-test.js
Normal 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'
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
87
functions/api/debug/settings-check.js
Normal file
87
functions/api/debug/settings-check.js
Normal 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'
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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' };
|
||||
|
||||
Reference in New Issue
Block a user