mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 21:45:31 +09:00
refactor: simplify account standing and verification levels (#3130)
This commit is contained in:
1 parent
1abde06824
commit
1ec8f31253
488 files changed
+10390
-47117
No files matched your search
@@ -13,6 +13,7 @@ import {OpenAPI} from '@app/api/middleware/ResponseTypeMiddleware';
|
||||
import {SudoModeMiddleware} from '@app/api/middleware/SudoModeMiddleware';
|
||||
import {RateLimitConfigs} from '@app/api/RateLimitConfig';
|
||||
import type {HonoApp} from '@app/api/types/HonoEnv';
|
||||
import {assertAccountNotLimited} from '@app/api/user/AccountLimit';
|
||||
import {Validator} from '@app/api/Validator';
|
||||
import {AdminACLs} from '@fluxer/constants/src/AdminACLs';
|
||||
import {GuildCreationPermissionRequiredError} from '@fluxer/errors/src/domains/guild/GuildCreationPermissionRequiredError';
|
||||
@@ -62,6 +63,7 @@ export function GuildBaseController(app: HonoApp) {
|
||||
if (!user.isUnclaimedAccount()) {
|
||||
requireEmailVerified(user, 'guild_creation');
|
||||
}
|
||||
assertAccountNotLimited(user);
|
||||
if (Config.instance.selfHosted && !policy.guild_create_access) {
|
||||
const granted =
|
||||
user.acls.has(AdminACLs.WILDCARD) ||
|
||||
|
||||
@@ -434,11 +434,7 @@ export class GuildDiscoveryService extends IGuildDiscoveryService {
|
||||
member_count: hit.memberCount,
|
||||
online_count: 0,
|
||||
features: hit.features,
|
||||
verification_level: getEffectiveGuildVerificationLevel(
|
||||
hit.verificationLevel,
|
||||
hit.isDiscoverable,
|
||||
Config.instance.phoneVerificationEnabled,
|
||||
),
|
||||
verification_level: getEffectiveGuildVerificationLevel(hit.verificationLevel, hit.isDiscoverable),
|
||||
}));
|
||||
const total = results.total;
|
||||
if (guilds.length > 0) {
|
||||
|
||||
@@ -307,6 +307,7 @@ export class GuildMemberService {
|
||||
sendJoinMessage?: boolean;
|
||||
skipGuildLimitCheck?: boolean;
|
||||
skipBanCheck?: boolean;
|
||||
skipAccountLimitCheck?: boolean;
|
||||
isTemporary?: boolean;
|
||||
joinSourceType?: JoinSourceType;
|
||||
sourceInviteCode?: InviteCode;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import type {ChannelID, GuildID, RoleID, UserID} from '@app/api/BrandedTypes';
|
||||
import {createChannelID, createGuildID, createRoleID, guildIdToRoleId} from '@app/api/BrandedTypes';
|
||||
import {Config} from '@app/api/Config';
|
||||
import type {IChannelRepository} from '@app/api/channel/IChannelRepository';
|
||||
import {
|
||||
type ChannelFollowerRemovalCopyMode,
|
||||
@@ -482,17 +481,6 @@ export class GuildOperationsService {
|
||||
ValidationErrorCodes.DISCOVERABLE_GUILD_VERIFICATION_LEVEL_TOO_LOW,
|
||||
);
|
||||
}
|
||||
if (
|
||||
data.verification_level === GuildVerificationLevel.VERY_HIGH &&
|
||||
data.verification_level !== currentGuild.verificationLevel &&
|
||||
!Config.instance.phoneVerificationEnabled
|
||||
) {
|
||||
throw InputValidationError.fromCode('verification_level', ValidationErrorCodes.VALUE_MUST_BE_INTEGER_IN_RANGE, {
|
||||
name: 'verification_level',
|
||||
minValue: GuildVerificationLevel.NONE,
|
||||
maxValue: GuildVerificationLevel.HIGH,
|
||||
});
|
||||
}
|
||||
const isMfaLevelChange = data.mfa_level !== undefined && data.mfa_level !== currentGuild.mfaLevel;
|
||||
if (isMfaLevelChange) {
|
||||
const isOwner = guildData.owner_id === userId.toString();
|
||||
@@ -1192,7 +1180,7 @@ export class GuildOperationsService {
|
||||
verificationLevel: this.clampTemplateSetting(
|
||||
template?.verification_level,
|
||||
GuildVerificationLevel.NONE,
|
||||
Config.instance.phoneVerificationEnabled ? GuildVerificationLevel.VERY_HIGH : GuildVerificationLevel.HIGH,
|
||||
GuildVerificationLevel.HIGH,
|
||||
GuildVerificationLevel.NONE,
|
||||
),
|
||||
explicitContentFilter: this.clampTemplateSetting(template?.explicit_content_filter, 0, 2, 0),
|
||||
|
||||
@@ -31,6 +31,7 @@ import type {GuildMember} from '@app/api/models/GuildMember';
|
||||
import type {User} from '@app/api/models/User';
|
||||
import type {UserGuildSettings} from '@app/api/models/UserGuildSettings';
|
||||
import type {UserSettings} from '@app/api/models/UserSettings';
|
||||
import {assertAccountNotLimited} from '@app/api/user/AccountLimit';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import {isProfileSubstringExempt} from '@app/api/user/UserHelpers';
|
||||
import {mapUserGuildSettingsToResponse, mapUserSettingsToResponse} from '@app/api/user/UserMappers';
|
||||
@@ -418,6 +419,7 @@ export class GuildMemberOperationsService {
|
||||
sendJoinMessage?: boolean;
|
||||
skipGuildLimitCheck?: boolean;
|
||||
skipBanCheck?: boolean;
|
||||
skipAccountLimitCheck?: boolean;
|
||||
isTemporary?: boolean;
|
||||
joinSourceType?: JoinSourceType;
|
||||
sourceInviteCode?: InviteCode;
|
||||
@@ -435,6 +437,7 @@ export class GuildMemberOperationsService {
|
||||
sendJoinMessage = true,
|
||||
skipGuildLimitCheck = false,
|
||||
skipBanCheck = false,
|
||||
skipAccountLimitCheck = false,
|
||||
isTemporary = false,
|
||||
joinSourceType = JoinSourceTypes.INSTANT_INVITE,
|
||||
sourceInviteCode = null,
|
||||
@@ -449,6 +452,9 @@ export class GuildMemberOperationsService {
|
||||
assertMutableUserId(userId);
|
||||
const user = await this.userRepository.findUnique(userId);
|
||||
if (!user) throw new UnknownGuildError();
|
||||
if (!skipAccountLimitCheck) {
|
||||
assertAccountNotLimited(user);
|
||||
}
|
||||
if (!skipBanCheck) {
|
||||
await this.validationService.checkUserBanStatus({userId, guildId});
|
||||
}
|
||||
@@ -706,6 +712,7 @@ export class GuildMemberOperationsService {
|
||||
const {targetId, guildId, targetUser, targetMember, data, updateData, preparedAssets} = params;
|
||||
if (hasSelfProfileCustomizationUpdate(data)) {
|
||||
requireEmailVerified(targetUser, 'profile');
|
||||
assertAccountNotLimited(targetUser);
|
||||
}
|
||||
const ctx = createLimitMatchContext({user: targetUser});
|
||||
const hasGuildProfileCustomization = resolveLimitSafe(
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {createTestAccount} from '@app/api/auth/tests/AuthTestUtils';
|
||||
import {Config} from '@app/api/Config';
|
||||
import {createGuild, updateGuild} from '@app/api/guild/tests/GuildTestUtils';
|
||||
import {type ApiTestHarness, createApiTestHarness} from '@app/api/test/ApiTestHarness';
|
||||
import {HTTP_STATUS} from '@app/api/test/TestConstants';
|
||||
import {createBuilder} from '@app/api/test/TestRequestBuilder';
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {ChannelTypes} from '@fluxer/constants/src/ChannelConstants';
|
||||
import {GuildVerificationLevel} from '@fluxer/constants/src/GuildConstants';
|
||||
import type {GuildResponse} from '@fluxer/schema/src/domains/guild/GuildResponseSchemas';
|
||||
import {afterAll, afterEach, beforeAll, beforeEach, describe, expect, test} from 'vitest';
|
||||
|
||||
describe('Guild verification level without phone verification', () => {
|
||||
let harness: ApiTestHarness;
|
||||
let savedPhoneVerificationEnabled: boolean;
|
||||
beforeAll(async () => {
|
||||
harness = await createApiTestHarness();
|
||||
});
|
||||
afterAll(async () => {
|
||||
await harness?.shutdown();
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await harness.reset();
|
||||
savedPhoneVerificationEnabled = Config.instance.phoneVerificationEnabled;
|
||||
});
|
||||
afterEach(() => {
|
||||
Config.instance.phoneVerificationEnabled = savedPhoneVerificationEnabled;
|
||||
});
|
||||
|
||||
test('rejects very high when phone verification is unavailable', async () => {
|
||||
Config.instance.phoneVerificationEnabled = false;
|
||||
const account = await createTestAccount(harness);
|
||||
const guild = await createGuild(harness, account.token, 'No Phone Guild');
|
||||
await createBuilder(harness, account.token)
|
||||
.patch(`/guilds/${guild.id}`)
|
||||
.body({verification_level: GuildVerificationLevel.VERY_HIGH})
|
||||
.expect(HTTP_STATUS.BAD_REQUEST, APIErrorCodes.INVALID_FORM_BODY)
|
||||
.execute();
|
||||
const updated = await updateGuild(harness, account.token, guild.id, {
|
||||
verification_level: GuildVerificationLevel.HIGH,
|
||||
});
|
||||
expect(updated.verification_level).toBe(GuildVerificationLevel.HIGH);
|
||||
});
|
||||
|
||||
test('accepts very high when phone verification is available', async () => {
|
||||
Config.instance.phoneVerificationEnabled = true;
|
||||
const account = await createTestAccount(harness);
|
||||
const guild = await createGuild(harness, account.token, 'Phone Guild');
|
||||
const updated = await updateGuild(harness, account.token, guild.id, {
|
||||
verification_level: GuildVerificationLevel.VERY_HIGH,
|
||||
});
|
||||
expect(updated.verification_level).toBe(GuildVerificationLevel.VERY_HIGH);
|
||||
});
|
||||
|
||||
test('accepts an unchanged stored very high when phone verification is unavailable', async () => {
|
||||
Config.instance.phoneVerificationEnabled = true;
|
||||
const account = await createTestAccount(harness);
|
||||
const guild = await createGuild(harness, account.token, 'Stored Phone Guild');
|
||||
await updateGuild(harness, account.token, guild.id, {verification_level: GuildVerificationLevel.VERY_HIGH});
|
||||
Config.instance.phoneVerificationEnabled = false;
|
||||
const updated = await updateGuild(harness, account.token, guild.id, {
|
||||
name: 'Renamed Phone Guild',
|
||||
verification_level: GuildVerificationLevel.VERY_HIGH,
|
||||
});
|
||||
expect(updated.name).toBe('Renamed Phone Guild');
|
||||
expect(updated.verification_level).toBe(GuildVerificationLevel.VERY_HIGH);
|
||||
});
|
||||
|
||||
test('clamps a template level to high when phone verification is unavailable', async () => {
|
||||
Config.instance.phoneVerificationEnabled = false;
|
||||
const account = await createTestAccount(harness);
|
||||
const guild = await createBuilder<GuildResponse>(harness, account.token)
|
||||
.post('/guilds')
|
||||
.body({
|
||||
name: 'Template Guild',
|
||||
template: {
|
||||
name: 'Template Source',
|
||||
description: null,
|
||||
verification_level: GuildVerificationLevel.VERY_HIGH,
|
||||
default_message_notifications: 0,
|
||||
explicit_content_filter: 0,
|
||||
system_channel_id: 1001,
|
||||
afk_timeout: 300,
|
||||
system_channel_flags: 0,
|
||||
roles: [{id: 0, name: '@everyone', permissions: '0'}],
|
||||
channels: [{id: 1001, type: ChannelTypes.GUILD_TEXT, name: 'general', position: 0}],
|
||||
},
|
||||
})
|
||||
.execute();
|
||||
expect(guild.verification_level).toBe(GuildVerificationLevel.HIGH);
|
||||
});
|
||||
});
|
||||
@@ -103,7 +103,7 @@ describe('Guild Template Import', () => {
|
||||
},
|
||||
})
|
||||
.execute();
|
||||
expect(guild.verification_level).toBe(4);
|
||||
expect(guild.verification_level).toBe(3);
|
||||
expect(guild.default_message_notifications).toBe(1);
|
||||
expect(guild.explicit_content_filter).toBe(2);
|
||||
expect(guild.system_channel_flags).toBe(SystemChannelFlags.SUPPRESS_JOIN_NOTIFICATIONS);
|
||||
|
||||
Reference in new issue
Block a user