feat(limits): add max_screen_share_bitrate limit (#3410)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-12 02:16:40 +02:00
1 parent 3292dc5f89
commit e0640d68ff
9 files changed
+66 -6

No files matched your search

+2 -1
View File
@@ -11107,7 +11107,7 @@
"category": {"type": "string"},
"scope": {"type": "string"},
"isToggle": {"type": "boolean"},
"unit": {"type": "string", "enum": ["bytes", "count"]},
"unit": {"type": "string", "enum": ["bytes", "count", "bps"]},
"min": {"type": "number"},
"max": {"type": "number"}
},
@@ -11154,6 +11154,7 @@
"max_private_channels_per_user",
"max_reactions_per_message",
"max_relationships",
"max_screen_share_bitrate",
"max_users_per_message_reaction",
"max_voice_message_duration",
"max_webhooks_per_channel",
+5
View File
@@ -12,6 +12,11 @@ pub fn limit_key_hint(key: &str) -> Option<Hint<'static>> {
"Community creation policy",
)),
}),
"max_screen_share_bitrate" => Some(Hint {
name: Some("Max Screen Share Bitrate"),
body: "In bits per second, so 9000000 is 9 Mbps. Raising it also raises the automatic bitrate of every screen share quality. Accounts with Higher Video Quality can pick their own bitrate up to this value.",
link: None,
}),
_ => None,
}
}
@@ -28,6 +28,7 @@ const CATEGORY_ORDER: &[&str] = &[
"expressions",
"files",
"social",
"media",
];
pub fn limit_config_page(
@@ -8,7 +8,11 @@ import {HTTP_STATUS} from '@app/api/test/TestConstants';
import {createBuilder} from '@app/api/test/TestRequestBuilder';
import {AdminACLs} from '@fluxer/constants/src/AdminACLs';
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
import {MAX_BIO_LENGTH} from '@fluxer/constants/src/LimitConstants';
import {
MAX_BIO_LENGTH,
MAX_SCREEN_SHARE_BITRATE_CEILING,
MIN_SCREEN_SHARE_BITRATE_CEILING,
} from '@fluxer/constants/src/LimitConstants';
import type {LimitConfigGetResponse} from '@fluxer/schema/src/domains/admin/AdminSchemas';
import {afterAll, beforeAll, beforeEach, describe, expect, it} from 'vitest';
import type {z} from 'zod';
@@ -59,6 +63,30 @@ describe('limit config allowed ranges', () => {
expect(saved.metadata.max_bio_length).toMatchObject({min: 0, max: MAX_BIO_LENGTH});
});
it('keeps a screen share bitrate ceiling inside its allowed range', async () => {
const admin = await createAdmin();
const rule = (bitrate: number) => ({
id: 'vip',
filters: {traits: ['vip']},
limits: {max_screen_share_bitrate: bitrate},
});
for (const bitrate of [MIN_SCREEN_SHARE_BITRATE_CEILING - 1, MAX_SCREEN_SHARE_BITRATE_CEILING + 1]) {
await createBuilder(harness, admin.token)
.put('/admin/limit-config')
.body({limit_config: {traitDefinitions: ['vip'], rules: [rule(bitrate)]}})
.expect(HTTP_STATUS.BAD_REQUEST, APIErrorCodes.INVALID_FORM_BODY)
.execute();
}
const saved = await createBuilder<LimitConfigResponse>(harness, admin.token)
.put('/admin/limit-config')
.body({limit_config: {traitDefinitions: ['vip'], rules: [rule(25_000_000)]}})
.execute();
expect(saved.limit_config.rules.find((entry) => entry.id === 'vip')?.limits).toEqual({
max_screen_share_bitrate: 25_000_000,
});
expect(saved.defaults.default?.max_screen_share_bitrate).toBe(9_000_000);
});
it('reads a stored bio length above the profile maximum as the maximum', async () => {
const admin = await createAdmin();
await getInstanceConfigRepository().setLimitConfig({traitDefinitions: ['vip'], rules: [vipRule(1000)]});
@@ -385,6 +385,7 @@ Each key names one limit. A key whose name begins with `feature_` is a feature g
| max_private_channels_per_user | Maximum direct message channels per user, in user scope |
| max_reactions_per_message | Maximum distinct reactions per message, in guild scope |
| max_relationships | Maximum friend and blocked relationships, in user scope |
| max_screen_share_bitrate | Highest screen share video bitrate in bits per second, from 1000000 to 50000000, in user scope |
| max_users_per_message_reaction | Maximum users who can use the same reaction on a message, in guild scope |
| max_voice_message_duration | Maximum voice message duration in seconds, in user and guild scope |
| max_webhooks_per_channel | Maximum webhooks per channel, in guild scope |
+22 -3
View File
@@ -1,6 +1,11 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {BASE64_IMAGE_MAX_SIZE, MAX_BIO_LENGTH} from '@fluxer/constants/src/LimitConstants';
import {
BASE64_IMAGE_MAX_SIZE,
MAX_BIO_LENGTH,
MAX_SCREEN_SHARE_BITRATE_CEILING,
MIN_SCREEN_SHARE_BITRATE_CEILING,
} from '@fluxer/constants/src/LimitConstants';
export const LIMIT_KEYS = [
'avatar_max_size',
@@ -42,6 +47,7 @@ export const LIMIT_KEYS = [
'max_private_channels_per_user',
'max_reactions_per_message',
'max_relationships',
'max_screen_share_bitrate',
'max_users_per_message_reaction',
'max_voice_message_duration',
'max_webhooks_per_channel',
@@ -92,6 +98,7 @@ export const LIMIT_KEY_SCOPES: Record<LimitKey, LimitScope> = {
max_private_channels_per_user: 'user',
max_reactions_per_message: 'guild',
max_relationships: 'user',
max_screen_share_bitrate: 'user',
max_users_per_message_reaction: 'guild',
max_voice_message_duration: 'both',
max_webhooks_per_channel: 'guild',
@@ -99,7 +106,7 @@ export const LIMIT_KEY_SCOPES: Record<LimitKey, LimitScope> = {
sticker_max_size: 'guild',
};
type LimitCategory = 'messages' | 'guilds' | 'channels' | 'expressions' | 'files' | 'social' | 'features';
type LimitCategory = 'messages' | 'guilds' | 'channels' | 'expressions' | 'files' | 'social' | 'media' | 'features';
interface LimitKeyMetadata {
key: LimitKey;
@@ -108,7 +115,7 @@ interface LimitKeyMetadata {
category: LimitCategory;
scope: LimitScope;
isToggle: boolean;
unit?: 'bytes' | 'count';
unit?: 'bytes' | 'count' | 'bps';
min?: number;
max?: number;
}
@@ -120,6 +127,7 @@ export const LIMIT_CATEGORY_LABELS: Record<LimitCategory, string> = {
expressions: 'Expressions & Stickers',
files: 'Files & Attachments',
social: 'Social & Private',
media: 'Voice & Video',
features: 'Feature Toggles',
};
export const LIMIT_KEY_METADATA: Record<LimitKey, LimitKeyMetadata> = {
@@ -437,6 +445,17 @@ export const LIMIT_KEY_METADATA: Record<LimitKey, LimitKeyMetadata> = {
isToggle: false,
unit: 'count',
},
max_screen_share_bitrate: {
key: 'max_screen_share_bitrate',
label: 'Max Screen Share Bitrate',
description: 'Highest video bitrate a screen share can send, in bits per second',
category: 'media',
scope: 'user',
isToggle: false,
unit: 'bps',
min: MIN_SCREEN_SHARE_BITRATE_CEILING,
max: MAX_SCREEN_SHARE_BITRATE_CEILING,
},
feature_animated_avatar: {
key: 'feature_animated_avatar',
label: 'Animated Avatar',
+3
View File
@@ -72,6 +72,9 @@ export const MAX_FAVORITE_MEMES_NON_PREMIUM = 50;
export const MAX_FAVORITE_MEME_TAGS = 10;
export const MAX_FAVORITE_GIFS = 10000;
export const MAX_VOICE_MESSAGE_DURATION = 1200;
export const MAX_SCREEN_SHARE_BITRATE = 9_000_000;
export const MIN_SCREEN_SHARE_BITRATE_CEILING = 1_000_000;
export const MAX_SCREEN_SHARE_BITRATE_CEILING = 50_000_000;
export const MAX_MEDIA_DURATION_SECONDS = 86400;
export const EMOJI_MAX_SIZE = ASSET_FORMAT_POLICY.emoji.maxBytes;
export const STICKER_MAX_SIZE = ASSET_FORMAT_POLICY.sticker.maxBytes;
+2
View File
@@ -33,6 +33,7 @@ import {
MAX_PRIVATE_CHANNELS_PER_USER,
MAX_REACTIONS_PER_MESSAGE,
MAX_RELATIONSHIPS,
MAX_SCREEN_SHARE_BITRATE,
MAX_USERS_PER_MESSAGE_REACTION,
MAX_VOICE_MESSAGE_DURATION,
MAX_WEBHOOKS_PER_CHANNEL,
@@ -80,6 +81,7 @@ export const DEFAULT_RESTRICTED_LIMITS: Record<LimitKey, number> = {
max_private_channels_per_user: MAX_PRIVATE_CHANNELS_PER_USER,
max_reactions_per_message: MAX_REACTIONS_PER_MESSAGE,
max_relationships: MAX_RELATIONSHIPS,
max_screen_share_bitrate: MAX_SCREEN_SHARE_BITRATE,
max_users_per_message_reaction: MAX_USERS_PER_MESSAGE_REACTION,
max_voice_message_duration: MAX_VOICE_MESSAGE_DURATION,
max_webhooks_per_channel: MAX_WEBHOOKS_PER_CHANNEL,
@@ -1683,7 +1683,7 @@ const LimitKeyMetadataSchema = z.object({
category: z.string(),
scope: z.string(),
isToggle: z.boolean(),
unit: z.enum(['bytes', 'count']).optional(),
unit: z.enum(['bytes', 'count', 'bps']).optional(),
min: z.number().optional(),
max: z.number().optional(),
});