This commit is contained in:
2026-05-09 13:41:07 -03:00
parent ff3db71084
commit 7b5d4afd07
4 changed files with 8 additions and 8 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ The server listens on `http://localhost:8080` by default.
- `OPENAI_MODERATION_MODEL`: moderation vision model, default `gpt-4o-mini` - `OPENAI_MODERATION_MODEL`: moderation vision model, default `gpt-4o-mini`
- `TRUST_PROXY`: set to `true` when running behind a trusted reverse proxy so upload limits use `X-Forwarded-For` - `TRUST_PROXY`: set to `true` when running behind a trusted reverse proxy so upload limits use `X-Forwarded-For`
Uploads accept PNG and JPEG images. The server rejects files over 5 MB, images over `6000x6000`, and images over 20 million pixels. Accepted uploads are decoded, metadata-stripped, resized so the longest edge is at most `1600px`, and stored as WebP. Uploads accept PNG and JPEG images. The server rejects files over 5 MB, any image edge over `6000px`, and images over 20 million pixels. Accepted uploads are decoded, metadata-stripped, resized so the longest edge is at most `1600px`, and stored as WebP.
Upload caps are 5 per hour per IP, 10 per day per IP, and 100 globally per day. AI-approved uploads publish immediately; ambiguous uploads are queued for the secret admin review page; likely illegal uploads are rejected immediately. Upload caps are 5 per hour per IP, 10 per day per IP, and 100 globally per day. AI-approved uploads publish immediately; ambiguous uploads are queued for the secret admin review page; likely illegal uploads are rejected immediately.
Files are stored under sharded date/hash paths: Files are stored under sharded date/hash paths:
+3 -1
View File
@@ -1,5 +1,6 @@
const PAGE_SIZE = 12; const PAGE_SIZE = 12;
const MAX_FILE_BYTES = 5 * 1024 * 1024; const MAX_FILE_BYTES = 5 * 1024 * 1024;
const MAX_IMAGE_PIXELS = 20_000_000;
const VIEW_DWELL_MS = 1000; const VIEW_DWELL_MS = 1000;
const VIEW_THRESHOLD = 0.9; const VIEW_THRESHOLD = 0.9;
const VIEW_DEDUPE_MS = 24 * 60 * 60 * 1000; const VIEW_DEDUPE_MS = 24 * 60 * 60 * 1000;
@@ -193,7 +194,8 @@ async function validateClientFile(file) {
try { try {
const dimensions = await readImageDimensions(file); const dimensions = await readImageDimensions(file);
if (dimensions.width > 6000 || dimensions.height > 6000) return 'IMAGE EXCEEDS 6000x6000.'; if (dimensions.width > 6000 || dimensions.height > 6000) return 'IMAGE EDGE EXCEEDS 6000PX.';
if (dimensions.width * dimensions.height > MAX_IMAGE_PIXELS) return 'IMAGE EXCEEDS 20MP.';
} catch { } catch {
return 'IMAGE COULD NOT BE INSPECTED.'; return 'IMAGE COULD NOT BE INSPECTED.';
} }
+4 -2
View File
@@ -86,9 +86,11 @@
</label> </label>
<div class="upload-rules"> <div class="upload-rules">
<span>MAX_SIZE: 5MB</span> <span>MAX_SIZE: 5MB</span>
<span>REQUIRED_RATIO: 1:1 SQUARE</span> <span>RATIO: ANY</span>
<span>OUTPUT_FORMAT: WEBP</span> <span>OUTPUT_FORMAT: WEBP</span>
<span>MAX_DIMENSIONS: 6000x6000</span> <span>MAX_INPUT_EDGE: 6000PX</span>
<span>MAX_PIXELS: 20MP</span>
<span>OUTPUT_MAX_EDGE: 1600PX</span>
</div> </div>
<div class="modal-actions"> <div class="modal-actions">
<button class="secondary-action" id="cancel-upload" type="button">CANCEL</button> <button class="secondary-action" id="cancel-upload" type="button">CANCEL</button>
-4
View File
@@ -23,10 +23,6 @@ export function validateImage(buffer, limits) {
if (image.width * image.height > limits.maxPixels) { if (image.width * image.height > limits.maxPixels) {
throw new HttpError(413, 'Image has too many pixels.'); throw new HttpError(413, 'Image has too many pixels.');
} }
if (limits.requireSquare && image.width !== image.height) {
throw new HttpError(422, 'Image must be square.');
}
return { ...image, byteSize: buffer.length }; return { ...image, byteSize: buffer.length };
} }