diff --git a/admin.php b/admin.php
index 106bd40..a9b2862 100644
--- a/admin.php
+++ b/admin.php
@@ -287,10 +287,12 @@ function fb_admin_save_settings() {
else { fb_set_setting('footer.credit_url', $curl); }
$ints = [
- 'upload.image_max_per_post' => [0, 20],
- 'upload.video_max_per_post' => [0, 10],
- 'upload.retention_days' => [1, 3650],
- 'upload.upload_per_hour' => [1, 2000],
+ // 区间下限统一改 0:0 = 不限制(见 #4)。
+ // 只有 image_max_side 例外 —— 边长 0 没有意义,它是压缩目标不是限制。
+ 'upload.image_max_per_post' => [0, 1000],
+ 'upload.video_max_per_post' => [0, 1000],
+ 'upload.retention_days' => [0, 36500],
+ 'upload.upload_per_hour' => [0, 100000],
'upload.image_max_side' => [480, 4096],
];
foreach ($ints as $k => $range) {
@@ -298,12 +300,16 @@ function fb_admin_save_settings() {
$v = max($range[0], min($range[1], $v));
fb_set_setting($k, $v);
}
+ // 图片/视频上传总开关(0=不限个数后,开关必须独立存在)
+ fb_set_setting('upload.image_on', (int)(bool)$post('upload.image_on'));
+ fb_set_setting('upload.video_on', (int)(bool)$post('upload.video_on'));
// MB → 字节
+ // 下限改 0 = 不限制(见 #4)
$mbs = [
- 'upload.image_max_bytes' => [0.2, 50],
- 'upload.video_max_bytes' => [1, 500],
- 'upload.total_cap_bytes' => [10, 100000],
+ 'upload.image_max_bytes' => [0, 1024],
+ 'upload.video_max_bytes' => [0, 10240],
+ 'upload.total_cap_bytes' => [0, 1048576],
];
foreach ($mbs as $k => $range) {
$mb = (float)$post($k, 0);
@@ -762,23 +768,48 @@ fb_layout_head('管理后台', ['robots' => 'noindex,nofollow']);
本机:文件落在站点 uploads/ 目录;OSS:文件经服务器代理写入私有桶(走内网 Endpoint),公网无法直连桶。两种方式都需要服务器磁盘能放下单个上传文件。前台展示一律经 media.php 代理——无论存哪,页面上的图片/视频地址都是 media.php?k=…,不暴露 uploads/ 真实路径,也不出现任何 OSS 桶信息。
-
+ 填 0 表示不限制。';
+ ?>
+
+
+
+
+
+
+
+ 填 0 时注意:服务器磁盘可能被大图写满。
+
+
+
+ 填 0 时注意:服务器磁盘可能被大视频写满。
+
+ 填 0 表示不限总量,磁盘写满后上传会失败。
+
+ 填 0 = 永不自动清理。
+
+
+
+ 这是压缩目标边长,不是限制项,最小 480。
阿里云 OSS(选「OSS」时才需要填)
diff --git a/assets/app.js b/assets/app.js
index 2bf3226..e3dae10 100644
--- a/assets/app.js
+++ b/assets/app.js
@@ -301,7 +301,9 @@
alert('「' + file.name + '」超过 ' + human(maxBytes) + ' 上限。');
continue;
}
- if (countOf(kind) >= maxCount) {
+ // maxCount = 0 表示不限个数(后台可填 0),此时不做任何拦截。
+ // 别写成 `count >= 0`,那样等于一个都不让传。
+ if (maxCount > 0 && countOf(kind) >= maxCount) {
alert(label + '最多上传 ' + maxCount + ' 个,请先移除已上传的文件。');
break;
}
diff --git a/lib/attach.php b/lib/attach.php
index ce6e2c1..9a36e2a 100644
--- a/lib/attach.php
+++ b/lib/attach.php
@@ -201,10 +201,17 @@ function fb_attach_delete($key) {
return fb_oss_delete($key);
}
-/** 该类型是否可用(数量上限为 0 或未配置则视为关闭) */
+/**
+ * 该类型是否可用。
+ *
+ * ⚠️ 原先靠「数量上限是否为 0」判断开关 —— 但 0 现在表示「不限个数」,
+ * 两者语义冲突(一个说"关掉"、一个说"不限制")。改为看独立开关:
+ * upload.image_on / upload.video_on,默认都开。
+ */
function fb_upload_kind_on($kind) {
if (!fb_store_enabled()) { return false; }
- return (int)fb_upload_cfg($kind . '_max_per_post', 0) > 0;
+ $key = $kind . '_on';
+ return (bool)fb_upload_cfg($key, true);
}
/** 归一化扩展名 */
@@ -313,9 +320,12 @@ function fb_attach_parse($raw) {
}
$counts[$kind]++;
- if ($counts[$kind] > (int)fb_upload_cfg($kind . '_max_per_post', 0)) {
+ // 0 = 不限个数。注意别写成 `count > max`,那样 max=0 时传一个就超限,
+ // 等于「填 0 反而一个都不让传」,与用户意图相反。
+ $maxPerPost = (int)fb_upload_cfg($kind . '_max_per_post', 0);
+ if ($maxPerPost > 0 && $counts[$kind] > $maxPerPost) {
return [false, [], ($kind === 'image' ? '图片最多上传 ' : '视频最多上传 ')
- . (int)fb_upload_cfg($kind . '_max_per_post', 0) . ' 个。'];
+ . $maxPerPost . ' 个。'];
}
$seen[$key] = true;
diff --git a/lib/layout.php b/lib/layout.php
index 2c1bcab..703a179 100644
--- a/lib/layout.php
+++ b/lib/layout.php
@@ -139,9 +139,12 @@ function fb_uploaders(array $existing = []) {
$maxB = (int)fb_upload_cfg($kind . '_max_bytes', 0);
$accept = implode(',', (array)fb_upload_cfg($kind . '_mimes', []));
$label = $isImg ? '图片' : '视频';
- $desc = $isImg
- ? ('最多 ' . $maxN . ' 张,单张不超过 ' . fb_size_h($maxB) . ';大图会自动压缩后上传')
- : ('最多 ' . $maxN . ' 个,单个不超过 ' . fb_size_h($maxB));
+ // 0 表示不限制,文案别写成「最多 0 张」
+ $cntTxt = $maxN > 0 ? ('最多 ' . $maxN . ($isImg ? ' 张' : ' 个')) : '数量不限';
+ $sizeTxt = $maxB > 0
+ ? ('单' . ($isImg ? '张' : '个') . '不超过 ' . fb_size_h($maxB))
+ : '大小不限';
+ $desc = $cntTxt . ',' . $sizeTxt . ($isImg ? ';大图会自动压缩后上传' : '');
?>
prepare('SELECT COUNT(*) FROM rate_limit WHERE ip_hash=? AND action=? AND created_at>=?');
$st->execute([fb_ip_hash(), $action, $since]);
- return ((int)$st->fetchColumn()) < (int)$limit;
+ return ((int)$st->fetchColumn()) < $limit;
}
function fb_rate_hit($action) {
$pdo = fb_db();
@@ -382,6 +392,7 @@ function fb_setting_keys() {
'footer.show_credit', 'footer.credit_url',
'installed_at',
'attachment.storage',
+ 'upload.image_on', 'upload.video_on',
'upload.image_max_per_post', 'upload.image_max_bytes',
'upload.video_max_per_post', 'upload.video_max_bytes',
'upload.total_cap_bytes', 'upload.retention_days',