mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
fix(api): support int53 attachment sizes (#1261)
This commit is contained in:
@@ -8977,7 +8977,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 8640000000000000,
|
"maximum": 8640000000000000,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"description": "Inclusive start timestamp in milliseconds"
|
"description": "Inclusive start timestamp in milliseconds"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -8989,7 +8989,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 8640000000000000,
|
"maximum": 8640000000000000,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"description": "Inclusive end timestamp in milliseconds"
|
"description": "Inclusive end timestamp in milliseconds"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -9087,7 +9087,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 8640000000000000,
|
"maximum": 8640000000000000,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"description": "Inclusive start timestamp in milliseconds"
|
"description": "Inclusive start timestamp in milliseconds"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -9099,7 +9099,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 8640000000000000,
|
"maximum": 8640000000000000,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"description": "Inclusive end timestamp in milliseconds"
|
"description": "Inclusive end timestamp in milliseconds"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -10613,7 +10613,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"description": "Number of entries to skip"
|
"description": "Number of entries to skip"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10645,7 +10645,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"description": "Number of entries to skip"
|
"description": "Number of entries to skip"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11147,7 +11147,7 @@
|
|||||||
"amount_cents": {
|
"amount_cents": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"exclusiveMinimum": true
|
"exclusiveMinimum": true
|
||||||
},
|
},
|
||||||
@@ -11981,7 +11981,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"guild_id": {"$ref": "#/components/schemas/SnowflakeType"},
|
"guild_id": {"$ref": "#/components/schemas/SnowflakeType"},
|
||||||
"limit": {"type": "integer", "minimum": 1, "maximum": 200, "format": "int32"},
|
"limit": {"type": "integer", "minimum": 1, "maximum": 200, "format": "int32"},
|
||||||
"offset": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"}
|
"offset": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"}
|
||||||
},
|
},
|
||||||
"required": ["guild_id"]
|
"required": ["guild_id"]
|
||||||
},
|
},
|
||||||
@@ -12219,7 +12219,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"query": {"type": "string"},
|
"query": {"type": "string"},
|
||||||
"limit": {"type": "integer", "minimum": 1, "maximum": 200, "format": "int32"},
|
"limit": {"type": "integer", "minimum": 1, "maximum": 200, "format": "int32"},
|
||||||
"offset": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"}
|
"offset": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ShutdownGuildRequest": {
|
"ShutdownGuildRequest": {
|
||||||
@@ -12439,9 +12439,9 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64"
|
"format": "int53"
|
||||||
},
|
},
|
||||||
"use_count": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
"use_count": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||||
"revoked_at": {"nullable": true, "type": "string", "format": "date-time"},
|
"revoked_at": {"nullable": true, "type": "string", "format": "date-time"},
|
||||||
"approval_required": {"type": "boolean"},
|
"approval_required": {"type": "boolean"},
|
||||||
"last_used_at": {"nullable": true, "type": "string", "format": "date-time"},
|
"last_used_at": {"nullable": true, "type": "string", "format": "date-time"},
|
||||||
@@ -12668,7 +12668,7 @@
|
|||||||
"logo_uri": {"nullable": true, "type": "string"},
|
"logo_uri": {"nullable": true, "type": "string"},
|
||||||
"tos_uri": {"nullable": true, "type": "string"},
|
"tos_uri": {"nullable": true, "type": "string"},
|
||||||
"policy_uri": {"nullable": true, "type": "string"},
|
"policy_uri": {"nullable": true, "type": "string"},
|
||||||
"key_count": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"}
|
"key_count": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"enabled",
|
"enabled",
|
||||||
@@ -12698,7 +12698,7 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"exclusiveMinimum": true
|
"exclusiveMinimum": true
|
||||||
},
|
},
|
||||||
@@ -12706,7 +12706,7 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"exclusiveMinimum": true
|
"exclusiveMinimum": true
|
||||||
},
|
},
|
||||||
@@ -12715,7 +12715,7 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"exclusiveMinimum": true
|
"exclusiveMinimum": true
|
||||||
},
|
},
|
||||||
@@ -12723,7 +12723,7 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"exclusiveMinimum": true
|
"exclusiveMinimum": true
|
||||||
},
|
},
|
||||||
@@ -12737,14 +12737,14 @@
|
|||||||
"min_lifetime_days": {
|
"min_lifetime_days": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"exclusiveMinimum": true
|
"exclusiveMinimum": true
|
||||||
},
|
},
|
||||||
"max_lifetime_days": {
|
"max_lifetime_days": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"exclusiveMinimum": true
|
"exclusiveMinimum": true
|
||||||
},
|
},
|
||||||
@@ -12752,14 +12752,14 @@
|
|||||||
"renew_threshold_days": {
|
"renew_threshold_days": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"exclusiveMinimum": true
|
"exclusiveMinimum": true
|
||||||
},
|
},
|
||||||
"renew_window_days": {
|
"renew_window_days": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"exclusiveMinimum": true
|
"exclusiveMinimum": true
|
||||||
}
|
}
|
||||||
@@ -12871,9 +12871,9 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64"
|
"format": "int53"
|
||||||
},
|
},
|
||||||
"use_count": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
"use_count": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||||
"revoked_at": {"nullable": true, "type": "string", "format": "date-time"},
|
"revoked_at": {"nullable": true, "type": "string", "format": "date-time"},
|
||||||
"approval_required": {"type": "boolean"},
|
"approval_required": {"type": "boolean"},
|
||||||
"last_used_at": {"nullable": true, "type": "string", "format": "date-time"},
|
"last_used_at": {"nullable": true, "type": "string", "format": "date-time"},
|
||||||
@@ -13064,7 +13064,7 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"exclusiveMinimum": true
|
"exclusiveMinimum": true
|
||||||
},
|
},
|
||||||
@@ -13072,7 +13072,7 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"exclusiveMinimum": true
|
"exclusiveMinimum": true
|
||||||
},
|
},
|
||||||
@@ -13081,7 +13081,7 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"exclusiveMinimum": true
|
"exclusiveMinimum": true
|
||||||
},
|
},
|
||||||
@@ -13089,7 +13089,7 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"exclusiveMinimum": true
|
"exclusiveMinimum": true
|
||||||
}
|
}
|
||||||
@@ -13296,7 +13296,7 @@
|
|||||||
},
|
},
|
||||||
"limits": {
|
"limits": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": {"type": "number", "minimum": 0},
|
"additionalProperties": {"$ref": "#/components/schemas/NonNegativeSafeIntegerType"},
|
||||||
"description": "Per-limit key values"
|
"description": "Per-limit key values"
|
||||||
},
|
},
|
||||||
"modifiedFields": {"type": "array", "items": {"type": "string"}}
|
"modifiedFields": {"type": "array", "items": {"type": "string"}}
|
||||||
@@ -13352,6 +13352,7 @@
|
|||||||
"limit_keys"
|
"limit_keys"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"NonNegativeSafeIntegerType": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||||
"LimitConfigUpdateRequest": {
|
"LimitConfigUpdateRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -13387,7 +13388,7 @@
|
|||||||
},
|
},
|
||||||
"limits": {
|
"limits": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": {"type": "number", "minimum": 0},
|
"additionalProperties": {"$ref": "#/components/schemas/NonNegativeSafeIntegerType"},
|
||||||
"description": "Per-limit key values"
|
"description": "Per-limit key values"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -13455,7 +13456,7 @@
|
|||||||
"content_type": {"nullable": true, "type": "string"},
|
"content_type": {"nullable": true, "type": "string"},
|
||||||
"width": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/Int32Type"}]},
|
"width": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/Int32Type"}]},
|
||||||
"height": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/Int32Type"}]},
|
"height": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/Int32Type"}]},
|
||||||
"size": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/Int32Type"}]},
|
"size": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/NonNegativeSafeIntegerType"}]},
|
||||||
"ncmec_status": {
|
"ncmec_status": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["not_submitted", "submitted", "failed"],
|
"enum": ["not_submitted", "submitted", "failed"],
|
||||||
@@ -14124,8 +14125,8 @@
|
|||||||
"size": {
|
"size": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 2147483647,
|
"maximum": 9007199254740991,
|
||||||
"format": "int32",
|
"format": "int53",
|
||||||
"description": "The size of the attachment in bytes"
|
"description": "The size of the attachment in bytes"
|
||||||
},
|
},
|
||||||
"url": {"description": "The URL of the attachment", "nullable": true, "type": "string"},
|
"url": {"description": "The URL of the attachment", "nullable": true, "type": "string"},
|
||||||
@@ -14387,7 +14388,7 @@
|
|||||||
"items": {"$ref": "#/components/schemas/MessageResponseSchema"},
|
"items": {"$ref": "#/components/schemas/MessageResponseSchema"},
|
||||||
"maxItems": 100
|
"maxItems": 100
|
||||||
},
|
},
|
||||||
"total": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"}
|
"total": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"}
|
||||||
},
|
},
|
||||||
"required": ["messages", "total"]
|
"required": ["messages", "total"]
|
||||||
},
|
},
|
||||||
@@ -14405,7 +14406,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"success": {"type": "boolean", "enum": [true]},
|
"success": {"type": "boolean", "enum": [true]},
|
||||||
"job_id": {"type": "string"},
|
"job_id": {"type": "string"},
|
||||||
"requested": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"}
|
"requested": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"}
|
||||||
},
|
},
|
||||||
"required": ["success", "job_id"]
|
"required": ["success", "job_id"]
|
||||||
},
|
},
|
||||||
@@ -14558,7 +14559,10 @@
|
|||||||
"content_type": {"nullable": true, "type": "string"},
|
"content_type": {"nullable": true, "type": "string"},
|
||||||
"width": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/Int32Type"}]},
|
"width": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/Int32Type"}]},
|
||||||
"height": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/Int32Type"}]},
|
"height": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/Int32Type"}]},
|
||||||
"size": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/Int32Type"}]},
|
"size": {
|
||||||
|
"nullable": true,
|
||||||
|
"allOf": [{"$ref": "#/components/schemas/NonNegativeSafeIntegerType"}]
|
||||||
|
},
|
||||||
"ncmec_status": {
|
"ncmec_status": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["not_submitted", "submitted", "failed"],
|
"enum": ["not_submitted", "submitted", "failed"],
|
||||||
@@ -14664,7 +14668,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"description": "Number of reports to skip"
|
"description": "Number of reports to skip"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14712,7 +14716,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"description": "Number of entries to skip"
|
"description": "Number of entries to skip"
|
||||||
},
|
},
|
||||||
"reporter_id": {"$ref": "#/components/schemas/SnowflakeType"},
|
"reporter_id": {"$ref": "#/components/schemas/SnowflakeType"},
|
||||||
@@ -15435,7 +15439,7 @@
|
|||||||
"email": {"type": "string"},
|
"email": {"type": "string"},
|
||||||
"last_active_ip": {"type": "string"},
|
"last_active_ip": {"type": "string"},
|
||||||
"limit": {"type": "integer", "minimum": 1, "maximum": 200, "format": "int32"},
|
"limit": {"type": "integer", "minimum": 1, "maximum": 200, "format": "int32"},
|
||||||
"offset": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"}
|
"offset": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SendPasswordResetRequest": {
|
"SendPasswordResetRequest": {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import {getPurgeQueue, getStorageService} from '../../middleware/ServiceSingleto
|
|||||||
import {getMessageSearchService} from '../../SearchFactory';
|
import {getMessageSearchService} from '../../SearchFactory';
|
||||||
import {deleteMessageSearchDocuments} from '../../search/MessageSearchIndexCleanup';
|
import {deleteMessageSearchDocuments} from '../../search/MessageSearchIndexCleanup';
|
||||||
import {searchExistingMessages} from '../../search/MessageSearchResultReconciler';
|
import {searchExistingMessages} from '../../search/MessageSearchResultReconciler';
|
||||||
|
import {assertSafeByteSize} from '../../utils/ByteSizeUtils';
|
||||||
import type {AdminAuditService} from './AdminAuditService';
|
import type {AdminAuditService} from './AdminAuditService';
|
||||||
|
|
||||||
interface AdminMessageServiceDeps {
|
interface AdminMessageServiceDeps {
|
||||||
@@ -330,7 +331,7 @@ export class AdminMessageService {
|
|||||||
content_type: attachment.content_type ?? null,
|
content_type: attachment.content_type ?? null,
|
||||||
width: attachment.width ?? null,
|
width: attachment.width ?? null,
|
||||||
height: attachment.height ?? 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_status: attachmentStatuses.get(attachment.id)?.status ?? 'not_submitted',
|
||||||
ncmec_report_id: attachmentStatuses.get(attachment.id)?.ncmec_report_id ?? null,
|
ncmec_report_id: attachmentStatuses.get(attachment.id)?.ncmec_report_id ?? null,
|
||||||
ncmec_failure_reason: attachmentStatuses.get(attachment.id)?.failure_reason ?? 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 type {ReportService} from '../../report/ReportService';
|
||||||
import {getReportSearchService} from '../../SearchFactory';
|
import {getReportSearchService} from '../../SearchFactory';
|
||||||
import type {UserChannelService} from '../../user/services/UserChannelService';
|
import type {UserChannelService} from '../../user/services/UserChannelService';
|
||||||
|
import {assertSafeByteSize} from '../../utils/ByteSizeUtils';
|
||||||
import type {AdminAuditService} from './AdminAuditService';
|
import type {AdminAuditService} from './AdminAuditService';
|
||||||
|
|
||||||
interface AdminReportServiceDeps {
|
interface AdminReportServiceDeps {
|
||||||
@@ -580,7 +581,7 @@ export class AdminReportService {
|
|||||||
content_type: attachment.content_type ?? null,
|
content_type: attachment.content_type ?? null,
|
||||||
width: attachment.width ?? null,
|
width: attachment.width ?? null,
|
||||||
height: attachment.height ?? 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_status: attachmentStatusesById.get(attachment.attachment_id.toString())?.status ?? 'not_submitted',
|
||||||
ncmec_report_id: attachmentStatusesById.get(attachment.attachment_id.toString())?.ncmec_report_id ?? null,
|
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,
|
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 {userIdToChannelId} from '../BrandedTypes';
|
||||||
import {makeAttachmentCdnUrl} from '../channel/services/message/MessageHelpers';
|
import {makeAttachmentCdnUrl} from '../channel/services/message/MessageHelpers';
|
||||||
import type {FavoriteMeme} from '../models/FavoriteMeme';
|
import type {FavoriteMeme} from '../models/FavoriteMeme';
|
||||||
|
import {assertSafeByteSize} from '../utils/ByteSizeUtils';
|
||||||
|
|
||||||
export function mapFavoriteMemeToResponse(meme: FavoriteMeme): FavoriteMemeResponse {
|
export function mapFavoriteMemeToResponse(meme: FavoriteMeme): FavoriteMemeResponse {
|
||||||
const url = makeAttachmentCdnUrl(userIdToChannelId(meme.userId), meme.attachmentId, meme.filename);
|
const url = makeAttachmentCdnUrl(userIdToChannelId(meme.userId), meme.attachmentId, meme.filename);
|
||||||
@@ -17,7 +18,7 @@ export function mapFavoriteMemeToResponse(meme: FavoriteMeme): FavoriteMemeRespo
|
|||||||
filename: meme.filename,
|
filename: meme.filename,
|
||||||
content_type: meme.contentType,
|
content_type: meme.contentType,
|
||||||
content_hash: meme.contentHash ?? null,
|
content_hash: meme.contentHash ?? null,
|
||||||
size: Number(meme.size),
|
size: assertSafeByteSize(meme.size, 'favorite meme size'),
|
||||||
width: meme.width ?? null,
|
width: meme.width ?? null,
|
||||||
height: meme.height ?? null,
|
height: meme.height ?? null,
|
||||||
duration: meme.duration ?? null,
|
duration: meme.duration ?? null,
|
||||||
|
|||||||
@@ -6224,7 +6224,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"description": "Pagination offset"
|
"description": "Pagination offset"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27115,8 +27115,8 @@
|
|||||||
"size": {
|
"size": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 2147483647,
|
"maximum": 9007199254740991,
|
||||||
"format": "int32",
|
"format": "int53",
|
||||||
"description": "The size of the attachment in bytes"
|
"description": "The size of the attachment in bytes"
|
||||||
},
|
},
|
||||||
"url": {"anyOf": [{"type": "string"}, {"type": "null"}], "description": "The URL of the attachment"},
|
"url": {"anyOf": [{"type": "string"}, {"type": "null"}], "description": "The URL of the attachment"},
|
||||||
@@ -27900,7 +27900,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Temporary upload key to reference in message send payloads"
|
"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"},
|
"content_type": {"type": "string", "description": "Expected MIME type for this upload"},
|
||||||
"upload_mode": {"type": "string", "enum": ["singlepart"]},
|
"upload_mode": {"type": "string", "enum": ["singlepart"]},
|
||||||
"upload_url": {
|
"upload_url": {
|
||||||
@@ -27910,6 +27910,7 @@
|
|||||||
},
|
},
|
||||||
"required": ["id", "filename", "upload_filename", "file_size", "content_type", "upload_mode", "upload_url"]
|
"required": ["id", "filename", "upload_filename", "file_size", "content_type", "upload_mode", "upload_url"]
|
||||||
},
|
},
|
||||||
|
"NonNegativeSafeIntegerType": {"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||||
"MultipartPresignedAttachmentUploadResponseItem": {
|
"MultipartPresignedAttachmentUploadResponseItem": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -27919,14 +27920,14 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Temporary upload key to reference in message send payloads"
|
"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"},
|
"content_type": {"type": "string", "description": "Expected MIME type for this upload"},
|
||||||
"upload_mode": {"type": "string", "enum": ["multipart"]},
|
"upload_mode": {"type": "string", "enum": ["multipart"]},
|
||||||
"upload_id": {
|
"upload_id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "S3 multipart upload identifier; required to complete the upload"
|
"description": "S3 multipart upload identifier; required to complete the upload"
|
||||||
},
|
},
|
||||||
"part_size": {"$ref": "#/components/schemas/Int32Type"},
|
"part_size": {"$ref": "#/components/schemas/NonNegativeSafeIntegerType"},
|
||||||
"parts": {
|
"parts": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
@@ -27972,7 +27973,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"id": {"$ref": "#/components/schemas/Int32Type"},
|
"id": {"$ref": "#/components/schemas/Int32Type"},
|
||||||
"filename": {"type": "string", "description": "The name of the file that will be uploaded"},
|
"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"}
|
"content_type": {"type": "string", "description": "MIME type the client will upload"}
|
||||||
},
|
},
|
||||||
"required": ["id", "filename", "file_size", "content_type"]
|
"required": ["id", "filename", "file_size", "content_type"]
|
||||||
@@ -28482,7 +28483,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?$"
|
"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"}
|
{"type": "null"}
|
||||||
],
|
],
|
||||||
"description": "ISO8601 timestamp for the embed"
|
"description": "ISO8601 timestamp for the embed"
|
||||||
@@ -28599,7 +28600,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Temporary upload key returned by the attachment upload endpoint"
|
"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"}
|
"content_type": {"type": "string", "description": "MIME type of the uploaded file"}
|
||||||
},
|
},
|
||||||
"required": ["id", "filename", "upload_filename", "file_size", "content_type"]
|
"required": ["id", "filename", "upload_filename", "file_size", "content_type"]
|
||||||
@@ -29234,7 +29235,13 @@
|
|||||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
||||||
"description": "Hash of the file content for deduplication"
|
"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": {
|
"width": {
|
||||||
"anyOf": [{"type": "integer", "format": "int53"}, {"type": "null"}],
|
"anyOf": [{"type": "integer", "format": "int53"}, {"type": "null"}],
|
||||||
"description": "Width of the image or video in pixels"
|
"description": "Width of the image or video in pixels"
|
||||||
@@ -29311,7 +29318,7 @@
|
|||||||
},
|
},
|
||||||
"embed_index": {
|
"embed_index": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||||
{"type": "null"}
|
{"type": "null"}
|
||||||
],
|
],
|
||||||
"description": "Index of the message embed to save as a meme"
|
"description": "Index of the message embed to save as a meme"
|
||||||
@@ -29529,14 +29536,14 @@
|
|||||||
},
|
},
|
||||||
"started_at_ms": {
|
"started_at_ms": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||||
{"type": "null"}
|
{"type": "null"}
|
||||||
],
|
],
|
||||||
"description": "Session start Unix timestamp in milliseconds"
|
"description": "Session start Unix timestamp in milliseconds"
|
||||||
},
|
},
|
||||||
"expires_at_ms": {
|
"expires_at_ms": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||||
{"type": "null"}
|
{"type": "null"}
|
||||||
],
|
],
|
||||||
"description": "Session expiration Unix timestamp in milliseconds"
|
"description": "Session expiration Unix timestamp in milliseconds"
|
||||||
@@ -29605,7 +29612,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"description": "Unix timestamp in milliseconds when this heartbeat expires"
|
"description": "Unix timestamp in milliseconds when this heartbeat expires"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -30183,7 +30190,7 @@
|
|||||||
"explicit_content_filter": {"type": "number", "description": "The explicit content filter level"},
|
"explicit_content_filter": {"type": "number", "description": "The explicit content filter level"},
|
||||||
"system_channel_id": {
|
"system_channel_id": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||||
{"type": "string"},
|
{"type": "string"},
|
||||||
{"type": "null"}
|
{"type": "null"}
|
||||||
],
|
],
|
||||||
@@ -30209,7 +30216,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||||
{"type": "string"}
|
{"type": "string"}
|
||||||
],
|
],
|
||||||
"description": "The template-local role ID"
|
"description": "The template-local role ID"
|
||||||
@@ -30218,14 +30225,14 @@
|
|||||||
"permissions": {
|
"permissions": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{"type": "string"},
|
{"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)"
|
"description": "The permissions bitfield as a string (legacy)"
|
||||||
},
|
},
|
||||||
"permissions_new": {
|
"permissions_new": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{"type": "string"},
|
{"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)"
|
"description": "The permissions bitfield as a string (preferred)"
|
||||||
},
|
},
|
||||||
@@ -30244,7 +30251,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||||
{"type": "string"}
|
{"type": "string"}
|
||||||
],
|
],
|
||||||
"description": "The template-local channel ID"
|
"description": "The template-local channel ID"
|
||||||
@@ -30255,7 +30262,7 @@
|
|||||||
"position": {"type": "number", "description": "The position of the channel"},
|
"position": {"type": "number", "description": "The position of the channel"},
|
||||||
"parent_id": {
|
"parent_id": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||||
{"type": "string"},
|
{"type": "string"},
|
||||||
{"type": "null"}
|
{"type": "null"}
|
||||||
],
|
],
|
||||||
@@ -30279,7 +30286,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int64"},
|
{"type": "integer", "minimum": 0, "maximum": 9007199254740991, "format": "int53"},
|
||||||
{"type": "string"}
|
{"type": "string"}
|
||||||
],
|
],
|
||||||
"description": "The ID of the role or user for this overwrite"
|
"description": "The ID of the role or user for this overwrite"
|
||||||
@@ -30291,14 +30298,14 @@
|
|||||||
"allow": {
|
"allow": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{"type": "string"},
|
{"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"
|
"description": "The allowed permissions bitfield as a string"
|
||||||
},
|
},
|
||||||
"deny": {
|
"deny": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{"type": "string"},
|
{"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"
|
"description": "The denied permissions bitfield as a string"
|
||||||
}
|
}
|
||||||
@@ -30960,7 +30967,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"description": "New position for the channel"
|
"description": "New position for the channel"
|
||||||
},
|
},
|
||||||
"parent_id": {
|
"parent_id": {
|
||||||
@@ -31336,7 +31343,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"description": "Number of results to skip for pagination"
|
"description": "Number of results to skip for pagination"
|
||||||
},
|
},
|
||||||
"role_ids": {
|
"role_ids": {
|
||||||
@@ -33959,7 +33966,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"description": "Page number for pagination (ignored when cursor is provided)"
|
"description": "Page number for pagination (ignored when cursor is provided)"
|
||||||
},
|
},
|
||||||
"cursor": {
|
"cursor": {
|
||||||
@@ -35434,7 +35441,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?$"
|
"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"}
|
{"type": "null"}
|
||||||
],
|
],
|
||||||
"description": "When the mute expires"
|
"description": "When the mute expires"
|
||||||
@@ -35479,7 +35486,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?$"
|
"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"}
|
{"type": "null"}
|
||||||
],
|
],
|
||||||
"description": "When the mute expires"
|
"description": "When the mute expires"
|
||||||
@@ -35530,7 +35537,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?$"
|
"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"}
|
{"type": "null"}
|
||||||
],
|
],
|
||||||
"description": "When the mute expires"
|
"description": "When the mute expires"
|
||||||
@@ -36416,7 +36423,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"exclusiveMinimum": 0,
|
"exclusiveMinimum": 0,
|
||||||
"format": "int64"
|
"format": "int53"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -36977,7 +36984,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?$"
|
"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"}
|
{"type": "null"}
|
||||||
],
|
],
|
||||||
"description": "When status resets"
|
"description": "When status resets"
|
||||||
@@ -37157,7 +37164,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?$"
|
"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"}
|
{"type": "null"}
|
||||||
],
|
],
|
||||||
"description": "When the custom status expires"
|
"description": "When the custom status expires"
|
||||||
@@ -37423,7 +37430,7 @@
|
|||||||
"description": "Description for the attachment (max 4096 characters)"
|
"description": "Description for the attachment (max 4096 characters)"
|
||||||
},
|
},
|
||||||
"content_type": {"type": "string", "description": "MIME type of the file"},
|
"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"},
|
"url": {"type": "string", "description": "URL of the attachment"},
|
||||||
"proxy_url": {"type": "string", "description": "Proxied 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"},
|
"height": {"type": "integer", "format": "int53", "description": "Height of the image/video in pixels"},
|
||||||
@@ -37896,7 +37903,7 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 9007199254740991,
|
"maximum": 9007199254740991,
|
||||||
"format": "int64",
|
"format": "int53",
|
||||||
"description": "Unix timestamp for the attachment footer"
|
"description": "Unix timestamp for the attachment footer"
|
||||||
},
|
},
|
||||||
"author_name": {"type": "string", "description": "Name of the author"},
|
"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 {MessageSnapshot} from '../../models/MessageSnapshot';
|
||||||
import type {StickerItem} from '../../models/StickerItem';
|
import type {StickerItem} from '../../models/StickerItem';
|
||||||
import {mapUserToPartialResponse} from '../../user/UserMappers';
|
import {mapUserToPartialResponse} from '../../user/UserMappers';
|
||||||
|
import {assertSafeByteSize} from '../../utils/ByteSizeUtils';
|
||||||
|
|
||||||
class NoopNatsConnectionManager implements INatsConnectionManager {
|
class NoopNatsConnectionManager implements INatsConnectionManager {
|
||||||
async connect(): Promise<void> {}
|
async connect(): Promise<void> {}
|
||||||
@@ -373,7 +374,7 @@ export class RepositoryBackedMessageResponseDataService extends MessageResponseD
|
|||||||
description: attachment.description,
|
description: attachment.description,
|
||||||
content_type: attachment.contentType,
|
content_type: attachment.contentType,
|
||||||
content_hash: attachment.contentHash,
|
content_hash: attachment.contentHash,
|
||||||
size: Number(attachment.size),
|
size: assertSafeByteSize(attachment.size, 'message attachment size'),
|
||||||
url,
|
url,
|
||||||
proxy_url: url,
|
proxy_url: url,
|
||||||
width: attachment.width,
|
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;
|
||||||
|
}
|
||||||
@@ -58,6 +58,15 @@ const ENRICHMENT_QUERY_CONCURRENCY: usize = 16;
|
|||||||
const REACTION_MESSAGE_BATCH_SIZE: usize = 64;
|
const REACTION_MESSAGE_BATCH_SIZE: usize = 64;
|
||||||
const ATTACHMENT_DECAY_BATCH_SIZE: usize = 128;
|
const ATTACHMENT_DECAY_BATCH_SIZE: usize = 128;
|
||||||
const BUCKET_INDEX_PAGE_SIZE: u32 = 200;
|
const BUCKET_INDEX_PAGE_SIZE: u32 = 200;
|
||||||
|
const JS_MAX_SAFE_INTEGER: i64 = 9_007_199_254_740_991;
|
||||||
|
|
||||||
|
fn assert_safe_byte_size(value: i64) -> i64 {
|
||||||
|
assert!(
|
||||||
|
(0..=JS_MAX_SAFE_INTEGER).contains(&value),
|
||||||
|
"attachment size must fit a non-negative JavaScript safe integer"
|
||||||
|
);
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "scylla")]
|
#[cfg(feature = "scylla")]
|
||||||
const MESSAGE_COLUMNS: &str = "\
|
const MESSAGE_COLUMNS: &str = "\
|
||||||
@@ -1232,7 +1241,7 @@ impl MessagesShard {
|
|||||||
description: attachment.description.clone(),
|
description: attachment.description.clone(),
|
||||||
content_type: Some(content_type),
|
content_type: Some(content_type),
|
||||||
content_hash: attachment.content_hash.clone(),
|
content_hash: attachment.content_hash.clone(),
|
||||||
size: attachment.size.unwrap_or_default(),
|
size: assert_safe_byte_size(attachment.size.unwrap_or_default()),
|
||||||
url: (!expired).then_some(url.clone()),
|
url: (!expired).then_some(url.clone()),
|
||||||
proxy_url: (!expired).then_some(url),
|
proxy_url: (!expired).then_some(url),
|
||||||
width: (!is_audio).then_some(attachment.width).flatten(),
|
width: (!is_audio).then_some(attachment.width).flatten(),
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ export const Int32TypeSchema: OpenAPISchema = {
|
|||||||
format: 'int32',
|
format: 'int32',
|
||||||
};
|
};
|
||||||
export const Int32TypeRef: OpenAPIRef = {$ref: '#/components/schemas/Int32Type'};
|
export const Int32TypeRef: OpenAPIRef = {$ref: '#/components/schemas/Int32Type'};
|
||||||
|
export const NonNegativeSafeIntegerTypeSchema: OpenAPISchema = {
|
||||||
|
type: 'integer',
|
||||||
|
minimum: 0,
|
||||||
|
maximum: 9007199254740991,
|
||||||
|
format: 'int53',
|
||||||
|
};
|
||||||
|
export const NonNegativeSafeIntegerTypeRef: OpenAPIRef = {$ref: '#/components/schemas/NonNegativeSafeIntegerType'};
|
||||||
export const Int64TypeSchema: OpenAPISchema = {
|
export const Int64TypeSchema: OpenAPISchema = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
format: 'int64',
|
format: 'int64',
|
||||||
|
|||||||
@@ -371,7 +371,13 @@ export function zodToOpenAPISchema(schema: ZodTypeAny, depth = 0): OpenAPISchema
|
|||||||
const min = result.minimum ?? result.exclusiveMinimum;
|
const min = result.minimum ?? result.exclusiveMinimum;
|
||||||
const max = result.maximum ?? result.exclusiveMaximum;
|
const max = result.maximum ?? result.exclusiveMaximum;
|
||||||
if (min != null && max != null) {
|
if (min != null && max != null) {
|
||||||
result.format = min >= -2147483648 && max <= 2147483647 ? 'int32' : 'int64';
|
if (min >= -2147483648 && max <= 2147483647) {
|
||||||
|
result.format = 'int32';
|
||||||
|
} else if (min >= Number.MIN_SAFE_INTEGER && max <= Number.MAX_SAFE_INTEGER) {
|
||||||
|
result.format = 'int53';
|
||||||
|
} else {
|
||||||
|
result.format = 'int64';
|
||||||
|
}
|
||||||
} else if (min == null && max == null) {
|
} else if (min == null && max == null) {
|
||||||
result.format = 'int53';
|
result.format = 'int53';
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Int64StringTypeRef,
|
Int64StringTypeRef,
|
||||||
Int64TypeRef,
|
Int64TypeRef,
|
||||||
LocaleRef,
|
LocaleRef,
|
||||||
|
NonNegativeSafeIntegerTypeRef,
|
||||||
PasswordTypeRef,
|
PasswordTypeRef,
|
||||||
PhoneNumberTypeRef,
|
PhoneNumberTypeRef,
|
||||||
SnowflakeTypeRef,
|
SnowflakeTypeRef,
|
||||||
@@ -51,6 +52,8 @@ function getRefForCustomTypeName(typeName: string): OpenAPISchemaOrRef | null {
|
|||||||
return SnowflakeTypeRef;
|
return SnowflakeTypeRef;
|
||||||
case 'Int32Type':
|
case 'Int32Type':
|
||||||
return Int32TypeRef;
|
return Int32TypeRef;
|
||||||
|
case 'NonNegativeSafeIntegerType':
|
||||||
|
return NonNegativeSafeIntegerTypeRef;
|
||||||
case 'Int64Type':
|
case 'Int64Type':
|
||||||
return Int64TypeRef;
|
return Int64TypeRef;
|
||||||
case 'Int64StringType':
|
case 'Int64StringType':
|
||||||
@@ -217,6 +220,7 @@ const FLUXER_CUSTOM_TYPES: Record<string, OpenAPISchema> = {
|
|||||||
BitflagStringType: {type: 'string', format: 'int64', pattern: '^[0-9]+$'},
|
BitflagStringType: {type: 'string', format: 'int64', pattern: '^[0-9]+$'},
|
||||||
ColorType: {type: 'integer', minimum: 0, maximum: 16777215, format: 'int32'},
|
ColorType: {type: 'integer', minimum: 0, maximum: 16777215, format: 'int32'},
|
||||||
Int32Type: {type: 'integer', minimum: 0, maximum: 2147483647, format: 'int32'},
|
Int32Type: {type: 'integer', minimum: 0, maximum: 2147483647, format: 'int32'},
|
||||||
|
NonNegativeSafeIntegerType: {type: 'integer', minimum: 0, maximum: 9007199254740991, format: 'int53'},
|
||||||
EmailType: {type: 'string', format: 'email'},
|
EmailType: {type: 'string', format: 'email'},
|
||||||
PasswordType: {type: 'string', minLength: 8, maxLength: 256},
|
PasswordType: {type: 'string', minLength: 8, maxLength: 256},
|
||||||
UsernameType: {type: 'string', minLength: 1, maxLength: 32, pattern: '^[a-zA-Z0-9_]+$'},
|
UsernameType: {type: 'string', minLength: 1, maxLength: 32, pattern: '^[a-zA-Z0-9_]+$'},
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
Int64StringTypeSchema,
|
Int64StringTypeSchema,
|
||||||
Int64TypeSchema,
|
Int64TypeSchema,
|
||||||
LocaleSchema,
|
LocaleSchema,
|
||||||
|
NonNegativeSafeIntegerTypeSchema,
|
||||||
PasswordTypeSchema,
|
PasswordTypeSchema,
|
||||||
PhoneNumberTypeSchema,
|
PhoneNumberTypeSchema,
|
||||||
SnowflakeTypeSchema,
|
SnowflakeTypeSchema,
|
||||||
@@ -127,6 +128,7 @@ const BUILT_IN_SCHEMAS: ReadonlyArray<readonly [string, OpenAPISchema]> = [
|
|||||||
['APIErrorCode', APIErrorCodeSchema],
|
['APIErrorCode', APIErrorCodeSchema],
|
||||||
['SnowflakeType', SnowflakeTypeSchema],
|
['SnowflakeType', SnowflakeTypeSchema],
|
||||||
['Int32Type', Int32TypeSchema],
|
['Int32Type', Int32TypeSchema],
|
||||||
|
['NonNegativeSafeIntegerType', NonNegativeSafeIntegerTypeSchema],
|
||||||
['Int64Type', Int64TypeSchema],
|
['Int64Type', Int64TypeSchema],
|
||||||
['Int64StringType', Int64StringTypeSchema],
|
['Int64StringType', Int64StringTypeSchema],
|
||||||
['UnsignedInt64Type', UnsignedInt64TypeSchema],
|
['UnsignedInt64Type', UnsignedInt64TypeSchema],
|
||||||
|
|||||||
@@ -253,6 +253,9 @@ export class OpenAPIOperationBuilder {
|
|||||||
if (typeString.includes('UnsignedInt64Type')) {
|
if (typeString.includes('UnsignedInt64Type')) {
|
||||||
return {type: 'string', format: 'int64', pattern: '^[0-9]+$'};
|
return {type: 'string', format: 'int64', pattern: '^[0-9]+$'};
|
||||||
}
|
}
|
||||||
|
if (typeString.includes('NonNegativeSafeIntegerType')) {
|
||||||
|
return {type: 'integer', minimum: 0, maximum: 9007199254740991, format: 'int53'};
|
||||||
|
}
|
||||||
if (typeString.includes('Int64StringType')) {
|
if (typeString.includes('Int64StringType')) {
|
||||||
return {type: 'string', format: 'int64', pattern: '^-?[0-9]+$'};
|
return {type: 'string', format: 'int64', pattern: '^-?[0-9]+$'};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import {
|
|||||||
createStringType,
|
createStringType,
|
||||||
Int32Type,
|
Int32Type,
|
||||||
Int64StringType,
|
Int64StringType,
|
||||||
|
NonNegativeSafeIntegerType,
|
||||||
SnowflakeStringType,
|
SnowflakeStringType,
|
||||||
SnowflakeType,
|
SnowflakeType,
|
||||||
withOpenApiType,
|
withOpenApiType,
|
||||||
@@ -800,7 +801,7 @@ const LimitRuleSchema = z.object({
|
|||||||
id: z.string().min(1).describe('Unique rule identifier'),
|
id: z.string().min(1).describe('Unique rule identifier'),
|
||||||
filters: LimitFilterSchema.optional().describe('Optional filters that scope the rule'),
|
filters: LimitFilterSchema.optional().describe('Optional filters that scope the rule'),
|
||||||
limits: z
|
limits: z
|
||||||
.record(z.string(), z.number().min(0))
|
.record(z.string(), NonNegativeSafeIntegerType)
|
||||||
.refine(
|
.refine(
|
||||||
(limits) => {
|
(limits) => {
|
||||||
const limitKeys = Object.keys(limits);
|
const limitKeys = Object.keys(limits);
|
||||||
@@ -1258,7 +1259,7 @@ const AdminMessageAttachmentSchema = z.object({
|
|||||||
content_type: z.string().nullable(),
|
content_type: z.string().nullable(),
|
||||||
width: Int32Type.nullable(),
|
width: Int32Type.nullable(),
|
||||||
height: Int32Type.nullable(),
|
height: Int32Type.nullable(),
|
||||||
size: Int32Type.nullable().optional(),
|
size: NonNegativeSafeIntegerType.nullable().optional(),
|
||||||
ncmec_status: NcmecSubmissionStatusEnum,
|
ncmec_status: NcmecSubmissionStatusEnum,
|
||||||
ncmec_report_id: createStringType(1, 256).nullable(),
|
ncmec_report_id: createStringType(1, 256).nullable(),
|
||||||
ncmec_failure_reason: createStringType(1, 4000).nullable(),
|
ncmec_failure_reason: createStringType(1, 4000).nullable(),
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
import {GifMediaFormat} from '@fluxer/schema/src/domains/gif/GifSchemas';
|
import {GifMediaFormat} from '@fluxer/schema/src/domains/gif/GifSchemas';
|
||||||
import {createStringType, SnowflakeStringType, SnowflakeType} from '@fluxer/schema/src/primitives/SchemaPrimitives';
|
import {
|
||||||
|
createStringType,
|
||||||
|
NonNegativeSafeIntegerType,
|
||||||
|
SnowflakeStringType,
|
||||||
|
SnowflakeType,
|
||||||
|
} from '@fluxer/schema/src/primitives/SchemaPrimitives';
|
||||||
import {z} from 'zod';
|
import {z} from 'zod';
|
||||||
|
|
||||||
const FavoriteMemeBase = z.object({
|
const FavoriteMemeBase = z.object({
|
||||||
@@ -67,7 +72,7 @@ export const FavoriteMemeResponse = z.object({
|
|||||||
filename: z.string().describe('Original filename of the meme'),
|
filename: z.string().describe('Original filename of the meme'),
|
||||||
content_type: z.string().describe('MIME type of the meme file'),
|
content_type: z.string().describe('MIME type of the meme file'),
|
||||||
content_hash: z.string().nullish().describe('Hash of the file content for deduplication'),
|
content_hash: z.string().nullish().describe('Hash of the file content for deduplication'),
|
||||||
size: z.number().describe('File size in bytes'),
|
size: NonNegativeSafeIntegerType.describe('File size in bytes'),
|
||||||
width: z.number().int().nullish().describe('Width of the image or video in pixels'),
|
width: z.number().int().nullish().describe('Width of the image or video in pixels'),
|
||||||
height: z.number().int().nullish().describe('Height of the image or video in pixels'),
|
height: z.number().int().nullish().describe('Height of the image or video in pixels'),
|
||||||
duration: z.number().nullish().describe('Duration of the video in seconds'),
|
duration: z.number().nullish().describe('Duration of the video in seconds'),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
createBitflagInt32Type,
|
createBitflagInt32Type,
|
||||||
createStringType,
|
createStringType,
|
||||||
Int32Type,
|
Int32Type,
|
||||||
|
NonNegativeSafeIntegerType,
|
||||||
SnowflakeType,
|
SnowflakeType,
|
||||||
} from '@fluxer/schema/src/primitives/SchemaPrimitives';
|
} from '@fluxer/schema/src/primitives/SchemaPrimitives';
|
||||||
import {z} from 'zod';
|
import {z} from 'zod';
|
||||||
@@ -41,7 +42,7 @@ export const ClientUploadedAttachmentRequest = ClientAttachmentBase.extend({
|
|||||||
upload_filename: createStringType(1, 4096).describe(
|
upload_filename: createStringType(1, 4096).describe(
|
||||||
'Temporary upload key returned by the attachment upload endpoint',
|
'Temporary upload key returned by the attachment upload endpoint',
|
||||||
),
|
),
|
||||||
file_size: coerceNumberFromString(Int32Type).describe('Uploaded file size in bytes'),
|
file_size: coerceNumberFromString(NonNegativeSafeIntegerType).describe('Uploaded file size in bytes'),
|
||||||
content_type: createStringType(1, 255).describe('MIME type of the uploaded file'),
|
content_type: createStringType(1, 255).describe('MIME type of the uploaded file'),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,19 @@
|
|||||||
|
|
||||||
import {ATTACHMENT_UPLOAD_MAX_CHUNKS, MAX_ATTACHMENTS_PER_MESSAGE} from '@fluxer/constants/src/LimitConstants';
|
import {ATTACHMENT_UPLOAD_MAX_CHUNKS, MAX_ATTACHMENTS_PER_MESSAGE} from '@fluxer/constants/src/LimitConstants';
|
||||||
import {FilenameType} from '@fluxer/schema/src/primitives/FileValidators';
|
import {FilenameType} from '@fluxer/schema/src/primitives/FileValidators';
|
||||||
import {coerceNumberFromString, createStringType, Int32Type} from '@fluxer/schema/src/primitives/SchemaPrimitives';
|
import {
|
||||||
|
coerceNumberFromString,
|
||||||
|
createStringType,
|
||||||
|
Int32Type,
|
||||||
|
NonNegativeSafeIntegerType,
|
||||||
|
} from '@fluxer/schema/src/primitives/SchemaPrimitives';
|
||||||
import {URLType} from '@fluxer/schema/src/primitives/UrlValidators';
|
import {URLType} from '@fluxer/schema/src/primitives/UrlValidators';
|
||||||
import {z} from 'zod';
|
import {z} from 'zod';
|
||||||
|
|
||||||
export const PresignedAttachmentUploadRequestItem = z.object({
|
export const PresignedAttachmentUploadRequestItem = z.object({
|
||||||
id: coerceNumberFromString(Int32Type).describe('The client-side identifier for this attachment'),
|
id: coerceNumberFromString(Int32Type).describe('The client-side identifier for this attachment'),
|
||||||
filename: FilenameType.describe('The name of the file that will be uploaded'),
|
filename: FilenameType.describe('The name of the file that will be uploaded'),
|
||||||
file_size: coerceNumberFromString(Int32Type).describe('Expected file size in bytes'),
|
file_size: coerceNumberFromString(NonNegativeSafeIntegerType).describe('Expected file size in bytes'),
|
||||||
content_type: createStringType(1, 255).describe('MIME type the client will upload'),
|
content_type: createStringType(1, 255).describe('MIME type the client will upload'),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -29,7 +34,7 @@ const PresignedAttachmentUploadBase = z.object({
|
|||||||
id: coerceNumberFromString(Int32Type).describe('The client-side identifier for this attachment'),
|
id: coerceNumberFromString(Int32Type).describe('The client-side identifier for this attachment'),
|
||||||
filename: FilenameType.describe('The original filename for this upload'),
|
filename: FilenameType.describe('The original filename for this upload'),
|
||||||
upload_filename: createStringType(1, 4096).describe('Temporary upload key to reference in message send payloads'),
|
upload_filename: createStringType(1, 4096).describe('Temporary upload key to reference in message send payloads'),
|
||||||
file_size: coerceNumberFromString(Int32Type).describe('Expected file size in bytes'),
|
file_size: coerceNumberFromString(NonNegativeSafeIntegerType).describe('Expected file size in bytes'),
|
||||||
content_type: createStringType(1, 255).describe('Expected MIME type for this upload'),
|
content_type: createStringType(1, 255).describe('Expected MIME type for this upload'),
|
||||||
});
|
});
|
||||||
const PresignedAttachmentUploadSinglepart = PresignedAttachmentUploadBase.extend({
|
const PresignedAttachmentUploadSinglepart = PresignedAttachmentUploadBase.extend({
|
||||||
@@ -45,7 +50,7 @@ const PresignedAttachmentUploadPart = z.object({
|
|||||||
const PresignedAttachmentUploadMultipart = PresignedAttachmentUploadBase.extend({
|
const PresignedAttachmentUploadMultipart = PresignedAttachmentUploadBase.extend({
|
||||||
upload_mode: z.literal('multipart'),
|
upload_mode: z.literal('multipart'),
|
||||||
upload_id: createStringType(1, 1024).describe('S3 multipart upload identifier; required to complete the upload'),
|
upload_id: createStringType(1, 1024).describe('S3 multipart upload identifier; required to complete the upload'),
|
||||||
part_size: coerceNumberFromString(Int32Type).describe('Size in bytes of each part except the last'),
|
part_size: coerceNumberFromString(NonNegativeSafeIntegerType).describe('Size in bytes of each part except the last'),
|
||||||
parts: z
|
parts: z
|
||||||
.array(PresignedAttachmentUploadPart)
|
.array(PresignedAttachmentUploadPart)
|
||||||
.min(1)
|
.min(1)
|
||||||
|
|||||||
@@ -11,7 +11,12 @@ import type {GuildMemberData} from '@fluxer/schema/src/domains/guild/GuildMember
|
|||||||
import {type MessageEmbed, MessageEmbedResponse} from '@fluxer/schema/src/domains/message/EmbedSchemas';
|
import {type MessageEmbed, MessageEmbedResponse} from '@fluxer/schema/src/domains/message/EmbedSchemas';
|
||||||
import {type UserPartial, UserPartialResponse} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
|
import {type UserPartial, UserPartialResponse} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
|
||||||
import {MessageReferenceTypeSchema, MessageTypeSchema} from '@fluxer/schema/src/primitives/MessageValidators';
|
import {MessageReferenceTypeSchema, MessageTypeSchema} from '@fluxer/schema/src/primitives/MessageValidators';
|
||||||
import {createBitflagInt32Type, Int32Type, SnowflakeStringType} from '@fluxer/schema/src/primitives/SchemaPrimitives';
|
import {
|
||||||
|
createBitflagInt32Type,
|
||||||
|
Int32Type,
|
||||||
|
NonNegativeSafeIntegerType,
|
||||||
|
SnowflakeStringType,
|
||||||
|
} from '@fluxer/schema/src/primitives/SchemaPrimitives';
|
||||||
import {z} from 'zod';
|
import {z} from 'zod';
|
||||||
|
|
||||||
export const MessageAttachmentResponse = z.object({
|
export const MessageAttachmentResponse = z.object({
|
||||||
@@ -21,7 +26,7 @@ export const MessageAttachmentResponse = z.object({
|
|||||||
description: z.string().nullish().describe('The description of the attachment'),
|
description: z.string().nullish().describe('The description of the attachment'),
|
||||||
content_type: z.string().nullish().describe('The MIME type of the attachment'),
|
content_type: z.string().nullish().describe('The MIME type of the attachment'),
|
||||||
content_hash: z.string().nullish().describe('The hash of the attachment content'),
|
content_hash: z.string().nullish().describe('The hash of the attachment content'),
|
||||||
size: Int32Type.describe('The size of the attachment in bytes'),
|
size: NonNegativeSafeIntegerType.describe('The size of the attachment in bytes'),
|
||||||
url: z.string().nullish().describe('The URL of the attachment'),
|
url: z.string().nullish().describe('The URL of the attachment'),
|
||||||
proxy_url: z.string().nullish().describe('The proxied URL of the attachment'),
|
proxy_url: z.string().nullish().describe('The proxied URL of the attachment'),
|
||||||
width: Int32Type.nullish().describe('The width of the attachment in pixels (for images/videos)'),
|
width: Int32Type.nullish().describe('The width of the attachment in pixels (for images/videos)'),
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import {
|
||||||
|
PresignedAttachmentUploadRequest,
|
||||||
|
PresignedAttachmentUploadResponse,
|
||||||
|
} from '@fluxer/schema/src/domains/message/AttachmentUploadSchemas';
|
||||||
|
import {ClientUploadedAttachmentRequest} from '@fluxer/schema/src/domains/message/AttachmentSchemas';
|
||||||
|
import {MessageAttachmentResponse} from '@fluxer/schema/src/domains/message/MessageResponseSchemas';
|
||||||
|
import {describe, expect, it} from 'vitest';
|
||||||
|
|
||||||
|
describe('attachment byte size schemas', () => {
|
||||||
|
it('accepts upload request file sizes above int32', () => {
|
||||||
|
const result = PresignedAttachmentUploadRequest.safeParse({
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
filename: 'large.bin',
|
||||||
|
file_size: 2147483648,
|
||||||
|
content_type: 'application/octet-stream',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts upload response file and part sizes above int32', () => {
|
||||||
|
const result = PresignedAttachmentUploadResponse.safeParse({
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
filename: 'large.bin',
|
||||||
|
upload_filename: 'uploads/tmp-large',
|
||||||
|
file_size: 2147483648,
|
||||||
|
content_type: 'application/octet-stream',
|
||||||
|
upload_mode: 'multipart',
|
||||||
|
upload_id: 'multipart-id',
|
||||||
|
part_size: 2147483648,
|
||||||
|
parts: [{part_number: 1, upload_url: 'https://uploads.example.test/part'}],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts completed upload file sizes up to the JavaScript safe integer limit', () => {
|
||||||
|
const result = ClientUploadedAttachmentRequest.safeParse({
|
||||||
|
id: 0,
|
||||||
|
filename: 'max.bin',
|
||||||
|
upload_filename: 'uploads/tmp-max',
|
||||||
|
file_size: Number.MAX_SAFE_INTEGER,
|
||||||
|
content_type: 'application/octet-stream',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects unsafe upload file sizes', () => {
|
||||||
|
const result = ClientUploadedAttachmentRequest.safeParse({
|
||||||
|
id: 0,
|
||||||
|
filename: 'unsafe.bin',
|
||||||
|
upload_filename: 'uploads/tmp-unsafe',
|
||||||
|
file_size: Number.MAX_SAFE_INTEGER + 1,
|
||||||
|
content_type: 'application/octet-stream',
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits message attachment sizes as safe integer numbers', () => {
|
||||||
|
const result = MessageAttachmentResponse.safeParse({
|
||||||
|
id: '123456789012345678',
|
||||||
|
filename: 'large.bin',
|
||||||
|
title: null,
|
||||||
|
description: null,
|
||||||
|
content_type: 'application/octet-stream',
|
||||||
|
content_hash: null,
|
||||||
|
size: Number.MAX_SAFE_INTEGER,
|
||||||
|
url: 'https://cdn.example.test/large.bin',
|
||||||
|
proxy_url: 'https://cdn.example.test/large.bin',
|
||||||
|
width: null,
|
||||||
|
height: null,
|
||||||
|
placeholder: null,
|
||||||
|
flags: 0,
|
||||||
|
nsfw: null,
|
||||||
|
duration: null,
|
||||||
|
waveform: null,
|
||||||
|
expires_at: null,
|
||||||
|
expired: null,
|
||||||
|
});
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
createStringType,
|
createStringType,
|
||||||
createUnboundedStringType,
|
createUnboundedStringType,
|
||||||
Int32Type,
|
Int32Type,
|
||||||
|
NonNegativeSafeIntegerType,
|
||||||
SnowflakeType,
|
SnowflakeType,
|
||||||
} from '@fluxer/schema/src/primitives/SchemaPrimitives';
|
} from '@fluxer/schema/src/primitives/SchemaPrimitives';
|
||||||
import {URLType} from '@fluxer/schema/src/primitives/UrlValidators';
|
import {URLType} from '@fluxer/schema/src/primitives/UrlValidators';
|
||||||
@@ -68,7 +69,7 @@ const WebhookAttachmentRequest = z.object({
|
|||||||
filename: createStringType(1, 1024).optional().describe('Name of the file (1-1024 characters)'),
|
filename: createStringType(1, 1024).optional().describe('Name of the file (1-1024 characters)'),
|
||||||
description: createStringType(1, 4096).optional().describe('Description for the attachment (max 4096 characters)'),
|
description: createStringType(1, 4096).optional().describe('Description for the attachment (max 4096 characters)'),
|
||||||
content_type: createStringType(1, 256).optional().describe('MIME type of the file'),
|
content_type: createStringType(1, 256).optional().describe('MIME type of the file'),
|
||||||
size: z.number().int().optional().describe('Size of the file in bytes'),
|
size: NonNegativeSafeIntegerType.optional().describe('Size of the file in bytes'),
|
||||||
url: URLType.optional().describe('URL of the attachment'),
|
url: URLType.optional().describe('URL of the attachment'),
|
||||||
proxy_url: URLType.optional().describe('Proxied URL of the attachment'),
|
proxy_url: URLType.optional().describe('Proxied URL of the attachment'),
|
||||||
height: z.number().int().optional().describe('Height of the image/video in pixels'),
|
height: z.number().int().optional().describe('Height of the image/video in pixels'),
|
||||||
|
|||||||
@@ -171,6 +171,12 @@ export const ColorType = z
|
|||||||
.describe('fluxer:ColorType');
|
.describe('fluxer:ColorType');
|
||||||
export const Int32Type = z.number().int().min(0).max(2147483647).describe('fluxer:Int32Type');
|
export const Int32Type = z.number().int().min(0).max(2147483647).describe('fluxer:Int32Type');
|
||||||
export const SignedInt32Type = z.number().int().min(-2147483648).max(2147483647).describe('fluxer:SignedInt32Type');
|
export const SignedInt32Type = z.number().int().min(-2147483648).max(2147483647).describe('fluxer:SignedInt32Type');
|
||||||
|
export const NonNegativeSafeIntegerType = z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.min(0)
|
||||||
|
.max(Number.MAX_SAFE_INTEGER)
|
||||||
|
.describe('fluxer:NonNegativeSafeIntegerType');
|
||||||
const INTEGER_STRING_REGEX = /^[+-]?\d+$/;
|
const INTEGER_STRING_REGEX = /^[+-]?\d+$/;
|
||||||
|
|
||||||
function coerceNumericStringToNumber(value: unknown): unknown {
|
function coerceNumericStringToNumber(value: unknown): unknown {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
Int32Type,
|
Int32Type,
|
||||||
Int64StringType,
|
Int64StringType,
|
||||||
Int64Type,
|
Int64Type,
|
||||||
|
NonNegativeSafeIntegerType,
|
||||||
normalizeString,
|
normalizeString,
|
||||||
normalizeWhitespace,
|
normalizeWhitespace,
|
||||||
removeStandaloneSurrogates,
|
removeStandaloneSurrogates,
|
||||||
@@ -214,6 +215,29 @@ describe('Int32Type', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('NonNegativeSafeIntegerType', () => {
|
||||||
|
it('accepts maximum JavaScript safe integer', () => {
|
||||||
|
const result = NonNegativeSafeIntegerType.safeParse(Number.MAX_SAFE_INTEGER);
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
it('accepts values above int32', () => {
|
||||||
|
const result = NonNegativeSafeIntegerType.safeParse(2147483648);
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
});
|
||||||
|
it('rejects unsafe JavaScript integers', () => {
|
||||||
|
const result = NonNegativeSafeIntegerType.safeParse(Number.MAX_SAFE_INTEGER + 1);
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
it('rejects negative values', () => {
|
||||||
|
const result = NonNegativeSafeIntegerType.safeParse(-1);
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
it('rejects non-integer values', () => {
|
||||||
|
const result = NonNegativeSafeIntegerType.safeParse(1.5);
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('coerceNumberFromString', () => {
|
describe('coerceNumberFromString', () => {
|
||||||
it('coerces valid integer strings to numbers', () => {
|
it('coerces valid integer strings to numbers', () => {
|
||||||
const schema = coerceNumberFromString(z.number().int().min(0).max(100));
|
const schema = coerceNumberFromString(z.number().int().min(0).max(100));
|
||||||
|
|||||||
Reference in New Issue
Block a user