From 1a1d13b57191425ed07eaa62001c47c31fa68075 Mon Sep 17 00:00:00 2001 From: Hampus Kraft Date: Thu, 19 Feb 2026 14:59:46 +0000 Subject: [PATCH] chore: remove chunked uploads for now --- fluxer_app/src/Endpoints.tsx | 5 - fluxer_app/src/lib/ChunkedUploadService.tsx | 195 --------- fluxer_app/src/lib/CloudUpload.tsx | 1 - fluxer_app/src/lib/MessageQueue.tsx | 98 ----- .../src/utils/MessageAttachmentUtils.tsx | 3 +- fluxer_app/src/utils/MessageRequestUtils.tsx | 1 - .../controllers/ChunkedUploadController.tsx | 119 ------ .../api/src/channel/controllers/index.tsx | 2 - .../channel/services/ChunkedUploadService.tsx | 227 ---------- .../services/message/MessageRequestParser.tsx | 32 +- .../src/channel/tests/ChunkedUpload.test.tsx | 396 ------------------ .../api/src/middleware/ServiceMiddleware.tsx | 17 - .../ChannelRateLimitConfig.tsx | 15 - packages/api/src/types/HonoEnv.tsx | 2 - packages/constants/src/ApiErrorCodes.tsx | 4 - .../src/ApiErrorCodesDescriptions.tsx | 4 - packages/constants/src/LimitConstants.tsx | 4 - ...ChunkedUploadChunkIndexOutOfRangeError.tsx | 27 -- .../channel/ChunkedUploadIncompleteError.tsx | 27 -- .../channel/ChunkedUploadNotFoundError.tsx | 27 -- .../channel/ChunkedUploadNotOwnedError.tsx | 27 -- .../errors/src/i18n/ErrorCodeMappings.tsx | 5 - .../src/i18n/ErrorI18nTypes.generated.tsx | 4 - packages/errors/src/i18n/locales/ar.yaml | 4 - packages/errors/src/i18n/locales/bg.yaml | 4 - packages/errors/src/i18n/locales/cs.yaml | 4 - packages/errors/src/i18n/locales/da.yaml | 4 - packages/errors/src/i18n/locales/de.yaml | 4 - packages/errors/src/i18n/locales/el.yaml | 4 - packages/errors/src/i18n/locales/en-GB.yaml | 4 - packages/errors/src/i18n/locales/es-419.yaml | 4 - packages/errors/src/i18n/locales/es-ES.yaml | 4 - packages/errors/src/i18n/locales/fi.yaml | 4 - packages/errors/src/i18n/locales/fr.yaml | 4 - packages/errors/src/i18n/locales/he.yaml | 4 - packages/errors/src/i18n/locales/hi.yaml | 4 - packages/errors/src/i18n/locales/hr.yaml | 4 - packages/errors/src/i18n/locales/hu.yaml | 4 - packages/errors/src/i18n/locales/id.yaml | 4 - packages/errors/src/i18n/locales/it.yaml | 4 - packages/errors/src/i18n/locales/ja.yaml | 4 - packages/errors/src/i18n/locales/ko.yaml | 4 - packages/errors/src/i18n/locales/lt.yaml | 4 - .../errors/src/i18n/locales/messages.yaml | 4 - packages/errors/src/i18n/locales/nl.yaml | 4 - packages/errors/src/i18n/locales/no.yaml | 4 - packages/errors/src/i18n/locales/pl.yaml | 4 - packages/errors/src/i18n/locales/pt-BR.yaml | 4 - packages/errors/src/i18n/locales/ro.yaml | 4 - packages/errors/src/i18n/locales/ru.yaml | 4 - packages/errors/src/i18n/locales/sv-SE.yaml | 4 - packages/errors/src/i18n/locales/th.yaml | 4 - packages/errors/src/i18n/locales/tr.yaml | 4 - packages/errors/src/i18n/locales/uk.yaml | 4 - packages/errors/src/i18n/locales/vi.yaml | 4 - packages/errors/src/i18n/locales/zh-CN.yaml | 4 - packages/errors/src/i18n/locales/zh-TW.yaml | 4 - .../domains/channel/ChunkedUploadSchemas.tsx | 79 ---- .../src/domains/message/AttachmentSchemas.tsx | 1 - 59 files changed, 2 insertions(+), 1456 deletions(-) delete mode 100644 fluxer_app/src/lib/ChunkedUploadService.tsx delete mode 100644 packages/api/src/channel/controllers/ChunkedUploadController.tsx delete mode 100644 packages/api/src/channel/services/ChunkedUploadService.tsx delete mode 100644 packages/api/src/channel/tests/ChunkedUpload.test.tsx delete mode 100644 packages/errors/src/domains/channel/ChunkedUploadChunkIndexOutOfRangeError.tsx delete mode 100644 packages/errors/src/domains/channel/ChunkedUploadIncompleteError.tsx delete mode 100644 packages/errors/src/domains/channel/ChunkedUploadNotFoundError.tsx delete mode 100644 packages/errors/src/domains/channel/ChunkedUploadNotOwnedError.tsx delete mode 100644 packages/schema/src/domains/channel/ChunkedUploadSchemas.tsx diff --git a/fluxer_app/src/Endpoints.tsx b/fluxer_app/src/Endpoints.tsx index f97b081d..b357a1eb 100644 --- a/fluxer_app/src/Endpoints.tsx +++ b/fluxer_app/src/Endpoints.tsx @@ -96,11 +96,6 @@ export const Endpoints = { CHANNEL_TYPING: (channelId: string) => `/channels/${channelId}/typing`, CHANNEL_WEBHOOKS: (channelId: string) => `/channels/${channelId}/webhooks`, CHANNEL_RTC_REGIONS: (channelId: string) => `/channels/${channelId}/rtc-regions`, - CHANNEL_CHUNKED_UPLOADS: (channelId: string) => `/channels/${channelId}/chunked-uploads`, - CHANNEL_CHUNKED_UPLOAD_CHUNK: (channelId: string, uploadId: string, chunkIndex: number) => - `/channels/${channelId}/chunked-uploads/${uploadId}/chunks/${chunkIndex}`, - CHANNEL_CHUNKED_UPLOAD_COMPLETE: (channelId: string, uploadId: string) => - `/channels/${channelId}/chunked-uploads/${uploadId}/complete`, CHANNEL_CALL: (channelId: string) => `/channels/${channelId}/call`, CHANNEL_CALL_RING: (channelId: string) => `/channels/${channelId}/call/ring`, CHANNEL_CALL_STOP_RINGING: (channelId: string) => `/channels/${channelId}/call/stop-ringing`, diff --git a/fluxer_app/src/lib/ChunkedUploadService.tsx b/fluxer_app/src/lib/ChunkedUploadService.tsx deleted file mode 100644 index 8f7b0933..00000000 --- a/fluxer_app/src/lib/ChunkedUploadService.tsx +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2026 Fluxer Contributors - * - * This file is part of Fluxer. - * - * Fluxer is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Fluxer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Fluxer. If not, see . - */ - -import {Endpoints} from '@app/Endpoints'; -import http from '@app/lib/HttpClient'; -import {Logger} from '@app/lib/Logger'; -import {CHUNKED_UPLOAD_CHUNK_SIZE} from '@fluxer/constants/src/LimitConstants'; - -const logger = new Logger('ChunkedUploadService'); - -const MAX_CONCURRENT_CHUNKS = 4; -const MAX_CHUNK_RETRIES = 3; -const RETRY_BASE_DELAY_MS = 1000; - -interface ChunkedUploadResult { - upload_filename: string; - file_size: number; - content_type: string; -} - -interface InitiateUploadResponse { - upload_id: string; - upload_filename: string; - chunk_size: number; - chunk_count: number; -} - -interface UploadChunkResponse { - etag: string; -} - -interface CompleteUploadResponse { - upload_filename: string; - file_size: number; - content_type: string; -} - -export async function uploadFileChunked( - channelId: string, - file: File, - onProgress?: (loaded: number, total: number) => void, - signal?: AbortSignal, -): Promise { - const initiateResponse = await http.post({ - url: Endpoints.CHANNEL_CHUNKED_UPLOADS(channelId), - body: { - filename: file.name, - file_size: file.size, - }, - signal, - rejectWithError: true, - }); - - const {upload_id, chunk_size, chunk_count} = initiateResponse.body; - - logger.debug(`Initiated chunked upload: ${upload_id}, ${chunk_count} chunks of ${chunk_size} bytes`); - - const chunkProgress = new Array(chunk_count).fill(0); - const etags = new Array<{chunk_index: number; etag: string}>(chunk_count); - - function reportProgress() { - if (!onProgress) return; - const loaded = chunkProgress.reduce((sum, bytes) => sum + bytes, 0); - onProgress(loaded, file.size); - } - - const chunkIndices = Array.from({length: chunk_count}, (_, i) => i); - let cursor = 0; - const activeTasks: Array> = []; - - async function uploadOneChunk(chunkIndex: number): Promise { - const start = chunkIndex * chunk_size; - const end = Math.min(start + chunk_size, file.size); - const chunkBlob = file.slice(start, end); - const chunkData = new Uint8Array(await chunkBlob.arrayBuffer()); - const chunkLength = chunkData.byteLength; - - let lastError: unknown; - for (let attempt = 0; attempt <= MAX_CHUNK_RETRIES; attempt++) { - if (signal?.aborted) { - throw new DOMException('Upload cancelled', 'AbortError'); - } - - try { - const response = await http.put({ - url: Endpoints.CHANNEL_CHUNKED_UPLOAD_CHUNK(channelId, upload_id, chunkIndex), - body: chunkData, - headers: {'Content-Type': 'application/octet-stream'}, - signal, - rejectWithError: true, - }); - - etags[chunkIndex] = {chunk_index: chunkIndex, etag: response.body.etag}; - chunkProgress[chunkIndex] = chunkLength; - reportProgress(); - return; - } catch (error) { - lastError = error; - - if (signal?.aborted) { - throw error; - } - - const isRetryable = - error instanceof Error && - 'status' in error && - ((error as {status: number}).status >= 500 || (error as {status: number}).status === 429); - - if (!isRetryable || attempt === MAX_CHUNK_RETRIES) { - throw error; - } - - const delay = RETRY_BASE_DELAY_MS * 2 ** attempt; - logger.debug( - `Chunk ${chunkIndex} failed (attempt ${attempt + 1}/${MAX_CHUNK_RETRIES + 1}), retrying in ${delay}ms`, - ); - await new Promise((resolve) => setTimeout(resolve, delay)); - } - } - - throw lastError; - } - - await new Promise((resolve, reject) => { - let settled = false; - - function settle(error?: unknown) { - if (settled) return; - settled = true; - if (error) { - reject(error); - } else { - resolve(); - } - } - - function scheduleNext() { - while (activeTasks.length < MAX_CONCURRENT_CHUNKS && cursor < chunkIndices.length) { - const chunkIndex = chunkIndices[cursor++]; - const task = uploadOneChunk(chunkIndex).then( - () => { - const idx = activeTasks.indexOf(task); - if (idx !== -1) activeTasks.splice(idx, 1); - if (cursor >= chunkIndices.length && activeTasks.length === 0) { - settle(); - } else { - scheduleNext(); - } - }, - (error) => { - settle(error); - }, - ); - activeTasks.push(task); - } - } - - scheduleNext(); - }); - - logger.debug(`All ${chunk_count} chunks uploaded, completing upload`); - - const completeResponse = await http.post({ - url: Endpoints.CHANNEL_CHUNKED_UPLOAD_COMPLETE(channelId, upload_id), - body: {etags}, - signal, - rejectWithError: true, - }); - - return { - upload_filename: completeResponse.body.upload_filename, - file_size: completeResponse.body.file_size, - content_type: completeResponse.body.content_type, - }; -} - -export function shouldUseChunkedUpload(file: File): boolean { - return file.size > CHUNKED_UPLOAD_CHUNK_SIZE; -} diff --git a/fluxer_app/src/lib/CloudUpload.tsx b/fluxer_app/src/lib/CloudUpload.tsx index 965b1fa0..0010f5e1 100644 --- a/fluxer_app/src/lib/CloudUpload.tsx +++ b/fluxer_app/src/lib/CloudUpload.tsx @@ -48,7 +48,6 @@ export interface CloudAttachment { duration?: number | null; waveform?: string | null; isVoiceMessage?: boolean; - uploadedFilename?: string; } export interface MessageUpload { diff --git a/fluxer_app/src/lib/MessageQueue.tsx b/fluxer_app/src/lib/MessageQueue.tsx index 5fb29e99..b9578b07 100644 --- a/fluxer_app/src/lib/MessageQueue.tsx +++ b/fluxer_app/src/lib/MessageQueue.tsx @@ -32,7 +32,6 @@ import {NSFWContentRejectedModal} from '@app/components/alerts/NSFWContentReject import {SlowmodeRateLimitedModal} from '@app/components/alerts/SlowmodeRateLimitedModal'; import {Endpoints} from '@app/Endpoints'; import i18n from '@app/I18n'; -import {shouldUseChunkedUpload, uploadFileChunked} from '@app/lib/ChunkedUploadService'; import {CloudUpload} from '@app/lib/CloudUpload'; import http, {type HttpResponse} from '@app/lib/HttpClient'; import type {HttpError} from '@app/lib/HttpError'; @@ -225,32 +224,6 @@ class MessageQueue extends Queue | un files = result.files; } - if (hasAttachments && files?.length && attachments?.length) { - const abortController = new AbortController(); - this.abortControllers.set(nonce, abortController); - - try { - const chunkedResult = await this.performChunkedUploads( - channelId, - nonce, - files, - attachments, - abortController.signal, - ); - files = chunkedResult.files; - attachments = chunkedResult.attachments; - } catch (error) { - this.abortControllers.delete(nonce); - const httpError = error as HttpError; - logger.error(`Chunked upload failed for channel ${channelId}:`, error); - this.handleSendError(channelId, nonce, httpError, i18n, payload.hasAttachments); - completed(null, undefined, error); - return; - } - - this.abortControllers.delete(nonce); - } - const requestBody = buildMessageCreateRequest({ content: payload.content, nonce, @@ -321,77 +294,6 @@ class MessageQueue extends Queue | un } } - private async performChunkedUploads( - channelId: string, - nonce: string, - files: Array, - attachments: Array, - signal: AbortSignal, - ): Promise<{files: Array; attachments: Array}> { - const largeFileIndices = new Set(); - - for (let i = 0; i < files.length; i++) { - if (shouldUseChunkedUpload(files[i])) { - largeFileIndices.add(i); - } - } - - if (largeFileIndices.size === 0) { - return {files, attachments}; - } - - const totalChunkedSize = Array.from(largeFileIndices).reduce((sum, i) => sum + files[i].size, 0); - const totalOverallSize = files.reduce((sum, f) => sum + f.size, 0); - const chunkedRatio = totalOverallSize > 0 ? totalChunkedSize / totalOverallSize : 0; - const chunkedProgressWeight = chunkedRatio * 90; - - const perFileProgress = new Map(); - for (const i of largeFileIndices) { - perFileProgress.set(i, 0); - } - - const updatedAttachments = [...attachments]; - - await Promise.all( - Array.from(largeFileIndices).map(async (fileIndex) => { - const file = files[fileIndex]; - const result = await uploadFileChunked( - channelId, - file, - (loaded, _total) => { - perFileProgress.set(fileIndex, loaded); - const totalLoaded = Array.from(perFileProgress.values()).reduce((s, v) => s + v, 0); - const ratio = totalChunkedSize > 0 ? totalLoaded / totalChunkedSize : 0; - const overallProgress = ratio * chunkedProgressWeight; - CloudUpload.updateSendingProgress(nonce, overallProgress); - }, - signal, - ); - - if (updatedAttachments[fileIndex]) { - updatedAttachments[fileIndex] = { - ...updatedAttachments[fileIndex], - uploaded_filename: result.upload_filename, - }; - } - }), - ); - - const inlineFiles: Array = []; - let inlineIndex = 0; - const remappedAttachments = updatedAttachments.map((att, originalIndex) => { - if (largeFileIndices.has(originalIndex)) { - return att; - } - const newId = String(inlineIndex); - inlineFiles.push(files[originalIndex]); - inlineIndex++; - return {...att, id: newId}; - }); - - return {files: inlineFiles, attachments: remappedAttachments}; - } - private async sendMultipartMessage( channelId: string, requestBody: MessageCreateRequest, diff --git a/fluxer_app/src/utils/MessageAttachmentUtils.tsx b/fluxer_app/src/utils/MessageAttachmentUtils.tsx index 0557d949..e881bd9e 100644 --- a/fluxer_app/src/utils/MessageAttachmentUtils.tsx +++ b/fluxer_app/src/utils/MessageAttachmentUtils.tsx @@ -39,7 +39,7 @@ export async function prepareAttachmentsForNonce( throw new Error('No message upload found'); } - const inlineAttachments = messageUpload.attachments.filter((att) => !att.uploadedFilename); + const inlineAttachments = messageUpload.attachments; const files = inlineAttachments.map((att) => att.file); const attachments = favoriteMemeId ? undefined : mapMessageUploadAttachments(messageUpload.attachments); @@ -55,6 +55,5 @@ export function mapMessageUploadAttachments(attachments: Array) flags: att.flags, duration: att.duration != null ? Math.ceil(att.duration) : undefined, waveform: att.waveform ?? undefined, - uploaded_filename: att.uploadedFilename, })); } diff --git a/fluxer_app/src/utils/MessageRequestUtils.tsx b/fluxer_app/src/utils/MessageRequestUtils.tsx index 04031889..0717fa26 100644 --- a/fluxer_app/src/utils/MessageRequestUtils.tsx +++ b/fluxer_app/src/utils/MessageRequestUtils.tsx @@ -34,7 +34,6 @@ export interface ApiAttachmentMetadata { flags?: number; duration?: number; waveform?: string; - uploaded_filename?: string; } export interface MessageCreateRequest { diff --git a/packages/api/src/channel/controllers/ChunkedUploadController.tsx b/packages/api/src/channel/controllers/ChunkedUploadController.tsx deleted file mode 100644 index 61dcd2c3..00000000 --- a/packages/api/src/channel/controllers/ChunkedUploadController.tsx +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2026 Fluxer Contributors - * - * This file is part of Fluxer. - * - * Fluxer is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Fluxer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Fluxer. If not, see . - */ - -import {createChannelID} from '@fluxer/api/src/BrandedTypes'; -import {DefaultUserOnly, LoginRequired} from '@fluxer/api/src/middleware/AuthMiddleware'; -import {RateLimitMiddleware} from '@fluxer/api/src/middleware/RateLimitMiddleware'; -import {OpenAPI} from '@fluxer/api/src/middleware/ResponseTypeMiddleware'; -import {RateLimitConfigs} from '@fluxer/api/src/RateLimitConfig'; -import type {HonoApp} from '@fluxer/api/src/types/HonoEnv'; -import {Validator} from '@fluxer/api/src/Validator'; -import { - ChunkedUploadChunkParam, - ChunkedUploadParam, - CompleteChunkedUploadRequest, - CompleteChunkedUploadResponse, - CreateChunkedUploadRequest, - CreateChunkedUploadResponse, - UploadChunkResponse, -} from '@fluxer/schema/src/domains/channel/ChunkedUploadSchemas'; -import {ChannelIdParam} from '@fluxer/schema/src/domains/common/CommonParamSchemas'; - -export function ChunkedUploadController(app: HonoApp) { - app.post( - '/channels/:channel_id/chunked-uploads', - RateLimitMiddleware(RateLimitConfigs.CHANNEL_CHUNKED_UPLOAD_CREATE), - LoginRequired, - DefaultUserOnly, - Validator('param', ChannelIdParam), - Validator('json', CreateChunkedUploadRequest), - OpenAPI({ - operationId: 'create_chunked_upload', - summary: 'Initiate a chunked upload session', - description: - 'Creates a new chunked upload session for uploading large files. Returns the upload ID, expected chunk size, and total chunk count. The client should then upload each chunk individually and complete the upload when all chunks are uploaded.', - responseSchema: CreateChunkedUploadResponse, - statusCode: 201, - security: ['bearerToken', 'sessionToken'], - tags: ['Channels', 'Attachments'], - }), - async (ctx) => { - const user = ctx.get('user'); - const channelId = createChannelID(ctx.req.valid('param').channel_id); - const body = ctx.req.valid('json'); - const chunkedUploadService = ctx.get('chunkedUploadService'); - const result = await chunkedUploadService.initiateUpload(user.id, channelId, body); - return ctx.json(result, 201); - }, - ); - - app.put( - '/channels/:channel_id/chunked-uploads/:upload_id/chunks/:chunk_index', - RateLimitMiddleware(RateLimitConfigs.CHANNEL_CHUNKED_UPLOAD_CHUNK), - LoginRequired, - DefaultUserOnly, - Validator('param', ChunkedUploadChunkParam), - OpenAPI({ - operationId: 'upload_chunk', - summary: 'Upload a file chunk', - description: - 'Uploads a single chunk of a file as part of a chunked upload session. The chunk index is zero-based. Returns an ETag that must be provided when completing the upload.', - responseSchema: UploadChunkResponse, - statusCode: 200, - security: ['bearerToken', 'sessionToken'], - tags: ['Channels', 'Attachments'], - }), - async (ctx) => { - const user = ctx.get('user'); - const {upload_id, chunk_index} = ctx.req.valid('param'); - const arrayBuffer = await ctx.req.arrayBuffer(); - const body = new Uint8Array(arrayBuffer); - const chunkedUploadService = ctx.get('chunkedUploadService'); - const result = await chunkedUploadService.uploadChunk(user.id, upload_id, chunk_index, body); - return ctx.json(result); - }, - ); - - app.post( - '/channels/:channel_id/chunked-uploads/:upload_id/complete', - RateLimitMiddleware(RateLimitConfigs.CHANNEL_CHUNKED_UPLOAD_COMPLETE), - LoginRequired, - DefaultUserOnly, - Validator('param', ChunkedUploadParam), - Validator('json', CompleteChunkedUploadRequest), - OpenAPI({ - operationId: 'complete_chunked_upload', - summary: 'Complete a chunked upload', - description: - 'Completes a chunked upload session by assembling all uploaded chunks. Requires ETags for all chunks. Returns the upload filename that can be referenced when sending a message with the uploaded file.', - responseSchema: CompleteChunkedUploadResponse, - statusCode: 200, - security: ['bearerToken', 'sessionToken'], - tags: ['Channels', 'Attachments'], - }), - async (ctx) => { - const user = ctx.get('user'); - const {upload_id} = ctx.req.valid('param'); - const body = ctx.req.valid('json'); - const chunkedUploadService = ctx.get('chunkedUploadService'); - const result = await chunkedUploadService.completeUpload(user.id, upload_id, body); - return ctx.json(result); - }, - ); -} diff --git a/packages/api/src/channel/controllers/index.tsx b/packages/api/src/channel/controllers/index.tsx index 9b6a3a31..81b3b412 100644 --- a/packages/api/src/channel/controllers/index.tsx +++ b/packages/api/src/channel/controllers/index.tsx @@ -19,7 +19,6 @@ import {CallController} from '@fluxer/api/src/channel/controllers/CallController'; import {ChannelController} from '@fluxer/api/src/channel/controllers/ChannelController'; -import {ChunkedUploadController} from '@fluxer/api/src/channel/controllers/ChunkedUploadController'; import {MessageController} from '@fluxer/api/src/channel/controllers/MessageController'; import {MessageInteractionController} from '@fluxer/api/src/channel/controllers/MessageInteractionController'; import {ScheduledMessageController} from '@fluxer/api/src/channel/controllers/ScheduledMessageController'; @@ -31,7 +30,6 @@ export function registerChannelControllers(app: HonoApp) { MessageInteractionController(app); MessageController(app); ScheduledMessageController(app); - ChunkedUploadController(app); CallController(app); StreamController(app); } diff --git a/packages/api/src/channel/services/ChunkedUploadService.tsx b/packages/api/src/channel/services/ChunkedUploadService.tsx deleted file mode 100644 index 889d0c5c..00000000 --- a/packages/api/src/channel/services/ChunkedUploadService.tsx +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2026 Fluxer Contributors - * - * This file is part of Fluxer. - * - * Fluxer is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Fluxer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Fluxer. If not, see . - */ - -import type {ChannelID, UserID} from '@fluxer/api/src/BrandedTypes'; -import {Config} from '@fluxer/api/src/Config'; -import type {AuthenticatedChannel} from '@fluxer/api/src/channel/services/AuthenticatedChannel'; -import {getContentType} from '@fluxer/api/src/channel/services/message/MessageHelpers'; -import type {IStorageService} from '@fluxer/api/src/infrastructure/IStorageService'; -import type {LimitConfigService} from '@fluxer/api/src/limits/LimitConfigService'; -import {resolveLimitSafe} from '@fluxer/api/src/limits/LimitConfigUtils'; -import {createLimitMatchContext} from '@fluxer/api/src/limits/LimitMatchContextBuilder'; -import type {Channel} from '@fluxer/api/src/models/Channel'; -import type {IUserRepository} from '@fluxer/api/src/user/IUserRepository'; -import {Permissions} from '@fluxer/constants/src/ChannelConstants'; -import { - ATTACHMENT_MAX_SIZE_NON_PREMIUM, - CHUNKED_UPLOAD_CHUNK_SIZE, - CHUNKED_UPLOAD_MAX_CHUNKS, - CHUNKED_UPLOAD_SESSION_TTL_SECONDS, -} from '@fluxer/constants/src/LimitConstants'; -import {ChunkedUploadChunkIndexOutOfRangeError} from '@fluxer/errors/src/domains/channel/ChunkedUploadChunkIndexOutOfRangeError'; -import {ChunkedUploadIncompleteError} from '@fluxer/errors/src/domains/channel/ChunkedUploadIncompleteError'; -import {ChunkedUploadNotFoundError} from '@fluxer/errors/src/domains/channel/ChunkedUploadNotFoundError'; -import {ChunkedUploadNotOwnedError} from '@fluxer/errors/src/domains/channel/ChunkedUploadNotOwnedError'; -import {FileSizeTooLargeError} from '@fluxer/errors/src/domains/core/FileSizeTooLargeError'; -import {UnknownUserError} from '@fluxer/errors/src/domains/user/UnknownUserError'; -import type {IKVProvider} from '@fluxer/kv_client/src/IKVProvider'; -import type { - CompleteChunkedUploadRequest, - CompleteChunkedUploadResponse, - CreateChunkedUploadRequest, - CreateChunkedUploadResponse, - UploadChunkResponse, -} from '@fluxer/schema/src/domains/channel/ChunkedUploadSchemas'; - -interface ChunkedUploadSession { - userId: string; - channelId: string; - s3UploadId: string; - uploadFilename: string; - filename: string; - fileSize: number; - chunkSize: number; - chunkCount: number; - contentType: string; -} - -function sessionKey(uploadId: string): string { - return `chunked_upload:${uploadId}`; -} - -export class ChunkedUploadService { - constructor( - private storageService: IStorageService, - private kvProvider: IKVProvider, - private userRepository: IUserRepository, - private limitConfigService: LimitConfigService, - private getChannelAuthenticated: (params: {userId: UserID; channelId: ChannelID}) => Promise, - private ensureTextChannel: (channel: Channel) => void, - ) {} - - async initiateUpload( - userId: UserID, - channelId: ChannelID, - request: CreateChunkedUploadRequest, - ): Promise { - const {channel, guild, checkPermission} = await this.getChannelAuthenticated({userId, channelId}); - this.ensureTextChannel(channel); - - if (guild) { - await checkPermission(Permissions.SEND_MESSAGES | Permissions.ATTACH_FILES); - } - - const user = await this.userRepository.findUnique(userId); - if (!user) { - throw new UnknownUserError(); - } - - const fallbackMaxSize = ATTACHMENT_MAX_SIZE_NON_PREMIUM; - const ctx = createLimitMatchContext({user, guildFeatures: guild?.features ?? null}); - const maxFileSize = resolveLimitSafe( - this.limitConfigService.getConfigSnapshot(), - ctx, - 'max_attachment_file_size', - fallbackMaxSize, - ); - - if (request.file_size > maxFileSize) { - throw new FileSizeTooLargeError(maxFileSize); - } - - const chunkCount = Math.ceil(request.file_size / CHUNKED_UPLOAD_CHUNK_SIZE); - if (chunkCount > CHUNKED_UPLOAD_MAX_CHUNKS) { - throw new FileSizeTooLargeError(maxFileSize); - } - - const uploadFilename = crypto.randomUUID(); - const contentType = getContentType(request.filename); - - const {uploadId: s3UploadId} = await this.storageService.createMultipartUpload({ - bucket: Config.s3.buckets.uploads, - key: uploadFilename, - contentType, - }); - - const uploadId = crypto.randomUUID(); - - const session: ChunkedUploadSession = { - userId: userId.toString(), - channelId: channelId.toString(), - s3UploadId, - uploadFilename, - filename: request.filename, - fileSize: request.file_size, - chunkSize: CHUNKED_UPLOAD_CHUNK_SIZE, - chunkCount, - contentType, - }; - - await this.kvProvider.setex(sessionKey(uploadId), CHUNKED_UPLOAD_SESSION_TTL_SECONDS, JSON.stringify(session)); - - return { - upload_id: uploadId, - upload_filename: uploadFilename, - chunk_size: CHUNKED_UPLOAD_CHUNK_SIZE, - chunk_count: chunkCount, - }; - } - - async uploadChunk( - userId: UserID, - uploadId: string, - chunkIndex: number, - body: Uint8Array, - ): Promise { - const session = await this.getSession(uploadId); - this.verifyOwnership(session, userId); - - if (chunkIndex < 0 || chunkIndex >= session.chunkCount) { - throw new ChunkedUploadChunkIndexOutOfRangeError(); - } - - const {etag} = await this.storageService.uploadPart({ - bucket: Config.s3.buckets.uploads, - key: session.uploadFilename, - uploadId: session.s3UploadId, - partNumber: chunkIndex + 1, - body, - }); - - return {etag}; - } - - async completeUpload( - userId: UserID, - uploadId: string, - request: CompleteChunkedUploadRequest, - ): Promise { - const session = await this.getSession(uploadId); - this.verifyOwnership(session, userId); - - if (request.etags.length !== session.chunkCount) { - throw new ChunkedUploadIncompleteError(); - } - - const seenIndices = new Set(); - for (const entry of request.etags) { - if (entry.chunk_index < 0 || entry.chunk_index >= session.chunkCount) { - throw new ChunkedUploadChunkIndexOutOfRangeError(); - } - if (seenIndices.has(entry.chunk_index)) { - throw new ChunkedUploadIncompleteError(); - } - seenIndices.add(entry.chunk_index); - } - - const parts = request.etags.map((entry) => ({ - partNumber: entry.chunk_index + 1, - etag: entry.etag, - })); - - await this.storageService.completeMultipartUpload({ - bucket: Config.s3.buckets.uploads, - key: session.uploadFilename, - uploadId: session.s3UploadId, - parts, - }); - - await this.kvProvider.del(sessionKey(uploadId)); - - return { - upload_filename: session.uploadFilename, - file_size: session.fileSize, - content_type: session.contentType, - }; - } - - private async getSession(uploadId: string): Promise { - const raw = await this.kvProvider.get(sessionKey(uploadId)); - if (!raw) { - throw new ChunkedUploadNotFoundError(); - } - return JSON.parse(raw) as ChunkedUploadSession; - } - - private verifyOwnership(session: ChunkedUploadSession, userId: UserID): void { - if (session.userId !== userId.toString()) { - throw new ChunkedUploadNotOwnedError(); - } - } -} diff --git a/packages/api/src/channel/services/message/MessageRequestParser.tsx b/packages/api/src/channel/services/message/MessageRequestParser.tsx index 54cc2a87..39478684 100644 --- a/packages/api/src/channel/services/message/MessageRequestParser.tsx +++ b/packages/api/src/channel/services/message/MessageRequestParser.tsx @@ -18,7 +18,6 @@ */ import type {ChannelID} from '@fluxer/api/src/BrandedTypes'; -import {Config} from '@fluxer/api/src/Config'; import { type AttachmentRequestData, mergeUploadWithClientData, @@ -26,7 +25,6 @@ import { } from '@fluxer/api/src/channel/AttachmentDTOs'; import type {IChannelRepository} from '@fluxer/api/src/channel/IChannelRepository'; import type {MessageRequest, MessageUpdateRequest} from '@fluxer/api/src/channel/MessageTypes'; -import {getContentType} from '@fluxer/api/src/channel/services/message/MessageHelpers'; import type {GuildService} from '@fluxer/api/src/guild/services/GuildService'; import type {LimitConfigService} from '@fluxer/api/src/limits/LimitConfigService'; import {resolveLimitSafe} from '@fluxer/api/src/limits/LimitConfigUtils'; @@ -172,14 +170,11 @@ export async function parseMultipartMessageData( const fileIds = new Set(filesWithIndices.map((f) => f.index)); const inlineNewAttachments: Array = []; - const preUploadedNewAttachments: Array = []; for (const att of newAttachments) { const id = typeof att.id === 'string' ? parseInt(att.id, 10) : att.id; if (fileIds.has(id)) { inlineNewAttachments.push(att); - } else if (att.uploaded_filename) { - preUploadedNewAttachments.push(att); } else { throw InputValidationError.fromCode('attachments', ValidationErrorCodes.NO_FILE_FOR_ATTACHMENT_METADATA, { attachmentId: att.id, @@ -232,32 +227,7 @@ export async function parseMultipartMessageData( }); } - let processedPreUploadedAttachments: Array = []; - if (preUploadedNewAttachments.length > 0) { - const storageService = ctx.get('storageService'); - - processedPreUploadedAttachments = await Promise.all( - preUploadedNewAttachments.map(async (clientData) => { - const uploadFilename = clientData.uploaded_filename!; - const metadata = await storageService.getObjectMetadata(Config.s3.buckets.uploads, uploadFilename); - if (!metadata) { - throw InputValidationError.fromCode('attachments', ValidationErrorCodes.NO_FILE_FOR_ATTACHMENT_METADATA, { - attachmentId: clientData.id, - }); - } - const uploaded: UploadedAttachment = { - id: typeof clientData.id === 'string' ? parseInt(clientData.id, 10) : clientData.id, - upload_filename: uploadFilename, - filename: clientData.filename, - file_size: metadata.contentLength, - content_type: getContentType(clientData.filename), - }; - return mergeUploadWithClientData(uploaded, clientData); - }), - ); - } - - data.attachments = [...existingAttachments, ...processedInlineAttachments, ...processedPreUploadedAttachments]; + data.attachments = [...existingAttachments, ...processedInlineAttachments]; } return data as MessageRequest | MessageUpdateRequest; diff --git a/packages/api/src/channel/tests/ChunkedUpload.test.tsx b/packages/api/src/channel/tests/ChunkedUpload.test.tsx deleted file mode 100644 index 1f4b207e..00000000 --- a/packages/api/src/channel/tests/ChunkedUpload.test.tsx +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Copyright (C) 2026 Fluxer Contributors - * - * This file is part of Fluxer. - * - * Fluxer is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Fluxer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Fluxer. If not, see . - */ - -import type {TestAccount} from '@fluxer/api/src/auth/tests/AuthTestUtils'; -import {createMultipartFormData, setupTestGuildAndChannel} from '@fluxer/api/src/channel/tests/AttachmentTestUtils'; -import type {ApiTestHarness} from '@fluxer/api/src/test/ApiTestHarness'; -import {createApiTestHarness} from '@fluxer/api/src/test/ApiTestHarness'; -import {createBuilder} from '@fluxer/api/src/test/TestRequestBuilder'; -import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes'; -import {CHUNKED_UPLOAD_CHUNK_SIZE} from '@fluxer/constants/src/LimitConstants'; -import type {ChannelResponse} from '@fluxer/schema/src/domains/channel/ChannelSchemas'; -import type { - CompleteChunkedUploadResponse, - CreateChunkedUploadResponse, - UploadChunkResponse, -} from '@fluxer/schema/src/domains/channel/ChunkedUploadSchemas'; -import type {MessageResponse} from '@fluxer/schema/src/domains/message/MessageResponseSchemas'; -import {afterAll, beforeAll, beforeEach, describe, expect, it} from 'vitest'; - -let harness: ApiTestHarness; - -beforeAll(async () => { - harness = await createApiTestHarness(); -}); - -beforeEach(async () => { - await harness.reset(); -}); - -afterAll(async () => { - await harness?.shutdown(); -}); - -async function initiateChunkedUpload( - token: string, - channelId: string, - filename: string, - fileSize: number, -): Promise { - return createBuilder(harness, token) - .post(`/channels/${channelId}/chunked-uploads`) - .body({filename, file_size: fileSize}) - .expect(201) - .execute(); -} - -async function uploadChunk( - token: string, - channelId: string, - uploadId: string, - chunkIndex: number, - data: Buffer, -): Promise { - const response = await harness.app.request( - `/channels/${channelId}/chunked-uploads/${uploadId}/chunks/${chunkIndex}`, - { - method: 'PUT', - headers: new Headers({ - Authorization: token, - 'Content-Type': 'application/octet-stream', - 'x-forwarded-for': '127.0.0.1', - }), - body: data, - }, - ); - - expect(response.status).toBe(200); - return (await response.json()) as UploadChunkResponse; -} - -async function completeChunkedUpload( - token: string, - channelId: string, - uploadId: string, - etags: Array<{chunk_index: number; etag: string}>, -): Promise { - return createBuilder(harness, token) - .post(`/channels/${channelId}/chunked-uploads/${uploadId}/complete`) - .body({etags}) - .execute(); -} - -describe('Chunked Uploads', () => { - let account: TestAccount; - let channel: ChannelResponse; - - beforeEach(async () => { - const setup = await setupTestGuildAndChannel(harness); - account = setup.account; - channel = setup.channel; - }); - - describe('POST /channels/:channel_id/chunked-uploads', () => { - it('should initiate a chunked upload session', async () => { - const fileSize = CHUNKED_UPLOAD_CHUNK_SIZE * 2 + 100; - const result = await initiateChunkedUpload(account.token, channel.id, 'large-file.bin', fileSize); - - expect(result.upload_id).toBeDefined(); - expect(result.upload_filename).toBeDefined(); - expect(result.chunk_size).toBe(CHUNKED_UPLOAD_CHUNK_SIZE); - expect(result.chunk_count).toBe(3); - }); - - it('should reject when file size exceeds the limit', async () => { - const hugeSize = 1024 * 1024 * 1024 * 10; - await createBuilder(harness, account.token) - .post(`/channels/${channel.id}/chunked-uploads`) - .body({filename: 'huge.bin', file_size: hugeSize}) - .expect(400, APIErrorCodes.FILE_SIZE_TOO_LARGE) - .execute(); - }); - - it('should reject without authentication', async () => { - await createBuilder(harness, '') - .post(`/channels/${channel.id}/chunked-uploads`) - .body({filename: 'file.bin', file_size: 1024}) - .expect(401) - .execute(); - }); - }); - - describe('PUT /channels/:channel_id/chunked-uploads/:upload_id/chunks/:chunk_index', () => { - it('should upload a chunk and return an etag', async () => { - const fileSize = CHUNKED_UPLOAD_CHUNK_SIZE + 100; - const initResult = await initiateChunkedUpload(account.token, channel.id, 'test.bin', fileSize); - - const chunkData = Buffer.alloc(CHUNKED_UPLOAD_CHUNK_SIZE, 0xab); - const result = await uploadChunk(account.token, channel.id, initResult.upload_id, 0, chunkData); - - expect(result.etag).toBeDefined(); - expect(typeof result.etag).toBe('string'); - }); - - it('should reject chunk index out of range', async () => { - const fileSize = CHUNKED_UPLOAD_CHUNK_SIZE + 100; - const initResult = await initiateChunkedUpload(account.token, channel.id, 'test.bin', fileSize); - - const chunkData = Buffer.alloc(100, 0xab); - const response = await harness.app.request( - `/channels/${channel.id}/chunked-uploads/${initResult.upload_id}/chunks/99`, - { - method: 'PUT', - headers: new Headers({ - Authorization: account.token, - 'Content-Type': 'application/octet-stream', - 'x-forwarded-for': '127.0.0.1', - }), - body: chunkData, - }, - ); - - expect(response.status).toBe(400); - const body = (await response.json()) as {code: string}; - expect(body.code).toBe(APIErrorCodes.CHUNKED_UPLOAD_CHUNK_INDEX_OUT_OF_RANGE); - }); - - it('should reject for non-existent upload session', async () => { - const chunkData = Buffer.alloc(100, 0xab); - const response = await harness.app.request(`/channels/${channel.id}/chunked-uploads/non-existent-id/chunks/0`, { - method: 'PUT', - headers: new Headers({ - Authorization: account.token, - 'Content-Type': 'application/octet-stream', - 'x-forwarded-for': '127.0.0.1', - }), - body: chunkData, - }); - - expect(response.status).toBe(404); - const body = (await response.json()) as {code: string}; - expect(body.code).toBe(APIErrorCodes.CHUNKED_UPLOAD_NOT_FOUND); - }); - }); - - describe('POST /channels/:channel_id/chunked-uploads/:upload_id/complete', () => { - it('should complete a chunked upload', async () => { - const chunkSize = CHUNKED_UPLOAD_CHUNK_SIZE; - const fileSize = chunkSize * 2; - const initResult = await initiateChunkedUpload(account.token, channel.id, 'two-chunks.bin', fileSize); - - const chunk0 = Buffer.alloc(chunkSize, 0xaa); - const chunk1 = Buffer.alloc(chunkSize, 0xbb); - - const etag0 = await uploadChunk(account.token, channel.id, initResult.upload_id, 0, chunk0); - const etag1 = await uploadChunk(account.token, channel.id, initResult.upload_id, 1, chunk1); - - const result = await completeChunkedUpload(account.token, channel.id, initResult.upload_id, [ - {chunk_index: 0, etag: etag0.etag}, - {chunk_index: 1, etag: etag1.etag}, - ]); - - expect(result.upload_filename).toBe(initResult.upload_filename); - expect(result.file_size).toBe(fileSize); - expect(result.content_type).toBeDefined(); - }); - - it('should reject when not all chunks have been provided', async () => { - const fileSize = CHUNKED_UPLOAD_CHUNK_SIZE * 2; - const initResult = await initiateChunkedUpload(account.token, channel.id, 'test.bin', fileSize); - - const chunk0 = Buffer.alloc(CHUNKED_UPLOAD_CHUNK_SIZE, 0xaa); - const etag0 = await uploadChunk(account.token, channel.id, initResult.upload_id, 0, chunk0); - - await createBuilder(harness, account.token) - .post(`/channels/${channel.id}/chunked-uploads/${initResult.upload_id}/complete`) - .body({etags: [{chunk_index: 0, etag: etag0.etag}]}) - .expect(400, APIErrorCodes.CHUNKED_UPLOAD_INCOMPLETE) - .execute(); - }); - - it('should reject duplicate chunk indices', async () => { - const fileSize = CHUNKED_UPLOAD_CHUNK_SIZE * 2; - const initResult = await initiateChunkedUpload(account.token, channel.id, 'test.bin', fileSize); - - const chunk0 = Buffer.alloc(CHUNKED_UPLOAD_CHUNK_SIZE, 0xaa); - const etag0 = await uploadChunk(account.token, channel.id, initResult.upload_id, 0, chunk0); - - await createBuilder(harness, account.token) - .post(`/channels/${channel.id}/chunked-uploads/${initResult.upload_id}/complete`) - .body({ - etags: [ - {chunk_index: 0, etag: etag0.etag}, - {chunk_index: 0, etag: etag0.etag}, - ], - }) - .expect(400, APIErrorCodes.CHUNKED_UPLOAD_INCOMPLETE) - .execute(); - }); - }); - - describe('Upload ownership', () => { - it('should reject chunk upload from a different user', async () => { - const fileSize = CHUNKED_UPLOAD_CHUNK_SIZE + 100; - const initResult = await initiateChunkedUpload(account.token, channel.id, 'test.bin', fileSize); - - const otherSetup = await setupTestGuildAndChannel(harness); - const otherAccount = otherSetup.account; - - const chunkData = Buffer.alloc(100, 0xab); - const response = await harness.app.request( - `/channels/${channel.id}/chunked-uploads/${initResult.upload_id}/chunks/0`, - { - method: 'PUT', - headers: new Headers({ - Authorization: otherAccount.token, - 'Content-Type': 'application/octet-stream', - 'x-forwarded-for': '127.0.0.1', - }), - body: chunkData, - }, - ); - - expect(response.status).toBe(403); - const body = (await response.json()) as {code: string}; - expect(body.code).toBe(APIErrorCodes.CHUNKED_UPLOAD_NOT_OWNED); - }); - }); - - describe('End-to-end: chunked upload + message send', () => { - it('should send a message with a pre-uploaded file', async () => { - const chunkSize = CHUNKED_UPLOAD_CHUNK_SIZE; - const fileSize = chunkSize + 500; - const initResult = await initiateChunkedUpload(account.token, channel.id, 'uploaded-file.txt', fileSize); - - const chunk0 = Buffer.alloc(chunkSize, 0x41); - const chunk1 = Buffer.alloc(500, 0x42); - - const etag0 = await uploadChunk(account.token, channel.id, initResult.upload_id, 0, chunk0); - const etag1 = await uploadChunk(account.token, channel.id, initResult.upload_id, 1, chunk1); - - await completeChunkedUpload(account.token, channel.id, initResult.upload_id, [ - {chunk_index: 0, etag: etag0.etag}, - {chunk_index: 1, etag: etag1.etag}, - ]); - - const payload = { - content: 'Message with chunked upload', - attachments: [ - { - id: 0, - filename: 'uploaded-file.txt', - uploaded_filename: initResult.upload_filename, - }, - ], - }; - - const {body, contentType} = createMultipartFormData(payload, []); - - const mergedHeaders = new Headers(); - mergedHeaders.set('Content-Type', contentType); - mergedHeaders.set('Authorization', account.token); - mergedHeaders.set('x-forwarded-for', '127.0.0.1'); - - const response = await harness.app.request(`/channels/${channel.id}/messages`, { - method: 'POST', - headers: mergedHeaders, - body, - }); - - expect(response.status).toBe(200); - const message = (await response.json()) as MessageResponse; - expect(message.content).toBe('Message with chunked upload'); - expect(message.attachments).toBeDefined(); - expect(message.attachments!.length).toBe(1); - expect(message.attachments![0].filename).toBe('uploaded-file.txt'); - }); - - it('should send a message with both inline and pre-uploaded files', async () => { - const chunkSize = CHUNKED_UPLOAD_CHUNK_SIZE; - const fileSize = chunkSize + 100; - const initResult = await initiateChunkedUpload(account.token, channel.id, 'large.bin', fileSize); - - const chunk0 = Buffer.alloc(chunkSize, 0xcc); - const chunk1 = Buffer.alloc(100, 0xdd); - - const etag0 = await uploadChunk(account.token, channel.id, initResult.upload_id, 0, chunk0); - const etag1 = await uploadChunk(account.token, channel.id, initResult.upload_id, 1, chunk1); - - await completeChunkedUpload(account.token, channel.id, initResult.upload_id, [ - {chunk_index: 0, etag: etag0.etag}, - {chunk_index: 1, etag: etag1.etag}, - ]); - - const smallFileData = Buffer.from('small inline file content'); - const payload = { - content: 'Mixed upload message', - attachments: [ - { - id: 0, - filename: 'small.txt', - }, - { - id: 1, - filename: 'large.bin', - uploaded_filename: initResult.upload_filename, - }, - ], - }; - - const {response, json} = await sendMixedMessage(account.token, channel.id, payload, [ - {index: 0, filename: 'small.txt', data: smallFileData}, - ]); - - expect(response.status).toBe(200); - expect(json.content).toBe('Mixed upload message'); - expect(json.attachments).toBeDefined(); - expect(json.attachments!.length).toBe(2); - }); - }); -}); - -async function sendMixedMessage( - token: string, - channelId: string, - payload: Record, - files: Array<{index: number; filename: string; data: Buffer}>, -): Promise<{response: Response; json: MessageResponse}> { - const {body, contentType} = createMultipartFormData(payload, files); - - const mergedHeaders = new Headers(); - mergedHeaders.set('Content-Type', contentType); - mergedHeaders.set('Authorization', token); - mergedHeaders.set('x-forwarded-for', '127.0.0.1'); - - const response = await harness.app.request(`/channels/${channelId}/messages`, { - method: 'POST', - headers: mergedHeaders, - body, - }); - - const text = await response.text(); - let json: MessageResponse = undefined as unknown as MessageResponse; - if (text.length > 0) { - try { - json = JSON.parse(text) as MessageResponse; - } catch {} - } - - return {response, json}; -} diff --git a/packages/api/src/middleware/ServiceMiddleware.tsx b/packages/api/src/middleware/ServiceMiddleware.tsx index e8ab98d4..b7f6b6ab 100644 --- a/packages/api/src/middleware/ServiceMiddleware.tsx +++ b/packages/api/src/middleware/ServiceMiddleware.tsx @@ -35,7 +35,6 @@ import {Config} from '@fluxer/api/src/Config'; import {ChannelRepository} from '@fluxer/api/src/channel/ChannelRepository'; import {ChannelRequestService} from '@fluxer/api/src/channel/services/ChannelRequestService'; import {ChannelService} from '@fluxer/api/src/channel/services/ChannelService'; -import {ChunkedUploadService} from '@fluxer/api/src/channel/services/ChunkedUploadService'; import {MessageRequestService} from '@fluxer/api/src/channel/services/message/MessageRequestService'; import {ScheduledMessageService} from '@fluxer/api/src/channel/services/ScheduledMessageService'; import {StreamPreviewService} from '@fluxer/api/src/channel/services/StreamPreviewService'; @@ -160,13 +159,11 @@ import {WebhookRequestService} from '@fluxer/api/src/webhook/WebhookRequestServi import {WebhookService} from '@fluxer/api/src/webhook/WebhookService'; import type {ICacheService} from '@fluxer/cache/src/ICacheService'; import {KVCacheProvider} from '@fluxer/cache/src/providers/KVCacheProvider'; -import {TEXT_BASED_CHANNEL_TYPES} from '@fluxer/constants/src/ChannelConstants'; import {EmailI18nService} from '@fluxer/email/src/EmailI18nService'; import type {EmailConfig, UserBouncedEmailChecker} from '@fluxer/email/src/EmailProviderTypes'; import {EmailService} from '@fluxer/email/src/EmailService'; import type {IEmailService} from '@fluxer/email/src/IEmailService'; import {TestEmailService} from '@fluxer/email/src/TestEmailService'; -import {CannotSendMessageToNonTextChannelError} from '@fluxer/errors/src/domains/channel/CannotSendMessageToNonTextChannelError'; import {createMockLogger} from '@fluxer/logger/src/mock'; import {RateLimitService} from '@fluxer/rate_limit/src/RateLimitService'; import type {ISmsProvider} from '@fluxer/sms/src/providers/ISmsProvider'; @@ -474,19 +471,6 @@ export const ServiceMiddleware = createMiddleware(async (ctx, next) => mediaService, ); - const chunkedUploadService = new ChunkedUploadService( - storageService, - kvClient, - userRepository, - limitConfigService, - channelService.getChannelAuthenticated.bind(channelService), - (channel) => { - if (!TEXT_BASED_CHANNEL_TYPES.has(channel.type)) { - throw new CannotSendMessageToNonTextChannelError(); - } - }, - ); - const scheduledMessageRepository = new ScheduledMessageRepository(); const scheduledMessageService = new ScheduledMessageService( channelService, @@ -886,7 +870,6 @@ export const ServiceMiddleware = createMiddleware(async (ctx, next) => ctx.set('cacheService', cacheService); ctx.set('channelService', channelService); ctx.set('channelRequestService', channelRequestService); - ctx.set('chunkedUploadService', chunkedUploadService); ctx.set('messageRequestService', messageRequestService); ctx.set('channelRepository', channelRepository); ctx.set('connectionService', connectionService); diff --git a/packages/api/src/rate_limit_configs/ChannelRateLimitConfig.tsx b/packages/api/src/rate_limit_configs/ChannelRateLimitConfig.tsx index 27ff548c..223098bd 100644 --- a/packages/api/src/rate_limit_configs/ChannelRateLimitConfig.tsx +++ b/packages/api/src/rate_limit_configs/ChannelRateLimitConfig.tsx @@ -140,19 +140,4 @@ export const ChannelRateLimitConfigs = { bucket: 'channel:stream:preview:post::stream_key', config: {limit: 20, windowMs: ms('10 seconds')}, } as RouteRateLimitConfig, - - CHANNEL_CHUNKED_UPLOAD_CREATE: { - bucket: 'channel:chunked_upload:create::channel_id', - config: {limit: 5, windowMs: ms('10 seconds')}, - } as RouteRateLimitConfig, - - CHANNEL_CHUNKED_UPLOAD_CHUNK: { - bucket: 'channel:chunked_upload:chunk::channel_id', - config: {limit: 50, windowMs: ms('10 seconds')}, - } as RouteRateLimitConfig, - - CHANNEL_CHUNKED_UPLOAD_COMPLETE: { - bucket: 'channel:chunked_upload:complete::channel_id', - config: {limit: 5, windowMs: ms('10 seconds')}, - } as RouteRateLimitConfig, } as const; diff --git a/packages/api/src/types/HonoEnv.tsx b/packages/api/src/types/HonoEnv.tsx index 122aad31..0195bd0f 100644 --- a/packages/api/src/types/HonoEnv.tsx +++ b/packages/api/src/types/HonoEnv.tsx @@ -31,7 +31,6 @@ import type {IBlueskyOAuthService} from '@fluxer/api/src/bluesky/IBlueskyOAuthSe import type {IChannelRepository} from '@fluxer/api/src/channel/IChannelRepository'; import type {ChannelRequestService} from '@fluxer/api/src/channel/services/ChannelRequestService'; import type {ChannelService} from '@fluxer/api/src/channel/services/ChannelService'; -import type {ChunkedUploadService} from '@fluxer/api/src/channel/services/ChunkedUploadService'; import type {MessageRequestService} from '@fluxer/api/src/channel/services/message/MessageRequestService'; import type {ScheduledMessageService} from '@fluxer/api/src/channel/services/ScheduledMessageService'; import type {StreamPreviewService} from '@fluxer/api/src/channel/services/StreamPreviewService'; @@ -134,7 +133,6 @@ export interface HonoEnv { cacheService: ICacheService; channelService: ChannelService; channelRequestService: ChannelRequestService; - chunkedUploadService: ChunkedUploadService; messageRequestService: MessageRequestService; channelRepository: IChannelRepository; connectionService: ConnectionService; diff --git a/packages/constants/src/ApiErrorCodes.tsx b/packages/constants/src/ApiErrorCodes.tsx index 70720a36..4e144e8d 100644 --- a/packages/constants/src/ApiErrorCodes.tsx +++ b/packages/constants/src/ApiErrorCodes.tsx @@ -66,10 +66,6 @@ export const APIErrorCodes = { CANNOT_SHRINK_RESERVED_SLOTS: 'CANNOT_SHRINK_RESERVED_SLOTS', CAPTCHA_REQUIRED: 'CAPTCHA_REQUIRED', CHANNEL_INDEXING: 'CHANNEL_INDEXING', - CHUNKED_UPLOAD_CHUNK_INDEX_OUT_OF_RANGE: 'CHUNKED_UPLOAD_CHUNK_INDEX_OUT_OF_RANGE', - CHUNKED_UPLOAD_INCOMPLETE: 'CHUNKED_UPLOAD_INCOMPLETE', - CHUNKED_UPLOAD_NOT_FOUND: 'CHUNKED_UPLOAD_NOT_FOUND', - CHUNKED_UPLOAD_NOT_OWNED: 'CHUNKED_UPLOAD_NOT_OWNED', COMMUNICATION_DISABLED: 'COMMUNICATION_DISABLED', CONNECTION_ALREADY_EXISTS: 'CONNECTION_ALREADY_EXISTS', CONNECTION_INITIATION_TOKEN_INVALID: 'CONNECTION_INITIATION_TOKEN_INVALID', diff --git a/packages/constants/src/ApiErrorCodesDescriptions.tsx b/packages/constants/src/ApiErrorCodesDescriptions.tsx index 99d5ad90..12ae7940 100644 --- a/packages/constants/src/ApiErrorCodesDescriptions.tsx +++ b/packages/constants/src/ApiErrorCodesDescriptions.tsx @@ -66,10 +66,6 @@ export const APIErrorCodesDescriptions: Record = new Set([ 1 * 3600, 12 * 3600, diff --git a/packages/errors/src/domains/channel/ChunkedUploadChunkIndexOutOfRangeError.tsx b/packages/errors/src/domains/channel/ChunkedUploadChunkIndexOutOfRangeError.tsx deleted file mode 100644 index 5fa4280e..00000000 --- a/packages/errors/src/domains/channel/ChunkedUploadChunkIndexOutOfRangeError.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2026 Fluxer Contributors - * - * This file is part of Fluxer. - * - * Fluxer is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Fluxer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Fluxer. If not, see . - */ - -import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes'; -import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError'; - -export class ChunkedUploadChunkIndexOutOfRangeError extends BadRequestError { - constructor() { - super({code: APIErrorCodes.CHUNKED_UPLOAD_CHUNK_INDEX_OUT_OF_RANGE}); - } -} diff --git a/packages/errors/src/domains/channel/ChunkedUploadIncompleteError.tsx b/packages/errors/src/domains/channel/ChunkedUploadIncompleteError.tsx deleted file mode 100644 index 735f4c4a..00000000 --- a/packages/errors/src/domains/channel/ChunkedUploadIncompleteError.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2026 Fluxer Contributors - * - * This file is part of Fluxer. - * - * Fluxer is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Fluxer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Fluxer. If not, see . - */ - -import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes'; -import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError'; - -export class ChunkedUploadIncompleteError extends BadRequestError { - constructor() { - super({code: APIErrorCodes.CHUNKED_UPLOAD_INCOMPLETE}); - } -} diff --git a/packages/errors/src/domains/channel/ChunkedUploadNotFoundError.tsx b/packages/errors/src/domains/channel/ChunkedUploadNotFoundError.tsx deleted file mode 100644 index 0ac79409..00000000 --- a/packages/errors/src/domains/channel/ChunkedUploadNotFoundError.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2026 Fluxer Contributors - * - * This file is part of Fluxer. - * - * Fluxer is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Fluxer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Fluxer. If not, see . - */ - -import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes'; -import {NotFoundError} from '@fluxer/errors/src/domains/core/NotFoundError'; - -export class ChunkedUploadNotFoundError extends NotFoundError { - constructor() { - super({code: APIErrorCodes.CHUNKED_UPLOAD_NOT_FOUND}); - } -} diff --git a/packages/errors/src/domains/channel/ChunkedUploadNotOwnedError.tsx b/packages/errors/src/domains/channel/ChunkedUploadNotOwnedError.tsx deleted file mode 100644 index 1ae96a13..00000000 --- a/packages/errors/src/domains/channel/ChunkedUploadNotOwnedError.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2026 Fluxer Contributors - * - * This file is part of Fluxer. - * - * Fluxer is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Fluxer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Fluxer. If not, see . - */ - -import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes'; -import {ForbiddenError} from '@fluxer/errors/src/domains/core/ForbiddenError'; - -export class ChunkedUploadNotOwnedError extends ForbiddenError { - constructor() { - super({code: APIErrorCodes.CHUNKED_UPLOAD_NOT_OWNED}); - } -} diff --git a/packages/errors/src/i18n/ErrorCodeMappings.tsx b/packages/errors/src/i18n/ErrorCodeMappings.tsx index c56626d5..091b4e60 100644 --- a/packages/errors/src/i18n/ErrorCodeMappings.tsx +++ b/packages/errors/src/i18n/ErrorCodeMappings.tsx @@ -78,11 +78,6 @@ export const ErrorCodeToI18nKey = { [APIErrorCodes.CANNOT_SHRINK_RESERVED_SLOTS]: 'limits.cannot_shrink_reserved_slots', [APIErrorCodes.CAPTCHA_REQUIRED]: 'captcha.required', [APIErrorCodes.CHANNEL_INDEXING]: 'admin_and_system.channel_indexing', - [APIErrorCodes.CHUNKED_UPLOAD_CHUNK_INDEX_OUT_OF_RANGE]: - 'attachments_and_uploads.chunked_upload_chunk_index_out_of_range', - [APIErrorCodes.CHUNKED_UPLOAD_INCOMPLETE]: 'attachments_and_uploads.chunked_upload_incomplete', - [APIErrorCodes.CHUNKED_UPLOAD_NOT_FOUND]: 'attachments_and_uploads.chunked_upload_not_found', - [APIErrorCodes.CHUNKED_UPLOAD_NOT_OWNED]: 'attachments_and_uploads.chunked_upload_not_owned', [APIErrorCodes.COMMUNICATION_DISABLED]: 'account.communication_disabled', [APIErrorCodes.CONNECTION_ALREADY_EXISTS]: 'connections.already_exists', [APIErrorCodes.CONNECTION_INITIATION_TOKEN_INVALID]: 'connections.initiation_token_invalid', diff --git a/packages/errors/src/i18n/ErrorI18nTypes.generated.tsx b/packages/errors/src/i18n/ErrorI18nTypes.generated.tsx index 12d59729..84cdf6dd 100644 --- a/packages/errors/src/i18n/ErrorI18nTypes.generated.tsx +++ b/packages/errors/src/i18n/ErrorI18nTypes.generated.tsx @@ -79,10 +79,6 @@ export type ErrorI18nKey = | 'attachments_and_uploads.attachments_not_allowed_for_message' | 'attachments_and_uploads.cannot_edit_attachment_metadata' | 'attachments_and_uploads.cannot_reference_attachments_without_attachments' - | 'attachments_and_uploads.chunked_upload_chunk_index_out_of_range' - | 'attachments_and_uploads.chunked_upload_incomplete' - | 'attachments_and_uploads.chunked_upload_not_found' - | 'attachments_and_uploads.chunked_upload_not_owned' | 'attachments_and_uploads.duplicate_attachment_ids_not_allowed' | 'attachments_and_uploads.duplicate_file_index' | 'attachments_and_uploads.failed_to_parse_multipart_form_data' diff --git a/packages/errors/src/i18n/locales/ar.yaml b/packages/errors/src/i18n/locales/ar.yaml index 9d837295..86eb8004 100644 --- a/packages/errors/src/i18n/locales/ar.yaml +++ b/packages/errors/src/i18n/locales/ar.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: بيئة الاختبار محظورة. admin_and_system.unknown_suspicious_flag: suspicious flag غير معروف. admin_and_system.update_failed: ما قدرنا نحدّث المورد. جرّب مرة ثانية. admin_and_system.user_must_be_bot_for_system_user: المستخدم لازم يكون بوت عشان يتم تعيينه كمستخدم نظام. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: فهرس الجزء خارج النطاق المسموح لهذا الرفع. -attachments_and_uploads.chunked_upload_incomplete: لم يتم رفع جميع الأجزاء بعد. -attachments_and_uploads.chunked_upload_not_found: جلسة الرفع غير موجودة أو انتهت صلاحيتها. -attachments_and_uploads.chunked_upload_not_owned: أنت لا تملك جلسة الرفع هذه. attachments_and_uploads.attachment_fields_required: '`attachment_id` و `channel_id` و `message_id` و `expires_at` مطلوبين.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id` و `channel_id` و `message_id` لازم تكون أرقام صحيحة.' attachments_and_uploads.cannot_edit_attachment_metadata: المستخدمون الذين لديهم صلاحية MANAGE_MESSAGES يمكنهم تعديل أوصاف المرفقات فقط، وليس البيانات الوصفية الأخرى. diff --git a/packages/errors/src/i18n/locales/bg.yaml b/packages/errors/src/i18n/locales/bg.yaml index 96899080..d2804624 100644 --- a/packages/errors/src/i18n/locales/bg.yaml +++ b/packages/errors/src/i18n/locales/bg.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Test harness е забранен. admin_and_system.unknown_suspicious_flag: Непознат suspicious flag. admin_and_system.update_failed: Не успяхме да обновим ресурса. Моля, опитай пак. admin_and_system.user_must_be_bot_for_system_user: Потребителят трябва да е бот, за да бъде маркиран като системен потребител. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Индексът на частта е извън обхвата за това качване. -attachments_and_uploads.chunked_upload_incomplete: Не всички части са качени. -attachments_and_uploads.chunked_upload_not_found: Сесията за качване не беше намерена или е изтекла. -attachments_and_uploads.chunked_upload_not_owned: Тази сесия за качване не е твоя. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` и `expires_at` са задължителни.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` и `message_id` трябва да са валидни цели числа.' attachments_and_uploads.cannot_edit_attachment_metadata: Потребителите с MANAGE_MESSAGES могат да редактират само описанията на прикачените файлове, не и други metadata. diff --git a/packages/errors/src/i18n/locales/cs.yaml b/packages/errors/src/i18n/locales/cs.yaml index 9858ade2..9ab4296d 100644 --- a/packages/errors/src/i18n/locales/cs.yaml +++ b/packages/errors/src/i18n/locales/cs.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Test harness je zakázaný. admin_and_system.unknown_suspicious_flag: Neznámý suspicious flag. admin_and_system.update_failed: Zdroj se nepodařilo aktualizovat. Zkus to znovu. admin_and_system.user_must_be_bot_for_system_user: Uživatel musí být bot, aby mohl být označený jako systémový uživatel. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Index části je mimo rozsah pro toto nahrávání. -attachments_and_uploads.chunked_upload_incomplete: Nebyly nahrány všechny části. -attachments_and_uploads.chunked_upload_not_found: Relace nahrávání nebyla nalezena nebo vypršela. -attachments_and_uploads.chunked_upload_not_owned: Tato relace nahrávání ti nepatří. attachments_and_uploads.attachment_fields_required: Pole `attachment_id`, `channel_id`, `message_id` a `expires_at` jsou povinná. attachments_and_uploads.attachment_ids_must_be_valid_integers: Pole `attachment_id`, `channel_id` a `message_id` musí být platná celá čísla. attachments_and_uploads.cannot_edit_attachment_metadata: Uživatelé s oprávněním MANAGE_MESSAGES můžou upravovat jen popisy příloh, ne další metadata. diff --git a/packages/errors/src/i18n/locales/da.yaml b/packages/errors/src/i18n/locales/da.yaml index 8e9532b8..7ebb8a50 100644 --- a/packages/errors/src/i18n/locales/da.yaml +++ b/packages/errors/src/i18n/locales/da.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Test harness er ikke tilladt. admin_and_system.unknown_suspicious_flag: Ukendt mistænkeligt flag. admin_and_system.update_failed: Opdatering mislykkedes. admin_and_system.user_must_be_bot_for_system_user: Brugeren skal være en bot for at blive markeret som systembruger. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Chunk-indekset er uden for intervallet for denne upload. -attachments_and_uploads.chunked_upload_incomplete: Ikke alle chunks er blevet uploadet. -attachments_and_uploads.chunked_upload_not_found: Uploadsessionen blev ikke fundet eller er udløbet. -attachments_and_uploads.chunked_upload_not_owned: Du ejer ikke denne uploadsession. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` og `expires_at` er påkrævet.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` og `message_id` skal være gyldige heltal.' attachments_and_uploads.cannot_edit_attachment_metadata: Brugere med MANAGE_MESSAGES kan kun redigere vedhæftningsbeskrivelser, ikke andre metadata. diff --git a/packages/errors/src/i18n/locales/de.yaml b/packages/errors/src/i18n/locales/de.yaml index 416dd545..d77e63eb 100644 --- a/packages/errors/src/i18n/locales/de.yaml +++ b/packages/errors/src/i18n/locales/de.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Test-Harness ist nicht erlaubt. admin_and_system.unknown_suspicious_flag: Unbekanntes verdächtiges Flag. admin_and_system.update_failed: Wir konnten die Ressource nicht aktualisieren. Bitte versuche es nochmal. admin_and_system.user_must_be_bot_for_system_user: Nutzer muss ein Bot sein, um als Systemnutzer markiert zu werden. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Der Chunk-Index liegt außerhalb des gültigen Bereichs für diesen Upload. -attachments_and_uploads.chunked_upload_incomplete: Es wurden nicht alle Chunks hochgeladen. -attachments_and_uploads.chunked_upload_not_found: Die Upload-Sitzung wurde nicht gefunden oder ist abgelaufen. -attachments_and_uploads.chunked_upload_not_owned: Diese Upload-Sitzung gehört nicht dir. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` und `expires_at` sind erforderlich.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` und `message_id` müssen gültige Integer sein.' attachments_and_uploads.cannot_edit_attachment_metadata: Nutzer mit MANAGE_MESSAGES können nur Attachment-Beschreibungen bearbeiten, keine anderen Metadaten. diff --git a/packages/errors/src/i18n/locales/el.yaml b/packages/errors/src/i18n/locales/el.yaml index 923832fd..8a320ef7 100644 --- a/packages/errors/src/i18n/locales/el.yaml +++ b/packages/errors/src/i18n/locales/el.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Το test harness δεν επιτρέπ admin_and_system.unknown_suspicious_flag: Άγνωστο suspicious flag. admin_and_system.update_failed: Δεν μπορέσαμε να ενημερώσουμε τον πόρο. Παρακαλώ προσπάθησε ξανά. admin_and_system.user_must_be_bot_for_system_user: Ο χρήστης πρέπει να είναι bot για να σημειωθεί ως system user. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Ο δείκτης του τμήματος είναι εκτός εύρους για αυτό το ανέβασμα. -attachments_and_uploads.chunked_upload_incomplete: Δεν έχουν ανέβει όλα τα τμήματα. -attachments_and_uploads.chunked_upload_not_found: Η συνεδρία ανεβάσματος δεν βρέθηκε ή έχει λήξει. -attachments_and_uploads.chunked_upload_not_owned: Δεν σου ανήκει αυτή η συνεδρία ανεβάσματος. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` και `expires_at` είναι υποχρεωτικά.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` και `message_id` πρέπει να είναι έγκυροι ακέραιοι αριθμοί.' attachments_and_uploads.cannot_edit_attachment_metadata: Οι χρήστες με MANAGE_MESSAGES μπορούν να επεξεργαστούν μόνο τις περιγραφές συνημμένων, όχι άλλα metadata. diff --git a/packages/errors/src/i18n/locales/en-GB.yaml b/packages/errors/src/i18n/locales/en-GB.yaml index 90ac1d34..eae258f5 100644 --- a/packages/errors/src/i18n/locales/en-GB.yaml +++ b/packages/errors/src/i18n/locales/en-GB.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Test harness is forbidden. admin_and_system.unknown_suspicious_flag: Unknown suspicious flag. admin_and_system.update_failed: We couldn't update the resource. Please try again. admin_and_system.user_must_be_bot_for_system_user: User must be a bot to be marked as a system user. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Chunk index is out of range for this upload. -attachments_and_uploads.chunked_upload_incomplete: Not all chunks have been uploaded. -attachments_and_uploads.chunked_upload_not_found: Upload session wasn't found or has expired. -attachments_and_uploads.chunked_upload_not_owned: You don't own this upload session. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id`, and `expires_at` are required.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id`, and `message_id` must be valid integers.' attachments_and_uploads.cannot_edit_attachment_metadata: Users with MANAGE_MESSAGES can only edit attachment descriptions, not other metadata. diff --git a/packages/errors/src/i18n/locales/es-419.yaml b/packages/errors/src/i18n/locales/es-419.yaml index aaf56859..21f1f300 100644 --- a/packages/errors/src/i18n/locales/es-419.yaml +++ b/packages/errors/src/i18n/locales/es-419.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: El arnés de prueba está prohibido. admin_and_system.unknown_suspicious_flag: Bandera sospechosa desconocida. admin_and_system.update_failed: No pudimos actualizar el recurso. Intenta de nuevo. admin_and_system.user_must_be_bot_for_system_user: El usuario debe ser un bot para marcarse como usuario del sistema. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: El índice del fragmento está fuera de rango para esta subida. -attachments_and_uploads.chunked_upload_incomplete: No se subieron todos los fragmentos. -attachments_and_uploads.chunked_upload_not_found: La sesión de subida no fue encontrada o expiró. -attachments_and_uploads.chunked_upload_not_owned: No sos dueño de esta sesión de subida. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` y `expires_at` son obligatorios.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` y `message_id` deben ser enteros válidos.' attachments_and_uploads.cannot_edit_attachment_metadata: Los usuarios con MANAGE_MESSAGES solo pueden editar descripciones de adjuntos, no otros metadatos. diff --git a/packages/errors/src/i18n/locales/es-ES.yaml b/packages/errors/src/i18n/locales/es-ES.yaml index 8dff3248..9e81c705 100644 --- a/packages/errors/src/i18n/locales/es-ES.yaml +++ b/packages/errors/src/i18n/locales/es-ES.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: El soporte de pruebas no está permitid admin_and_system.unknown_suspicious_flag: El indicador sospechoso no es conocido. admin_and_system.update_failed: No hemos podido actualizar el recurso. Por favor, inténtalo de nuevo. admin_and_system.user_must_be_bot_for_system_user: El usuario debe ser un bot para marcarse como usuario del sistema. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: El índice del fragmento está fuera de rango para esta subida. -attachments_and_uploads.chunked_upload_incomplete: No se han subido todos los fragmentos. -attachments_and_uploads.chunked_upload_not_found: La sesión de subida no se encontró o ha expirado. -attachments_and_uploads.chunked_upload_not_owned: No eres el propietario de esta sesión de subida. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` y `expires_at` son obligatorios.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` y `message_id` deben ser números enteros válidos.' attachments_and_uploads.cannot_edit_attachment_metadata: Los usuarios con MANAGE_MESSAGES solo pueden editar descripciones de adjuntos, no otros metadatos. diff --git a/packages/errors/src/i18n/locales/fi.yaml b/packages/errors/src/i18n/locales/fi.yaml index d8ebe214..694a2f75 100644 --- a/packages/errors/src/i18n/locales/fi.yaml +++ b/packages/errors/src/i18n/locales/fi.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Testikehykseen ei ole oikeutta. admin_and_system.unknown_suspicious_flag: Tuntematon epäilyttävä lippu. admin_and_system.update_failed: Resurssia ei voitu päivittää. Yritä uudelleen. admin_and_system.user_must_be_bot_for_system_user: Käyttäjän pitää olla botti, jotta sen voi merkitä järjestelmäkäyttäjäksi. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Osan indeksi on alueen ulkopuolella tälle lataukselle. -attachments_and_uploads.chunked_upload_incomplete: Kaikkia osia ei ole ladattu. -attachments_and_uploads.chunked_upload_not_found: Latausistuntoa ei löytynyt tai se on vanhentunut. -attachments_and_uploads.chunked_upload_not_owned: Et omista tätä latausistuntoa. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` ja `expires_at` vaaditaan.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` ja `message_id` pitää olla kelvollisia kokonaislukuja.' attachments_and_uploads.cannot_edit_attachment_metadata: Käyttäjät, joilla on MANAGE_MESSAGES, voivat muokata vain liitteiden kuvauksia, eivät muita metatietoja. diff --git a/packages/errors/src/i18n/locales/fr.yaml b/packages/errors/src/i18n/locales/fr.yaml index 71b86582..7226187d 100644 --- a/packages/errors/src/i18n/locales/fr.yaml +++ b/packages/errors/src/i18n/locales/fr.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Le bâti de test est interdit. admin_and_system.unknown_suspicious_flag: Flag suspect inconnu. admin_and_system.update_failed: Nous n'avons pas pu mettre à jour la ressource. Veuillez réessayer. admin_and_system.user_must_be_bot_for_system_user: L'utilisateur doit être un bot pour être marqué en tant qu'utilisateur système. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: L'index du fragment est hors limites pour cet envoi. -attachments_and_uploads.chunked_upload_incomplete: Tous les fragments n'ont pas été envoyés. -attachments_and_uploads.chunked_upload_not_found: La session d'envoi est introuvable ou a expiré. -attachments_and_uploads.chunked_upload_not_owned: Cette session d'envoi ne t'appartient pas. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` et `expires_at` sont obligatoires.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` et `message_id` doivent être des entiers valides.' attachments_and_uploads.cannot_edit_attachment_metadata: Les utilisateurs avec MANAGE_MESSAGES peuvent uniquement modifier les descriptions de pièces jointes, pas les autres métadonnées. diff --git a/packages/errors/src/i18n/locales/he.yaml b/packages/errors/src/i18n/locales/he.yaml index f9f2dc8a..6c4aa3eb 100644 --- a/packages/errors/src/i18n/locales/he.yaml +++ b/packages/errors/src/i18n/locales/he.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: ניסיון בדיקה אסור. admin_and_system.unknown_suspicious_flag: דגל חשוד לא מוכר. admin_and_system.update_failed: לא הצלחנו לעדכן את המשאב. נא לנסות שוב. admin_and_system.user_must_be_bot_for_system_user: משתמש חייב להיות בוט כדי לסמן אותו כמשתמש מערכת. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: אינדקס החלק מחוץ לטווח להעלאה זו. -attachments_and_uploads.chunked_upload_incomplete: לא כל החלקים הועלו. -attachments_and_uploads.chunked_upload_not_found: סשן ההעלאה לא נמצא או שפג תוקפו. -attachments_and_uploads.chunked_upload_not_owned: סשן ההעלאה הזה לא שייך לך. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` ו-`expires_at` הם שדות חובה.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id`, and `message_id` must be valid integers.' attachments_and_uploads.cannot_edit_attachment_metadata: משתמשים עם MANAGE_MESSAGES יכולים לערוך רק תיאורי קבצים מצורפים, לא מטא-דאטה אחרת. diff --git a/packages/errors/src/i18n/locales/hi.yaml b/packages/errors/src/i18n/locales/hi.yaml index c775ebfa..6957d83e 100644 --- a/packages/errors/src/i18n/locales/hi.yaml +++ b/packages/errors/src/i18n/locales/hi.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: परीक्षण हार्न admin_and_system.unknown_suspicious_flag: अज्ञात संदिग्ध ध्वज। admin_and_system.update_failed: हम संसाधन को अद्यतन करने में विफल रहे। कृपया दोबारा प्रयास करें। admin_and_system.user_must_be_bot_for_system_user: उपयोगकर्ता को सिस्टम उपयोगकर्ता के रूप में चिह्नित किए जाने के लिए बॉट होना चाहिए। -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: इस upload के लिए chunk index सीमा से बाहर है। -attachments_and_uploads.chunked_upload_incomplete: सभी chunks upload नहीं हुए हैं। -attachments_and_uploads.chunked_upload_not_found: Upload session नहीं मिला या expire हो गया है। -attachments_and_uploads.chunked_upload_not_owned: यह upload session तुम्हारा नहीं है। attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id`, और `expires_at` जरूरी हैं।' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id`, and `message_id` must be valid integers.' attachments_and_uploads.cannot_edit_attachment_metadata: MANAGE_MESSAGES permission वाले users सिर्फ attachment descriptions edit कर सकते हैं, दूसरा metadata नहीं। diff --git a/packages/errors/src/i18n/locales/hr.yaml b/packages/errors/src/i18n/locales/hr.yaml index 4404f04e..08ca7027 100644 --- a/packages/errors/src/i18n/locales/hr.yaml +++ b/packages/errors/src/i18n/locales/hr.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Test harness nije dopušten. admin_and_system.unknown_suspicious_flag: Nepoznat suspicious flag. admin_and_system.update_failed: Ažuriranje nije uspjelo. admin_and_system.user_must_be_bot_for_system_user: Korisnik mora biti bot da bi bio označen kao sistemski korisnik. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Indeks dijela je izvan raspona za ovaj upload. -attachments_and_uploads.chunked_upload_incomplete: Nisu svi dijelovi uploadani. -attachments_and_uploads.chunked_upload_not_found: Sesija uploada nije pronađena ili je istekla. -attachments_and_uploads.chunked_upload_not_owned: Ne posjeduješ ovu sesiju uploada. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` i `expires_at` su obavezni.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` i `message_id` moraju biti valjani cijeli brojevi.' attachments_and_uploads.cannot_edit_attachment_metadata: Korisnici s MANAGE_MESSAGES mogu uređivati samo opise privitaka, ne ostale metapodatke. diff --git a/packages/errors/src/i18n/locales/hu.yaml b/packages/errors/src/i18n/locales/hu.yaml index 0b3ed8ea..a90e28bc 100644 --- a/packages/errors/src/i18n/locales/hu.yaml +++ b/packages/errors/src/i18n/locales/hu.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: A tesztkészlet nem engedélyezett. admin_and_system.unknown_suspicious_flag: Ismeretlen gyanús jelzőszám. admin_and_system.update_failed: Nem sikerült frissíteni az erőforrást. Próbáld újra. admin_and_system.user_must_be_bot_for_system_user: A felhasználónak botnak kell lennie ahhoz, hogy rendszerfelhasználóként legyen megjelölve. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: A darab indexe kívül esik az érvényes tartományon ehhez a feltöltéshez. -attachments_and_uploads.chunked_upload_incomplete: Nem minden darab lett feltöltve. -attachments_and_uploads.chunked_upload_not_found: A feltöltési munkamenet nem található vagy lejárt. -attachments_and_uploads.chunked_upload_not_owned: Ez a feltöltési munkamenet nem a tiéd. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` és `expires_at` kötelezőek.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` és `message_id` érvényes egész számoknak kell lenniük.' attachments_and_uploads.cannot_edit_attachment_metadata: A MANAGE_MESSAGES engedéllyel rendelkező felhasználók csak a melléklet leírását szerkeszthetik, más metaadatokat nem. diff --git a/packages/errors/src/i18n/locales/id.yaml b/packages/errors/src/i18n/locales/id.yaml index 8291c2e8..333a0467 100644 --- a/packages/errors/src/i18n/locales/id.yaml +++ b/packages/errors/src/i18n/locales/id.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Test harness dilarang. admin_and_system.unknown_suspicious_flag: Suspicious flag tidak dikenal. admin_and_system.update_failed: Kami tidak dapat memperbarui sumber daya tersebut. Harap coba lagi. admin_and_system.user_must_be_bot_for_system_user: Pengguna harus berupa bot untuk ditandai sebagai pengguna sistem. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Indeks chunk di luar jangkauan untuk unggahan ini. -attachments_and_uploads.chunked_upload_incomplete: Belum semua chunk diunggah. -attachments_and_uploads.chunked_upload_not_found: Sesi unggahan tidak ditemukan atau sudah kedaluwarsa. -attachments_and_uploads.chunked_upload_not_owned: Kamu bukan pemilik sesi unggahan ini. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id`, dan `expires_at` wajib disertakan.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id`, dan `message_id` harus berupa bilangan bulat yang valid.' attachments_and_uploads.cannot_edit_attachment_metadata: Pengguna dengan izin MANAGE_MESSAGES hanya bisa mengedit deskripsi lampiran, bukan metadata lain. diff --git a/packages/errors/src/i18n/locales/it.yaml b/packages/errors/src/i18n/locales/it.yaml index b1dcdda8..6135565b 100644 --- a/packages/errors/src/i18n/locales/it.yaml +++ b/packages/errors/src/i18n/locales/it.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Test harness non consentito. admin_and_system.unknown_suspicious_flag: Suspicious flag sconosciuta. admin_and_system.update_failed: Aggiornamento non riuscito. admin_and_system.user_must_be_bot_for_system_user: Per essere marcato come utente di sistema, deve essere un bot. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: L'indice del frammento è fuori dall'intervallo per questo caricamento. -attachments_and_uploads.chunked_upload_incomplete: Non tutti i frammenti sono stati caricati. -attachments_and_uploads.chunked_upload_not_found: La sessione di caricamento non è stata trovata o è scaduta. -attachments_and_uploads.chunked_upload_not_owned: Questa sessione di caricamento non è tua. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` e `expires_at` sono obbligatori.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id`, and `message_id` must be valid integers.' attachments_and_uploads.cannot_edit_attachment_metadata: Gli utenti con MANAGE_MESSAGES possono modificare solo le descrizioni degli allegati, non altri metadati. diff --git a/packages/errors/src/i18n/locales/ja.yaml b/packages/errors/src/i18n/locales/ja.yaml index f56d803f..78069c69 100644 --- a/packages/errors/src/i18n/locales/ja.yaml +++ b/packages/errors/src/i18n/locales/ja.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: テストハーネスは許可されて admin_and_system.unknown_suspicious_flag: 不明な不審フラグです。 admin_and_system.update_failed: リソースを更新できませんでした。もう一度お試しください。 admin_and_system.user_must_be_bot_for_system_user: ユーザーをシステムユーザーとしてマークするには、ボットである必要があります。 -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: チャンクのインデックスがこのアップロードの範囲外だよ。 -attachments_and_uploads.chunked_upload_incomplete: まだすべてのチャンクがアップロードされてないよ。 -attachments_and_uploads.chunked_upload_not_found: アップロードセッションが見つからないか、有効期限が切れてるよ。 -attachments_and_uploads.chunked_upload_not_owned: このアップロードセッションはあなたのものじゃないよ。 attachments_and_uploads.attachment_fields_required: '`attachment_id`、`channel_id`、`message_id`、`expires_at`が全て必要だよ。' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`、`channel_id`、`message_id`は有効な整数じゃないとダメだよ。' attachments_and_uploads.cannot_edit_attachment_metadata: MANAGE_MESSAGES権限を持つユーザーは添付ファイルの説明だけを編集できて、他のメタデータは編集できないよ。 diff --git a/packages/errors/src/i18n/locales/ko.yaml b/packages/errors/src/i18n/locales/ko.yaml index f18328dc..f6535fdd 100644 --- a/packages/errors/src/i18n/locales/ko.yaml +++ b/packages/errors/src/i18n/locales/ko.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: 테스트 하네스 사용이 금지돼 admin_and_system.unknown_suspicious_flag: 알 수 없는 의심 플래그예요. admin_and_system.update_failed: 리소스를 업데이트하지 못했어요. 잠시 후 다시 시도해 주세요. admin_and_system.user_must_be_bot_for_system_user: 시스템 유저로 표시하려면 사용자는 봇이어야 해요. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: 청크 인덱스가 이 업로드의 범위를 벗어났어요. -attachments_and_uploads.chunked_upload_incomplete: 아직 모든 청크가 업로드되지 않았어요. -attachments_and_uploads.chunked_upload_not_found: 업로드 세션을 찾을 수 없거나 만료됐어요. -attachments_and_uploads.chunked_upload_not_owned: 이 업로드 세션의 소유자가 아니에요. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id`, `expires_at` 필드가 필요해요.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id`, `message_id`는 올바른 정수여야 해요.' attachments_and_uploads.cannot_edit_attachment_metadata: MANAGE_MESSAGES 권한을 가진 사용자는 첨부 파일 설명만 수정할 수 있고, 다른 메타데이터는 수정할 수 없어요. diff --git a/packages/errors/src/i18n/locales/lt.yaml b/packages/errors/src/i18n/locales/lt.yaml index cce766ec..540669e7 100644 --- a/packages/errors/src/i18n/locales/lt.yaml +++ b/packages/errors/src/i18n/locales/lt.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Bandymo arklys uždrausta. admin_and_system.unknown_suspicious_flag: Nežinoma įtartina žyma. admin_and_system.update_failed: Nepavyko atnaujinti resurso. Prašau bandyti dar kartą. admin_and_system.user_must_be_bot_for_system_user: Naudotojas turi būti botas, kad būtų pažymėtas kaip sistemos naudotojas. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Dalies indeksas yra už leistino diapazono šiam įkėlimui. -attachments_and_uploads.chunked_upload_incomplete: Ne visos dalys buvo įkeltos. -attachments_and_uploads.chunked_upload_not_found: Įkėlimo sesija nerasta arba pasibaigė jos galiojimas. -attachments_and_uploads.chunked_upload_not_owned: Ši įkėlimo sesija tau nepriklauso. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` ir `expires_at` yra būtini.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` ir `message_id` turi būti galiojantys sveikieji skaičiai.' attachments_and_uploads.cannot_edit_attachment_metadata: Naudotojai su MANAGE_MESSAGES gali redaguoti tik priedu aprašus, o ne kitus metaduomenis. diff --git a/packages/errors/src/i18n/locales/messages.yaml b/packages/errors/src/i18n/locales/messages.yaml index 1f5dd295..4033f39c 100644 --- a/packages/errors/src/i18n/locales/messages.yaml +++ b/packages/errors/src/i18n/locales/messages.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Test harness is forbidden. admin_and_system.unknown_suspicious_flag: Unknown suspicious flag. admin_and_system.update_failed: We couldn't update the resource. Please try again. admin_and_system.user_must_be_bot_for_system_user: User must be a bot to be marked as a system user. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Chunk index is out of range for this upload. -attachments_and_uploads.chunked_upload_incomplete: Not all chunks have been uploaded. -attachments_and_uploads.chunked_upload_not_found: Upload session wasn't found or has expired. -attachments_and_uploads.chunked_upload_not_owned: You don't own this upload session. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id`, and `expires_at` are required.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id`, and `message_id` must be valid integers.' attachments_and_uploads.cannot_edit_attachment_metadata: Users with MANAGE_MESSAGES can only edit attachment descriptions, not other metadata. diff --git a/packages/errors/src/i18n/locales/nl.yaml b/packages/errors/src/i18n/locales/nl.yaml index e6a7eb66..215090ee 100644 --- a/packages/errors/src/i18n/locales/nl.yaml +++ b/packages/errors/src/i18n/locales/nl.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Test harness is niet toegestaan. admin_and_system.unknown_suspicious_flag: Onbekende suspicious flag. admin_and_system.update_failed: We konden de resource niet bijwerken. Probeer het opnieuw. admin_and_system.user_must_be_bot_for_system_user: Gebruiker moet een bot zijn om als systeemgebruiker gemarkeerd te worden. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: De chunk-index valt buiten het bereik voor deze upload. -attachments_and_uploads.chunked_upload_incomplete: Niet alle chunks zijn geüpload. -attachments_and_uploads.chunked_upload_not_found: De uploadsessie is niet gevonden of is verlopen. -attachments_and_uploads.chunked_upload_not_owned: Je bent niet de eigenaar van deze uploadsessie. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` en `expires_at` zijn verplicht.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` en `message_id` moeten geldige integers zijn.' attachments_and_uploads.cannot_edit_attachment_metadata: Gebruikers met MANAGE_MESSAGES kunnen alleen bijlagebeschrijvingen bewerken, niet andere metadata. diff --git a/packages/errors/src/i18n/locales/no.yaml b/packages/errors/src/i18n/locales/no.yaml index a9ee8ad4..fc97ff52 100644 --- a/packages/errors/src/i18n/locales/no.yaml +++ b/packages/errors/src/i18n/locales/no.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Test harness er ikke tillatt. admin_and_system.unknown_suspicious_flag: Ukjent mistenkelig flagg. admin_and_system.update_failed: Vi klarte ikke å oppdatere ressursen. Prøv igjen. admin_and_system.user_must_be_bot_for_system_user: Brukeren må være en bot for å bli markert som systembruker. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Chunk-indeksen er utenfor gyldig område for denne opplastinga. -attachments_and_uploads.chunked_upload_incomplete: Ikke alle chunks er lasta opp. -attachments_and_uploads.chunked_upload_not_found: Opplastingsøkta ble ikke funnet eller har utløpt. -attachments_and_uploads.chunked_upload_not_owned: Du eier ikke denne opplastingsøkta. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id`, og `expires_at` er påkrevd.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` og `message_id` må være gyldige heltall.' attachments_and_uploads.cannot_edit_attachment_metadata: Brukere med MANAGE_MESSAGES kan bare redigere vedleggsbeskrivelser, ikke andre metadata. diff --git a/packages/errors/src/i18n/locales/pl.yaml b/packages/errors/src/i18n/locales/pl.yaml index b029930b..7b00add1 100644 --- a/packages/errors/src/i18n/locales/pl.yaml +++ b/packages/errors/src/i18n/locales/pl.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Test harness jest zabroniony. admin_and_system.unknown_suspicious_flag: Nieznana podejrzana flaga. admin_and_system.update_failed: Nie mogliśmy zaktualizować zasobu. Spróbuj ponownie. admin_and_system.user_must_be_bot_for_system_user: Użytkownik musi być botem, aby został oznaczony jako użytkownik systemowy. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Indeks fragmentu jest poza zakresem dla tego przesyłania. -attachments_and_uploads.chunked_upload_incomplete: Nie wszystkie fragmenty zostały przesłane. -attachments_and_uploads.chunked_upload_not_found: Sesja przesyłania nie została znaleziona lub wygasła. -attachments_and_uploads.chunked_upload_not_owned: Ta sesja przesyłania nie należy do ciebie. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` i `expires_at` są wymagane.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` i `message_id` muszą być prawidłowymi liczbami całkowitymi.' attachments_and_uploads.cannot_edit_attachment_metadata: Użytkownicy z uprawnieniem MANAGE_MESSAGES mogą edytować tylko opisy załączników, a nie inne metadane. diff --git a/packages/errors/src/i18n/locales/pt-BR.yaml b/packages/errors/src/i18n/locales/pt-BR.yaml index 2f89e73c..4ebe0300 100644 --- a/packages/errors/src/i18n/locales/pt-BR.yaml +++ b/packages/errors/src/i18n/locales/pt-BR.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: O teste harness não é permitido. admin_and_system.unknown_suspicious_flag: Sinalizador suspeito desconhecido. admin_and_system.update_failed: Não conseguimos atualizar o recurso. Por favor, tente novamente. admin_and_system.user_must_be_bot_for_system_user: O usuário deve ser um bot para ser marcado como usuário do sistema. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: O índice do fragmento está fora do intervalo para este envio. -attachments_and_uploads.chunked_upload_incomplete: Nem todos os fragmentos foram enviados. -attachments_and_uploads.chunked_upload_not_found: A sessão de envio não foi encontrada ou expirou. -attachments_and_uploads.chunked_upload_not_owned: Você não é o dono desta sessão de envio. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` e `expires_at` são obrigatórios.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` e `message_id` devem ser inteiros válidos.' attachments_and_uploads.cannot_edit_attachment_metadata: Usuários com MANAGE_MESSAGES só podem editar descrições de anexos, não outros metadados. diff --git a/packages/errors/src/i18n/locales/ro.yaml b/packages/errors/src/i18n/locales/ro.yaml index dea047f9..f96b9825 100644 --- a/packages/errors/src/i18n/locales/ro.yaml +++ b/packages/errors/src/i18n/locales/ro.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Harnesul de testare este interzis. admin_and_system.unknown_suspicious_flag: Steag suspect necunoscut. admin_and_system.update_failed: Nu am putut actualiza resursa. Te rugăm să încerci din nou. admin_and_system.user_must_be_bot_for_system_user: Utilizatorul trebuie să fie bot ca să fie marcat ca utilizator de sistem. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Indexul fragmentului este în afara intervalului pentru această încărcare. -attachments_and_uploads.chunked_upload_incomplete: Nu toate fragmentele au fost încărcate. -attachments_and_uploads.chunked_upload_not_found: Sesiunea de încărcare nu a fost găsită sau a expirat. -attachments_and_uploads.chunked_upload_not_owned: Nu ești proprietarul acestei sesiuni de încărcare. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` și `expires_at` sunt obligatorii.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` și `message_id` trebuie să fie numere întregi valide.' attachments_and_uploads.cannot_edit_attachment_metadata: Utilizatorii cu MANAGE_MESSAGES pot edita doar descrierile atașamentelor, nu alte metadate. diff --git a/packages/errors/src/i18n/locales/ru.yaml b/packages/errors/src/i18n/locales/ru.yaml index 64a28ff8..4b0b6a33 100644 --- a/packages/errors/src/i18n/locales/ru.yaml +++ b/packages/errors/src/i18n/locales/ru.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Тестовая инфраструкт admin_and_system.unknown_suspicious_flag: Неизвестный флаг подозрительности. admin_and_system.update_failed: Не удалось обновить ресурс. Попробуй еще раз. admin_and_system.user_must_be_bot_for_system_user: Пользователь должен быть ботом, чтобы его пометить как системного. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Индекс части выходит за допустимый диапазон для этой загрузки. -attachments_and_uploads.chunked_upload_incomplete: Не все части были загружены. -attachments_and_uploads.chunked_upload_not_found: Сессия загрузки не найдена или истекла. -attachments_and_uploads.chunked_upload_not_owned: Эта сессия загрузки не принадлежит тебе. attachments_and_uploads.attachment_fields_required: Обязательны `attachment_id`, `channel_id`, `message_id` и `expires_at`. attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` и `message_id` должны быть валидными целыми числами.' attachments_and_uploads.cannot_edit_attachment_metadata: Пользователи с правом MANAGE_MESSAGES могут редактировать только описания вложений, но не другие метаданные. diff --git a/packages/errors/src/i18n/locales/sv-SE.yaml b/packages/errors/src/i18n/locales/sv-SE.yaml index caf0b9d8..2572aeb6 100644 --- a/packages/errors/src/i18n/locales/sv-SE.yaml +++ b/packages/errors/src/i18n/locales/sv-SE.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Testhärnessen är förbjuden. admin_and_system.unknown_suspicious_flag: Okänd flagga för misstänkt aktivitet. admin_and_system.update_failed: Vi kunde inte uppdatera resursen. Försök igen. admin_and_system.user_must_be_bot_for_system_user: Användaren måste vara en bot för att kunna markeras som systemanvändare. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Chunk-indexet är utanför det giltiga intervallet för den här uppladdningen. -attachments_and_uploads.chunked_upload_incomplete: Inte alla chunks har laddats upp. -attachments_and_uploads.chunked_upload_not_found: Uppladdningssessionen hittades inte eller har gått ut. -attachments_and_uploads.chunked_upload_not_owned: Du äger inte den här uppladdningssessionen. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` och `expires_at` krävs.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` och `message_id` måste vara giltiga heltal.' attachments_and_uploads.cannot_edit_attachment_metadata: Användare med MANAGE_MESSAGES kan bara redigera bilagebeskrivningar, inte annan metadata. diff --git a/packages/errors/src/i18n/locales/th.yaml b/packages/errors/src/i18n/locales/th.yaml index a4f009d5..89168310 100644 --- a/packages/errors/src/i18n/locales/th.yaml +++ b/packages/errors/src/i18n/locales/th.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: ห้ามใช้ test harness admin_and_system.unknown_suspicious_flag: แฟล็กที่น่าสงสัยไม่รู้จัก admin_and_system.update_failed: ไม่สามารถอัปเดตทรัพยากรได้ โปรดลองใหม่ admin_and_system.user_must_be_bot_for_system_user: ผู้ใช้ต้องเป็น bot เพื่อให้ทำเครื่องหมายว่าเป็นผู้ใช้ระบบ -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: ดัชนี chunk อยู่นอกช่วงที่อนุญาตสำหรับการอัปโหลดนี้ -attachments_and_uploads.chunked_upload_incomplete: ยังไม่ได้อัปโหลด chunk ทั้งหมด -attachments_and_uploads.chunked_upload_not_found: ไม่พบเซสชันการอัปโหลดหรือหมดอายุแล้ว -attachments_and_uploads.chunked_upload_not_owned: เซสชันการอัปโหลดนี้ไม่ใช่ของคุณ attachments_and_uploads.attachment_fields_required: ต้องระบุ `attachment_id`, `channel_id`, `message_id` และ `expires_at` attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` และ `message_id` ต้องเป็นจำนวนเต็มที่ถูกต้อง' attachments_and_uploads.cannot_edit_attachment_metadata: ผู้ใช้ที่มีสิทธิ์ MANAGE_MESSAGES สามารถแก้ไขเฉพาะคำอธิบายไฟล์แนบเท่านั้น diff --git a/packages/errors/src/i18n/locales/tr.yaml b/packages/errors/src/i18n/locales/tr.yaml index e7a9b0bb..dcb8969f 100644 --- a/packages/errors/src/i18n/locales/tr.yaml +++ b/packages/errors/src/i18n/locales/tr.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Test harness yasaklı. admin_and_system.unknown_suspicious_flag: Bilinmeyen şüpheli bayrak. admin_and_system.update_failed: Kaynağı güncellemedik. Lütfen tekrar dene. admin_and_system.user_must_be_bot_for_system_user: Bir kullanıcının sistem kullanıcısı olarak işaretlenmesi için bot olması gerekir. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Parça indeksi bu yükleme için geçerli aralığın dışında. -attachments_and_uploads.chunked_upload_incomplete: Tüm parçalar henüz yüklenmedi. -attachments_and_uploads.chunked_upload_not_found: Yükleme oturumu bulunamadı veya süresi dolmuş. -attachments_and_uploads.chunked_upload_not_owned: Bu yükleme oturumu sana ait değil. attachments_and_uploads.attachment_fields_required: '`attachment_id`, `channel_id`, `message_id` ve `expires_at` gerekli.' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` ve `message_id` geçerli integer olmalı.' attachments_and_uploads.cannot_edit_attachment_metadata: MANAGE_MESSAGES yetkisine sahip kullanıcılar yalnızca ek açıklamalarını düzenleyebilir, diğer metadataları düzenleyemez. diff --git a/packages/errors/src/i18n/locales/uk.yaml b/packages/errors/src/i18n/locales/uk.yaml index 71f97286..5fa82c4d 100644 --- a/packages/errors/src/i18n/locales/uk.yaml +++ b/packages/errors/src/i18n/locales/uk.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Тестовий стенд забор admin_and_system.unknown_suspicious_flag: Невідомий прапор підозрілої діяльності. admin_and_system.update_failed: Не вдалося оновити ресурс. Спробуй ще раз. admin_and_system.user_must_be_bot_for_system_user: Користувач має бути ботом, щоб його позначити як системного. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Індекс частини виходить за межі допустимого діапазону для цього завантаження. -attachments_and_uploads.chunked_upload_incomplete: Не всі частини було завантажено. -attachments_and_uploads.chunked_upload_not_found: Сеанс завантаження не знайдено або він закінчився. -attachments_and_uploads.chunked_upload_not_owned: Цей сеанс завантаження не належить тобі. attachments_and_uploads.attachment_fields_required: Обов'язкові `attachment_id`, `channel_id`, `message_id` і `expires_at`. attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id` і `message_id` мають бути валідними цілими числами.' attachments_and_uploads.cannot_edit_attachment_metadata: Користувачі з правом MANAGE_MESSAGES можуть редагувати лише описи вкладень, а не інші метадані. diff --git a/packages/errors/src/i18n/locales/vi.yaml b/packages/errors/src/i18n/locales/vi.yaml index 3cf430a8..600c7ef2 100644 --- a/packages/errors/src/i18n/locales/vi.yaml +++ b/packages/errors/src/i18n/locales/vi.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: Thiết bị kiểm thử bị cấm. admin_and_system.unknown_suspicious_flag: Cờ đáng ngờ không xác định. admin_and_system.update_failed: Không thể cập nhật tài nguyên. Vui lòng thử lại. admin_and_system.user_must_be_bot_for_system_user: Người dùng phải là bot để được đánh dấu là người dùng hệ thống. -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: Chỉ mục phần nằm ngoài phạm vi cho lần tải lên này. -attachments_and_uploads.chunked_upload_incomplete: Chưa tải lên hết tất cả các phần. -attachments_and_uploads.chunked_upload_not_found: Phiên tải lên không tìm thấy hoặc đã hết hạn. -attachments_and_uploads.chunked_upload_not_owned: Bạn không sở hữu phiên tải lên này. attachments_and_uploads.attachment_fields_required: Cần có `attachment_id`, `channel_id`, `message_id`, và `expires_at`. attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`, `channel_id`, và `message_id` phải là số nguyên hợp lệ.' attachments_and_uploads.cannot_edit_attachment_metadata: Người dùng có quyền MANAGE_MESSAGES chỉ có thể chỉnh sửa mô tả attachment, không thể sửa metadata khác. diff --git a/packages/errors/src/i18n/locales/zh-CN.yaml b/packages/errors/src/i18n/locales/zh-CN.yaml index 013f8aab..3978c091 100644 --- a/packages/errors/src/i18n/locales/zh-CN.yaml +++ b/packages/errors/src/i18n/locales/zh-CN.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: 禁止使用测试工具。 admin_and_system.unknown_suspicious_flag: 未知的可疑标记。 admin_and_system.update_failed: 无法更新该资源,请重试。 admin_and_system.user_must_be_bot_for_system_user: 要标记为系统用户,该用户必须是机器人。 -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: 分片索引超出此上传的有效范围。 -attachments_and_uploads.chunked_upload_incomplete: 尚未上传所有分片。 -attachments_and_uploads.chunked_upload_not_found: 上传会话未找到或已过期。 -attachments_and_uploads.chunked_upload_not_owned: 你不是这个上传会话的所有者。 attachments_and_uploads.attachment_fields_required: 必须提供 `attachment_id`、`channel_id`、`message_id` 和 `expires_at`。 attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`、`channel_id` 和 `message_id` 必须为有效的整数。' attachments_and_uploads.cannot_edit_attachment_metadata: 仅具有 MANAGE_MESSAGES 权限的用户可以编辑附件描述,不能编辑其他元数据。 diff --git a/packages/errors/src/i18n/locales/zh-TW.yaml b/packages/errors/src/i18n/locales/zh-TW.yaml index cdd83307..b77be2f2 100644 --- a/packages/errors/src/i18n/locales/zh-TW.yaml +++ b/packages/errors/src/i18n/locales/zh-TW.yaml @@ -50,10 +50,6 @@ admin_and_system.test_harness_forbidden: 禁止使用測試工具。 admin_and_system.unknown_suspicious_flag: 未知的可疑旗標。 admin_and_system.update_failed: 我們沒能更新該資源。請再試一次。 admin_and_system.user_must_be_bot_for_system_user: 使用者必須是機器人才能標記為系統使用者。 -attachments_and_uploads.chunked_upload_chunk_index_out_of_range: 分片索引超出此上傳的有效範圍。 -attachments_and_uploads.chunked_upload_incomplete: 尚未上傳所有分片。 -attachments_and_uploads.chunked_upload_not_found: 上傳工作階段未找到或已過期。 -attachments_and_uploads.chunked_upload_not_owned: 你不是這個上傳工作階段的擁有者。 attachments_and_uploads.attachment_fields_required: '`attachment_id`、`channel_id`、`message_id` 和 `expires_at` 為必填。' attachments_and_uploads.attachment_ids_must_be_valid_integers: '`attachment_id`、`channel_id` 和 `message_id` 必須是有效的整數。' attachments_and_uploads.cannot_edit_attachment_metadata: 擁有 MANAGE_MESSAGES 權限的使用者只能編輯附件描述,無法編輯其他中繼資料。 diff --git a/packages/schema/src/domains/channel/ChunkedUploadSchemas.tsx b/packages/schema/src/domains/channel/ChunkedUploadSchemas.tsx deleted file mode 100644 index 7f748347..00000000 --- a/packages/schema/src/domains/channel/ChunkedUploadSchemas.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2026 Fluxer Contributors - * - * This file is part of Fluxer. - * - * Fluxer is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Fluxer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Fluxer. If not, see . - */ - -import {FilenameType} from '@fluxer/schema/src/primitives/FileValidators'; -import { - coerceNumberFromString, - createStringType, - Int32Type, - SnowflakeType, -} from '@fluxer/schema/src/primitives/SchemaPrimitives'; -import {z} from 'zod'; - -export const CreateChunkedUploadRequest = z.object({ - filename: FilenameType.describe('The name of the file being uploaded'), - file_size: z.number().int().positive().describe('The total size of the file in bytes'), -}); -export type CreateChunkedUploadRequest = z.infer; - -export const CreateChunkedUploadResponse = z.object({ - upload_id: z.string().describe('The unique identifier for the upload session'), - upload_filename: z.string().describe('The temporary filename used to reference this upload'), - chunk_size: z.number().int().describe('The size of each chunk in bytes'), - chunk_count: z.number().int().describe('The total number of chunks to upload'), -}); -export type CreateChunkedUploadResponse = z.infer; - -export const UploadChunkResponse = z.object({ - etag: z.string().describe('The ETag of the uploaded chunk'), -}); -export type UploadChunkResponse = z.infer; - -export const CompleteChunkedUploadRequest = z.object({ - etags: z - .array( - z.object({ - chunk_index: z.number().int().min(0).describe('The zero-based index of the chunk'), - etag: z.string().describe('The ETag returned when the chunk was uploaded'), - }), - ) - .min(1) - .describe('Array of chunk ETags in order'), -}); -export type CompleteChunkedUploadRequest = z.infer; - -export const CompleteChunkedUploadResponse = z.object({ - upload_filename: z.string().describe('The temporary filename used to reference this upload'), - file_size: z.number().int().describe('The total size of the uploaded file in bytes'), - content_type: z.string().describe('The MIME type of the uploaded file'), -}); -export type CompleteChunkedUploadResponse = z.infer; - -export const ChunkedUploadParam = z.object({ - channel_id: SnowflakeType.describe('The ID of the channel'), - upload_id: createStringType(1, 128).describe('The ID of the chunked upload session'), -}); -export type ChunkedUploadParam = z.infer; - -export const ChunkedUploadChunkParam = z.object({ - channel_id: SnowflakeType.describe('The ID of the channel'), - upload_id: createStringType(1, 128).describe('The ID of the chunked upload session'), - chunk_index: coerceNumberFromString(Int32Type.min(0)).describe('The zero-based index of the chunk'), -}); -export type ChunkedUploadChunkParam = z.infer; diff --git a/packages/schema/src/domains/message/AttachmentSchemas.tsx b/packages/schema/src/domains/message/AttachmentSchemas.tsx index 1f2591b2..d5d54732 100644 --- a/packages/schema/src/domains/message/AttachmentSchemas.tsx +++ b/packages/schema/src/domains/message/AttachmentSchemas.tsx @@ -48,7 +48,6 @@ const ClientAttachmentBase = z.object({ export const ClientAttachmentRequest = ClientAttachmentBase.extend({ id: coerceNumberFromString(Int32Type).describe('The client-side identifier for this attachment'), filename: FilenameType.describe('The name of the file being uploaded'), - uploaded_filename: z.string().optional().describe('The temporary filename from a completed chunked upload'), }); export type ClientAttachmentRequest = z.infer;