mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
fix(api): accept long forum topics in imported templates (#3244)
This commit is contained in:
@@ -6,7 +6,7 @@ import {type ApiTestHarness, createApiTestHarness} from '@app/api/test/ApiTestHa
|
|||||||
import {createBuilder} from '@app/api/test/TestRequestBuilder';
|
import {createBuilder} from '@app/api/test/TestRequestBuilder';
|
||||||
import {ChannelTypes, Permissions} from '@fluxer/constants/src/ChannelConstants';
|
import {ChannelTypes, Permissions} from '@fluxer/constants/src/ChannelConstants';
|
||||||
import {SystemChannelFlags} from '@fluxer/constants/src/GuildConstants';
|
import {SystemChannelFlags} from '@fluxer/constants/src/GuildConstants';
|
||||||
import {VOICE_CHANNEL_USER_LIMIT_MAX} from '@fluxer/constants/src/LimitConstants';
|
import {CHANNEL_TOPIC_MAX_LENGTH, VOICE_CHANNEL_USER_LIMIT_MAX} from '@fluxer/constants/src/LimitConstants';
|
||||||
import type {GuildResponse} from '@fluxer/schema/src/domains/guild/GuildResponseSchemas';
|
import type {GuildResponse} from '@fluxer/schema/src/domains/guild/GuildResponseSchemas';
|
||||||
import {afterAll, beforeAll, beforeEach, describe, expect, test} from 'vitest';
|
import {afterAll, beforeAll, beforeEach, describe, expect, test} from 'vitest';
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ describe('Guild Template Import', () => {
|
|||||||
['a slowmode above the channel maximum', {rate_limit_per_user: 1_000_000_000}],
|
['a slowmode above the channel maximum', {rate_limit_per_user: 1_000_000_000}],
|
||||||
['a fractional position', {position: 0.5}],
|
['a fractional position', {position: 0.5}],
|
||||||
['a negative position', {position: -3}],
|
['a negative position', {position: -3}],
|
||||||
['a topic above the channel maximum', {topic: 'x'.repeat(1025)}],
|
['a topic above the template maximum', {topic: 'x'.repeat(4097)}],
|
||||||
['a name above the channel maximum', {name: 'x'.repeat(101)}],
|
['a name above the channel maximum', {name: 'x'.repeat(101)}],
|
||||||
['a negative user limit', {type: ChannelTypes.GUILD_VOICE, user_limit: -1}],
|
['a negative user limit', {type: ChannelTypes.GUILD_VOICE, user_limit: -1}],
|
||||||
['a voice connection limit above the maximum', {type: ChannelTypes.GUILD_VOICE, voice_connection_limit: 100_000}],
|
['a voice connection limit above the maximum', {type: ChannelTypes.GUILD_VOICE, voice_connection_limit: 100_000}],
|
||||||
@@ -273,6 +273,26 @@ describe('Guild Template Import', () => {
|
|||||||
expect(channels.find((channel) => channel.name === 'town-hall')?.user_limit).toBe(VOICE_CHANNEL_USER_LIMIT_MAX);
|
expect(channels.find((channel) => channel.name === 'town-hall')?.user_limit).toBe(VOICE_CHANNEL_USER_LIMIT_MAX);
|
||||||
expect(channels.find((channel) => channel.name === 'lounge')?.voice_connection_limit).toBe(100);
|
expect(channels.find((channel) => channel.name === 'lounge')?.voice_connection_limit).toBe(100);
|
||||||
});
|
});
|
||||||
|
test('accepts forum-length topics and shortens them to the channel topic limit', async () => {
|
||||||
|
const account = await createTestAccount(harness);
|
||||||
|
const longTopic = `${'a'.repeat(CHANNEL_TOPIC_MAX_LENGTH - 1)}\u{1F600}${'b'.repeat(300)}`;
|
||||||
|
const guild = await createBuilder<GuildResponse>(harness, account.token)
|
||||||
|
.post('/guilds')
|
||||||
|
.body({
|
||||||
|
name: 'Forum Guild',
|
||||||
|
template: buildMinimalTemplate({
|
||||||
|
channels: [
|
||||||
|
{id: 6001, type: ChannelTypes.GUILD_TEXT, name: 'general', position: 0, topic: longTopic},
|
||||||
|
{id: 6002, type: 15, name: 'projects', position: 1, topic: 'c'.repeat(1356)},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.execute();
|
||||||
|
const channels = await getGuildChannels(harness, account.token, guild.id);
|
||||||
|
const general = channels.find((channel) => channel.name === 'general');
|
||||||
|
expect(general?.topic).toBe('a'.repeat(CHANNEL_TOPIC_MAX_LENGTH - 1));
|
||||||
|
expect(channels.find((channel) => channel.name === 'projects')).toBeUndefined();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const DEFAULT_EVERYONE_PERMISSIONS = Permissions.VIEW_CHANNEL.toString();
|
const DEFAULT_EVERYONE_PERMISSIONS = Permissions.VIEW_CHANNEL.toString();
|
||||||
|
|||||||
@@ -33075,8 +33075,8 @@
|
|||||||
"anyOf": [{"type": "string", "maxLength": 100}, {"type": "null"}]
|
"anyOf": [{"type": "string", "maxLength": 100}, {"type": "null"}]
|
||||||
},
|
},
|
||||||
"topic": {
|
"topic": {
|
||||||
"description": "The channel topic",
|
"description": "The channel topic, shortened to the Fluxer channel topic limit",
|
||||||
"anyOf": [{"type": "string", "maxLength": 1024}, {"type": "null"}]
|
"anyOf": [{"type": "string", "maxLength": 4096}, {"type": "null"}]
|
||||||
},
|
},
|
||||||
"position": {"description": "The position of the channel", "$ref": "#/components/schemas/Int32Type"},
|
"position": {"description": "The position of the channel", "$ref": "#/components/schemas/Int32Type"},
|
||||||
"parent_id": {
|
"parent_id": {
|
||||||
|
|||||||
@@ -12,6 +12,14 @@ import {ColorType, createStringType, Int32Type} from '@fluxer/schema/src/primiti
|
|||||||
import {z} from 'zod';
|
import {z} from 'zod';
|
||||||
|
|
||||||
const TEMPLATE_NAME_MAX_LENGTH = 100;
|
const TEMPLATE_NAME_MAX_LENGTH = 100;
|
||||||
|
const TEMPLATE_TOPIC_MAX_LENGTH = 4096;
|
||||||
|
|
||||||
|
function clipTopic(value: string): string {
|
||||||
|
if (value.length <= CHANNEL_TOPIC_MAX_LENGTH) return value;
|
||||||
|
const last = value.charCodeAt(CHANNEL_TOPIC_MAX_LENGTH - 1);
|
||||||
|
const end = last >= 0xd800 && last <= 0xdbff ? CHANNEL_TOPIC_MAX_LENGTH - 1 : CHANNEL_TOPIC_MAX_LENGTH;
|
||||||
|
return value.slice(0, end);
|
||||||
|
}
|
||||||
|
|
||||||
const TemplateEntityId = z
|
const TemplateEntityId = z
|
||||||
.union([
|
.union([
|
||||||
@@ -50,7 +58,12 @@ export const TemplateChannel = z.object({
|
|||||||
.nullish()
|
.nullish()
|
||||||
.transform((value) => value ?? '')
|
.transform((value) => value ?? '')
|
||||||
.describe('The name of the channel'),
|
.describe('The name of the channel'),
|
||||||
topic: z.string().max(CHANNEL_TOPIC_MAX_LENGTH).nullish().describe('The channel topic'),
|
topic: z
|
||||||
|
.string()
|
||||||
|
.max(TEMPLATE_TOPIC_MAX_LENGTH)
|
||||||
|
.nullish()
|
||||||
|
.transform((value) => (value == null ? value : clipTopic(value)))
|
||||||
|
.describe('The channel topic, shortened to the Fluxer channel topic limit'),
|
||||||
position: Int32Type.describe('The position of the channel'),
|
position: Int32Type.describe('The position of the channel'),
|
||||||
parent_id: TemplateEntityId.nullish().describe('The template-local ID of the parent category'),
|
parent_id: TemplateEntityId.nullish().describe('The template-local ID of the parent category'),
|
||||||
bitrate: z.number().int().nonnegative().nullish().describe('The bitrate for voice channels'),
|
bitrate: z.number().int().nonnegative().nullish().describe('The bitrate for voice channels'),
|
||||||
|
|||||||
Reference in New Issue
Block a user