Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

后台优化(音频上传/音视频卡片/黑白名单/优化代码结构) #210

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
442 changes: 442 additions & 0 deletions admin-imgtc.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,442 @@
body {
background: linear-gradient(90deg, #ffd7e4 0%, #c8f1ff 100%);
font-family: 'Arial', sans-serif;
color: #333;
margin: 0;
padding: 0;
}

.header-content {
position: fixed;
top: 0;
left: 1%;
right: 1%;
z-index: 999;
height: clamp(40px, 8vh, 60px);
display: flex;
align-items: center;
padding: 10px 20px;
background-color: rgba(255, 255, 255, 0.75);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: background-color 0.5s ease, box-shadow 0.5s ease;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
}

.header-content:hover {
background-color: rgba(255, 255, 255, 0.85);
box-shadow: 0 6px 10px rgba(0, 0, 0, 0.2);
}

.title {
font-size: clamp(1.2em, 2vw, 1.8em);
font-weight: bold;
cursor: pointer;
transition: color 0.3s ease;
margin-right: 4px;
color: #333;
}

.title:hover {
color: #B39DDB; /* 使用柔和的淡紫色 */
}

.search-card {
margin-left: auto;
margin-right: 16px;
}

.stats {
display: flex;
align-items: center;
cursor: pointer;
font-size: clamp(0.9em, 1.5vw, 1.2em);
background: rgba(255, 255, 255, 0.9);
margin-right: 8px;
padding: 2px 8px;
border-radius: 12px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
transition: all 0.3s ease;
color: #333;
}

.stats:hover {
background-color: #f0eaf8; /* 柔和的淡紫色 */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
transform: scale(1.02);
color: #B39DDB;
}

.upload-icon {
margin-right: 10px;
font-size: clamp(1.2em, 1.5vw, 1.6em);
transition: color 0.3s ease, transform 0.3s ease, background-color 0.3s ease;
color: inherit;
cursor: pointer;
border-radius: 50%;
padding: 8px;
background-color: rgba(179, 157, 219, 0.15);
border: 1px solid transparent;
}

.header-content .actions {
display: flex;
align-items: center;
}

.header-content .actions i {
font-size: clamp(1.2em, 1.5vw, 1.6em);
cursor: pointer;
transition: color 0.3s, transform 0.3s;
color: #333;
margin: 0 8px;
}

.header-content .actions i:hover {
color: #B39DDB;
transform: scale(1.1);
}

.header-content .actions .el-dropdown-link i {
color: #333;
}

.header-content .actions .disabled {
color: #bbb;
pointer-events: none;
}

.header-content .actions .enabled {
color: #B39DDB; /* 使用柔和的淡紫色 */
}

.search-card .el-input__inner {
border-radius: 20px;
width: clamp(200px, 25vw, 300px);
height: clamp(30px, 5vh, 40px);
font-size: 1.2em;
border: none;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
transition: width 0.3s;
}

.search-card .el-input__inner:focus {
width: clamp(250px, 30vw, 400px);
}

.main-container {
display: flex;
flex-direction: column;
margin-top: 20px;
padding: 20px;
min-height: calc(100vh - 80px);
}

.content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 20px;
padding: 10px;
flex-grow: 1;
}

/* 图片卡片样式 */
.image-card {
width: 100%;
aspect-ratio: 4 / 3;
background: rgba(255, 255, 255, 0.6) !important;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
overflow: hidden;
position: relative;
object-fit: cover;
transition: transform 0.3s ease;
border: none;
}

.image-card:hover {
transform: scale(1.02);
cursor: pointer;
}

.image-card .el-checkbox {
position: absolute;
top: 10px;
right: 10px;
transform: scale(1.5);
z-index: 10;
margin: 0; /* 重要:移除默认边距 */
padding: 0; /* 重要:移除默认内边距 */
}

.el-image {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
transition: opacity 0.3s ease;
}
.el-image:hover {
opacity: 0.8;
}

.card-footer {
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 10px;
background: rgba(0, 0, 0, 0.6);
text-align: center;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
box-sizing: border-box;
}

.image-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.6);
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
}

.el-card, .el-card__body{
border: none !important;
}
.el-card__body{
padding: 14px !important;
}
.el-card:hover .image-overlay {
opacity: 1;
}

.overlay-buttons {
display: flex;
gap: 10px;
pointer-events: auto;
}

.pagination-container {
display: flex;
justify-content: center;
margin-top: 16px;
}

.collect-icon {
position: absolute;
top: -1.3px;
left: 10px;
cursor: pointer;
font-size: 1.5em;
z-index: 10;
transition: color 0.3s ease, transform 0.3s ease;
}
.collect-icon:hover {transform: scale(1.2);}
.liked {color: #FFD7E4;}
.not-liked {color: #b39edb80;}

.footer {
display: flex;
justify-content: center;
align-items: center;
gap: 15px;
margin-top: 20px;
font-size: 0.9em;
color: #555; /* 字体颜色改为黑色 */
}

.footer a {
color: #555; /* 链接颜色 */
text-decoration: none;
transition: color 0.3s ease;
}

.footer a:hover {
color: #333; /* 悬停时的链接颜色改为黑色 */
}

.footer i {
color: #333; /* 图标颜色改为黑色 */
}

/* 适配移动端 */
@media (max-width: 768px) {
.content {
grid-template-columns: 1fr; /* 单列布局 */
gap: 10px;
}
.stats-text{display: none;}
.header-content{padding: 8px 12px;}
.stats{margin: 0 4px;}
.stats, .actions i{margin: 0;}
.el-card{aspect-ratio: unset; min-height: 180px;}
.el-card__body{padding: 0;}
.search-card {display: none;}
.header-content .actions i{margin: 0 4px;}
}

/* 音视频卡片基础样式 */
.video-card, .audio-card, .file-card {
background: linear-gradient(135deg, #8a4bffd9 0%, #6d82ffd9 100%);
border: none;
border-radius: 16px !important;
overflow: hidden;
position: relative;
justify-content: center;
align-items: center;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}

.video-card{
background-color: #8EC5FC;
background-image: linear-gradient(62deg, #8EC5FC 0%, #E0C3FC 100%);
}

.video-card .el-card__body{
padding: 0 !important;
}

.audio-card.selected, .video-card.selected, .file-card.selected {
background: linear-gradient(135deg, rgba(138, 75, 255, 0.85) 0%, rgba(255, 215, 228, 0.85) 100%);
}

.video-card:hover, .audio-card:hover, .file-card:hover {
transform: scale(1.02);
}

.video-content, .audio-content, .file-content {
padding: 12px;
color: white;
width: 100%;
box-sizing: border-box;
overflow: hidden;
text-overflow: ellipsis;
}

.video-content{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}

.audio-header, .file-header {
display: flex;
align-items: center;
margin-bottom: 15px;
}

.audio-avatar, .file-avatar {
width: 48px;
height: 48px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
padding: 8px;
margin-right: 15px;
}

.file-avatar{
background: none;
display: flex;
justify-content: center;
align-items: center;
}

.audio-avatar img {
width: 100%;
height: 100%;
object-fit: contain;
}

.audio-info, .file-info {
flex: 1;
min-width: 0;
}

.video-title, .audio-title, .file-title {
font-size: 16px;
font-weight: 500;
max-width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 4px;
}
.video-title{
margin: 6px 0;
font-size: 0.9em;
}

.audio-subtitle, .file-subtitle {
font-size: 14px;
color: rgba(255, 255, 255, 0.7);
}

/* 自定义audio元素样式 */
.custom-audio-player {
width: 100%;
height: 42px;
margin: 15px 0;
border-radius: 28px;
background: rgba(255, 255, 255, 0.2);
}

.video-controls, .audio-controls, .file-controls {
display: flex;
justify-content: center;
gap: 12px;
}

.like-btn i, .select-btn i {
font-size: 1.2em;
}

.like-btn .liked, .select-btn .selected {
color: #FFD7E4;
}

.control-btn {
width: 40px;
height: 40px;
flex-shrink: 0;
border: none;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
color: white;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
flex-shrink: 0;
z-index: 1; /* 确保按钮可点击 */
}

.control-btn:hover {
background: rgba(255, 255, 255, 0.3);
}

/* 移动端适配 */
@media (max-width: 768px) {
.audio-card { max-height: none; }
.custom-audio-player { height: 32px; }
}
.website-edit-dialog .el-textarea__inner{
height: 240px !important;
}
1,540 changes: 824 additions & 716 deletions admin-imgtc.html

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions functions/api/manage/editName/[id].js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export async function onRequest(context) {
const { params, env } = context;

console.log("Request ID:", params.id);

// 获取元数据
const value = await env.img_url.getWithMetadata(params.id);
console.log("Current metadata:", value);

// 如果记录不存在
if (!value.metadata) return new Response(`Image metadata not found for ID: ${params.id}`, { status: 404 });

// 更新文件名
value.metadata.fileName = params.name;
await env.img_url.put(params.id, "", { metadata: value.metadata });

console.log("Updated metadata:", value.metadata);

return new Response(JSON.stringify({ success: true, fileName: value.metadata.fileName }), {
headers: { 'Content-Type': 'application/json' },
});
}
129 changes: 65 additions & 64 deletions functions/file/[id].js
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ export async function onRequest(context) {

const url = new URL(request.url);
let fileUrl = 'https://telegra.ph/' + url.pathname + url.search
if (url.pathname.length > 39) {
if (url.pathname.length > 39) { // 路径长度大于39位,说明是Telegram Bot API 上传的文件
const formdata = new FormData();
formdata.append("file_id", url.pathname);

@@ -21,8 +21,7 @@ export async function onRequest(context) {
console.log(url.pathname.split(".")[0].split("/")[2])
const filePath = await getFilePath(env, url.pathname.split(".")[0].split("/")[2]);
console.log(filePath)
fileUrl = `https://api.telegram.org/file/bot${env.TG_Bot_Token}/${filePath}`;

fileUrl = `https://api.telegram.org/file/bot${env.TG_Bot_Token}/${filePath}`;
}

const response = await fetch(fileUrl, {
@@ -31,78 +30,80 @@ export async function onRequest(context) {
body: request.body,
});

// If the response is OK, proceed with further checks
if (!response.ok) return response;

// Log response details
console.log(response.ok, response.status);

// If the response is OK, proceed with further checks
if (response.ok) {
// Allow the admin page to directly view the image
if (request.headers.get('Referer') === `${url.origin}/admin`) {
return response;
}
// Allow the admin page to directly view the image
const isAdmin = request.headers.get('Referer')?.includes(`${url.origin}/admin`);
if (isAdmin) {
return response;
}

// Fetch KV metadata if available
if (env.img_url) {
const record = await env.img_url.getWithMetadata(params.id);
console.log("Record:", record);

// Ensure metadata exists and add default values for missing properties
if (record && record.metadata) {
const metadata = {
ListType: record.metadata.ListType || "None",
Label: record.metadata.Label || "None",
TimeStamp: record.metadata.TimeStamp || Date.now(),
liked: record.metadata.liked !== undefined ? record.metadata.liked : false
};

// Handle based on ListType and Label
if (metadata.ListType === "White") {
return response;
} else if (metadata.ListType === "Block" || metadata.Label === "adult") {
const referer = request.headers.get('Referer');
const redirectUrl = referer ? "https://static-res.pages.dev/teleimage/img-block-compressed.png" : `${url.origin}/block-img.html`;
return Response.redirect(redirectUrl, 302);
}

// Check if WhiteList_Mode is enabled
if (env.WhiteList_Mode === "true") {
return Response.redirect(`${url.origin}/whitelist-on.html`, 302);
}
} else {
// If metadata does not exist, initialize it in KV with default values
await env.img_url.put(params.id, "", {
metadata: { ListType: "None", Label: "None", TimeStamp: Date.now(), liked: false },
});
}
}
// check if kv storage is available
if (!env.img_url) {
console.error("KV storage not available");
return new Response("img_url KV storage not configured", { status: 500 });
}

// If no metadata or further actions required, moderate content and add to KV if needed
const time = Date.now();
if (env.ModerateContentApiKey) {
const moderateResponse = await fetch(`https://api.moderatecontent.com/moderate/?key=${env.ModerateContentApiKey}&url=https://telegra.ph${url.pathname}${url.search}`);
const moderateData = await moderateResponse.json();
console.log("Moderate Data:", moderateData);

if (env.img_url) {
await env.img_url.put(params.id, "", {
metadata: { ListType: "None", Label: moderateData.rating_label, TimeStamp: time, liked: false },
});
let record = await env.img_url.getWithMetadata(params.id);
if (!record || !record.metadata) {
// Initialize metadata if it doesn't exist
console.log("metadata not found, initializing...");
record = {
metadata: {
ListType: "None",
Label: "None",
TimeStamp: Date.now(),
liked: false,
fileName: params.id,
fileSize: 0,
}
};
await env.img_url.put(params.id, "", { metadata: record.metadata });
}

if (moderateData.rating_label === "adult") {
return Response.redirect(`${url.origin}/block-img.html`, 302);
}
} else if (env.img_url) {
// Add image to KV with default metadata if ModerateContentApiKey is not available
console.log("KV not enabled for moderation, adding default metadata.");
await env.img_url.put(params.id, "", {
metadata: { ListType: "None", Label: "None", TimeStamp: time, liked: false },
});
const metadata = {
ListType: record.metadata.ListType || "None",
Label: record.metadata.Label || "None",
TimeStamp: record.metadata.TimeStamp || Date.now(),
liked: record.metadata.liked !== undefined ? record.metadata.liked : false,
fileName: record.metadata.fileName || params.id,
fileSize: record.metadata.fileSize || 0,
};

// Handle based on ListType and Label
if (metadata.ListType === "White") {
return response;
} else if (metadata.ListType === "Block" || metadata.Label === "adult") {
const referer = request.headers.get('Referer');
const redirectUrl = referer ? "https://static-res.pages.dev/teleimage/img-block-compressed.png" : `${url.origin}/block-img.html`;
return Response.redirect(redirectUrl, 302);
}

// Check if WhiteList_Mode is enabled
if (env.WhiteList_Mode === "true") {
return Response.redirect(`${url.origin}/whitelist-on.html`, 302);
}

// If no metadata or further actions required, moderate content and add to KV if needed
if (env.ModerateContentApiKey) {
const moderateResponse = await fetch(`https://api.moderatecontent.com/moderate/?key=${env.ModerateContentApiKey}&url=https://telegra.ph${url.pathname}${url.search}`);
const moderateData = await moderateResponse.json();
console.log("Moderate Data:", moderateData);
metadata.Label = moderateData.rating_label;

if (moderateData.rating_label === "adult") {
await env.img_url.put(params.id, "", { metadata });
return Response.redirect(`${url.origin}/block-img.html`, 302);
}
}

// save metadata directly, no additional conditions are needed
await env.img_url.put(params.id, "", { metadata });
return response;

}

async function getFilePath(env, file_id) {
22 changes: 20 additions & 2 deletions functions/upload.js
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ export async function onRequestPost(context) {

await errorHandling(context);
telemetryData(context);

const uploadFile = formData.get('file');
if (!uploadFile) {
throw new Error('No file uploaded');
@@ -27,6 +27,9 @@ export async function onRequestPost(context) {
if (uploadFile.type.startsWith('image/')) {
telegramFormData.append("photo", uploadFile);
apiEndpoint = 'sendPhoto';
} else if (uploadFile.type.startsWith('audio/')) {
telegramFormData.append("audio", uploadFile);
apiEndpoint = 'sendAudio';
} else {
telegramFormData.append("document", uploadFile);
apiEndpoint = 'sendDocument';
@@ -58,6 +61,20 @@ export async function onRequestPost(context) {
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 new Response(
JSON.stringify([{ 'src': `/file/${fileId}.${fileExtension}` }]),
{
@@ -88,6 +105,7 @@ function getFileId(response) {
}
if (result.document) return result.document.file_id;
if (result.video) return result.video.file_id;
if (result.audio) return result.audio.file_id;

return null;
}
}
1 change: 1 addition & 0 deletions music.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.