mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
fix(api): support int53 attachment sizes (#1261)
This commit is contained in:
1 parent
40d1e90a3c
commit
3a8ed1012f
22 files changed
+289
-89
No files matched your search
@@ -35,6 +35,7 @@ import {getPurgeQueue, getStorageService} from '../../middleware/ServiceSingleto
|
||||
import {getMessageSearchService} from '../../SearchFactory';
|
||||
import {deleteMessageSearchDocuments} from '../../search/MessageSearchIndexCleanup';
|
||||
import {searchExistingMessages} from '../../search/MessageSearchResultReconciler';
|
||||
import {assertSafeByteSize} from '../../utils/ByteSizeUtils';
|
||||
import type {AdminAuditService} from './AdminAuditService';
|
||||
|
||||
interface AdminMessageServiceDeps {
|
||||
@@ -330,7 +331,7 @@ export class AdminMessageService {
|
||||
content_type: attachment.content_type ?? null,
|
||||
width: attachment.width ?? null,
|
||||
height: attachment.height ?? null,
|
||||
size: attachment.size ?? null,
|
||||
size: attachment.size == null ? null : assertSafeByteSize(attachment.size, 'admin message attachment size'),
|
||||
ncmec_status: attachmentStatuses.get(attachment.id)?.status ?? 'not_submitted',
|
||||
ncmec_report_id: attachmentStatuses.get(attachment.id)?.ncmec_report_id ?? null,
|
||||
ncmec_failure_reason: attachmentStatuses.get(attachment.id)?.failure_reason ?? null,
|
||||
|
||||
@@ -38,6 +38,7 @@ import type {IARMessageContext, IARSubmission} from '../../report/IReportReposit
|
||||
import type {ReportService} from '../../report/ReportService';
|
||||
import {getReportSearchService} from '../../SearchFactory';
|
||||
import type {UserChannelService} from '../../user/services/UserChannelService';
|
||||
import {assertSafeByteSize} from '../../utils/ByteSizeUtils';
|
||||
import type {AdminAuditService} from './AdminAuditService';
|
||||
|
||||
interface AdminReportServiceDeps {
|
||||
@@ -580,7 +581,7 @@ export class AdminReportService {
|
||||
content_type: attachment.content_type ?? null,
|
||||
width: attachment.width ?? null,
|
||||
height: attachment.height ?? null,
|
||||
size: attachment.size != null ? Number(attachment.size) : null,
|
||||
size: attachment.size != null ? assertSafeByteSize(attachment.size, 'admin report attachment size') : null,
|
||||
ncmec_status: attachmentStatusesById.get(attachment.attachment_id.toString())?.status ?? 'not_submitted',
|
||||
ncmec_report_id: attachmentStatusesById.get(attachment.attachment_id.toString())?.ncmec_report_id ?? null,
|
||||
ncmec_failure_reason: attachmentStatusesById.get(attachment.attachment_id.toString())?.failure_reason ?? null,
|
||||
|
||||
@@ -4,6 +4,7 @@ import type {FavoriteMemeResponse} from '@fluxer/schema/src/domains/meme/MemeSch
|
||||
import {userIdToChannelId} from '../BrandedTypes';
|
||||
import {makeAttachmentCdnUrl} from '../channel/services/message/MessageHelpers';
|
||||
import type {FavoriteMeme} from '../models/FavoriteMeme';
|
||||
import {assertSafeByteSize} from '../utils/ByteSizeUtils';
|
||||
|
||||
export function mapFavoriteMemeToResponse(meme: FavoriteMeme): FavoriteMemeResponse {
|
||||
const url = makeAttachmentCdnUrl(userIdToChannelId(meme.userId), meme.attachmentId, meme.filename);
|
||||
@@ -17,7 +18,7 @@ export function mapFavoriteMemeToResponse(meme: FavoriteMeme): FavoriteMemeRespo
|
||||
filename: meme.filename,
|
||||
content_type: meme.contentType,
|
||||
content_hash: meme.contentHash ?? null,
|
||||
size: Number(meme.size),
|
||||
size: assertSafeByteSize(meme.size, 'favorite meme size'),
|
||||
width: meme.width ?? null,
|
||||
height: meme.height ?? null,
|
||||
duration: meme.duration ?? null,
|
||||
|
||||
@@ -6224,7 +6224,7 @@
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 9007199254740991,
|
||||
"format": "int64",
|
||||
"format": "int53",
|
||||
"description": "Pagination offset"
|
||||
}
|
||||
}
|
||||
@@ -27115,8 +27115,8 @@
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 2147483647,
|
||||
"format": "int32",
|
||||
"maximum": 9007199254740991,
|
||||
"format": "int53",
|
||||
"description": "The size of the attachment in bytes"
|
||||
},
|
||||
"url": {"anyOf": [{"type": "string"}, {"type": "null"}], "description": "The URL of the attachment"},
|
||||
@@ -27900,7 +27900,7 @@
|
||||
"type": "string",
|
||||
"description": "Temporary upload key to reference in message send payloads"
|
||||
},
|
||||
"file_size": {"$ref": "#/components/schemas/Int32Type"},
|
||||
"file_size": {"$ref": "#/components/schemas/NonNegativeSafeIntegerType"},
|
||||
"content_type": {"type": "string", "description": "Expected MIME type for this upload"},
|
||||
"upload_mode": {"type": "string", "enum": ["singlepart"]},
|
||||
"upload_url": {
|
||||
@@ -27910,6 +27910,7 @@
|
||||
},
|
||||
"required": ["id", "filename", "upload_filename", "file_size", "content_type", "upload_mode", "upload_url"]
|
||||
},
|
||||
"NonNegativeSafeIntegerType": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||
"MultipartPresignedAttachmentUploadResponseItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -27919,14 +27920,14 @@
|
||||
"type": "string",
|
||||
"description": "Temporary upload key to reference in message send payloads"
|
||||
},
|
||||
"file_size": {"$ref": "#/components/schemas/Int32Type"},
|
||||
"file_size": {"$ref": "#/components/schemas/NonNegativeSafeIntegerType"},
|
||||
"content_type": {"type": "string", "description": "Expected MIME type for this upload"},
|
||||
"upload_mode": {"type": "string", "enum": ["multipart"]},
|
||||
"upload_id": {
|
||||
"type": "string",
|
||||
"description": "S3 multipart upload identifier; required to complete the upload"
|
||||
},
|
||||
"part_size": {"$ref": "#/components/schemas/Int32Type"},
|
||||
"part_size": {"$ref": "#/components/schemas/NonNegativeSafeIntegerType"},
|
||||
"parts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -27972,7 +27973,7 @@
|
||||
"properties": {
|
||||
"id": {"$ref": "#/components/schemas/Int32Type"},
|
||||
"filename": {"type": "string", "description": "The name of the file that will be uploaded"},
|
||||
"file_size": {"$ref": "#/components/schemas/Int32Type"},
|
||||
"file_size": {"$ref": "#/components/schemas/NonNegativeSafeIntegerType"},
|
||||
"content_type": {"type": "string", "description": "MIME type the client will upload"}
|
||||
},
|
||||
"required": ["id", "filename", "file_size", "content_type"]
|
||||
@@ -28482,7 +28483,7 @@
|
||||
"type": "string",
|
||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?$"
|
||||
},
|
||||
{"type": "integer", "minimum": 0, "maximum": 8640000000000000, "format": "int64"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 8640000000000000, "format": "int53"},
|
||||
{"type": "null"}
|
||||
],
|
||||
"description": "ISO8601 timestamp for the embed"
|
||||
@@ -28599,7 +28600,7 @@
|
||||
"type": "string",
|
||||
"description": "Temporary upload key returned by the attachment upload endpoint"
|
||||
},
|
||||
"file_size": {"$ref": "#/components/schemas/Int32Type"},
|
||||
"file_size": {"$ref": "#/components/schemas/NonNegativeSafeIntegerType"},
|
||||
"content_type": {"type": "string", "description": "MIME type of the uploaded file"}
|
||||
},
|
||||
"required": ["id", "filename", "upload_filename", "file_size", "content_type"]
|
||||
@@ -29234,7 +29235,13 @@
|
||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||
"description": "Hash of the file content for deduplication"
|
||||
},
|
||||
"size": {"type": "number", "description": "File size in bytes"},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 9007199254740991,
|
||||
"format": "int53",
|
||||
"description": "File size in bytes"
|
||||
},
|
||||
"width": {
|
||||
"anyOf": [{"type": "integer", "format": "int53"}, {"type": "null"}],
|
||||
"description": "Width of the image or video in pixels"
|
||||
@@ -29311,7 +29318,7 @@
|
||||
},
|
||||
"embed_index": {
|
||||
"anyOf": [
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||
{"type": "null"}
|
||||
],
|
||||
"description": "Index of the message embed to save as a meme"
|
||||
@@ -29529,14 +29536,14 @@
|
||||
},
|
||||
"started_at_ms": {
|
||||
"anyOf": [
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||
{"type": "null"}
|
||||
],
|
||||
"description": "Session start Unix timestamp in milliseconds"
|
||||
},
|
||||
"expires_at_ms": {
|
||||
"anyOf": [
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||
{"type": "null"}
|
||||
],
|
||||
"description": "Session expiration Unix timestamp in milliseconds"
|
||||
@@ -29605,7 +29612,7 @@
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 9007199254740991,
|
||||
"format": "int64",
|
||||
"format": "int53",
|
||||
"description": "Unix timestamp in milliseconds when this heartbeat expires"
|
||||
}
|
||||
},
|
||||
@@ -30183,7 +30190,7 @@
|
||||
"explicit_content_filter": {"type": "number", "description": "The explicit content filter level"},
|
||||
"system_channel_id": {
|
||||
"oneOf": [
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||
{"type": "string"},
|
||||
{"type": "null"}
|
||||
],
|
||||
@@ -30209,7 +30216,7 @@
|
||||
"properties": {
|
||||
"id": {
|
||||
"oneOf": [
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||
{"type": "string"}
|
||||
],
|
||||
"description": "The template-local role ID"
|
||||
@@ -30218,14 +30225,14 @@
|
||||
"permissions": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"}
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"}
|
||||
],
|
||||
"description": "The permissions bitfield as a string (legacy)"
|
||||
},
|
||||
"permissions_new": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"}
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"}
|
||||
],
|
||||
"description": "The permissions bitfield as a string (preferred)"
|
||||
},
|
||||
@@ -30244,7 +30251,7 @@
|
||||
"properties": {
|
||||
"id": {
|
||||
"oneOf": [
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||
{"type": "string"}
|
||||
],
|
||||
"description": "The template-local channel ID"
|
||||
@@ -30255,7 +30262,7 @@
|
||||
"position": {"type": "number", "description": "The position of the channel"},
|
||||
"parent_id": {
|
||||
"oneOf": [
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||
{"type": "string"},
|
||||
{"type": "null"}
|
||||
],
|
||||
@@ -30279,7 +30286,7 @@
|
||||
"properties": {
|
||||
"id": {
|
||||
"oneOf": [
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||
{"type": "string"}
|
||||
],
|
||||
"description": "The ID of the role or user for this overwrite"
|
||||
@@ -30291,14 +30298,14 @@
|
||||
"allow": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"}
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"}
|
||||
],
|
||||
"description": "The allowed permissions bitfield as a string"
|
||||
},
|
||||
"deny": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"}
|
||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"}
|
||||
],
|
||||
"description": "The denied permissions bitfield as a string"
|
||||
}
|
||||
@@ -30960,7 +30967,7 @@
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 9007199254740991,
|
||||
"format": "int64",
|
||||
"format": "int53",
|
||||
"description": "New position for the channel"
|
||||
},
|
||||
"parent_id": {
|
||||
@@ -31336,7 +31343,7 @@
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 9007199254740991,
|
||||
"format": "int64",
|
||||
"format": "int53",
|
||||
"description": "Number of results to skip for pagination"
|
||||
},
|
||||
"role_ids": {
|
||||
@@ -33959,7 +33966,7 @@
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 9007199254740991,
|
||||
"format": "int64",
|
||||
"format": "int53",
|
||||
"description": "Page number for pagination (ignored when cursor is provided)"
|
||||
},
|
||||
"cursor": {
|
||||
@@ -35434,7 +35441,7 @@
|
||||
"type": "string",
|
||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?$"
|
||||
},
|
||||
{"type": "integer", "minimum": 0, "maximum": 8640000000000000, "format": "int64"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 8640000000000000, "format": "int53"},
|
||||
{"type": "null"}
|
||||
],
|
||||
"description": "When the mute expires"
|
||||
@@ -35479,7 +35486,7 @@
|
||||
"type": "string",
|
||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?$"
|
||||
},
|
||||
{"type": "integer", "minimum": 0, "maximum": 8640000000000000, "format": "int64"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 8640000000000000, "format": "int53"},
|
||||
{"type": "null"}
|
||||
],
|
||||
"description": "When the mute expires"
|
||||
@@ -35530,7 +35537,7 @@
|
||||
"type": "string",
|
||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?$"
|
||||
},
|
||||
{"type": "integer", "minimum": 0, "maximum": 8640000000000000, "format": "int64"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 8640000000000000, "format": "int53"},
|
||||
{"type": "null"}
|
||||
],
|
||||
"description": "When the mute expires"
|
||||
@@ -36416,7 +36423,7 @@
|
||||
"type": "integer",
|
||||
"maximum": 9007199254740991,
|
||||
"exclusiveMinimum": 0,
|
||||
"format": "int64"
|
||||
"format": "int53"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -36977,7 +36984,7 @@
|
||||
"type": "string",
|
||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?$"
|
||||
},
|
||||
{"type": "integer", "minimum": 0, "maximum": 8640000000000000, "format": "int64"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 8640000000000000, "format": "int53"},
|
||||
{"type": "null"}
|
||||
],
|
||||
"description": "When status resets"
|
||||
@@ -37157,7 +37164,7 @@
|
||||
"type": "string",
|
||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?$"
|
||||
},
|
||||
{"type": "integer", "minimum": 0, "maximum": 8640000000000000, "format": "int64"},
|
||||
{"type": "integer", "minimum": 0, "maximum": 8640000000000000, "format": "int53"},
|
||||
{"type": "null"}
|
||||
],
|
||||
"description": "When the custom status expires"
|
||||
@@ -37423,7 +37430,7 @@
|
||||
"description": "Description for the attachment (max 4096 characters)"
|
||||
},
|
||||
"content_type": {"type": "string", "description": "MIME type of the file"},
|
||||
"size": {"type": "integer", "format": "int53", "description": "Size of the file in bytes"},
|
||||
"size": {"$ref": "#/components/schemas/NonNegativeSafeIntegerType"},
|
||||
"url": {"type": "string", "description": "URL of the attachment"},
|
||||
"proxy_url": {"type": "string", "description": "Proxied URL of the attachment"},
|
||||
"height": {"type": "integer", "format": "int53", "description": "Height of the image/video in pixels"},
|
||||
@@ -37896,7 +37903,7 @@
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 9007199254740991,
|
||||
"format": "int64",
|
||||
"format": "int53",
|
||||
"description": "Unix timestamp for the attachment footer"
|
||||
},
|
||||
"author_name": {"type": "string", "description": "Name of the author"},
|
||||
|
||||
@@ -42,6 +42,7 @@ import type {Message} from '../../models/Message';
|
||||
import type {MessageSnapshot} from '../../models/MessageSnapshot';
|
||||
import type {StickerItem} from '../../models/StickerItem';
|
||||
import {mapUserToPartialResponse} from '../../user/UserMappers';
|
||||
import {assertSafeByteSize} from '../../utils/ByteSizeUtils';
|
||||
|
||||
class NoopNatsConnectionManager implements INatsConnectionManager {
|
||||
async connect(): Promise<void> {}
|
||||
@@ -373,7 +374,7 @@ export class RepositoryBackedMessageResponseDataService extends MessageResponseD
|
||||
description: attachment.description,
|
||||
content_type: attachment.contentType,
|
||||
content_hash: attachment.contentHash,
|
||||
size: Number(attachment.size),
|
||||
size: assertSafeByteSize(attachment.size, 'message attachment size'),
|
||||
url,
|
||||
proxy_url: url,
|
||||
width: attachment.width,
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
const MAX_SAFE_INTEGER_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
|
||||
|
||||
export function assertSafeByteSize(value: bigint | number, fieldName = 'byte size'): number {
|
||||
if (typeof value === 'bigint') {
|
||||
if (value < 0n || value > MAX_SAFE_INTEGER_BIGINT) {
|
||||
throw new Error(`${fieldName} must fit a non-negative JavaScript safe integer`);
|
||||
}
|
||||
return Number(value);
|
||||
}
|
||||
if (!Number.isSafeInteger(value) || value < 0) {
|
||||
throw new Error(`${fieldName} must fit a non-negative JavaScript safe integer`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
Reference in new issue
Block a user