fix: various fixes to sentry-reported errors and more

This commit is contained in:
Hampus Kraft
2026-02-18 15:38:51 +00:00
parent 302c0d2a0c
commit 0517a966a3
357 changed files with 25420 additions and 16281 deletions

View File

@@ -93,25 +93,21 @@ export interface SearchableGuild {
verificationLevel: number;
mfaLevel: number;
nsfwLevel: number;
memberCount: number;
createdAt: number;
discoveryDescription: string | null;
discoveryCategory: number | null;
isDiscoverable: boolean;
onlineCount: number;
}
export interface GuildSearchFilters {
ownerId?: string;
minMembers?: number;
maxMembers?: number;
verificationLevel?: number;
mfaLevel?: number;
nsfwLevel?: number;
hasFeature?: Array<string>;
isDiscoverable?: boolean;
discoveryCategory?: number;
sortBy?: 'createdAt' | 'memberCount' | 'onlineCount' | 'relevance';
sortBy?: 'createdAt' | 'relevance';
sortOrder?: 'asc' | 'desc';
}

View File

@@ -115,6 +115,7 @@ const SearchIndexTypeEnum = createNamedStringLiteralUnion(
['channel_messages', 'channel_messages', 'Channel message search index'],
['guild_members', 'guild_members', 'Guild member search index'],
['favorite_memes', 'favorite_memes', 'Favourite meme search index'],
['discovery', 'discovery', 'Discovery guild search index'],
],
'Type of search index to refresh',
);

View File

@@ -407,6 +407,19 @@ export const BulkScheduleUserDeletionRequest = z.object({
export type BulkScheduleUserDeletionRequest = z.infer<typeof BulkScheduleUserDeletionRequest>;
export const ListWebAuthnCredentialsRequest = z.object({
user_id: SnowflakeType.describe('ID of the user to list WebAuthn credentials for'),
});
export type ListWebAuthnCredentialsRequest = z.infer<typeof ListWebAuthnCredentialsRequest>;
export const DeleteWebAuthnCredentialRequest = z.object({
user_id: SnowflakeType.describe('ID of the user who owns the credential'),
credential_id: createStringType(1, 512).describe('ID of the WebAuthn credential to delete'),
});
export type DeleteWebAuthnCredentialRequest = z.infer<typeof DeleteWebAuthnCredentialRequest>;
export const ListUserChangeLogRequest = z.object({
user_id: SnowflakeType.describe('ID of the user to list change logs for'),
limit: z.number().min(1).max(200).default(50).describe('Maximum number of entries to return'),

View File

@@ -17,6 +17,7 @@
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
*/
import {MAX_GROUP_DM_RECIPIENTS} from '@fluxer/constants/src/LimitConstants';
import {type UserPartial, UserPartialResponse} from '@fluxer/schema/src/domains/user/UserResponseSchemas';
import {ChannelOverwriteTypeSchema, ChannelTypeSchema} from '@fluxer/schema/src/primitives/ChannelValidators';
import {PermissionStringType} from '@fluxer/schema/src/primitives/PermissionValidators';
@@ -73,7 +74,7 @@ export const ChannelResponse = z.object({
.describe('The permission overwrites for this channel'),
recipients: z
.array(z.lazy(() => UserPartialResponse))
.max(10)
.max(MAX_GROUP_DM_RECIPIENTS)
.optional()
.describe('The recipients of the DM channel'),
nsfw: z.boolean().optional().describe('Whether the channel is marked as NSFW'),
@@ -103,7 +104,11 @@ export const ChannelPartialResponse = z.object({
id: SnowflakeStringType.describe('The unique identifier (snowflake) for this channel'),
name: z.string().nullish().describe('The name of the channel'),
type: ChannelTypeSchema.describe('The type of the channel'),
recipients: z.array(ChannelPartialRecipientResponse).max(10).optional().describe('The recipients of the DM channel'),
recipients: z
.array(ChannelPartialRecipientResponse)
.max(MAX_GROUP_DM_RECIPIENTS)
.optional()
.describe('The recipients of the DM channel'),
});
export type ChannelPartialResponse = z.infer<typeof ChannelPartialResponse>;

View File

@@ -0,0 +1,79 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
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<typeof CreateChunkedUploadRequest>;
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<typeof CreateChunkedUploadResponse>;
export const UploadChunkResponse = z.object({
etag: z.string().describe('The ETag of the uploaded chunk'),
});
export type UploadChunkResponse = z.infer<typeof UploadChunkResponse>;
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<typeof CompleteChunkedUploadRequest>;
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<typeof CompleteChunkedUploadResponse>;
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<typeof ChunkedUploadParam>;
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<typeof ChunkedUploadChunkParam>;

View File

@@ -48,6 +48,7 @@ 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<typeof ClientAttachmentRequest>;

View File

@@ -80,7 +80,7 @@ function isValidBase64(value: string): boolean {
}
}
function normalizeFilename(value: string): string {
export function normalizeFilename(value: string): string {
let normalized = normalizeString(value);
// biome-ignore lint/suspicious/noControlCharactersInRegex: null byte filtering is intentional for security