Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules/.bin/*
node_modules/.mf/*
.wrangler/*
data/*
data/*
/mcp-feedback-enhanced
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
Comment on lines +1 to +2
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty configuration file should not be committed to the repository. Either remove this file or add it to .gitignore if it's intended for local development only.

Suggested change
{
}

Copilot uses AI. Check for mistakes.
8 changes: 3 additions & 5 deletions admin.html
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,9 @@
console.log(result);
if (result == "true") {
this.showLogoutButton = true;
} else if (result == "Not using basic auth.") {

}
else {
window.location.href = "./api/manage/login";
} else {
// 未认证,重定向到登录页面
window.location.href = "./login.html";
}
})
.catch(error => { alert("An error occurred while synchronizing data with the server, please check the network connection"); console.log('error', error) });
Expand Down
48 changes: 34 additions & 14 deletions functions/api/manage/_middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,27 +68,47 @@ async function errorHandling(context) {


function authentication(context) {
//context.env.BASIC_USER="admin"
//context.env.BASIC_PASS="admin"
//check if the env variables Disable_Dashboard are set
if (typeof context.env.img_url == "undefined" || context.env.img_url == null || context.env.img_url == "") {
const { request, env } = context;
const url = new URL(request.url);

// 检查是否禁用了仪表板
if (typeof env.img_url == "undefined" || env.img_url == null || env.img_url == "") {
return new Response('Dashboard is disabled. Please bind a KV namespace to use this feature.', { status: 200 });
}

console.log(context.env.BASIC_USER)
if(typeof context.env.BASIC_USER == "undefined" || context.env.BASIC_USER == null || context.env.BASIC_USER == ""){
// 跳过认证和登录相关的端点
const skipAuth = ['/api/manage/auth', '/api/manage/login'].some(path =>
url.pathname.endsWith(path)
);

if (skipAuth) {
return context.next();
}

// 检查cookie认证
const cookies = request.headers.get('Cookie') || '';
const isAuthenticated = cookies.includes('admin_auth=authenticated');

if (isAuthenticated) {
return context.next();
}

// 如果没有cookie认证,检查是否配置了基础认证
console.log(env.BASIC_USER)
if(typeof env.BASIC_USER == "undefined" || env.BASIC_USER == null || env.BASIC_USER == ""){
// 没有配置基础认证,需要cookie认证
return UnauthorizedException('Authentication required.');
}else{
if (context.request.headers.has('Authorization')) {
// 配置了基础认证,使用原有的基础认证逻辑
if (request.headers.has('Authorization')) {
// Throws exception when authorization fails.
const { user, pass } = basicAuthentication(context.request);
const { user, pass } = basicAuthentication(request);


if (context.env.BASIC_USER !== user || context.env.BASIC_PASS !== pass) {
return UnauthorizedException('Invalid credentials.');
}else{
return context.next();
}
if (env.BASIC_USER !== user || env.BASIC_PASS !== pass) {
return UnauthorizedException('Invalid credentials.');
}else{
return context.next();
}

} else {
return new Response('You need to login.', {
Expand Down
23 changes: 23 additions & 0 deletions functions/api/manage/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export async function onRequestPost(context) {
const { request, env } = context;

try {
const body = await request.json();
const { password } = body;

// 固定密码
const ADMIN_PASSWORD = 'Sangok#3';
Comment on lines +8 to +9
Copy link

Copilot AI Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded password in source code is a critical security vulnerability. The password should be stored in an environment variable (e.g., env.ADMIN_PASSWORD) instead of being committed to the repository.

Suggested change
// 固定密码
const ADMIN_PASSWORD = 'Sangok#3';
// 从环境变量读取管理员密码
const ADMIN_PASSWORD = env.ADMIN_PASSWORD;

Copilot uses AI. Check for mistakes.

if (password === ADMIN_PASSWORD) {
// 设置认证cookie,有效期24小时
const response = new Response('success', { status: 200 });
response.headers.set('Set-Cookie', 'admin_auth=authenticated; Path=/; HttpOnly; Max-Age=86400; SameSite=Strict');
return response;
} else {
return new Response('Invalid password', { status: 401 });
}
} catch (error) {
console.error('Auth error:', error);
return new Response('Invalid request', { status: 400 });
}
}
29 changes: 15 additions & 14 deletions functions/api/manage/check.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
export async function onRequest(context) {
// Contents of context object
const {
request, // same as existing Worker API
env, // same as existing Worker API
params, // if filename includes [id] or [[path]]
waitUntil, // same as ctx.waitUntil in existing Worker API
next, // used for middleware or to fetch assets
data, // arbitrary space for passing data between middlewares
} = context;
if(typeof context.env.BASIC_USER == "undefined" || context.env.BASIC_USER == null || context.env.BASIC_USER == ""){
return new Response('Not using basic auth.', { status: 200 });
}else{
const { request, env } = context;

// 检查cookie认证
const cookies = request.headers.get('Cookie') || '';
const isAuthenticated = cookies.includes('admin_auth=authenticated');

if (isAuthenticated) {
return new Response('true', { status: 200 });
}

}

// 如果没有cookie认证,检查是否配置了基础认证
if (typeof env.BASIC_USER == "undefined" || env.BASIC_USER == null || env.BASIC_USER == "") {
return new Response('false', { status: 401 });
} else {
return new Response('true', { status: 200 });
}
}
19 changes: 5 additions & 14 deletions functions/api/manage/login.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
export async function onRequest(context) {
// Contents of context object
const {
request, // same as existing Worker API
env, // same as existing Worker API
params, // if filename includes [id] or [[path]]
waitUntil, // same as ctx.waitUntil in existing Worker API
next, // used for middleware or to fetch assets
data, // arbitrary space for passing data between middlewares
} = context;
//get the request url
const { request } = context;
const url = new URL(request.url);
//redirect to admin page
return Response.redirect(url.origin+"/admin.html", 302)

}

// 重定向到新的登录页面
return Response.redirect(url.origin + "/login.html", 302);
}
20 changes: 8 additions & 12 deletions functions/api/manage/logout.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
export async function onRequest(context) {
// Contents of context object
const {
request, // same as existing Worker API
env, // same as existing Worker API
params, // if filename includes [id] or [[path]]
waitUntil, // same as ctx.waitUntil in existing Worker API
next, // used for middleware or to fetch assets
data, // arbitrary space for passing data between middlewares
} = context;
return new Response('Logged out.', { status: 401 });

}
const { request } = context;
const url = new URL(request.url);

// 清除认证cookie并重定向到首页
const response = Response.redirect(url.origin + "/", 302);
response.headers.set('Set-Cookie', 'admin_auth=; Path=/; HttpOnly; Max-Age=0; SameSite=Strict');
return response;
}
155 changes: 111 additions & 44 deletions functions/upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,61 +10,125 @@ export async function onRequestPost(context) {
await errorHandling(context);
telemetryData(context);

const uploadFile = formData.get('file');
if (!uploadFile) {
throw new Error('No file uploaded');
// 获取所有上传的文件
const uploadFiles = formData.getAll('file');
if (!uploadFiles || uploadFiles.length === 0) {
throw new Error('No files uploaded');
}

const fileName = uploadFile.name;
const fileExtension = fileName.split('.').pop().toLowerCase();

const telegramFormData = new FormData();
telegramFormData.append("chat_id", env.TG_Chat_ID);

// 根据文件类型选择合适的上传方式
let apiEndpoint;
if (uploadFile.type.startsWith('image/')) {
telegramFormData.append("photo", uploadFile);
apiEndpoint = 'sendPhoto';
} else if (uploadFile.type.startsWith('audio/')) {
telegramFormData.append("audio", uploadFile);
apiEndpoint = 'sendAudio';
} else if (uploadFile.type.startsWith('video/')) {
telegramFormData.append("video", uploadFile);
apiEndpoint = 'sendVideo';
} else {
telegramFormData.append("document", uploadFile);
apiEndpoint = 'sendDocument';
// 限制单次上传文件数量(可根据需要调整)
const MAX_FILES = 100;
if (uploadFiles.length > MAX_FILES) {
throw new Error(`Too many files. Maximum ${MAX_FILES} files allowed.`);
}

const result = await sendToTelegram(telegramFormData, apiEndpoint, env);
const results = [];
const errors = [];

if (!result.success) {
throw new Error(result.error);
}
// 并发上传所有文件
const uploadPromises = uploadFiles.map(async (uploadFile, index) => {
try {
if (!uploadFile || !uploadFile.name) {
throw new Error(`Invalid file at index ${index}`);
}

const fileId = getFileId(result.data);
const fileName = uploadFile.name;
const fileExtension = fileName.split('.').pop().toLowerCase();

const telegramFormData = new FormData();
telegramFormData.append("chat_id", env.TG_Chat_ID);

// 根据文件类型选择合适的上传方式
let apiEndpoint;
if (uploadFile.type.startsWith('image/')) {
telegramFormData.append("photo", uploadFile);
apiEndpoint = 'sendPhoto';
} else if (uploadFile.type.startsWith('audio/')) {
telegramFormData.append("audio", uploadFile);
apiEndpoint = 'sendAudio';
} else if (uploadFile.type.startsWith('video/')) {
telegramFormData.append("video", uploadFile);
apiEndpoint = 'sendVideo';
} else {
telegramFormData.append("document", uploadFile);
apiEndpoint = 'sendDocument';
}

if (!fileId) {
throw new Error('Failed to get file ID');
}
const result = await sendToTelegram(telegramFormData, apiEndpoint, env);

// 将文件信息保存到 KV 存储
if (env.img_url) {
await env.img_url.put(`${fileId}.${fileExtension}`, "", {
metadata: {
TimeStamp: Date.now(),
ListType: "None",
Label: "None",
liked: false,
fileName: fileName,
fileSize: uploadFile.size,
if (!result.success) {
throw new Error(result.error);
}

const fileId = getFileId(result.data);

if (!fileId) {
throw new Error('Failed to get file ID');
}
});

// 将文件信息保存到 KV 存储
if (env.img_url) {
await env.img_url.put(`${fileId}.${fileExtension}`, "", {
metadata: {
TimeStamp: Date.now(),
ListType: "None",
Label: "None",
liked: false,
fileName: fileName,
fileSize: uploadFile.size,
}
});
}

return {
success: true,
src: `/file/${fileId}.${fileExtension}`,
fileName: fileName,
fileSize: uploadFile.size
};
} catch (error) {
console.error(`Upload error for file ${index}:`, error);
return {
success: false,
error: error.message,
fileName: uploadFile?.name || `file_${index}`
};
}
});

// 等待所有上传完成
const uploadResults = await Promise.all(uploadPromises);

// 分离成功和失败的结果
uploadResults.forEach(result => {
if (result.success) {
results.push({
src: result.src,
fileName: result.fileName,
fileSize: result.fileSize
});
} else {
errors.push({
fileName: result.fileName,
error: result.error
});
}
});

// 返回结果
const response = {
success: results.length > 0,
uploaded: results.length,
total: uploadFiles.length,
results: results
};

if (errors.length > 0) {
response.errors = errors;
}

return new Response(
JSON.stringify([{ 'src': `/file/${fileId}.${fileExtension}` }]),
JSON.stringify(response),
{
status: 200,
headers: { 'Content-Type': 'application/json' }
Expand All @@ -73,7 +137,10 @@ export async function onRequestPost(context) {
} catch (error) {
console.error('Upload error:', error);
return new Response(
JSON.stringify({ error: error.message }),
JSON.stringify({
success: false,
error: error.message
}),
{
status: 500,
headers: { 'Content-Type': 'application/json' }
Expand Down
Loading