mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
perf(message): reuse the resolved channel for dm send checks (#2197)
This commit is contained in:
1 parent
d5fb495e19
commit
b5496097d2
4 files changed
+50
-16
No files matched your search
@@ -37,6 +37,18 @@ export interface ChannelAuthOptions {
|
|||||||
validateNsfw: boolean;
|
validateNsfw: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DMSendPermissionsByChannelParams {
|
||||||
|
channel: Channel;
|
||||||
|
userId: UserID;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DMSendPermissionsByChannelIdParams {
|
||||||
|
channelId: ChannelID;
|
||||||
|
userId: UserID;
|
||||||
|
}
|
||||||
|
|
||||||
|
type DMSendPermissionsParams = DMSendPermissionsByChannelParams | DMSendPermissionsByChannelIdParams;
|
||||||
|
|
||||||
export abstract class BaseChannelAuthService {
|
export abstract class BaseChannelAuthService {
|
||||||
protected abstract readonly options: ChannelAuthOptions;
|
protected abstract readonly options: ChannelAuthOptions;
|
||||||
protected dmPermissionValidator: DMPermissionValidator;
|
protected dmPermissionValidator: DMPermissionValidator;
|
||||||
@@ -136,8 +148,10 @@ export abstract class BaseChannelAuthService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async validateDMSendPermissions({channelId, userId}: {channelId: ChannelID; userId: UserID}): Promise<void> {
|
async validateDMSendPermissions(params: DMSendPermissionsParams): Promise<void> {
|
||||||
const channel = await this.channelRepository.channelData.findUnique(channelId);
|
const {userId} = params;
|
||||||
|
const channel =
|
||||||
|
'channel' in params ? params.channel : await this.channelRepository.channelData.findUnique(params.channelId);
|
||||||
if (!channel) throw new UnknownChannelError();
|
if (!channel) throw new UnknownChannelError();
|
||||||
if (channel.type === ChannelTypes.GROUP_DM || channel.type === ChannelTypes.DM_PERSONAL_NOTES) {
|
if (channel.type === ChannelTypes.GROUP_DM || channel.type === ChannelTypes.DM_PERSONAL_NOTES) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -47,12 +47,7 @@ export class MessageInteractionService {
|
|||||||
guildRepository,
|
guildRepository,
|
||||||
gatewayService,
|
gatewayService,
|
||||||
);
|
);
|
||||||
this.pinAuthService = new MessagePinAuthService(
|
this.pinAuthService = new MessagePinAuthService(channelRepository, userRepository, guildRepository, gatewayService);
|
||||||
channelRepository,
|
|
||||||
userRepository,
|
|
||||||
guildRepository,
|
|
||||||
gatewayService,
|
|
||||||
);
|
|
||||||
this.readStateService = new MessageReadStateService(gatewayService);
|
this.readStateService = new MessageReadStateService(gatewayService);
|
||||||
this.pinService = new MessagePinService(
|
this.pinService = new MessagePinService(
|
||||||
gatewayService,
|
gatewayService,
|
||||||
@@ -110,7 +105,7 @@ export class MessageInteractionService {
|
|||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const authChannel = await this.authService.getChannelAuthenticated({userId, channelId});
|
const authChannel = await this.authService.getChannelAuthenticated({userId, channelId});
|
||||||
if (!authChannel.guild && authChannel.channel.type !== ChannelTypes.DM_PERSONAL_NOTES) {
|
if (!authChannel.guild && authChannel.channel.type !== ChannelTypes.DM_PERSONAL_NOTES) {
|
||||||
await this.authService.validateDMSendPermissions({channelId, userId});
|
await this.authService.validateDMSendPermissions({channel: authChannel.channel, userId});
|
||||||
}
|
}
|
||||||
await this.pinService.pinMessage({authChannel, messageId, userId, requestCache});
|
await this.pinService.pinMessage({authChannel, messageId, userId, requestCache});
|
||||||
}
|
}
|
||||||
@@ -128,7 +123,7 @@ export class MessageInteractionService {
|
|||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const authChannel = await this.authService.getChannelAuthenticated({userId, channelId});
|
const authChannel = await this.authService.getChannelAuthenticated({userId, channelId});
|
||||||
if (!authChannel.guild && authChannel.channel.type !== ChannelTypes.DM_PERSONAL_NOTES) {
|
if (!authChannel.guild && authChannel.channel.type !== ChannelTypes.DM_PERSONAL_NOTES) {
|
||||||
await this.authService.validateDMSendPermissions({channelId, userId});
|
await this.authService.validateDMSendPermissions({channel: authChannel.channel, userId});
|
||||||
}
|
}
|
||||||
await this.pinService.unpinMessage({authChannel, messageId, userId, requestCache});
|
await this.pinService.unpinMessage({authChannel, messageId, userId, requestCache});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -238,7 +238,6 @@ export class MessageSendService {
|
|||||||
user,
|
user,
|
||||||
checkPermission,
|
checkPermission,
|
||||||
hasPermission,
|
hasPermission,
|
||||||
channelId,
|
|
||||||
}: {
|
}: {
|
||||||
guild: GuildResponse | null;
|
guild: GuildResponse | null;
|
||||||
member: GuildMemberResponse | null;
|
member: GuildMemberResponse | null;
|
||||||
@@ -247,7 +246,6 @@ export class MessageSendService {
|
|||||||
user: User;
|
user: User;
|
||||||
checkPermission: (permission: bigint) => Promise<void>;
|
checkPermission: (permission: bigint) => Promise<void>;
|
||||||
hasPermission: (permission: bigint) => Promise<boolean>;
|
hasPermission: (permission: bigint) => Promise<boolean>;
|
||||||
channelId: ChannelID;
|
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
canEmbedLinks: boolean;
|
canEmbedLinks: boolean;
|
||||||
canMentionEveryone: boolean;
|
canMentionEveryone: boolean;
|
||||||
@@ -286,7 +284,7 @@ export class MessageSendService {
|
|||||||
}
|
}
|
||||||
await this.deps.channelAuthService.checkGuildVerification({user, guild, member});
|
await this.deps.channelAuthService.checkGuildVerification({user, guild, member});
|
||||||
} else if (channel.type === ChannelTypes.DM || channel.type === ChannelTypes.GROUP_DM) {
|
} else if (channel.type === ChannelTypes.DM || channel.type === ChannelTypes.GROUP_DM) {
|
||||||
await this.deps.channelAuthService.validateDMSendPermissions({channelId, userId: user.id});
|
await this.deps.channelAuthService.validateDMSendPermissions({channel, userId: user.id});
|
||||||
}
|
}
|
||||||
return {canEmbedLinks, canMentionEveryone, canAttachFiles};
|
return {canEmbedLinks, canMentionEveryone, canAttachFiles};
|
||||||
}
|
}
|
||||||
@@ -320,7 +318,6 @@ export class MessageSendService {
|
|||||||
user,
|
user,
|
||||||
checkPermission,
|
checkPermission,
|
||||||
hasPermission,
|
hasPermission,
|
||||||
channelId,
|
|
||||||
});
|
});
|
||||||
this.deps.validationService.ensureTextChannel(channel);
|
this.deps.validationService.ensureTextChannel(channel);
|
||||||
const isForwardMessage = this.ensureMessageRequestIsValid({user, data, guildFeatures: guild?.features ?? null});
|
const isForwardMessage = this.ensureMessageRequestIsValid({user, data, guildFeatures: guild?.features ?? null});
|
||||||
@@ -809,7 +806,6 @@ export class MessageSendService {
|
|||||||
user,
|
user,
|
||||||
checkPermission,
|
checkPermission,
|
||||||
hasPermission,
|
hasPermission,
|
||||||
channelId,
|
|
||||||
});
|
});
|
||||||
const needsSlowmodeCheck = guild && channel.rateLimitPerUser && channel.rateLimitPerUser > 0 && !user.isBot;
|
const needsSlowmodeCheck = guild && channel.rateLimitPerUser && channel.rateLimitPerUser > 0 && !user.isBot;
|
||||||
const slowmodeBypass = needsSlowmodeCheck ? await hasPermission(Permissions.BYPASS_SLOWMODE) : false;
|
const slowmodeBypass = needsSlowmodeCheck ? await hasPermission(Permissions.BYPASS_SLOWMODE) : false;
|
||||||
|
|||||||
@@ -4,13 +4,20 @@ import {Permissions} from '@fluxer/constants/src/ChannelConstants';
|
|||||||
import {MAX_MESSAGE_LENGTH_PREMIUM} from '@fluxer/constants/src/LimitConstants';
|
import {MAX_MESSAGE_LENGTH_PREMIUM} from '@fluxer/constants/src/LimitConstants';
|
||||||
import type {MessageResponse} from '@fluxer/schema/src/domains/message/MessageResponseSchemas';
|
import type {MessageResponse} from '@fluxer/schema/src/domains/message/MessageResponseSchemas';
|
||||||
import {afterAll, beforeAll, beforeEach, describe, expect, it, vi} from 'vitest';
|
import {afterAll, beforeAll, beforeEach, describe, expect, it, vi} from 'vitest';
|
||||||
|
import {createTestAccount} from '../../auth/tests/AuthTestUtils';
|
||||||
import {authorizeBot, createTestBotAccount} from '../../bot/tests/BotTestUtils';
|
import {authorizeBot, createTestBotAccount} from '../../bot/tests/BotTestUtils';
|
||||||
import {ensureSessionStarted} from '../../message/tests/MessageTestUtils';
|
import {ensureSessionStarted} from '../../message/tests/MessageTestUtils';
|
||||||
import {getGatewayService} from '../../middleware/ServiceRegistry';
|
import {getGatewayService} from '../../middleware/ServiceRegistry';
|
||||||
import {type ApiTestHarness, createApiTestHarness} from '../../test/ApiTestHarness';
|
import {type ApiTestHarness, createApiTestHarness} from '../../test/ApiTestHarness';
|
||||||
import {HTTP_STATUS} from '../../test/TestConstants';
|
import {HTTP_STATUS} from '../../test/TestConstants';
|
||||||
import {createBuilder} from '../../test/TestRequestBuilder';
|
import {createBuilder} from '../../test/TestRequestBuilder';
|
||||||
import {createPermissionOverwrite, setupTestGuildWithMembers} from './ChannelTestUtils';
|
import {ChannelDataRepository} from '../repositories/ChannelDataRepository';
|
||||||
|
import {
|
||||||
|
createDmChannel,
|
||||||
|
createFriendship,
|
||||||
|
createPermissionOverwrite,
|
||||||
|
setupTestGuildWithMembers,
|
||||||
|
} from './ChannelTestUtils';
|
||||||
|
|
||||||
describe('Message send permissions', () => {
|
describe('Message send permissions', () => {
|
||||||
let harness: ApiTestHarness;
|
let harness: ApiTestHarness;
|
||||||
@@ -140,6 +147,28 @@ describe('Message send permissions', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('resolves the DM channel once for routing and once for authentication when sending', async () => {
|
||||||
|
const sender = await createTestAccount(harness);
|
||||||
|
const recipient = await createTestAccount(harness);
|
||||||
|
await ensureSessionStarted(harness, sender.token);
|
||||||
|
await createFriendship(harness, sender, recipient);
|
||||||
|
const channel = await createDmChannel(harness, sender.token, recipient.userId);
|
||||||
|
const findUnique = vi.spyOn(ChannelDataRepository.prototype, 'findUnique');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await createBuilder<MessageResponse>(harness, sender.token)
|
||||||
|
.post(`/channels/${channel.id}/messages`)
|
||||||
|
.body({content: 'no redundant reads'})
|
||||||
|
.expect(HTTP_STATUS.OK)
|
||||||
|
.execute();
|
||||||
|
const channelReads = findUnique.mock.calls.filter((call) => call[0].toString() === channel.id).length;
|
||||||
|
|
||||||
|
expect(channelReads).toBe(2);
|
||||||
|
} finally {
|
||||||
|
findUnique.mockRestore();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('allows bot message content up to 4000 characters', async () => {
|
it('allows bot message content up to 4000 characters', async () => {
|
||||||
const {owner, guild, systemChannel} = await setupTestGuildWithMembers(harness, 0);
|
const {owner, guild, systemChannel} = await setupTestGuildWithMembers(harness, 0);
|
||||||
const botAccount = await createTestBotAccount(harness);
|
const botAccount = await createTestBotAccount(harness);
|
||||||
|
|||||||
Reference in new issue
Block a user