feat: configure image resizing in security settings

This commit is contained in:
MarSeventh
2026-07-28 11:54:01 +08:00
parent 7a398c5449
commit 39ae1a7407
32 changed files with 77 additions and 20 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover"><link rel="icon" href="/static/media/logo.png"><link rel="apple-touch-icon" href="/static/media/logo.png"><link rel="mask-icon" href="/static/media/logo.png" color="#f4b400"><meta name="description" content="Sanyue ImgHub - A modern file hosting platform"><meta name="keywords" content="Sanyue, ImgHub, file hosting, image hosting, cloud storage"><meta name="author" content="SanyueQi"><title>Sanyue ImgHub</title><script defer="defer" src="/js/chunk-vendors.6243340b.js"></script><script defer="defer" src="/js/app.739c9465.js"></script><link href="/css/chunk-vendors.f9cd4701.css" rel="stylesheet"><link href="/css/app.710cde74.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but sanyue_imghub doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover"><link rel="icon" href="/static/media/logo.png"><link rel="apple-touch-icon" href="/static/media/logo.png"><link rel="mask-icon" href="/static/media/logo.png" color="#f4b400"><meta name="description" content="Sanyue ImgHub - A modern file hosting platform"><meta name="keywords" content="Sanyue, ImgHub, file hosting, image hosting, cloud storage"><meta name="author" content="SanyueQi"><title>Sanyue ImgHub</title><script defer="defer" src="/js/chunk-vendors.6243340b.js"></script><script defer="defer" src="/js/app.50a5ba1e.js"></script><link href="/css/chunk-vendors.f9cd4701.css" rel="stylesheet"><link href="/css/app.710cde74.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but sanyue_imghub doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@@ -51,6 +51,16 @@ export async function onRequest(context) {
settings.access = newSettings.access || settings.access
settings.access.userSessionMaxAge = normalizeSessionMaxAgeDays(settings.access.userSessionMaxAge)
settings.access.adminSessionMaxAge = normalizeSessionMaxAgeDays(settings.access.adminSessionMaxAge)
settings.access.imageTransformEnabled = settings.access.imageTransformEnabled === true
const allowedSizes = normalizeImageTransformAllowedSizes(settings.access.imageTransformAllowedSizes)
if (allowedSizes.error) {
return new Response(JSON.stringify({ error: allowedSizes.error }), {
status: 400,
headers: { 'content-type': 'application/json' },
})
}
settings.access.imageTransformAllowedSizes = allowedSizes.value
// 处理认证设置:空密码表示不修改,_clear 标记表示清除密码
let userPasswordChanged = false;
@@ -172,6 +182,8 @@ export async function getSecurityConfig(db, env) {
const access = {
allowedDomains: kvAccess.allowedDomains || env.ALLOWED_DOMAINS || '',
whiteListMode: kvAccess.whiteListMode ?? env.WhiteList_Mode === 'true',
imageTransformEnabled: kvAccess.imageTransformEnabled === true,
imageTransformAllowedSizes: normalizeImageTransformAllowedSizes(kvAccess.imageTransformAllowedSizes).value,
// 新增会话安全策略字段
sessionSecure: kvAccess.sessionSecure ?? false,
userSessionMaxAge: normalizeSessionMaxAgeDays(kvAccess.userSessionMaxAge ?? 14),
@@ -189,6 +201,35 @@ export async function getSecurityConfig(db, env) {
return settings;
}
function normalizeImageTransformAllowedSizes(value) {
if (value === undefined || value === null || String(value).trim() === '') {
return { value: '' }
}
const sizes = String(value)
.split(',')
.map(size => size.trim().toLowerCase())
.filter(Boolean)
const normalized = []
for (const size of sizes) {
const match = size.match(/^(auto|[1-9]\d*)x(auto|[1-9]\d*)$/)
if (!match || (match[1] === 'auto' && match[2] === 'auto')) {
return { error: `Invalid image transform size: ${size}` }
}
for (const dimension of match.slice(1)) {
if (dimension !== 'auto' && Number(dimension) > 4096) {
return { error: `Invalid image transform size: ${size}` }
}
}
if (!normalized.includes(size)) normalized.push(size)
}
return { value: normalized.join(',') }
}
function normalizeIpQueryParams(params) {
if (!Array.isArray(params) || params.length === 0) {
return [{ key: 'ip', value: '{ip}' }];
+1 -1
View File
@@ -51,7 +51,7 @@ export async function onRequest(context) { // Contents of context object
const url = new URL(request.url);
context.url = url;
context.imageTransform = parseImageTransform(url, env);
context.imageTransform = parseImageTransform(url, securityConfig.access);
const imageTransformError = validateImageTransformRequest(request, context.imageTransform);
if (imageTransformError) {
return imageTransformError;
+11 -3
View File
@@ -13,7 +13,7 @@ const OUTPUT_FORMATS = new Map([
['image/svg+xml', 'image/png'],
]);
export function parseImageTransform(url, env = {}) {
export function parseImageTransform(url, accessConfig = {}) {
const widthResult = parseDimension(url.searchParams, 'width');
const heightResult = parseDimension(url.searchParams, 'height');
const fitResult = parseFit(url.searchParams);
@@ -30,6 +30,14 @@ export function parseImageTransform(url, env = {}) {
return { requested: false };
}
if (accessConfig.imageTransformEnabled !== true) {
return {
requested: true,
error: 'Image resizing is disabled',
errorStatus: 403,
};
}
const width = widthResult.value;
const height = heightResult.value;
const fit = fitResult.value;
@@ -42,7 +50,7 @@ export function parseImageTransform(url, env = {}) {
}
const sizeKey = `${width || 'auto'}x${height || 'auto'}`;
const allowedSizes = parseAllowedSizes(env.IMAGE_TRANSFORM_ALLOWED_SIZES);
const allowedSizes = parseAllowedSizes(accessConfig.imageTransformAllowedSizes);
if (allowedSizes.size > 0 && !allowedSizes.has(sizeKey)) {
return {
@@ -66,7 +74,7 @@ export function validateImageTransformRequest(request, imageTransform) {
if (!imageTransform.requested) return null;
if (imageTransform.error) {
return imageTransformError(imageTransform.error, 400);
return imageTransformError(imageTransform.error, imageTransform.errorStatus || 400);
}
if (request.method !== 'GET') {
+9 -1
View File
@@ -112,7 +112,15 @@ export async function fetchSecurityConfig(env, options = {}) {
customApi: { url: "", params: [{ key: "ip", value: "{ip}" }], responseFields: [] }
}
},
access: { allowedDomains: "", whiteListMode: false, sessionSecure: false, userSessionMaxAge: 14, adminSessionMaxAge: 14 }
access: {
allowedDomains: "",
whiteListMode: false,
imageTransformEnabled: false,
imageTransformAllowedSizes: "",
sessionSecure: false,
userSessionMaxAge: 14,
adminSessionMaxAge: 14
}
};
}
}