mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 13:35:42 +09:00
fix: tighten edge cases across services (#3158)
This commit is contained in:
1 parent
a9f7a23c0d
commit
4e6b837ccc
170 files changed
+2028
-421
No files matched your search
@@ -61,7 +61,7 @@ export type AdminBlocklistEntryListQuery = z.infer<typeof AdminBlocklistEntryLis
|
||||
const AdminBlocklistTypeResponse = z.object({
|
||||
list_type: AdminBlocklistListType,
|
||||
description: z.string().describe('What the blocklist matches and how matching is performed'),
|
||||
value_field: z.string().describe('The request body field that carries the entry value when adding to this blocklist'),
|
||||
value_field: z.string().describe('The request body field that holds the entry value when adding to this blocklist'),
|
||||
fields: z.array(z.string()).max(8).describe('Fields entries of this blocklist accept beyond the value itself'),
|
||||
scoped: z.boolean().describe('Whether entries are scoped to a profile field and a scope must be supplied'),
|
||||
supports_bulk_create: z.boolean().describe('Whether PUT on the entry collection is accepted'),
|
||||
|
||||
@@ -55,7 +55,7 @@ export const AdminMessageSearchQuery = z.object({
|
||||
'Return the single message with this ID together with its surrounding context; ignores every other filter',
|
||||
),
|
||||
attachment_id: SnowflakeType.optional().describe(
|
||||
'Return the single message carrying this attachment together with its surrounding context; requires filename',
|
||||
'Return the single message with this attachment together with its surrounding context; requires filename',
|
||||
),
|
||||
filename: FilenameType.optional().describe('The filename of the attachment named by attachment_id'),
|
||||
context_limit: createQueryIntegerType({defaultValue: 50, minValue: 1, maxValue: 100}).describe(
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {createStringType} from '@fluxer/schema/src/primitives/SchemaPrimitives';
|
||||
import {
|
||||
CHANNEL_RATE_LIMIT_PER_USER_MAX,
|
||||
CHANNEL_RATE_LIMIT_PER_USER_MIN,
|
||||
CHANNEL_TOPIC_MAX_LENGTH,
|
||||
VOICE_CHANNEL_CONNECTION_LIMIT_MAX,
|
||||
VOICE_CHANNEL_CONNECTION_LIMIT_MIN,
|
||||
VOICE_CHANNEL_USER_LIMIT_MIN,
|
||||
} from '@fluxer/constants/src/LimitConstants';
|
||||
import {ColorType, createStringType, Int32Type} from '@fluxer/schema/src/primitives/SchemaPrimitives';
|
||||
import {z} from 'zod';
|
||||
|
||||
const TEMPLATE_NAME_MAX_LENGTH = 100;
|
||||
|
||||
const TemplateEntityId = z
|
||||
.union([
|
||||
z.number().int().nonnegative(),
|
||||
@@ -36,17 +46,35 @@ export const TemplateChannel = z.object({
|
||||
type: z.number().describe('The channel type (0 = text, 2 = voice, 4 = category)'),
|
||||
name: z
|
||||
.string()
|
||||
.max(TEMPLATE_NAME_MAX_LENGTH)
|
||||
.nullish()
|
||||
.transform((value) => value ?? '')
|
||||
.describe('The name of the channel'),
|
||||
topic: z.string().nullish().describe('The channel topic'),
|
||||
position: z.number().describe('The position of the channel'),
|
||||
topic: z.string().max(CHANNEL_TOPIC_MAX_LENGTH).nullish().describe('The channel topic'),
|
||||
position: Int32Type.describe('The position of the channel'),
|
||||
parent_id: TemplateEntityId.nullish().describe('The template-local ID of the parent category'),
|
||||
bitrate: z.number().nullish().describe('The bitrate for voice channels'),
|
||||
user_limit: z.number().nullish().describe('The user limit for voice channels'),
|
||||
voice_connection_limit: z.number().nullish().describe('The per-user voice connection limit for voice channels'),
|
||||
bitrate: z.number().int().nonnegative().nullish().describe('The bitrate for voice channels'),
|
||||
user_limit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(VOICE_CHANNEL_USER_LIMIT_MIN)
|
||||
.nullish()
|
||||
.describe('The user limit for voice channels'),
|
||||
voice_connection_limit: z
|
||||
.number()
|
||||
.int()
|
||||
.min(VOICE_CHANNEL_CONNECTION_LIMIT_MIN)
|
||||
.max(VOICE_CHANNEL_CONNECTION_LIMIT_MAX)
|
||||
.nullish()
|
||||
.describe('The per-user voice connection limit for voice channels'),
|
||||
nsfw: z.boolean().optional().describe('Whether the channel is NSFW'),
|
||||
rate_limit_per_user: z.number().optional().describe('Slowmode rate limit in seconds'),
|
||||
rate_limit_per_user: z
|
||||
.number()
|
||||
.int()
|
||||
.min(CHANNEL_RATE_LIMIT_PER_USER_MIN)
|
||||
.max(CHANNEL_RATE_LIMIT_PER_USER_MAX)
|
||||
.optional()
|
||||
.describe('Slowmode rate limit in seconds'),
|
||||
permission_overwrites: z
|
||||
.array(TemplatePermissionOverwrite)
|
||||
.optional()
|
||||
@@ -59,12 +87,13 @@ export const TemplateRole = z.object({
|
||||
id: TemplateEntityId.describe('The template-local role ID'),
|
||||
name: z
|
||||
.string()
|
||||
.max(TEMPLATE_NAME_MAX_LENGTH)
|
||||
.nullish()
|
||||
.transform((value) => value ?? '')
|
||||
.describe('The name of the role'),
|
||||
permissions: TemplatePermissionBitfield.optional().describe('The permissions bitfield as a string (legacy)'),
|
||||
permissions_new: TemplatePermissionBitfield.optional().describe('The permissions bitfield as a string (preferred)'),
|
||||
color: z.number().optional().describe('The colour of the role as an integer'),
|
||||
color: ColorType.optional().describe('The colour of the role as an integer'),
|
||||
hoist: z.boolean().optional().describe('Whether the role is hoisted'),
|
||||
mentionable: z.boolean().optional().describe('Whether the role is mentionable'),
|
||||
unicode_emoji: z.string().nullish().describe('The unicode emoji for the role icon'),
|
||||
|
||||
@@ -69,7 +69,7 @@ export const RefreshedAttachmentUrl = z.object({
|
||||
original: z.string().describe('The requested URL, echoed back unchanged'),
|
||||
refreshed: z
|
||||
.string()
|
||||
.describe('The same URL carrying a fresh signature, or the original when it is not an attachment URL of ours'),
|
||||
.describe('The same URL with a fresh signature, or the original when it is not an attachment URL of ours'),
|
||||
});
|
||||
|
||||
export type RefreshedAttachmentUrl = z.infer<typeof RefreshedAttachmentUrl>;
|
||||
|
||||
@@ -166,7 +166,7 @@ export interface MessageResponse extends MessageBaseResponse {
|
||||
|
||||
export const MessageResponseSchema = MessageBaseResponseSchema.extend({
|
||||
referenced_message: MessageBaseResponseSchema.nullish().describe(
|
||||
'The reply target. Present and populated when the target resolved, present and null when the target is gone, absent when this message carries no default reference. Clients must tell null apart from absent by key presence.',
|
||||
'The reply target. Present and populated when the target resolved, present and null when the target is gone, absent when this message has no default reference. Clients must tell null apart from absent by key presence.',
|
||||
),
|
||||
});
|
||||
const ChannelPinMessageResponse = MessageResponseSchema.omit({
|
||||
|
||||
@@ -98,7 +98,7 @@ describe('webhook responses', () => {
|
||||
expect(WebhookCreateResponse.safeParse({...withoutToken, user: creator}).success).toBe(false);
|
||||
});
|
||||
|
||||
it('omits the token and carries the source on follower webhooks', () => {
|
||||
it('omits the token and includes the source on follower webhooks', () => {
|
||||
const {token: _token, ...withoutToken} = incomingWebhook;
|
||||
const follower = {
|
||||
...withoutToken,
|
||||
|
||||
@@ -168,6 +168,21 @@ export const UserPrivateResponse = UserPartialResponse.extend({
|
||||
|
||||
export type UserPrivateResponse = z.infer<typeof UserPrivateResponse>;
|
||||
|
||||
export const UserUpdateResponse = UserPrivateResponse.extend({
|
||||
token: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('Authentication token for the replacement session, present when the password was changed'),
|
||||
auth_session_id_hash: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
'Base64url-encoded hash of the replacement authentication session, present when the password was changed',
|
||||
),
|
||||
});
|
||||
|
||||
export type UserUpdateResponse = z.infer<typeof UserUpdateResponse>;
|
||||
|
||||
export const EmailChangeStartResponse = z.object({
|
||||
ticket: z.string().describe('Ticket returned for email change actions'),
|
||||
require_original: z.boolean().describe('Whether verification of the original email is required'),
|
||||
|
||||
Reference in new issue
Block a user