mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 21:45:31 +09:00
refactor(api): collapse single-impl interfaces and facades (#3382)
This commit is contained in:
1 parent
e7b13fd19f
commit
c16d414d93
426 files changed
+2085
-6445
No files matched your search
@@ -9,7 +9,7 @@ import {
|
||||
THREAD_SCOPED_AUDIT_OPTION,
|
||||
} from '@app/api/guild/GuildAuditLogEntryMapper';
|
||||
import type {AuditLogChange, GuildAuditLogChange} from '@app/api/guild/GuildAuditLogTypes';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import type {IGatewayService} from '@app/api/infrastructure/IGatewayService';
|
||||
import type {ISnowflakeService} from '@app/api/infrastructure/ISnowflakeService';
|
||||
import {Logger} from '@app/api/Logger';
|
||||
@@ -61,7 +61,7 @@ function normalizeAuditLogMetadata(metadata?: CreateGuildAuditLogParams['metadat
|
||||
|
||||
export class GuildAuditLogService {
|
||||
constructor(
|
||||
private readonly guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
private readonly snowflakeService: ISnowflakeService,
|
||||
private readonly workerService: IWorkerService<WorkerTaskName>,
|
||||
private readonly gatewayService: IGatewayService,
|
||||
|
||||
@@ -85,7 +85,7 @@ export function GuildBaseController(app: HonoApp) {
|
||||
}
|
||||
const auditLogReason = ctx.get('auditLogReason') ?? null;
|
||||
const locale = ctx.get('requestLocale') ?? null;
|
||||
return ctx.json(await ctx.get('guildService').data.createGuild({user, data, locale}, auditLogReason));
|
||||
return ctx.json(await ctx.get('guildService').data.operations.createGuild({user, data, locale}, auditLogReason));
|
||||
},
|
||||
);
|
||||
app.get(
|
||||
@@ -106,7 +106,7 @@ export function GuildBaseController(app: HonoApp) {
|
||||
async (ctx) => {
|
||||
const userId = ctx.get('user').id;
|
||||
const {before, after, limit, with_counts} = ctx.req.valid('query');
|
||||
const guilds = await ctx.get('guildService').data.getUserGuilds(userId, {
|
||||
const guilds = await ctx.get('guildService').data.operations.getUserGuilds(userId, {
|
||||
before: before != null ? createGuildID(before) : undefined,
|
||||
after: after != null ? createGuildID(after) : undefined,
|
||||
limit,
|
||||
@@ -148,7 +148,7 @@ export function GuildBaseController(app: HonoApp) {
|
||||
const {delete_messages} = ctx.req.valid('query');
|
||||
const body = ctx.req.valid('json');
|
||||
if (delete_messages) {
|
||||
await ctx.get('guildService').data.getGuild({userId, guildId});
|
||||
await ctx.get('guildService').data.operations.getGuild({userId, guildId});
|
||||
await requireSudoMode(ctx, user, body);
|
||||
await ctx.get('channelService').userMessageDeletion.deleteUserMessagesInScope(userId, {guildId});
|
||||
}
|
||||
@@ -178,7 +178,7 @@ export function GuildBaseController(app: HonoApp) {
|
||||
const userId = user.id;
|
||||
const guildId = createGuildID(ctx.req.valid('param').guild_id);
|
||||
const body = ctx.req.valid('json');
|
||||
await ctx.get('guildService').data.getGuild({userId, guildId});
|
||||
await ctx.get('guildService').data.operations.getGuild({userId, guildId});
|
||||
await requireSudoMode(ctx, user, body);
|
||||
await ctx.get('channelService').userMessageDeletion.deleteUserMessagesInScope(userId, {guildId});
|
||||
return ctx.body(null, 202);
|
||||
@@ -203,7 +203,7 @@ export function GuildBaseController(app: HonoApp) {
|
||||
async (ctx) => {
|
||||
const userId = ctx.get('user').id;
|
||||
const guildId = createGuildID(ctx.req.valid('param').guild_id);
|
||||
const guild = await ctx.get('guildService').data.getGuild({userId, guildId});
|
||||
const guild = await ctx.get('guildService').data.operations.getGuild({userId, guildId});
|
||||
return ctx.json(await maskGuildResponseThreadBits(guildId, viewerFromCtx(ctx), guild));
|
||||
},
|
||||
);
|
||||
@@ -231,7 +231,7 @@ export function GuildBaseController(app: HonoApp) {
|
||||
const data = ctx.req.valid('json');
|
||||
let shouldRequireSudoMode = false;
|
||||
if (data.mfa_level !== undefined) {
|
||||
const currentGuild = await ctx.get('guildService').data.getGuild({userId, guildId});
|
||||
const currentGuild = await ctx.get('guildService').data.operations.getGuild({userId, guildId});
|
||||
shouldRequireSudoMode = currentGuild.mfa_level !== data.mfa_level;
|
||||
}
|
||||
if (shouldRequireSudoMode) {
|
||||
@@ -270,7 +270,7 @@ export function GuildBaseController(app: HonoApp) {
|
||||
const body = ctx.req.valid('json');
|
||||
await requireSudoMode(ctx, user, body);
|
||||
const auditLogReason = ctx.get('auditLogReason') ?? null;
|
||||
await ctx.get('guildService').data.deleteGuild({user, guildId}, auditLogReason);
|
||||
await ctx.get('guildService').data.operations.deleteGuild({user, guildId}, auditLogReason);
|
||||
return ctx.body(null, 204);
|
||||
},
|
||||
);
|
||||
@@ -291,7 +291,7 @@ export function GuildBaseController(app: HonoApp) {
|
||||
async (ctx) => {
|
||||
const userId = ctx.get('user').id;
|
||||
const guildId = createGuildID(ctx.req.valid('param').guild_id);
|
||||
return ctx.json(await ctx.get('guildService').data.getVanityURL({userId, guildId}));
|
||||
return ctx.json(await ctx.get('guildService').data.vanity.getVanityURL({userId, guildId}));
|
||||
},
|
||||
);
|
||||
app.patch(
|
||||
@@ -317,7 +317,7 @@ export function GuildBaseController(app: HonoApp) {
|
||||
const auditLogReason = ctx.get('auditLogReason') ?? null;
|
||||
const {code: newCode} = await ctx
|
||||
.get('guildService')
|
||||
.data.updateVanityURL({userId, guildId, code: code ?? null, requestCache}, auditLogReason);
|
||||
.data.vanity.updateVanityURL({userId, guildId, code: code ?? null, requestCache}, auditLogReason);
|
||||
return ctx.json({code: newCode});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -151,7 +151,7 @@ export function GuildDiscoveryController(app: HonoApp) {
|
||||
if (!status || status.status !== DiscoveryApplicationStatus.APPROVED) {
|
||||
throw new DiscoveryNotDiscoverableError();
|
||||
}
|
||||
const guild = await ctx.get('guildService').data.getGuildSystem(guildId);
|
||||
const guild = await ctx.get('guildService').data.operations.getGuildSystem(guildId);
|
||||
if (guild.features.has(GuildFeatures.INVITES_DISABLED)) {
|
||||
throw new InvitesDisabledError();
|
||||
}
|
||||
|
||||
@@ -45,7 +45,9 @@ export function GuildEmojiController(app: HonoApp) {
|
||||
const guildId = createGuildID(ctx.req.valid('param').guild_id);
|
||||
const {name, image} = ctx.req.valid('json');
|
||||
const auditLogReason = ctx.get('auditLogReason') ?? null;
|
||||
const emoji = await ctx.get('guildService').content.createEmoji({user, guildId, name, image}, auditLogReason);
|
||||
const emoji = await ctx
|
||||
.get('guildService')
|
||||
.content.emojis.createEmoji({user, guildId, name, image}, auditLogReason);
|
||||
return ctx.json(emoji);
|
||||
},
|
||||
);
|
||||
@@ -70,7 +72,9 @@ export function GuildEmojiController(app: HonoApp) {
|
||||
const guildId = createGuildID(ctx.req.valid('param').guild_id);
|
||||
const {emojis} = ctx.req.valid('json');
|
||||
const auditLogReason = ctx.get('auditLogReason') ?? null;
|
||||
const result = await ctx.get('guildService').content.bulkCreateEmojis({user, guildId, emojis}, auditLogReason);
|
||||
const result = await ctx
|
||||
.get('guildService')
|
||||
.content.emojis.bulkCreateEmojis({user, guildId, emojis}, auditLogReason);
|
||||
return ctx.json(result);
|
||||
},
|
||||
);
|
||||
@@ -96,7 +100,9 @@ export function GuildEmojiController(app: HonoApp) {
|
||||
const {source_emoji_id} = ctx.req.valid('json');
|
||||
const sourceEmojiId = createEmojiID(source_emoji_id);
|
||||
const auditLogReason = ctx.get('auditLogReason') ?? null;
|
||||
const emoji = await ctx.get('guildService').content.cloneEmoji({user, guildId, sourceEmojiId}, auditLogReason);
|
||||
const emoji = await ctx
|
||||
.get('guildService')
|
||||
.content.emojis.cloneEmoji({user, guildId, sourceEmojiId}, auditLogReason);
|
||||
return ctx.json(emoji);
|
||||
},
|
||||
);
|
||||
@@ -120,7 +126,7 @@ export function GuildEmojiController(app: HonoApp) {
|
||||
const userId = ctx.get('user').id;
|
||||
const guildId = createGuildID(guild_id);
|
||||
const requestCache = ctx.get('requestCache');
|
||||
return ctx.json(await ctx.get('guildService').content.getEmojis({userId, guildId, requestCache}));
|
||||
return ctx.json(await ctx.get('guildService').content.emojis.getEmojis({userId, guildId, requestCache}));
|
||||
},
|
||||
);
|
||||
app.patch(
|
||||
@@ -146,7 +152,9 @@ export function GuildEmojiController(app: HonoApp) {
|
||||
const emojiId = createEmojiID(emoji_id);
|
||||
const {name} = ctx.req.valid('json');
|
||||
const auditLogReason = ctx.get('auditLogReason') ?? null;
|
||||
const emoji = await ctx.get('guildService').content.updateEmoji({userId, guildId, emojiId, name}, auditLogReason);
|
||||
const emoji = await ctx
|
||||
.get('guildService')
|
||||
.content.emojis.updateEmoji({userId, guildId, emojiId, name}, auditLogReason);
|
||||
return ctx.json(emoji);
|
||||
},
|
||||
);
|
||||
@@ -174,7 +182,7 @@ export function GuildEmojiController(app: HonoApp) {
|
||||
const emojiId = createEmojiID(emoji_id);
|
||||
const auditLogReason = ctx.get('auditLogReason') ?? null;
|
||||
const {purge = false} = ctx.req.valid('query');
|
||||
await ctx.get('guildService').content.deleteEmoji({userId, guildId, emojiId, purge}, auditLogReason);
|
||||
await ctx.get('guildService').content.emojis.deleteEmoji({userId, guildId, emojiId, purge}, auditLogReason);
|
||||
return ctx.body(null, 204);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -217,7 +217,7 @@ export function GuildMemberController(app: HonoApp) {
|
||||
const newOwnerId = createUserID(new_owner_id);
|
||||
const auditLogReason = ctx.get('auditLogReason') ?? null;
|
||||
return ctx.json(
|
||||
await ctx.get('guildService').data.transferOwnership({userId, guildId, newOwnerId}, auditLogReason),
|
||||
await ctx.get('guildService').data.ownership.transferOwnership({userId, guildId, newOwnerId}, auditLogReason),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -51,7 +51,7 @@ export function GuildStickerController(app: HonoApp) {
|
||||
const auditLogReason = ctx.get('auditLogReason') ?? null;
|
||||
const sticker = await ctx
|
||||
.get('guildService')
|
||||
.content.createSticker({user, guildId, name, description, tags, image}, auditLogReason);
|
||||
.content.stickers.createSticker({user, guildId, name, description, tags, image}, auditLogReason);
|
||||
return ctx.json(sticker);
|
||||
},
|
||||
);
|
||||
@@ -78,7 +78,7 @@ export function GuildStickerController(app: HonoApp) {
|
||||
const auditLogReason = ctx.get('auditLogReason') ?? null;
|
||||
const result = await ctx
|
||||
.get('guildService')
|
||||
.content.bulkCreateStickers({user, guildId, stickers}, auditLogReason);
|
||||
.content.stickers.bulkCreateStickers({user, guildId, stickers}, auditLogReason);
|
||||
return ctx.json(result);
|
||||
},
|
||||
);
|
||||
@@ -106,7 +106,7 @@ export function GuildStickerController(app: HonoApp) {
|
||||
const auditLogReason = ctx.get('auditLogReason') ?? null;
|
||||
const sticker = await ctx
|
||||
.get('guildService')
|
||||
.content.cloneSticker({user, guildId, sourceStickerId}, auditLogReason);
|
||||
.content.stickers.cloneSticker({user, guildId, sourceStickerId}, auditLogReason);
|
||||
return ctx.json(sticker);
|
||||
},
|
||||
);
|
||||
@@ -130,7 +130,7 @@ export function GuildStickerController(app: HonoApp) {
|
||||
const userId = ctx.get('user').id;
|
||||
const guildId = createGuildID(guild_id);
|
||||
const requestCache = ctx.get('requestCache');
|
||||
return ctx.json(await ctx.get('guildService').content.getStickers({userId, guildId, requestCache}));
|
||||
return ctx.json(await ctx.get('guildService').content.stickers.getStickers({userId, guildId, requestCache}));
|
||||
},
|
||||
);
|
||||
app.patch(
|
||||
@@ -159,7 +159,7 @@ export function GuildStickerController(app: HonoApp) {
|
||||
return ctx.json(
|
||||
await ctx
|
||||
.get('guildService')
|
||||
.content.updateSticker({userId, guildId, stickerId, name, description, tags}, auditLogReason),
|
||||
.content.stickers.updateSticker({userId, guildId, stickerId, name, description, tags}, auditLogReason),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -187,7 +187,7 @@ export function GuildStickerController(app: HonoApp) {
|
||||
const stickerId = createStickerID(sticker_id);
|
||||
const auditLogReason = ctx.get('auditLogReason') ?? null;
|
||||
const {purge = false} = ctx.req.valid('query');
|
||||
await ctx.get('guildService').content.deleteSticker({userId, guildId, stickerId, purge}, auditLogReason);
|
||||
await ctx.get('guildService').content.stickers.deleteSticker({userId, guildId, stickerId, purge}, auditLogReason);
|
||||
return ctx.body(null, 204);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
type GuildEmojiRow,
|
||||
type GuildStickerRow,
|
||||
} from '@app/api/database/types/GuildTypes';
|
||||
import {IGuildContentRepository} from '@app/api/guild/repositories/IGuildContentRepository';
|
||||
import {GuildEmoji} from '@app/api/models/GuildEmoji';
|
||||
import {GuildSticker} from '@app/api/models/GuildSticker';
|
||||
import {GuildEmojis, GuildEmojisByEmojiId, GuildStickers, GuildStickersByStickerId} from '@app/api/Tables';
|
||||
@@ -37,7 +36,7 @@ const FETCH_GUILD_STICKER_BY_STICKER_ID_ONLY_QUERY = GuildStickersByStickerId.se
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
export class GuildContentRepository extends IGuildContentRepository {
|
||||
export class GuildContentRepository {
|
||||
async getEmoji(emojiId: EmojiID, guildId: GuildID): Promise<GuildEmoji | null> {
|
||||
const emoji = await fetchOne<GuildEmojiRow>(FETCH_GUILD_EMOJI_BY_ID_QUERY, {
|
||||
guild_id: guildId,
|
||||
|
||||
@@ -4,7 +4,6 @@ import type {GuildID, UserID} from '@app/api/BrandedTypes';
|
||||
import {BatchBuilder, fetchMany, fetchOne} from '@app/api/database/CassandraQueryExecution';
|
||||
import {buildPatchFromData, executeVersionedUpdate} from '@app/api/database/CassandraVersionedUpdate';
|
||||
import {GUILD_COLUMNS, type GuildMemberByUserIdRow, type GuildRow} from '@app/api/database/types/GuildTypes';
|
||||
import {IGuildDataRepository} from '@app/api/guild/repositories/IGuildDataRepository';
|
||||
import type {RequestCache} from '@app/api/middleware/RequestCacheMiddleware';
|
||||
import {Guild} from '@app/api/models/Guild';
|
||||
import {GuildMembersByUserId, Guilds} from '@app/api/Tables';
|
||||
@@ -26,10 +25,8 @@ function createFetchAllGuildsFirstPageQuery(limit: number) {
|
||||
return Guilds.select({limit});
|
||||
}
|
||||
|
||||
export class GuildDataRepository extends IGuildDataRepository {
|
||||
constructor(private readonly requestCache?: RequestCache) {
|
||||
super();
|
||||
}
|
||||
export class GuildDataRepository {
|
||||
constructor(private readonly requestCache?: RequestCache) {}
|
||||
|
||||
async findUnique(guildId: GuildID): Promise<Guild | null> {
|
||||
const prefetched = this.requestCache?.takeGuild(guildId);
|
||||
|
||||
@@ -14,26 +14,7 @@ const FETCH_DISCOVERY_BY_STATUS = GuildDiscoveryByStatus.selectCql({
|
||||
where: GuildDiscoveryByStatus.where.eq('status'),
|
||||
});
|
||||
|
||||
export abstract class IGuildDiscoveryRepository {
|
||||
abstract findByGuildId(guildId: GuildID): Promise<GuildDiscoveryRow | null>;
|
||||
|
||||
abstract listByStatus(status: string): Promise<Array<GuildDiscoveryByStatusRow>>;
|
||||
|
||||
abstract listFullByStatus(status: string): Promise<Array<GuildDiscoveryRow>>;
|
||||
|
||||
abstract upsert(row: GuildDiscoveryRow): Promise<void>;
|
||||
|
||||
abstract deleteByGuildId(guildId: GuildID, status: string, appliedAt: Date): Promise<void>;
|
||||
|
||||
abstract updateStatus(
|
||||
guildId: GuildID,
|
||||
oldStatus: string,
|
||||
oldAppliedAt: Date,
|
||||
updatedRow: GuildDiscoveryRow,
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
export class GuildDiscoveryRepository extends IGuildDiscoveryRepository {
|
||||
export class GuildDiscoveryRepository {
|
||||
async findByGuildId(guildId: GuildID): Promise<GuildDiscoveryRow | null> {
|
||||
const row = await fetchOne<GuildDiscoveryRow>(FETCH_DISCOVERY_BY_GUILD_ID, {
|
||||
guild_id: guildId,
|
||||
|
||||
@@ -5,7 +5,6 @@ import {BatchBuilder, fetchMany, fetchOne, upsertOne} from '@app/api/database/Ca
|
||||
import {buildPatchFromData, executeVersionedUpdate} from '@app/api/database/CassandraVersionedUpdate';
|
||||
import type {GuildMemberRow, GuildMembershipMetadataRow} from '@app/api/database/types/GuildTypes';
|
||||
import {GUILD_MEMBER_COLUMNS} from '@app/api/database/types/GuildTypes';
|
||||
import {IGuildMemberRepository} from '@app/api/guild/repositories/IGuildMemberRepository';
|
||||
import {GuildMember} from '@app/api/models/GuildMember';
|
||||
import {GuildMembers, GuildMembersByUserId, GuildMembershipMetadata} from '@app/api/Tables';
|
||||
|
||||
@@ -38,7 +37,7 @@ function createPaginatedQuery(limit: number) {
|
||||
});
|
||||
}
|
||||
|
||||
export class GuildMemberRepository extends IGuildMemberRepository {
|
||||
export class GuildMemberRepository {
|
||||
async getMember(guildId: GuildID, userId: UserID): Promise<GuildMember | null> {
|
||||
const member = await fetchOne<GuildMemberRow>(FETCH_GUILD_MEMBER_BY_GUILD_AND_USER_ID_QUERY, {
|
||||
guild_id: guildId,
|
||||
|
||||
@@ -9,7 +9,6 @@ import type {
|
||||
GuildBanByUserIdRow,
|
||||
GuildBanRow,
|
||||
} from '@app/api/database/types/GuildTypes';
|
||||
import {IGuildModerationRepository} from '@app/api/guild/repositories/IGuildModerationRepository';
|
||||
import {GuildAuditLog} from '@app/api/models/GuildAuditLog';
|
||||
import {GuildBan} from '@app/api/models/GuildBan';
|
||||
import {
|
||||
@@ -47,7 +46,7 @@ const FETCH_GUILD_AUDIT_LOGS_BY_IDS_QUERY = GuildAuditLogs.selectCql({
|
||||
where: [GuildAuditLogs.where.eq('guild_id'), GuildAuditLogs.where.in('log_id', 'log_ids')],
|
||||
});
|
||||
|
||||
export class GuildModerationRepository extends IGuildModerationRepository {
|
||||
export class GuildModerationRepository {
|
||||
async getBan(guildId: GuildID, userId: UserID): Promise<GuildBan | null> {
|
||||
const ban = await fetchOne<GuildBanRow>(FETCH_GUILD_BAN_BY_GUILD_AND_USER_ID_QUERY, {
|
||||
guild_id: guildId,
|
||||
|
||||
@@ -18,7 +18,6 @@ import {GuildDataRepository} from '@app/api/guild/repositories/GuildDataReposito
|
||||
import {GuildMemberRepository} from '@app/api/guild/repositories/GuildMemberRepository';
|
||||
import {GuildModerationRepository} from '@app/api/guild/repositories/GuildModerationRepository';
|
||||
import {GuildRoleRepository} from '@app/api/guild/repositories/GuildRoleRepository';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {RequestCache} from '@app/api/middleware/RequestCacheMiddleware';
|
||||
import type {Guild} from '@app/api/models/Guild';
|
||||
import type {GuildAuditLog} from '@app/api/models/GuildAuditLog';
|
||||
@@ -29,7 +28,7 @@ import type {GuildRole} from '@app/api/models/GuildRole';
|
||||
import type {GuildSticker} from '@app/api/models/GuildSticker';
|
||||
import type {AuditLogActionType} from '@fluxer/constants/src/AuditLogActionType';
|
||||
|
||||
export class GuildRepository implements IGuildRepositoryAggregate {
|
||||
export class GuildRepository {
|
||||
private dataRepo: GuildDataRepository;
|
||||
private memberRepo: GuildMemberRepository;
|
||||
private roleRepo: GuildRoleRepository;
|
||||
|
||||
@@ -5,7 +5,6 @@ import {deleteOneOrMany, fetchMany, fetchOne} from '@app/api/database/CassandraQ
|
||||
import {buildPatchFromData, executeVersionedUpdate} from '@app/api/database/CassandraVersionedUpdate';
|
||||
import type {GuildRoleRow} from '@app/api/database/types/GuildTypes';
|
||||
import {GUILD_ROLE_COLUMNS} from '@app/api/database/types/GuildTypes';
|
||||
import {IGuildRoleRepository} from '@app/api/guild/repositories/IGuildRoleRepository';
|
||||
import {GuildRole} from '@app/api/models/GuildRole';
|
||||
import {GuildRoles} from '@app/api/Tables';
|
||||
|
||||
@@ -20,7 +19,7 @@ const FETCH_ROLES_BY_IDS_QUERY = GuildRoles.selectCql({
|
||||
where: [GuildRoles.where.eq('guild_id'), GuildRoles.where.in('role_id', 'role_ids')],
|
||||
});
|
||||
|
||||
export class GuildRoleRepository extends IGuildRoleRepository {
|
||||
export class GuildRoleRepository {
|
||||
async getRole(roleId: RoleID, guildId: GuildID): Promise<GuildRole | null> {
|
||||
const role = await fetchOne<GuildRoleRow>(FETCH_GUILD_ROLE_BY_ID_QUERY, {
|
||||
guild_id: guildId,
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {EmojiID, GuildID, StickerID} from '@app/api/BrandedTypes';
|
||||
import type {GuildEmojiRow, GuildStickerRow} from '@app/api/database/types/GuildTypes';
|
||||
import type {GuildEmoji} from '@app/api/models/GuildEmoji';
|
||||
import type {GuildSticker} from '@app/api/models/GuildSticker';
|
||||
|
||||
export abstract class IGuildContentRepository {
|
||||
abstract getEmoji(emojiId: EmojiID, guildId: GuildID): Promise<GuildEmoji | null>;
|
||||
|
||||
abstract getEmojiById(emojiId: EmojiID): Promise<GuildEmoji | null>;
|
||||
|
||||
abstract listEmojis(guildId: GuildID): Promise<Array<GuildEmoji>>;
|
||||
|
||||
abstract upsertEmoji(data: GuildEmojiRow): Promise<GuildEmoji>;
|
||||
|
||||
abstract deleteEmoji(guildId: GuildID, emojiId: EmojiID): Promise<void>;
|
||||
|
||||
abstract getSticker(stickerId: StickerID, guildId: GuildID): Promise<GuildSticker | null>;
|
||||
|
||||
abstract getStickerById(stickerId: StickerID): Promise<GuildSticker | null>;
|
||||
|
||||
abstract listStickers(guildId: GuildID): Promise<Array<GuildSticker>>;
|
||||
|
||||
abstract upsertSticker(data: GuildStickerRow): Promise<GuildSticker>;
|
||||
|
||||
abstract deleteSticker(guildId: GuildID, stickerId: StickerID): Promise<void>;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {GuildID, UserID} from '@app/api/BrandedTypes';
|
||||
import type {GuildRow} from '@app/api/database/types/GuildTypes';
|
||||
import type {Guild} from '@app/api/models/Guild';
|
||||
|
||||
export abstract class IGuildDataRepository {
|
||||
abstract findUnique(guildId: GuildID): Promise<Guild | null>;
|
||||
|
||||
abstract listGuilds(guildIds: Array<GuildID>): Promise<Array<Guild>>;
|
||||
|
||||
abstract listAllGuildsPaginated(limit: number, lastGuildId?: GuildID): Promise<Array<Guild>>;
|
||||
|
||||
abstract listUserGuilds(userId: UserID): Promise<Array<Guild>>;
|
||||
|
||||
abstract countUserGuilds(userId: UserID): Promise<number>;
|
||||
|
||||
abstract listOwnedGuildIds(userId: UserID): Promise<Array<GuildID>>;
|
||||
|
||||
abstract upsert(data: GuildRow, oldData?: GuildRow | null, previousOwnerId?: UserID): Promise<Guild>;
|
||||
|
||||
abstract upsertPartial(
|
||||
guildId: GuildID,
|
||||
patch: Partial<GuildRow>,
|
||||
oldData?: GuildRow | null,
|
||||
previousOwnerId?: UserID,
|
||||
): Promise<Guild>;
|
||||
|
||||
abstract delete(guildId: GuildID, ownerId?: UserID): Promise<void>;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {GuildID, UserID} from '@app/api/BrandedTypes';
|
||||
import type {GuildMemberRow, GuildMembershipMetadataRow} from '@app/api/database/types/GuildTypes';
|
||||
import type {GuildMember} from '@app/api/models/GuildMember';
|
||||
|
||||
export abstract class IGuildMemberRepository {
|
||||
abstract getMember(guildId: GuildID, userId: UserID): Promise<GuildMember | null>;
|
||||
|
||||
abstract listMembers(guildId: GuildID): Promise<Array<GuildMember>>;
|
||||
|
||||
abstract countMembers(guildId: GuildID): Promise<number>;
|
||||
|
||||
abstract upsertMember(data: GuildMemberRow): Promise<GuildMember>;
|
||||
|
||||
abstract deleteMember(guildId: GuildID, userId: UserID): Promise<void>;
|
||||
|
||||
abstract listMembersPaginated(guildId: GuildID, limit: number, afterUserId?: UserID): Promise<Array<GuildMember>>;
|
||||
|
||||
abstract getMembershipMetadata(guildId: GuildID, userId: UserID): Promise<GuildMembershipMetadataRow | null>;
|
||||
|
||||
abstract upsertMembershipMetadata(data: GuildMembershipMetadataRow, ttlSeconds: number): Promise<void>;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {GuildID, UserID} from '@app/api/BrandedTypes';
|
||||
import type {GuildAuditLogRow, GuildBanRow} from '@app/api/database/types/GuildTypes';
|
||||
import type {GuildAuditLog} from '@app/api/models/GuildAuditLog';
|
||||
import type {GuildBan} from '@app/api/models/GuildBan';
|
||||
import type {AuditLogActionType} from '@fluxer/constants/src/AuditLogActionType';
|
||||
|
||||
export abstract class IGuildModerationRepository {
|
||||
abstract getBan(guildId: GuildID, userId: UserID): Promise<GuildBan | null>;
|
||||
|
||||
abstract listBans(guildId: GuildID): Promise<Array<GuildBan>>;
|
||||
|
||||
abstract upsertBan(data: GuildBanRow): Promise<GuildBan>;
|
||||
|
||||
abstract deleteBan(guildId: GuildID, userId: UserID): Promise<void>;
|
||||
|
||||
abstract deleteAllBansForUser(userId: UserID): Promise<void>;
|
||||
|
||||
abstract getBanByEmail(guildId: GuildID, email: string): Promise<GuildBan | null>;
|
||||
|
||||
abstract createAuditLog(data: GuildAuditLogRow): Promise<GuildAuditLog>;
|
||||
|
||||
abstract batchDeleteAndCreateAuditLogs(
|
||||
guildId: GuildID,
|
||||
logsToDelete: Array<GuildAuditLog>,
|
||||
logToCreate: GuildAuditLogRow,
|
||||
): Promise<GuildAuditLog>;
|
||||
|
||||
abstract getAuditLog(guildId: GuildID, logId: bigint): Promise<GuildAuditLog | null>;
|
||||
|
||||
abstract listAuditLogs(params: {
|
||||
guildId: GuildID;
|
||||
limit: number;
|
||||
afterLogId?: bigint;
|
||||
beforeLogId?: bigint;
|
||||
userId?: UserID;
|
||||
actionType?: AuditLogActionType;
|
||||
}): Promise<Array<GuildAuditLog>>;
|
||||
|
||||
abstract listAuditLogsByIds(guildId: GuildID, logIds: Array<bigint>): Promise<Array<GuildAuditLog>>;
|
||||
|
||||
abstract deleteAuditLogs(guildId: GuildID, logs: Array<GuildAuditLog>): Promise<void>;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {IGuildContentRepository} from '@app/api/guild/repositories/IGuildContentRepository';
|
||||
import type {IGuildDataRepository} from '@app/api/guild/repositories/IGuildDataRepository';
|
||||
import type {IGuildMemberRepository} from '@app/api/guild/repositories/IGuildMemberRepository';
|
||||
import type {IGuildModerationRepository} from '@app/api/guild/repositories/IGuildModerationRepository';
|
||||
import type {IGuildRoleRepository} from '@app/api/guild/repositories/IGuildRoleRepository';
|
||||
|
||||
export interface IGuildRepositoryAggregate
|
||||
extends IGuildDataRepository,
|
||||
IGuildMemberRepository,
|
||||
IGuildRoleRepository,
|
||||
IGuildModerationRepository,
|
||||
IGuildContentRepository {}
|
||||
@@ -1,19 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {GuildID, RoleID} from '@app/api/BrandedTypes';
|
||||
import type {GuildRoleRow} from '@app/api/database/types/GuildTypes';
|
||||
import type {GuildRole} from '@app/api/models/GuildRole';
|
||||
|
||||
export abstract class IGuildRoleRepository {
|
||||
abstract getRole(roleId: RoleID, guildId: GuildID): Promise<GuildRole | null>;
|
||||
|
||||
abstract listRoles(guildId: GuildID): Promise<Array<GuildRole>>;
|
||||
|
||||
abstract listRolesByIds(roleIds: Array<RoleID>, guildId: GuildID): Promise<Array<GuildRole>>;
|
||||
|
||||
abstract countRoles(guildId: GuildID): Promise<number>;
|
||||
|
||||
abstract upsertRole(data: GuildRoleRow, oldData?: GuildRoleRow | null): Promise<GuildRole>;
|
||||
|
||||
abstract deleteRole(guildId: GuildID, roleId: RoleID): Promise<void>;
|
||||
}
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
import type {ChannelID, GuildID, UserID} from '@app/api/BrandedTypes';
|
||||
import {mapChannelToResponse} from '@app/api/channel/ChannelMappers';
|
||||
import type {IChannelRepository} from '@app/api/channel/IChannelRepository';
|
||||
import type {ChannelRepository} from '@app/api/channel/ChannelRepository';
|
||||
import {withThreadParentFieldsMany} from '@app/api/channel/services/thread/ThreadParentSettings';
|
||||
import {SYSTEM_THREAD_VIEWER, type ThreadViewer, viewerActive} from '@app/api/experiment/ChannelThreadsGate';
|
||||
import type {GuildAuditLogService} from '@app/api/guild/GuildAuditLogService';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import {ChannelOperationsService} from '@app/api/guild/services/channel/ChannelOperationsService';
|
||||
import {createGuildMfaEnforcer} from '@app/api/guild/services/GuildMfaEnforcement';
|
||||
import {maskChannelResponseThreadBits} from '@app/api/guild/services/ThreadPermissionBits';
|
||||
@@ -15,7 +15,7 @@ import type {ISnowflakeService} from '@app/api/infrastructure/ISnowflakeService'
|
||||
import type {UserCacheService} from '@app/api/infrastructure/UserCacheService';
|
||||
import type {LimitConfigService} from '@app/api/limits/LimitConfigService';
|
||||
import type {RequestCache} from '@app/api/middleware/RequestCacheMiddleware';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import {Permissions} from '@fluxer/constants/src/ChannelConstants';
|
||||
import {MissingPermissionsError} from '@fluxer/errors/src/domains/core/MissingPermissionsError';
|
||||
import {UnknownGuildError} from '@fluxer/errors/src/domains/guild/UnknownGuildError';
|
||||
@@ -27,15 +27,15 @@ export class GuildChannelService {
|
||||
private readonly channelOps: ChannelOperationsService;
|
||||
|
||||
constructor(
|
||||
private readonly channelRepository: IChannelRepository,
|
||||
guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly channelRepository: ChannelRepository,
|
||||
guildRepository: GuildRepository,
|
||||
private readonly userCacheService: UserCacheService,
|
||||
private readonly gatewayService: IGatewayService,
|
||||
cacheService: ICacheService,
|
||||
snowflakeService: ISnowflakeService,
|
||||
guildAuditLogService: GuildAuditLogService,
|
||||
limitConfigService: LimitConfigService,
|
||||
private readonly userRepository: IUserRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
) {
|
||||
this.channelOps = new ChannelOperationsService(
|
||||
channelRepository,
|
||||
@@ -67,7 +67,7 @@ export class GuildChannelService {
|
||||
guildId: params.guildId,
|
||||
userId: params.userId,
|
||||
});
|
||||
const channels = await this.channelRepository.listGuildChannels(params.guildId, 'enrolled');
|
||||
const channels = await this.channelRepository.channelData.listGuildChannels(params.guildId, 'enrolled');
|
||||
const viewer = params.viewer;
|
||||
const viewableChannels = channels.filter(
|
||||
(channel) =>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {EmojiID, GuildID, StickerID, UserID} from '@app/api/BrandedTypes';
|
||||
import type {GuildAuditLogService} from '@app/api/guild/GuildAuditLogService';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import {ContentHelpers} from '@app/api/guild/services/content/ContentHelpers';
|
||||
import {EmojiService} from '@app/api/guild/services/content/EmojiService';
|
||||
import {ExpressionAssetPurger} from '@app/api/guild/services/content/ExpressionAssetPurger';
|
||||
@@ -13,22 +12,14 @@ import type {IGatewayService} from '@app/api/infrastructure/IGatewayService';
|
||||
import type {ISnowflakeService} from '@app/api/infrastructure/ISnowflakeService';
|
||||
import type {UserCacheService} from '@app/api/infrastructure/UserCacheService';
|
||||
import type {LimitConfigService} from '@app/api/limits/LimitConfigService';
|
||||
import type {RequestCache} from '@app/api/middleware/RequestCacheMiddleware';
|
||||
import type {User} from '@app/api/models/User';
|
||||
import type {
|
||||
GuildEmojiResponse,
|
||||
GuildEmojiWithUserResponse,
|
||||
GuildStickerResponse,
|
||||
GuildStickerWithUserResponse,
|
||||
} from '@fluxer/schema/src/domains/guild/GuildEmojiSchemas';
|
||||
|
||||
export class GuildContentService {
|
||||
private readonly contentHelpers: ContentHelpers;
|
||||
private readonly emojiService: EmojiService;
|
||||
private readonly stickerService: StickerService;
|
||||
readonly emojis: EmojiService;
|
||||
readonly stickers: StickerService;
|
||||
|
||||
constructor(
|
||||
guildRepository: IGuildRepositoryAggregate,
|
||||
guildRepository: GuildRepository,
|
||||
userCacheService: UserCacheService,
|
||||
gatewayService: IGatewayService,
|
||||
avatarService: AvatarService,
|
||||
@@ -39,7 +30,7 @@ export class GuildContentService {
|
||||
) {
|
||||
this.contentHelpers = new ContentHelpers(gatewayService, guildAuditLogService);
|
||||
const expressionAssetPurger = new ExpressionAssetPurger(assetDeletionQueue);
|
||||
this.emojiService = new EmojiService(
|
||||
this.emojis = new EmojiService(
|
||||
guildRepository,
|
||||
userCacheService,
|
||||
gatewayService,
|
||||
@@ -49,7 +40,7 @@ export class GuildContentService {
|
||||
expressionAssetPurger,
|
||||
limitConfigService,
|
||||
);
|
||||
this.stickerService = new StickerService(
|
||||
this.stickers = new StickerService(
|
||||
guildRepository,
|
||||
userCacheService,
|
||||
gatewayService,
|
||||
@@ -60,160 +51,4 @@ export class GuildContentService {
|
||||
limitConfigService,
|
||||
);
|
||||
}
|
||||
|
||||
async getEmojis(params: {
|
||||
userId: UserID;
|
||||
guildId: GuildID;
|
||||
requestCache: RequestCache;
|
||||
}): Promise<Array<GuildEmojiWithUserResponse>> {
|
||||
return this.emojiService.getEmojis(params);
|
||||
}
|
||||
|
||||
async createEmoji(
|
||||
params: {
|
||||
user: User;
|
||||
guildId: GuildID;
|
||||
name: string;
|
||||
image: string;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<GuildEmojiResponse> {
|
||||
return this.emojiService.createEmoji(params, auditLogReason);
|
||||
}
|
||||
|
||||
async cloneEmoji(
|
||||
params: {
|
||||
user: User;
|
||||
guildId: GuildID;
|
||||
sourceEmojiId: EmojiID;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<GuildEmojiResponse> {
|
||||
return this.emojiService.cloneEmoji(params, auditLogReason);
|
||||
}
|
||||
|
||||
async bulkCreateEmojis(
|
||||
params: {
|
||||
user: User;
|
||||
guildId: GuildID;
|
||||
emojis: Array<{
|
||||
name: string;
|
||||
image: string;
|
||||
}>;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<{
|
||||
success: Array<GuildEmojiResponse>;
|
||||
failed: Array<{
|
||||
name: string;
|
||||
error: string;
|
||||
}>;
|
||||
}> {
|
||||
return this.emojiService.bulkCreateEmojis(params, auditLogReason);
|
||||
}
|
||||
|
||||
async updateEmoji(
|
||||
params: {
|
||||
userId: UserID;
|
||||
guildId: GuildID;
|
||||
emojiId: EmojiID;
|
||||
name: string;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<GuildEmojiResponse> {
|
||||
return this.emojiService.updateEmoji(params, auditLogReason);
|
||||
}
|
||||
|
||||
async deleteEmoji(
|
||||
params: {
|
||||
userId: UserID;
|
||||
guildId: GuildID;
|
||||
emojiId: EmojiID;
|
||||
purge?: boolean;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<void> {
|
||||
return this.emojiService.deleteEmoji(params, auditLogReason);
|
||||
}
|
||||
|
||||
async getStickers(params: {
|
||||
userId: UserID;
|
||||
guildId: GuildID;
|
||||
requestCache: RequestCache;
|
||||
}): Promise<Array<GuildStickerWithUserResponse>> {
|
||||
return this.stickerService.getStickers(params);
|
||||
}
|
||||
|
||||
async createSticker(
|
||||
params: {
|
||||
user: User;
|
||||
guildId: GuildID;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
tags: Array<string>;
|
||||
image: string;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<GuildStickerResponse> {
|
||||
return this.stickerService.createSticker(params, auditLogReason);
|
||||
}
|
||||
|
||||
async cloneSticker(
|
||||
params: {
|
||||
user: User;
|
||||
guildId: GuildID;
|
||||
sourceStickerId: StickerID;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<GuildStickerResponse> {
|
||||
return this.stickerService.cloneSticker(params, auditLogReason);
|
||||
}
|
||||
|
||||
async bulkCreateStickers(
|
||||
params: {
|
||||
user: User;
|
||||
guildId: GuildID;
|
||||
stickers: Array<{
|
||||
name: string;
|
||||
description?: string | null;
|
||||
tags: Array<string>;
|
||||
image: string;
|
||||
}>;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<{
|
||||
success: Array<GuildStickerResponse>;
|
||||
failed: Array<{
|
||||
name: string;
|
||||
error: string;
|
||||
}>;
|
||||
}> {
|
||||
return this.stickerService.bulkCreateStickers(params, auditLogReason);
|
||||
}
|
||||
|
||||
async updateSticker(
|
||||
params: {
|
||||
userId: UserID;
|
||||
guildId: GuildID;
|
||||
stickerId: StickerID;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
tags: Array<string>;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<GuildStickerResponse> {
|
||||
return this.stickerService.updateSticker(params, auditLogReason);
|
||||
}
|
||||
|
||||
async deleteSticker(
|
||||
params: {
|
||||
userId: UserID;
|
||||
guildId: GuildID;
|
||||
stickerId: StickerID;
|
||||
purge?: boolean;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<void> {
|
||||
return this.stickerService.deleteSticker(params, auditLogReason);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {GuildID, UserID} from '@app/api/BrandedTypes';
|
||||
import type {IChannelRepository} from '@app/api/channel/IChannelRepository';
|
||||
import type {ChannelRepository} from '@app/api/channel/ChannelRepository';
|
||||
import type {ChannelService} from '@app/api/channel/services/ChannelService';
|
||||
import type {GuildAuditLogService} from '@app/api/guild/GuildAuditLogService';
|
||||
import {GuildDiscoveryRepository} from '@app/api/guild/repositories/GuildDiscoveryRepository';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import {GuildDataHelpers} from '@app/api/guild/services/data/GuildDataHelpers';
|
||||
import {GuildOperationsService} from '@app/api/guild/services/data/GuildOperationsService';
|
||||
import {GuildOwnershipService} from '@app/api/guild/services/data/GuildOwnershipService';
|
||||
@@ -15,43 +14,30 @@ import type {IGatewayService} from '@app/api/infrastructure/IGatewayService';
|
||||
import type {ISnowflakeService} from '@app/api/infrastructure/ISnowflakeService';
|
||||
import type {InviteRepository} from '@app/api/invite/InviteRepository';
|
||||
import type {LimitConfigService} from '@app/api/limits/LimitConfigService';
|
||||
import type {RequestCache} from '@app/api/middleware/RequestCacheMiddleware';
|
||||
import type {Guild} from '@app/api/models/Guild';
|
||||
import type {User} from '@app/api/models/User';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {IWebhookRepository} from '@app/api/webhook/IWebhookRepository';
|
||||
import type {
|
||||
GuildCreateRequest,
|
||||
GuildUpdateRequest,
|
||||
GuildVanityURLUpdateResponse,
|
||||
} from '@fluxer/schema/src/domains/guild/GuildRequestSchemas';
|
||||
import type {
|
||||
GuildPartialResponse,
|
||||
GuildResponse,
|
||||
GuildVanityURLResponse,
|
||||
} from '@fluxer/schema/src/domains/guild/GuildResponseSchemas';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import type {WebhookRepository} from '@app/api/webhook/WebhookRepository';
|
||||
|
||||
export class GuildDataService {
|
||||
private readonly helpers: GuildDataHelpers;
|
||||
private readonly operationsService: GuildOperationsService;
|
||||
private readonly vanityService: GuildVanityService;
|
||||
private readonly ownershipService: GuildOwnershipService;
|
||||
readonly operations: GuildOperationsService;
|
||||
readonly vanity: GuildVanityService;
|
||||
readonly ownership: GuildOwnershipService;
|
||||
|
||||
constructor(
|
||||
private readonly guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly channelRepository: IChannelRepository,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
private readonly channelRepository: ChannelRepository,
|
||||
private readonly inviteRepository: InviteRepository,
|
||||
private readonly channelService: ChannelService,
|
||||
private readonly gatewayService: IGatewayService,
|
||||
private readonly entityAssetService: EntityAssetService,
|
||||
private readonly userRepository: IUserRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
private readonly snowflakeService: ISnowflakeService,
|
||||
private readonly webhookRepository: IWebhookRepository,
|
||||
private readonly webhookRepository: WebhookRepository,
|
||||
private readonly guildAuditLogService: GuildAuditLogService,
|
||||
private readonly limitConfigService: LimitConfigService,
|
||||
) {
|
||||
this.helpers = new GuildDataHelpers(this.gatewayService, this.guildAuditLogService, this.userRepository);
|
||||
this.operationsService = new GuildOperationsService(
|
||||
this.operations = new GuildOperationsService(
|
||||
this.guildRepository,
|
||||
this.channelRepository,
|
||||
this.inviteRepository,
|
||||
@@ -65,99 +51,7 @@ export class GuildDataService {
|
||||
this.limitConfigService,
|
||||
new GuildDiscoveryRepository(),
|
||||
);
|
||||
this.vanityService = new GuildVanityService(this.guildRepository, this.inviteRepository, this.helpers);
|
||||
this.ownershipService = new GuildOwnershipService(this.guildRepository, this.userRepository, this.helpers);
|
||||
}
|
||||
|
||||
async getGuild({userId, guildId}: {userId: UserID; guildId: GuildID}): Promise<GuildResponse> {
|
||||
return this.operationsService.getGuild({userId, guildId});
|
||||
}
|
||||
|
||||
async getUserGuilds(
|
||||
userId: UserID,
|
||||
options?: {
|
||||
before?: GuildID;
|
||||
after?: GuildID;
|
||||
limit?: number;
|
||||
withCounts?: boolean;
|
||||
},
|
||||
): Promise<Array<GuildResponse>> {
|
||||
return this.operationsService.getUserGuilds(userId, options);
|
||||
}
|
||||
|
||||
async getPublicGuildData(guildId: GuildID): Promise<GuildPartialResponse> {
|
||||
return this.operationsService.getPublicGuildData(guildId);
|
||||
}
|
||||
|
||||
async getGuildSystem(guildId: GuildID): Promise<Guild> {
|
||||
return this.operationsService.getGuildSystem(guildId);
|
||||
}
|
||||
|
||||
async createGuild(
|
||||
params: {
|
||||
user: User;
|
||||
data: GuildCreateRequest;
|
||||
locale?: string | null;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<GuildResponse> {
|
||||
return this.operationsService.createGuild(params, auditLogReason);
|
||||
}
|
||||
|
||||
async updateGuild(
|
||||
params: {
|
||||
userId: UserID;
|
||||
guildId: GuildID;
|
||||
data: GuildUpdateRequest;
|
||||
requestCache: RequestCache;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<{
|
||||
guild: GuildResponse;
|
||||
previousFeatures: Set<string>;
|
||||
updatedFeatures: Set<string>;
|
||||
}> {
|
||||
return this.operationsService.updateGuild(params, auditLogReason);
|
||||
}
|
||||
|
||||
async getVanityURL(params: {userId: UserID; guildId: GuildID}): Promise<GuildVanityURLResponse> {
|
||||
return this.vanityService.getVanityURL(params);
|
||||
}
|
||||
|
||||
async updateVanityURL(
|
||||
params: {
|
||||
userId: UserID;
|
||||
guildId: GuildID;
|
||||
code: string | null;
|
||||
requestCache: RequestCache;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<GuildVanityURLUpdateResponse> {
|
||||
return this.vanityService.updateVanityURL(params, auditLogReason);
|
||||
}
|
||||
|
||||
async deleteGuild(
|
||||
params: {
|
||||
user: User;
|
||||
guildId: GuildID;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<void> {
|
||||
return this.operationsService.deleteGuild(params, auditLogReason);
|
||||
}
|
||||
|
||||
async deleteGuildForAdmin(guildId: GuildID, _auditLogReason?: string | null): Promise<void> {
|
||||
return this.operationsService.deleteGuildById(guildId);
|
||||
}
|
||||
|
||||
async transferOwnership(
|
||||
params: {
|
||||
userId: UserID;
|
||||
guildId: GuildID;
|
||||
newOwnerId: UserID;
|
||||
},
|
||||
auditLogReason?: string | null,
|
||||
): Promise<GuildResponse> {
|
||||
return this.ownershipService.transferOwnership(params, auditLogReason);
|
||||
this.vanity = new GuildVanityService(this.guildRepository, this.inviteRepository, this.helpers);
|
||||
this.ownership = new GuildOwnershipService(this.guildRepository, this.userRepository, this.helpers);
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
import {type ChannelID, type GuildID, guildIdToRoleId, type UserID} from '@app/api/BrandedTypes';
|
||||
import {Config} from '@app/api/Config';
|
||||
import type {IChannelDataRepository} from '@app/api/channel/repositories/IChannelDataRepository';
|
||||
import type {ChannelDataRepository} from '@app/api/channel/repositories/ChannelDataRepository';
|
||||
import type {GuildDiscoveryRow} from '@app/api/database/types/GuildDiscoveryTypes';
|
||||
import {type ThreadViewer, viewerActive} from '@app/api/experiment/ChannelThreadsGate';
|
||||
import {mapGuildToGuildResponse} from '@app/api/guild/GuildModel';
|
||||
import type {IGuildDiscoveryRepository} from '@app/api/guild/repositories/GuildDiscoveryRepository';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildDiscoveryRepository} from '@app/api/guild/repositories/GuildDiscoveryRepository';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import {contentModerationService} from '@app/api/infrastructure/ContentModerationService';
|
||||
import type {IGatewayService} from '@app/api/infrastructure/IGatewayService';
|
||||
import {Logger} from '@app/api/Logger';
|
||||
@@ -54,60 +54,6 @@ function resolveLanguage(language: string | null | undefined, fallback: string |
|
||||
return DISCOVERY_DEFAULT_LANGUAGE;
|
||||
}
|
||||
|
||||
export abstract class IGuildDiscoveryService {
|
||||
abstract apply(params: {
|
||||
guildId: GuildID;
|
||||
userId: UserID;
|
||||
description: string;
|
||||
categoryId: number;
|
||||
primaryLanguage?: string;
|
||||
customTags?: ReadonlyArray<string>;
|
||||
}): Promise<GuildDiscoveryRow>;
|
||||
|
||||
abstract editApplication(params: {
|
||||
guildId: GuildID;
|
||||
userId: UserID;
|
||||
data: DiscoveryApplicationPatchRequest;
|
||||
}): Promise<GuildDiscoveryRow>;
|
||||
|
||||
abstract withdraw(params: {guildId: GuildID; userId: UserID}): Promise<void>;
|
||||
|
||||
abstract getStatus(guildId: GuildID): Promise<GuildDiscoveryRow | null>;
|
||||
|
||||
abstract approve(params: {guildId: GuildID; adminUserId: UserID; reason?: string}): Promise<GuildDiscoveryRow>;
|
||||
|
||||
abstract reject(params: {guildId: GuildID; adminUserId: UserID; reason: string}): Promise<GuildDiscoveryRow>;
|
||||
|
||||
abstract remove(params: {guildId: GuildID; adminUserId: UserID; reason: string}): Promise<GuildDiscoveryRow>;
|
||||
|
||||
abstract getEligibility(guildId: GuildID): Promise<{
|
||||
eligible: boolean;
|
||||
min_member_count: number;
|
||||
}>;
|
||||
|
||||
abstract listByStatus(params: {status: string}): Promise<Array<GuildDiscoveryRow>>;
|
||||
|
||||
abstract getChannelPreview(
|
||||
guildId: GuildID,
|
||||
channelId: ChannelID,
|
||||
viewer: ThreadViewer,
|
||||
): Promise<DiscoveryChannelPreview>;
|
||||
|
||||
abstract searchDiscoverable(params: {
|
||||
query?: string;
|
||||
categoryId?: number;
|
||||
primaryLanguage?: string;
|
||||
tag?: string;
|
||||
sortBy?: string;
|
||||
limit: number;
|
||||
offset: number;
|
||||
}): Promise<{
|
||||
guilds: Array<DiscoveryGuildResult>;
|
||||
total: number;
|
||||
category_counts: Array<DiscoveryCategoryCount>;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface DiscoveryCategoryCount {
|
||||
category_type: number;
|
||||
count: number;
|
||||
@@ -153,16 +99,14 @@ function toDiscoveryCategoryCounts(
|
||||
return entries.sort((left, right) => left.category_type - right.category_type);
|
||||
}
|
||||
|
||||
export class GuildDiscoveryService extends IGuildDiscoveryService {
|
||||
export class GuildDiscoveryService {
|
||||
constructor(
|
||||
private readonly discoveryRepository: IGuildDiscoveryRepository,
|
||||
private readonly guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly discoveryRepository: GuildDiscoveryRepository,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
private readonly gatewayService: IGatewayService,
|
||||
private readonly guildSearchService: IGuildSearchService | null,
|
||||
private readonly channelDataRepository: IChannelDataRepository,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
private readonly channelDataRepository: ChannelDataRepository,
|
||||
) {}
|
||||
|
||||
async apply(params: {
|
||||
guildId: GuildID;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import type {GuildID, InviteCode, RoleID, UserID} from '@app/api/BrandedTypes';
|
||||
import type {ChannelService} from '@app/api/channel/services/ChannelService';
|
||||
import type {GuildAuditLogService} from '@app/api/guild/GuildAuditLogService';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import {GuildMemberAuditService} from '@app/api/guild/services/member/GuildMemberAuditService';
|
||||
import {GuildMemberAuthService} from '@app/api/guild/services/member/GuildMemberAuthService';
|
||||
import {GuildMemberEventService} from '@app/api/guild/services/member/GuildMemberEventService';
|
||||
@@ -18,7 +18,7 @@ import type {LimitConfigService} from '@app/api/limits/LimitConfigService';
|
||||
import type {RequestCache} from '@app/api/middleware/RequestCacheMiddleware';
|
||||
import type {Guild} from '@app/api/models/Guild';
|
||||
import type {GuildMember} from '@app/api/models/GuildMember';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import {AuditLogActionType} from '@fluxer/constants/src/AuditLogActionType';
|
||||
import type {JoinSourceType} from '@fluxer/constants/src/GuildConstants';
|
||||
import {UnknownGuildMemberError} from '@fluxer/errors/src/domains/guild/UnknownGuildMemberError';
|
||||
@@ -34,15 +34,15 @@ export class GuildMemberService {
|
||||
private readonly operationsService: GuildMemberOperationsService;
|
||||
private readonly roleService: GuildMemberRoleService;
|
||||
private readonly searchIndexService: GuildMemberSearchIndexService;
|
||||
private readonly userRepository: IUserRepository;
|
||||
private readonly userRepository: UserRepository;
|
||||
|
||||
constructor(
|
||||
private readonly guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
channelService: ChannelService,
|
||||
userCacheService: UserCacheService,
|
||||
gatewayService: IGatewayService,
|
||||
entityAssetService: EntityAssetService,
|
||||
userRepository: IUserRepository,
|
||||
userRepository: UserRepository,
|
||||
rateLimitService: IRateLimitService,
|
||||
private readonly guildAuditLogService: GuildAuditLogService,
|
||||
limitConfigService: LimitConfigService,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {UserID} from '@app/api/BrandedTypes';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import {Permissions} from '@fluxer/constants/src/ChannelConstants';
|
||||
import {GuildMFALevel} from '@fluxer/constants/src/GuildConstants';
|
||||
import {ThreadPermissionFlags} from '@fluxer/constants/src/ThreadPermissionUtils';
|
||||
@@ -21,7 +21,7 @@ const ELEVATED_MFA_PERMISSIONS =
|
||||
ThreadPermissionFlags.MANAGE_THREADS;
|
||||
|
||||
export async function createGuildMfaEnforcer(params: {
|
||||
userRepository: IUserRepository;
|
||||
userRepository: UserRepository;
|
||||
guildData: Pick<GuildResponse, 'mfa_level' | 'owner_id'>;
|
||||
userId: UserID;
|
||||
}): Promise<(permission: bigint) => void> {
|
||||
|
||||
@@ -5,7 +5,7 @@ import {isIpBanExempt} from '@app/api/ban/IpBanExemptions';
|
||||
import type {GuildAuditLogService} from '@app/api/guild/GuildAuditLogService';
|
||||
import type {GuildAuditLogChange} from '@app/api/guild/GuildAuditLogTypes';
|
||||
import {mapGuildBansToResponse} from '@app/api/guild/GuildModel';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import {createGuildMfaEnforcer} from '@app/api/guild/services/GuildMfaEnforcement';
|
||||
import {GuildMemberSearchIndexService} from '@app/api/guild/services/member/GuildMemberSearchIndexService';
|
||||
import type {BanBy} from '@app/api/infrastructure/activity/Contract.generated';
|
||||
@@ -16,7 +16,7 @@ import {Logger} from '@app/api/Logger';
|
||||
import type {RequestCache} from '@app/api/middleware/RequestCacheMiddleware';
|
||||
import type {Guild} from '@app/api/models/Guild';
|
||||
import type {GuildBan} from '@app/api/models/GuildBan';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import type {WorkerTaskName} from '@app/api/worker/WorkerLaneConfig';
|
||||
import {AuditLogActionType} from '@fluxer/constants/src/AuditLogActionType';
|
||||
import {Permissions} from '@fluxer/constants/src/ChannelConstants';
|
||||
@@ -39,8 +39,8 @@ export class GuildModerationService {
|
||||
private readonly searchIndexService: GuildMemberSearchIndexService;
|
||||
|
||||
constructor(
|
||||
private readonly guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly userRepository: IUserRepository,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
private readonly gatewayService: IGatewayService,
|
||||
private readonly userCacheService: UserCacheService,
|
||||
private readonly workerService: IWorkerService<WorkerTaskName>,
|
||||
|
||||
@@ -6,8 +6,7 @@ import type {ThreadViewer} from '@app/api/experiment/ChannelThreadsGate';
|
||||
import type {GuildAuditLogService} from '@app/api/guild/GuildAuditLogService';
|
||||
import type {GuildAuditLogChange} from '@app/api/guild/GuildAuditLogTypes';
|
||||
import {mapGuildRoleToResponse} from '@app/api/guild/GuildModel';
|
||||
import type {IGuildMemberRepository} from '@app/api/guild/repositories/IGuildMemberRepository';
|
||||
import type {IGuildRoleRepository} from '@app/api/guild/repositories/IGuildRoleRepository';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import {createGuildMfaEnforcer} from '@app/api/guild/services/GuildMfaEnforcement';
|
||||
import {computeMovedIds, getMemberListRoleOrderIds} from '@app/api/guild/services/role/RoleOrderAuditUtils';
|
||||
import {
|
||||
@@ -23,7 +22,7 @@ import type {LimitConfigService} from '@app/api/limits/LimitConfigService';
|
||||
import {resolveLimitSafe} from '@app/api/limits/LimitConfigUtils';
|
||||
import {createLimitMatchContext} from '@app/api/limits/LimitMatchContextBuilder';
|
||||
import {GuildRole} from '@app/api/models/GuildRole';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import {applyProtectedRolePermissions, permissionWriteMask, protectedThreadBits} from '@app/api/utils/featureUtils';
|
||||
import {computePermissionsDiff} from '@app/api/utils/PermissionUtils';
|
||||
import {AuditLogActionType} from '@fluxer/constants/src/AuditLogActionType';
|
||||
@@ -45,8 +44,6 @@ import type {GuildResponse} from '@fluxer/schema/src/domains/guild/GuildResponse
|
||||
import type {GuildRoleResponse} from '@fluxer/schema/src/domains/guild/GuildRoleSchemas';
|
||||
import type {ICacheService} from '@pkgs/cache/src/ICacheService';
|
||||
|
||||
interface GuildRoleRepository extends IGuildRoleRepository, IGuildMemberRepository {}
|
||||
|
||||
interface GuildAuth {
|
||||
guildData: GuildResponse;
|
||||
checkPermission: (permission: bigint) => Promise<void>;
|
||||
@@ -67,13 +64,13 @@ type RoleUpdateData = Partial<{
|
||||
|
||||
export class GuildRoleService {
|
||||
constructor(
|
||||
private readonly guildRepository: GuildRoleRepository,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
private readonly snowflakeService: ISnowflakeService,
|
||||
private readonly cacheService: ICacheService,
|
||||
private readonly gatewayService: IGatewayService,
|
||||
private readonly guildAuditLogService: GuildAuditLogService,
|
||||
private readonly limitConfigService: LimitConfigService,
|
||||
private readonly userRepository: IUserRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
) {}
|
||||
|
||||
async systemCreateRole(params: {
|
||||
@@ -270,7 +267,7 @@ export class GuildRoleService {
|
||||
hoist: updateData.hoist ?? role.isHoisted,
|
||||
mentionable: updateData.mentionable ?? role.isMentionable,
|
||||
};
|
||||
const updatedRole = await this.guildRepository.upsertRole(updatedRoleData, role.toRow());
|
||||
const updatedRole = await this.guildRepository.upsertRole(updatedRoleData);
|
||||
await this.dispatchGuildRoleUpdate({guildId, role: updatedRole});
|
||||
const changes = this.guildAuditLogService.computeChanges(previousSnapshot, this.serializeRoleForAudit(updatedRole));
|
||||
if (role.permissions !== updatedRole.permissions) {
|
||||
@@ -449,14 +446,10 @@ export class GuildRoleService {
|
||||
for (const update of updates) {
|
||||
const role = roleMap.get(update.roleId)!;
|
||||
if (role.hoistPosition === update.hoistPosition) continue;
|
||||
const roleRow = role.toRow();
|
||||
const updatedRole = await this.guildRepository.upsertRole(
|
||||
{
|
||||
...roleRow,
|
||||
hoist_position: update.hoistPosition,
|
||||
},
|
||||
roleRow,
|
||||
);
|
||||
const updatedRole = await this.guildRepository.upsertRole({
|
||||
...role.toRow(),
|
||||
hoist_position: update.hoistPosition,
|
||||
});
|
||||
changedRoles.push(updatedRole);
|
||||
}
|
||||
if (changedRoles.length > 0) {
|
||||
@@ -497,14 +490,7 @@ export class GuildRoleService {
|
||||
const changedRoles: Array<GuildRole> = [];
|
||||
for (const role of allRoles) {
|
||||
if (role.hoistPosition === null) continue;
|
||||
const roleRow = role.toRow();
|
||||
const updatedRole = await this.guildRepository.upsertRole(
|
||||
{
|
||||
...roleRow,
|
||||
hoist_position: null,
|
||||
},
|
||||
roleRow,
|
||||
);
|
||||
const updatedRole = await this.guildRepository.upsertRole({...role.toRow(), hoist_position: null});
|
||||
changedRoles.push(updatedRole);
|
||||
}
|
||||
if (changedRoles.length > 0) {
|
||||
@@ -697,11 +683,7 @@ export class GuildRoleService {
|
||||
});
|
||||
const reorderedIds = targetOrder.map((r) => r.id);
|
||||
const reorderedRoles = this.reorderRolePositions({allRoles, reorderedIds, guildId});
|
||||
const updatePromises = reorderedRoles.map((role) => {
|
||||
const roleRow = role.toRow();
|
||||
const oldRole = roleMap.get(role.id);
|
||||
return this.guildRepository.upsertRole(roleRow, oldRole ? oldRole.toRow() : undefined);
|
||||
});
|
||||
const updatePromises = reorderedRoles.map((role) => this.guildRepository.upsertRole(role.toRow()));
|
||||
await Promise.all(updatePromises);
|
||||
const updatedRoles = await this.guildRepository.listRoles(guildId);
|
||||
const changedRoles = updatedRoles.filter((role) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {ChannelID, GuildID, UserID} from '@app/api/BrandedTypes';
|
||||
import type {IChannelRepository} from '@app/api/channel/IChannelRepository';
|
||||
import type {ChannelRepository} from '@app/api/channel/ChannelRepository';
|
||||
import {threadSearchIndexEnqueueCappedTotal} from '@app/api/channel/threads/ThreadMetrics';
|
||||
import {
|
||||
guildActive,
|
||||
@@ -21,7 +21,7 @@ import {MessageSearchResponseMapper} from '@app/api/search/MessageSearchResponse
|
||||
import {searchExistingMessages} from '@app/api/search/MessageSearchResultReconciler';
|
||||
import {channelRequiresAgeVerification} from '@app/api/search/SearchNsfwUtils';
|
||||
import {accessibleRequestedThreadIds, accessibleThreadIds} from '@app/api/search/ThreadSearchScope';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import {canUserAccessNsfwContent} from '@app/api/utils/AgeUtils';
|
||||
import {mapWithConcurrency} from '@app/api/utils/ConcurrencyUtils';
|
||||
import type {WorkerTaskName} from '@app/api/worker/WorkerLaneConfig';
|
||||
@@ -46,10 +46,10 @@ export class GuildSearchService {
|
||||
private readonly responseMapper: MessageSearchResponseMapper;
|
||||
|
||||
constructor(
|
||||
private readonly channelRepository: IChannelRepository,
|
||||
private readonly channelRepository: ChannelRepository,
|
||||
private readonly userCacheService: UserCacheService,
|
||||
private readonly gatewayService: IGatewayService,
|
||||
private readonly userRepository: IUserRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
private readonly workerService: IWorkerService<WorkerTaskName>,
|
||||
) {
|
||||
this.responseMapper = new MessageSearchResponseMapper(this.channelRepository, this.userCacheService);
|
||||
@@ -74,7 +74,7 @@ export class GuildSearchService {
|
||||
const threadsVisible = viewerActive(viewer, guildId);
|
||||
const explicitChannelIds = channelIds.length > 0;
|
||||
if (!explicitChannelIds) {
|
||||
const channels = await this.channelRepository.listGuildChannels(guildId, 'enrolled');
|
||||
const channels = await this.channelRepository.channelData.listGuildChannels(guildId, 'enrolled');
|
||||
channelIds = channels.filter((c) => threadsVisible || !c.isThreadOnly()).map((c) => c.id);
|
||||
}
|
||||
const includeNsfwRequested = searchParams.include_nsfw ?? false;
|
||||
@@ -85,7 +85,7 @@ export class GuildSearchService {
|
||||
}
|
||||
const canIncludeNsfw = canUserAccessNsfw && (includeNsfwRequested || guildIsAgeRestricted);
|
||||
const guildNsfw = guildData?.nsfw ?? false;
|
||||
const channels = await this.channelRepository.listChannels(channelIds);
|
||||
const channels = await this.channelRepository.channelData.listChannels(channelIds);
|
||||
const channelMap = new Map<string, Channel>();
|
||||
for (const channel of channels) {
|
||||
if (channel.guildId === guildId && (threadsVisible || !THREAD_FEATURE_CHANNEL_TYPES.has(channel.type))) {
|
||||
@@ -103,7 +103,7 @@ export class GuildSearchService {
|
||||
if (requestedThreads.length > 0) {
|
||||
const threadIds = new Set(requestedThreads.map((thread) => thread.id.toString()));
|
||||
const parentIds = requestedThreads.flatMap((thread) => (thread.parentId ? [thread.parentId] : []));
|
||||
for (const parent of await this.channelRepository.listChannels(
|
||||
for (const parent of await this.channelRepository.channelData.listChannels(
|
||||
parentIds.filter((id) => !channelMap.has(id.toString())),
|
||||
)) {
|
||||
if (parent.guildId === guildId) channelMap.set(parent.id.toString(), parent);
|
||||
@@ -357,7 +357,7 @@ export class GuildSearchService {
|
||||
}
|
||||
}
|
||||
if (missingParentIds.length > 0) {
|
||||
const parents = await this.channelRepository.listChannels(missingParentIds);
|
||||
const parents = await this.channelRepository.channelData.listChannels(missingParentIds);
|
||||
for (const parent of parents) {
|
||||
lookup.set(parent.id.toString(), parent);
|
||||
}
|
||||
@@ -393,7 +393,7 @@ export class GuildSearchService {
|
||||
threadParents: Map<string, Channel>,
|
||||
): Promise<void> {
|
||||
const threadsByGuild = new Map<string, Array<Channel>>();
|
||||
for (const thread of await this.channelRepository.listChannels([...threadIds])) {
|
||||
for (const thread of await this.channelRepository.channelData.listChannels([...threadIds])) {
|
||||
const parent = thread.parentId === null ? undefined : parentById.get(thread.parentId.toString());
|
||||
if (!thread.isThread() || !parent || thread.guildId === null || parent.guildId !== thread.guildId) continue;
|
||||
const threads = threadsByGuild.get(thread.guildId.toString());
|
||||
@@ -438,7 +438,7 @@ export class GuildSearchService {
|
||||
await mapWithConcurrency(guildIds, GUILD_FANOUT_CONCURRENCY, async (guildId) => {
|
||||
const [guildData, guildChannels, viewableChannels] = await Promise.all([
|
||||
this.gatewayService.getGuildData({guildId, userId}),
|
||||
this.channelRepository
|
||||
this.channelRepository.channelData
|
||||
.listGuildChannels(guildId, 'enrolled')
|
||||
.then((channels) =>
|
||||
channels.filter(
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import type {ApiContext} from '@app/api/ApiContext';
|
||||
import type {EmojiID, GuildID, RoleID, StickerID, UserID} from '@app/api/BrandedTypes';
|
||||
import {createChannelID, createWebhookID} from '@app/api/BrandedTypes';
|
||||
import type {IChannelRepository} from '@app/api/channel/IChannelRepository';
|
||||
import type {ChannelRepository} from '@app/api/channel/ChannelRepository';
|
||||
import type {ChannelService} from '@app/api/channel/services/ChannelService';
|
||||
import {mapThreadToResponse} from '@app/api/channel/services/thread/ThreadMappers';
|
||||
import {everEnabled, isTainted, type ThreadViewer, viewerActive} from '@app/api/experiment/ChannelThreadsGate';
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
THREAD_AUDIT_LOG_ACTION_TYPES,
|
||||
} from '@app/api/guild/GuildAuditLogEntryMapper';
|
||||
import type {GuildAuditLogService} from '@app/api/guild/GuildAuditLogService';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import {GuildChannelService} from '@app/api/guild/services/GuildChannelService';
|
||||
import {GuildContentService} from '@app/api/guild/services/GuildContentService';
|
||||
import {GuildDataService} from '@app/api/guild/services/GuildDataService';
|
||||
@@ -38,9 +38,9 @@ import type {LimitConfigService} from '@app/api/limits/LimitConfigService';
|
||||
import type {RequestCache} from '@app/api/middleware/RequestCacheMiddleware';
|
||||
import type {GuildAuditLog} from '@app/api/models/GuildAuditLog';
|
||||
import type {Webhook} from '@app/api/models/Webhook';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import {getCachedUserPartialResponses} from '@app/api/user/UserCacheHelpers';
|
||||
import type {IWebhookRepository} from '@app/api/webhook/IWebhookRepository';
|
||||
import type {WebhookRepository} from '@app/api/webhook/WebhookRepository';
|
||||
import {AuditLogActionType} from '@fluxer/constants/src/AuditLogActionType';
|
||||
import {Permissions} from '@fluxer/constants/src/ChannelConstants';
|
||||
import {GuildFeatures} from '@fluxer/constants/src/GuildConstants';
|
||||
@@ -99,25 +99,25 @@ export class GuildService {
|
||||
public readonly content: GuildContentService;
|
||||
public readonly channels: GuildChannelService;
|
||||
public readonly search: GuildSearchService;
|
||||
private readonly guildRepository: IGuildRepositoryAggregate;
|
||||
private readonly guildRepository: GuildRepository;
|
||||
private readonly cacheService: ICacheService;
|
||||
private readonly userCacheService: UserCacheService;
|
||||
private readonly webhookRepository: IWebhookRepository;
|
||||
private readonly webhookRepository: WebhookRepository;
|
||||
private readonly guildAuditLogService: GuildAuditLogService;
|
||||
private readonly gatewayService: IGatewayService;
|
||||
private readonly userRepository: IUserRepository;
|
||||
private readonly userRepository: UserRepository;
|
||||
|
||||
constructor(
|
||||
apiContext: ApiContext,
|
||||
guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly channelRepository: IChannelRepository,
|
||||
guildRepository: GuildRepository,
|
||||
private readonly channelRepository: ChannelRepository,
|
||||
inviteRepository: InviteRepository,
|
||||
channelService: ChannelService,
|
||||
userCacheService: UserCacheService,
|
||||
entityAssetService: EntityAssetService,
|
||||
avatarService: AvatarService,
|
||||
assetDeletionQueue: IAssetDeletionQueue,
|
||||
webhookRepository: IWebhookRepository,
|
||||
webhookRepository: WebhookRepository,
|
||||
guildAuditLogService: GuildAuditLogService,
|
||||
limitConfigService: LimitConfigService,
|
||||
) {
|
||||
@@ -218,7 +218,7 @@ export class GuildService {
|
||||
): Promise<GuildResponse> {
|
||||
const {guildId, requestCache} = params;
|
||||
const {guild, previousFeatures, updatedFeatures} = await this.withGuildUpdateLock(guildId, requestCache, () =>
|
||||
this.data.updateGuild(params, auditLogReason),
|
||||
this.data.operations.updateGuild(params, auditLogReason),
|
||||
);
|
||||
if (
|
||||
previousFeatures.has(GuildFeatures.TEXT_CHANNEL_FLEXIBLE_NAMES) &&
|
||||
@@ -258,7 +258,7 @@ export class GuildService {
|
||||
async getEmojiMetadata(emojiId: EmojiID): Promise<GuildEmojiMetadataResponse> {
|
||||
const emoji = await this.guildRepository.getEmojiById(emojiId);
|
||||
if (!emoji) throw new UnknownGuildEmojiError();
|
||||
const guild = await this.data.getGuildSystem(emoji.guildId);
|
||||
const guild = await this.data.operations.getGuildSystem(emoji.guildId);
|
||||
return {
|
||||
id: emoji.id.toString(),
|
||||
guild_id: guild.id.toString(),
|
||||
@@ -271,7 +271,7 @@ export class GuildService {
|
||||
async getStickerMetadata(stickerId: StickerID): Promise<GuildStickerMetadataResponse> {
|
||||
const sticker = await this.guildRepository.getStickerById(stickerId);
|
||||
if (!sticker) throw new UnknownGuildStickerError();
|
||||
const guild = await this.data.getGuildSystem(sticker.guildId);
|
||||
const guild = await this.data.operations.getGuildSystem(sticker.guildId);
|
||||
return {
|
||||
id: sticker.id.toString(),
|
||||
guild_id: guild.id.toString(),
|
||||
@@ -421,7 +421,7 @@ export class GuildService {
|
||||
);
|
||||
const channels =
|
||||
candidateIds.length > 0
|
||||
? await this.channelRepository.listChannels(candidateIds.map((id) => createChannelID(BigInt(id))))
|
||||
? await this.channelRepository.channelData.listChannels(candidateIds.map((id) => createChannelID(BigInt(id))))
|
||||
: [];
|
||||
const gatedIds = new Set(
|
||||
channels
|
||||
@@ -435,7 +435,7 @@ export class GuildService {
|
||||
const channelIds = [...new Set(webhooks.flatMap((webhook) => (webhook.channelId ? [webhook.channelId] : [])))];
|
||||
if (channelIds.length === 0) return webhooks;
|
||||
const forumIds = new Set(
|
||||
(await this.channelRepository.listChannels(channelIds))
|
||||
(await this.channelRepository.channelData.listChannels(channelIds))
|
||||
.filter((channel) => channel.isThreadOnly())
|
||||
.map((channel) => channel.id),
|
||||
);
|
||||
@@ -454,12 +454,14 @@ export class GuildService {
|
||||
.map((id) => createChannelID(BigInt(id)));
|
||||
if (threadIds.length === 0) return [];
|
||||
const [channels, states, stats] = await Promise.all([
|
||||
this.channelRepository.listChannels(threadIds),
|
||||
this.channelRepository.channelData.listChannels(threadIds),
|
||||
this.channelRepository.threads.getStates(threadIds),
|
||||
this.channelRepository.threads.getStatsMany(threadIds),
|
||||
]);
|
||||
const stateById = new Map(states.map((state) => [state.threadId.toString(), state]));
|
||||
const parents = await this.channelRepository.listChannels([...new Set(states.map((state) => state.parentId))]);
|
||||
const parents = await this.channelRepository.channelData.listChannels([
|
||||
...new Set(states.map((state) => state.parentId)),
|
||||
]);
|
||||
const parentTypeById = new Map(parents.map((parent) => [parent.id.toString(), parent.type]));
|
||||
return channels.flatMap((channel) => {
|
||||
const state = stateById.get(channel.id.toString());
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
resolveThreadPermissionMode,
|
||||
shouldMaskThreadPermissionBits,
|
||||
} from '@app/api/guild/services/ThreadPermissionBits';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import {
|
||||
applyProtectedOverwriteBits,
|
||||
applyProtectedRolePermissions,
|
||||
@@ -212,7 +212,7 @@ describe('guild MFA enforcement', () => {
|
||||
it('treats MANAGE_THREADS as elevated', async () => {
|
||||
const userRepository = {
|
||||
findUnique: async () => ({authenticatorTypes: new Set()}),
|
||||
} as unknown as IUserRepository;
|
||||
} as unknown as UserRepository;
|
||||
const enforce = await createGuildMfaEnforcer({
|
||||
userRepository,
|
||||
guildData: {mfa_level: GuildMFALevel.ELEVATED, owner_id: '1'},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import type {ChannelID, EmojiID, GuildID, RoleID, StickerID, UserID} from '@app/api/BrandedTypes';
|
||||
import {createChannelID, createRoleID, createUserID} from '@app/api/BrandedTypes';
|
||||
import {mapChannelToResponse} from '@app/api/channel/ChannelMappers';
|
||||
import type {IChannelRepository} from '@app/api/channel/IChannelRepository';
|
||||
import type {ChannelRepository} from '@app/api/channel/ChannelRepository';
|
||||
import {
|
||||
buildThreadParentPatch,
|
||||
loadThreadParentConfig,
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
} from '@app/api/experiment/ChannelThreadsGate';
|
||||
import type {GuildAuditLogService} from '@app/api/guild/GuildAuditLogService';
|
||||
import type {GuildAuditLogChange} from '@app/api/guild/GuildAuditLogTypes';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import {ChannelHelpers, type ChannelReorderOperation} from '@app/api/guild/services/channel/ChannelHelpers';
|
||||
import {resolveProtectedBitActor} from '@app/api/guild/services/ThreadPermissionBits';
|
||||
import type {IGatewayService} from '@app/api/infrastructure/IGatewayService';
|
||||
@@ -64,8 +64,8 @@ import type {ICacheService} from '@pkgs/cache/src/ICacheService';
|
||||
|
||||
export class ChannelOperationsService {
|
||||
constructor(
|
||||
private readonly channelRepository: IChannelRepository,
|
||||
private readonly guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly channelRepository: ChannelRepository,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
private readonly userCacheService: UserCacheService,
|
||||
private readonly gatewayService: IGatewayService,
|
||||
private readonly cacheService: ICacheService,
|
||||
@@ -86,7 +86,7 @@ export class ChannelOperationsService {
|
||||
auditLogReason?: string | null,
|
||||
): Promise<ChannelResponse> {
|
||||
await this.ensureGuildHasCapacity(params.guildId);
|
||||
const channels = await this.channelRepository.listGuildChannels(params.guildId, 'enrolled');
|
||||
const channels = await this.channelRepository.channelData.listGuildChannels(params.guildId, 'enrolled');
|
||||
const parentId = params.data.parent_id ? createChannelID(params.data.parent_id) : null;
|
||||
const parentChannel = this.validateParentCategory({
|
||||
parentId,
|
||||
@@ -212,7 +212,7 @@ export class ChannelOperationsService {
|
||||
const requestedContentWarningText =
|
||||
trimmedContentWarningText && trimmedContentWarningText.length > 0 ? trimmedContentWarningText : null;
|
||||
const channelId = createChannelID(await this.snowflakeService.generate());
|
||||
const channel = await this.channelRepository.upsert({
|
||||
const channel = await this.channelRepository.channelData.upsert({
|
||||
channel_id: channelId,
|
||||
guild_id: params.guildId,
|
||||
type: params.data.type,
|
||||
@@ -289,7 +289,7 @@ export class ChannelOperationsService {
|
||||
|
||||
async sanitizeTextChannelNames(params: {guildId: GuildID; requestCache: RequestCache}): Promise<void> {
|
||||
const {guildId, requestCache} = params;
|
||||
const channels = await this.channelRepository.listGuildChannels(guildId, 'enrolled');
|
||||
const channels = await this.channelRepository.channelData.listGuildChannels(guildId, 'enrolled');
|
||||
let hasChanges = false;
|
||||
const updatedChannels: Array<Channel> = [];
|
||||
for (const channel of channels) {
|
||||
@@ -305,7 +305,7 @@ export class ChannelOperationsService {
|
||||
updatedChannels.push(channel);
|
||||
continue;
|
||||
}
|
||||
const updated = await this.channelRepository.upsert({
|
||||
const updated = await this.channelRepository.channelData.upsert({
|
||||
...channel.toRow(),
|
||||
name: normalized,
|
||||
});
|
||||
@@ -372,7 +372,7 @@ export class ChannelOperationsService {
|
||||
}
|
||||
|
||||
private async listPositionableChannels(guildId: GuildID, hideThreadOnly: boolean): Promise<Array<Channel>> {
|
||||
const channels = await this.channelRepository.listGuildChannels(guildId, 'enrolled');
|
||||
const channels = await this.channelRepository.channelData.listGuildChannels(guildId, 'enrolled');
|
||||
return hideThreadOnly ? channels.filter((ch) => !ch.isThreadOnly()) : channels;
|
||||
}
|
||||
|
||||
@@ -461,9 +461,9 @@ export class ChannelOperationsService {
|
||||
clientFeatures?: ReadonlySet<string>;
|
||||
viewer?: ThreadViewer;
|
||||
}): Promise<void> {
|
||||
const parent = await this.channelRepository.findUnique(params.parentId);
|
||||
const parent = await this.channelRepository.channelData.findUnique(params.parentId);
|
||||
if (!parent || parent.guildId !== params.guildId || parent.type !== ChannelTypes.GUILD_CATEGORY) return;
|
||||
const child = await this.channelRepository.findUnique(params.channelId);
|
||||
const child = await this.channelRepository.channelData.findUnique(params.channelId);
|
||||
if (!child || child.guildId !== params.guildId) return;
|
||||
const userPermissions = await this.gatewayService.getUserPermissions({
|
||||
guildId: params.guildId,
|
||||
@@ -508,7 +508,7 @@ export class ChannelOperationsService {
|
||||
const existingAllow = child.permissionOverwrites.get(targetId)?.allow ?? 0n;
|
||||
if ((incoming.allow & ~existingAllow & ~userPermissions) !== 0n) throw new MissingPermissionsError();
|
||||
}
|
||||
await this.channelRepository.upsert({
|
||||
await this.channelRepository.channelData.upsert({
|
||||
...child.toRow(),
|
||||
permission_overwrites: new Map(
|
||||
Array.from(incomingOverwrites.entries()).map(([targetId, overwrite]) => [
|
||||
@@ -525,7 +525,7 @@ export class ChannelOperationsService {
|
||||
requestCache: RequestCache;
|
||||
}): Promise<void> {
|
||||
const {guildId, operation, requestCache} = params;
|
||||
const allChannels = await this.channelRepository.listGuildChannels(guildId, 'enrolled');
|
||||
const allChannels = await this.channelRepository.channelData.listGuildChannels(guildId, 'enrolled');
|
||||
const planResult = computeGuildChannelReorderPlan({channels: allChannels, operation});
|
||||
if (!planResult.ok) {
|
||||
this.throwReorderPlanError(planResult.code, operation);
|
||||
@@ -561,14 +561,14 @@ export class ChannelOperationsService {
|
||||
const currentParent = channel.parentId ?? null;
|
||||
if (channel.position !== desiredPosition || currentParent !== desiredParent) {
|
||||
updatePromises.push(
|
||||
this.channelRepository
|
||||
this.channelRepository.channelData
|
||||
.upsert({...channel.toRow(), position: desiredPosition, parent_id: desiredParent})
|
||||
.then(() => {}),
|
||||
);
|
||||
}
|
||||
}
|
||||
await Promise.all(updatePromises);
|
||||
const updatedChannels = await this.channelRepository.listGuildChannels(guildId, 'enrolled');
|
||||
const updatedChannels = await this.channelRepository.channelData.listGuildChannels(guildId, 'enrolled');
|
||||
await this.dispatchChannelUpdateBulk({guildId, channels: updatedChannels, requestCache});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import {createEmojiID, type EmojiID, type GuildID, type UserID} from '@app/api/BrandedTypes';
|
||||
import {getContentMessage} from '@app/api/content_i18n/ContentI18n';
|
||||
import {mapGuildEmojisWithUsersToResponse, mapGuildEmojiToResponse} from '@app/api/guild/GuildModel';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import type {ContentHelpers} from '@app/api/guild/services/content/ContentHelpers';
|
||||
import type {ExpressionAssetPurger} from '@app/api/guild/services/content/ExpressionAssetPurger';
|
||||
import type {AvatarService} from '@app/api/infrastructure/AvatarService';
|
||||
@@ -29,7 +29,7 @@ import type {GuildEmojiResponse, GuildEmojiWithUserResponse} from '@fluxer/schem
|
||||
|
||||
export class EmojiService {
|
||||
constructor(
|
||||
private readonly guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
private readonly userCacheService: UserCacheService,
|
||||
private readonly gatewayService: IGatewayService,
|
||||
private readonly avatarService: AvatarService,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import {createStickerID, type GuildID, type StickerID, type UserID} from '@app/api/BrandedTypes';
|
||||
import {getContentMessage} from '@app/api/content_i18n/ContentI18n';
|
||||
import {mapGuildStickersWithUsersToResponse, mapGuildStickerToResponse} from '@app/api/guild/GuildModel';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import type {ContentHelpers} from '@app/api/guild/services/content/ContentHelpers';
|
||||
import type {ExpressionAssetPurger} from '@app/api/guild/services/content/ExpressionAssetPurger';
|
||||
import type {AvatarService} from '@app/api/infrastructure/AvatarService';
|
||||
@@ -33,7 +33,7 @@ import type {
|
||||
|
||||
export class StickerService {
|
||||
constructor(
|
||||
private readonly guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
private readonly userCacheService: UserCacheService,
|
||||
private readonly gatewayService: IGatewayService,
|
||||
private readonly avatarService: AvatarService,
|
||||
|
||||
@@ -9,7 +9,7 @@ import {createGuildMfaEnforcer} from '@app/api/guild/services/GuildMfaEnforcemen
|
||||
import type {IGatewayService} from '@app/api/infrastructure/IGatewayService';
|
||||
import {Logger} from '@app/api/Logger';
|
||||
import type {Guild} from '@app/api/models/Guild';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import {serializeGuildForAudit as serializeGuildForAuditUtil} from '@app/api/utils/AuditSerializationUtils';
|
||||
import {requirePermission} from '@app/api/utils/PermissionUtils';
|
||||
import type {AuditLogActionType} from '@fluxer/constants/src/AuditLogActionType';
|
||||
@@ -26,7 +26,7 @@ export class GuildDataHelpers {
|
||||
constructor(
|
||||
private readonly gatewayService: IGatewayService,
|
||||
private readonly guildAuditLogService: GuildAuditLogService,
|
||||
private readonly userRepository: IUserRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
) {}
|
||||
|
||||
private readonly guildRepository = new GuildRepository();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import type {ChannelID, GuildID, RoleID, UserID} from '@app/api/BrandedTypes';
|
||||
import {createChannelID, createGuildID, createRoleID, guildIdToRoleId} from '@app/api/BrandedTypes';
|
||||
import type {IChannelRepository} from '@app/api/channel/IChannelRepository';
|
||||
import type {ChannelRepository} from '@app/api/channel/ChannelRepository';
|
||||
import {
|
||||
type ChannelFollowerRemovalCopyMode,
|
||||
scheduleDeletedChannelFollowerRemoval,
|
||||
@@ -14,8 +14,8 @@ import type {PermissionOverwrite} from '@app/api/database/types/ChannelTypes';
|
||||
import type {GuildRow} from '@app/api/database/types/GuildTypes';
|
||||
import {guildActive, isTainted} from '@app/api/experiment/ChannelThreadsGate';
|
||||
import {mapGuildToGuildResponse, mapGuildToPartialResponse} from '@app/api/guild/GuildModel';
|
||||
import type {IGuildDiscoveryRepository} from '@app/api/guild/repositories/GuildDiscoveryRepository';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildDiscoveryRepository} from '@app/api/guild/repositories/GuildDiscoveryRepository';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import type {GuildDataHelpers} from '@app/api/guild/services/data/GuildDataHelpers';
|
||||
import {contentModerationService} from '@app/api/infrastructure/ContentModerationService';
|
||||
import type {EntityAssetService, PreparedAssetUpload} from '@app/api/infrastructure/EntityAssetService';
|
||||
@@ -35,11 +35,11 @@ import type {GuildDiscoveryContext} from '@app/api/search/guild/GuildSearchSeria
|
||||
import {deleteChannelMessageSearchDocuments} from '@app/api/search/MessageSearchIndexCleanup';
|
||||
import {deleteThreadSearchDocuments} from '@app/api/search/thread/ThreadSearchService';
|
||||
import {Channels, ChannelsByGuild, GuildMembers, GuildMembersByUserId, GuildRoles, Guilds} from '@app/api/Tables';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import {mapUserSettingsToResponse} from '@app/api/user/UserMappers';
|
||||
import {addGuildToUncategorizedFolder, removeGuildFromUserFolders} from '@app/api/user/utils/GuildFolderUtils';
|
||||
import {mapWithConcurrency} from '@app/api/utils/ConcurrencyUtils';
|
||||
import type {IWebhookRepository} from '@app/api/webhook/IWebhookRepository';
|
||||
import type {WebhookRepository} from '@app/api/webhook/WebhookRepository';
|
||||
import {AuditLogActionType} from '@fluxer/constants/src/AuditLogActionType';
|
||||
import {ALL_PERMISSIONS, ChannelTypes, DEFAULT_PERMISSIONS, Permissions} from '@fluxer/constants/src/ChannelConstants';
|
||||
import {DiscoveryApplicationStatus} from '@fluxer/constants/src/DiscoveryConstants';
|
||||
@@ -185,18 +185,18 @@ function isSystemChannelType(type: number): boolean {
|
||||
|
||||
export class GuildOperationsService {
|
||||
constructor(
|
||||
private readonly guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly channelRepository: IChannelRepository,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
private readonly channelRepository: ChannelRepository,
|
||||
private readonly inviteRepository: InviteRepository,
|
||||
private readonly channelService: ChannelService,
|
||||
private readonly gatewayService: IGatewayService,
|
||||
private readonly entityAssetService: EntityAssetService,
|
||||
private readonly userRepository: IUserRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
private readonly snowflakeService: ISnowflakeService,
|
||||
private readonly webhookRepository: IWebhookRepository,
|
||||
private readonly webhookRepository: WebhookRepository,
|
||||
private readonly helpers: GuildDataHelpers,
|
||||
private readonly limitConfigService: LimitConfigService,
|
||||
private readonly discoveryRepository: IGuildDiscoveryRepository,
|
||||
private readonly discoveryRepository: GuildDiscoveryRepository,
|
||||
) {}
|
||||
|
||||
async getGuild({userId, guildId}: {userId: UserID; guildId: GuildID}): Promise<GuildResponse> {
|
||||
@@ -521,7 +521,7 @@ export class GuildOperationsService {
|
||||
if (data.afk_channel_id !== undefined) {
|
||||
if (data.afk_channel_id) {
|
||||
const afkChannelId = createChannelID(data.afk_channel_id);
|
||||
const afkChannel = await this.channelRepository.findUnique(afkChannelId);
|
||||
const afkChannel = await this.channelRepository.channelData.findUnique(afkChannelId);
|
||||
if (!afkChannel || afkChannel.guildId !== guildId) {
|
||||
throw InputValidationError.fromCode('afk_channel_id', ValidationErrorCodes.AFK_CHANNEL_MUST_BE_IN_GUILD);
|
||||
}
|
||||
@@ -539,7 +539,7 @@ export class GuildOperationsService {
|
||||
if (data.system_channel_id !== undefined) {
|
||||
if (data.system_channel_id) {
|
||||
const systemChannelId = createChannelID(data.system_channel_id);
|
||||
const systemChannel = await this.channelRepository.findUnique(systemChannelId);
|
||||
const systemChannel = await this.channelRepository.channelData.findUnique(systemChannelId);
|
||||
if (!systemChannel || systemChannel.guildId !== guildId) {
|
||||
throw InputValidationError.fromCode(
|
||||
'system_channel_id',
|
||||
@@ -768,7 +768,7 @@ export class GuildOperationsService {
|
||||
if (!guild) {
|
||||
throw new UnknownGuildError();
|
||||
}
|
||||
const channels = await this.channelRepository.listGuildChannels(guildId, 'complete');
|
||||
const channels = await this.channelRepository.channelData.listGuildChannels(guildId, 'complete');
|
||||
for (const channel of channels) {
|
||||
await scheduleDeletedChannelFollowerRemoval({channel, crossposts: this.channelRepository.crossposts, copyMode});
|
||||
}
|
||||
@@ -805,7 +805,7 @@ export class GuildOperationsService {
|
||||
for (const channel of channels) {
|
||||
await this.channelService.attachments.purgeChannelAttachments(channel);
|
||||
}
|
||||
await Promise.all(channels.map((channel) => this.channelRepository.deleteAllChannelMessages(channel.id)));
|
||||
await Promise.all(channels.map((channel) => this.channelRepository.messages.deleteAllChannelMessages(channel.id)));
|
||||
await Promise.all(
|
||||
channels.map((channel) => deleteChannelMessageSearchDocuments(channel.id, {context: {source: 'guild_delete'}})),
|
||||
);
|
||||
@@ -815,7 +815,7 @@ export class GuildOperationsService {
|
||||
}
|
||||
if (threadIds.length > 0) {
|
||||
const threadChannels = new Map(
|
||||
(await this.channelRepository.listChannels(threadIds)).map((channel) => [channel.id, channel]),
|
||||
(await this.channelRepository.channelData.listChannels(threadIds)).map((channel) => [channel.id, channel]),
|
||||
);
|
||||
await mapWithConcurrency(threadIds, THREAD_PURGE_CONCURRENCY, async (threadId) => {
|
||||
const thread = threadChannels.get(threadId);
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
import type {GuildID, UserID} from '@app/api/BrandedTypes';
|
||||
import {mapGuildToGuildResponse} from '@app/api/guild/GuildModel';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import type {GuildDataHelpers} from '@app/api/guild/services/data/GuildDataHelpers';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import {AuditLogActionType} from '@fluxer/constants/src/AuditLogActionType';
|
||||
import {MissingAccessError} from '@fluxer/errors/src/domains/core/MissingAccessError';
|
||||
import {MissingPermissionsError} from '@fluxer/errors/src/domains/core/MissingPermissionsError';
|
||||
@@ -17,8 +17,8 @@ const OWNERSHIP_AUDIT_KEYS: ReadonlySet<string> = new Set(['owner_id']);
|
||||
|
||||
export class GuildOwnershipService {
|
||||
constructor(
|
||||
private readonly guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly userRepository: IUserRepository,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
private readonly helpers: GuildDataHelpers,
|
||||
) {}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
type UserID,
|
||||
vanityCodeToInviteCode,
|
||||
} from '@app/api/BrandedTypes';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import type {GuildDataHelpers} from '@app/api/guild/services/data/GuildDataHelpers';
|
||||
import type {InviteRepository} from '@app/api/invite/InviteRepository';
|
||||
import type {RequestCache} from '@app/api/middleware/RequestCacheMiddleware';
|
||||
@@ -23,7 +23,7 @@ const VANITY_AUDIT_KEYS: ReadonlySet<string> = new Set(['vanity_url_code']);
|
||||
|
||||
export class GuildVanityService {
|
||||
constructor(
|
||||
private readonly guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
private readonly inviteRepository: InviteRepository,
|
||||
private readonly helpers: GuildDataHelpers,
|
||||
) {}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import type {GuildID, RoleID, UserID} from '@app/api/BrandedTypes';
|
||||
import {createGuildMfaEnforcer} from '@app/api/guild/services/GuildMfaEnforcement';
|
||||
import type {IGatewayService} from '@app/api/infrastructure/IGatewayService';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import {MissingAccessError} from '@fluxer/errors/src/domains/core/MissingAccessError';
|
||||
import {MissingPermissionsError} from '@fluxer/errors/src/domains/core/MissingPermissionsError';
|
||||
import type {GuildResponse} from '@fluxer/schema/src/domains/guild/GuildResponseSchemas';
|
||||
@@ -20,7 +20,7 @@ interface GuildAuth {
|
||||
export class GuildMemberAuthService {
|
||||
constructor(
|
||||
private readonly gatewayService: IGatewayService,
|
||||
private readonly userRepository: IUserRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
) {}
|
||||
|
||||
async getGuildAuthenticated({userId, guildId}: {userId: UserID; guildId: GuildID}): Promise<GuildAuth> {
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {GuildAuditLogService} from '@app/api/guild/GuildAuditLogService';
|
||||
import type {GuildAuditLogChange} from '@app/api/guild/GuildAuditLogTypes';
|
||||
import {resolveMaxGuildMembersLimit} from '@app/api/guild/GuildMemberLimitUtils';
|
||||
import {mapGuildMemberToResponse} from '@app/api/guild/GuildModel';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import type {GuildMemberAuthService} from '@app/api/guild/services/member/GuildMemberAuthService';
|
||||
import type {GuildMemberEventService} from '@app/api/guild/services/member/GuildMemberEventService';
|
||||
import type {GuildMemberSearchIndexService} from '@app/api/guild/services/member/GuildMemberSearchIndexService';
|
||||
@@ -32,7 +32,7 @@ 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 type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import {dispatchUserGuildSettingsUpdate} from '@app/api/user/UserGuildSettingsThreadView';
|
||||
import {isProfileSubstringExempt} from '@app/api/user/UserHelpers';
|
||||
import {mapUserSettingsToResponse} from '@app/api/user/UserMappers';
|
||||
@@ -145,12 +145,12 @@ function joinSourceName(type: JoinSourceType): JoinSource {
|
||||
|
||||
export class GuildMemberOperationsService {
|
||||
constructor(
|
||||
private readonly guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
private readonly channelService: ChannelService,
|
||||
private readonly userCacheService: UserCacheService,
|
||||
private readonly gatewayService: IGatewayService,
|
||||
private readonly entityAssetService: EntityAssetService,
|
||||
private readonly userRepository: IUserRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
private readonly rateLimitService: IRateLimitService,
|
||||
private readonly authService: GuildMemberAuthService,
|
||||
private readonly validationService: GuildMemberValidationService,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {GuildID, RoleID, UserID} from '@app/api/BrandedTypes';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import type {GuildMemberAuthService} from '@app/api/guild/services/member/GuildMemberAuthService';
|
||||
import type {GuildMemberValidationService} from '@app/api/guild/services/member/GuildMemberValidationService';
|
||||
import type {IGatewayService} from '@app/api/infrastructure/IGatewayService';
|
||||
@@ -10,7 +10,7 @@ import {UnknownGuildMemberError} from '@fluxer/errors/src/domains/guild/UnknownG
|
||||
|
||||
export class GuildMemberRoleService {
|
||||
constructor(
|
||||
private readonly guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
private readonly gatewayService: IGatewayService,
|
||||
private readonly authService: GuildMemberAuthService,
|
||||
private readonly validationService: GuildMemberValidationService,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import type {GuildID, UserID} from '@app/api/BrandedTypes';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import {Logger} from '@app/api/Logger';
|
||||
import type {GuildMember} from '@app/api/models/GuildMember';
|
||||
import type {User} from '@app/api/models/User';
|
||||
@@ -19,11 +19,7 @@ function getGuildMemberIndexService(options: GuildMemberSearchIndexOptions = {})
|
||||
}
|
||||
|
||||
export class GuildMemberSearchIndexService {
|
||||
async updateUserMembers(
|
||||
user: User,
|
||||
guildIds: Array<GuildID>,
|
||||
guildRepository: IGuildRepositoryAggregate,
|
||||
): Promise<void> {
|
||||
async updateUserMembers(user: User, guildIds: Array<GuildID>, guildRepository: GuildRepository): Promise<void> {
|
||||
if (guildIds.length === 0) return;
|
||||
const guilds = await guildRepository.listGuilds(guildIds);
|
||||
const indexedGuilds = guilds.filter((guild) => guild.membersIndexedAt != null);
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
import type {GuildID, RoleID, UserID} from '@app/api/BrandedTypes';
|
||||
import {guildIdToRoleId} from '@app/api/BrandedTypes';
|
||||
import {isIpBanExempt} from '@app/api/ban/IpBanExemptions';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import type {GuildMember} from '@app/api/models/GuildMember';
|
||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||
import type {UserRepository} from '@app/api/user/repositories/UserRepository';
|
||||
import {Permissions} from '@fluxer/constants/src/ChannelConstants';
|
||||
import {ValidationErrorCodes} from '@fluxer/constants/src/ValidationErrorCodes';
|
||||
import {InputValidationError} from '@fluxer/errors/src/domains/core/InputValidationError';
|
||||
@@ -24,8 +24,8 @@ function ensureNotEveryoneRole(roleId: RoleID, guildId: GuildID, path: string):
|
||||
|
||||
export class GuildMemberValidationService {
|
||||
constructor(
|
||||
private readonly guildRepository: IGuildRepositoryAggregate,
|
||||
private readonly userRepository: IUserRepository,
|
||||
private readonly guildRepository: GuildRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
) {}
|
||||
|
||||
async validateAndGetRoleIds(params: {
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
import type {GuildAuditLogRow} from '@app/api/database/types/GuildTypes';
|
||||
import {mapGuildAuditLogEntry} from '@app/api/guild/GuildAuditLogEntryMapper';
|
||||
import {GuildAuditLogService} from '@app/api/guild/GuildAuditLogService';
|
||||
import type {IGuildRepositoryAggregate} from '@app/api/guild/repositories/IGuildRepositoryAggregate';
|
||||
import type {GuildRepository} from '@app/api/guild/repositories/GuildRepository';
|
||||
import type {IGatewayService} from '@app/api/infrastructure/IGatewayService';
|
||||
import type {ISnowflakeService} from '@app/api/infrastructure/ISnowflakeService';
|
||||
import {ChannelPermissionOverwrite} from '@app/api/models/ChannelPermissionOverwrite';
|
||||
@@ -29,19 +29,6 @@ const MEMBER_ID = createUserID(1420000000000000002n);
|
||||
const ROLE_ID = createRoleID(1420000000000000003n);
|
||||
const CHANNEL_ID = createChannelID(1420000000000000004n);
|
||||
|
||||
const SKIPPABLE_ACTIONS = [
|
||||
AuditLogActionType.GUILD_UPDATE,
|
||||
AuditLogActionType.CHANNEL_UPDATE,
|
||||
AuditLogActionType.CHANNEL_OVERWRITE_UPDATE,
|
||||
AuditLogActionType.MEMBER_UPDATE,
|
||||
AuditLogActionType.MEMBER_ROLE_UPDATE,
|
||||
AuditLogActionType.MEMBER_MOVE,
|
||||
AuditLogActionType.ROLE_UPDATE,
|
||||
AuditLogActionType.WEBHOOK_UPDATE,
|
||||
AuditLogActionType.EMOJI_UPDATE,
|
||||
AuditLogActionType.STICKER_UPDATE,
|
||||
];
|
||||
|
||||
function createService(roleNames: Map<string, string> = new Map()) {
|
||||
let nextLogId = 1420000000000001000n;
|
||||
const createAuditLog = vi.fn(async (row: GuildAuditLogRow) => new GuildAuditLog(row));
|
||||
@@ -55,7 +42,7 @@ function createService(roleNames: Map<string, string> = new Map()) {
|
||||
const dispatchGuild = vi.fn().mockResolvedValue(undefined);
|
||||
const addJob = vi.fn().mockResolvedValue(undefined);
|
||||
const service = new GuildAuditLogService(
|
||||
{createAuditLog, batchDeleteAndCreateAuditLogs, getRole} as unknown as IGuildRepositoryAggregate,
|
||||
{createAuditLog, batchDeleteAndCreateAuditLogs, getRole} as unknown as GuildRepository,
|
||||
{generate: vi.fn(async () => nextLogId++)} as unknown as ISnowflakeService,
|
||||
{addJob} as unknown as IWorkerService<WorkerTaskName>,
|
||||
{dispatchGuild} as unknown as IGatewayService,
|
||||
@@ -175,11 +162,11 @@ describe('GuildAuditLogService dispatch', () => {
|
||||
});
|
||||
|
||||
describe('GuildAuditLogBuilder.commit', () => {
|
||||
it.each(SKIPPABLE_ACTIONS)('skips action %i with empty changes', async (actionType) => {
|
||||
it('skips an update with empty changes', async () => {
|
||||
const {service, createAuditLog, dispatchGuild} = createService();
|
||||
const result = await service
|
||||
.createBuilder(GUILD_ID, ACTOR_ID)
|
||||
.withAction(actionType, MEMBER_ID.toString())
|
||||
.withAction(AuditLogActionType.MEMBER_UPDATE, MEMBER_ID.toString())
|
||||
.withReason('ignored reason')
|
||||
.withMetadata({role_name: 'Moderators'})
|
||||
.withChanges([])
|
||||
@@ -189,31 +176,6 @@ describe('GuildAuditLogBuilder.commit', () => {
|
||||
expect(dispatchGuild).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each(SKIPPABLE_ACTIONS)('skips action %i without changes', async (actionType) => {
|
||||
const {service, createAuditLog, dispatchGuild} = createService();
|
||||
const result = await service
|
||||
.createBuilder(GUILD_ID, ACTOR_ID)
|
||||
.withAction(actionType, MEMBER_ID.toString())
|
||||
.commit();
|
||||
expect(result).toBeNull();
|
||||
expect(createAuditLog).not.toHaveBeenCalled();
|
||||
expect(dispatchGuild).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('skips a guild update that only changes internal keys', async () => {
|
||||
const {service, createAuditLog} = createService();
|
||||
const result = await service
|
||||
.createBuilder(GUILD_ID, ACTOR_ID)
|
||||
.withAction(AuditLogActionType.GUILD_UPDATE, GUILD_ID.toString())
|
||||
.withChanges([
|
||||
{key: 'member_count', old_value: 1, new_value: 2},
|
||||
{key: 'splash_width', old_value: null, new_value: 1920},
|
||||
])
|
||||
.commit();
|
||||
expect(result).toBeNull();
|
||||
expect(createAuditLog).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('records a kick without changes', async () => {
|
||||
const {service, createAuditLog, dispatchGuild} = createService();
|
||||
const result = await service
|
||||
@@ -227,18 +189,6 @@ describe('GuildAuditLogBuilder.commit', () => {
|
||||
expect(writtenRow(createAuditLog).changes).toBeNull();
|
||||
expect(dispatchGuild).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('records a webhook create with empty changes', async () => {
|
||||
const {service, createAuditLog, dispatchGuild} = createService();
|
||||
const result = await service
|
||||
.createBuilder(GUILD_ID, ACTOR_ID)
|
||||
.withAction(AuditLogActionType.WEBHOOK_CREATE, '1420000000000000005')
|
||||
.withChanges([])
|
||||
.commit();
|
||||
expect(result).not.toBeNull();
|
||||
expect(createAuditLog).toHaveBeenCalledTimes(1);
|
||||
expect(dispatchGuild).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GuildAuditLogService.recordPermissionOverwriteDiff', () => {
|
||||
|
||||
@@ -202,7 +202,7 @@ describe('Guild Channel Management', () => {
|
||||
expect.objectContaining({path: 'rtc_p2p', code: ValidationErrorCodes.GUILD_FEATURE_NOT_TOGGLEABLE}),
|
||||
);
|
||||
}
|
||||
const stored = await getChannelRepository().findUnique(createChannelID(BigInt(voiceChannel.id)));
|
||||
const stored = await getChannelRepository().channelData.findUnique(createChannelID(BigInt(voiceChannel.id)));
|
||||
expect(stored?.rtcP2p).toBe(false);
|
||||
await setVoiceP2pConfig({enabled: true, included_user_ids: [account.userId]});
|
||||
await createBuilder(harness, account.token)
|
||||
@@ -225,7 +225,7 @@ describe('Guild Channel Management', () => {
|
||||
.body({type: ChannelTypes.GUILD_TEXT, rtc_p2p: true})
|
||||
.execute();
|
||||
expect(data.rtc_p2p).toBeUndefined();
|
||||
const stored = await getChannelRepository().findUnique(createChannelID(BigInt(textChannel.id)));
|
||||
const stored = await getChannelRepository().channelData.findUnique(createChannelID(BigInt(textChannel.id)));
|
||||
expect(stored?.rtcP2p).toBe(false);
|
||||
});
|
||||
test('should require MANAGE_CHANNELS to update rtc_p2p', async () => {
|
||||
@@ -242,7 +242,7 @@ describe('Guild Channel Management', () => {
|
||||
.body({type: ChannelTypes.GUILD_VOICE, rtc_p2p: true})
|
||||
.expect(HTTP_STATUS.FORBIDDEN)
|
||||
.execute();
|
||||
const stored = await getChannelRepository().findUnique(createChannelID(BigInt(voiceChannel.id)));
|
||||
const stored = await getChannelRepository().channelData.findUnique(createChannelID(BigInt(voiceChannel.id)));
|
||||
expect(stored?.rtcP2p).toBe(false);
|
||||
});
|
||||
test('should clamp bitrate to 96000 without an audio bitrate feature', async () => {
|
||||
|
||||
@@ -227,7 +227,10 @@ describe('Guild Channel Positions', () => {
|
||||
{id: coopVoice.id, parent_id: coopGames.id},
|
||||
]);
|
||||
const channelRepository = new ChannelRepository();
|
||||
const channelsBeforeMove = await channelRepository.listGuildChannels(createGuildID(BigInt(guild.id)), 'enrolled');
|
||||
const channelsBeforeMove = await channelRepository.channelData.listGuildChannels(
|
||||
createGuildID(BigInt(guild.id)),
|
||||
'enrolled',
|
||||
);
|
||||
const storedCoopText = channelsBeforeMove.find((channel) => channel.id.toString() === coopText.id);
|
||||
const storedCoopVoice = channelsBeforeMove.find((channel) => channel.id.toString() === coopVoice.id);
|
||||
expect(storedCoopText).toBeDefined();
|
||||
@@ -235,8 +238,8 @@ describe('Guild Channel Positions', () => {
|
||||
if (!storedCoopText || !storedCoopVoice) {
|
||||
return;
|
||||
}
|
||||
await channelRepository.upsert({...storedCoopVoice.toRow(), position: 1});
|
||||
await channelRepository.upsert({...storedCoopText.toRow(), position: 2});
|
||||
await channelRepository.channelData.upsert({...storedCoopVoice.toRow(), position: 1});
|
||||
await channelRepository.channelData.upsert({...storedCoopText.toRow(), position: 2});
|
||||
await updateChannelPositions(harness, account.token, guild.id, [
|
||||
{
|
||||
id: frontDoor.id,
|
||||
|
||||
@@ -318,7 +318,7 @@ describe('thread permission bits across guild modes', () => {
|
||||
.execute();
|
||||
const channelId = createChannelID(BigInt(channel.id));
|
||||
const stored = async () => {
|
||||
const overwrites = (await new ChannelRepository().findUnique(channelId))!.permissionOverwrites;
|
||||
const overwrites = (await new ChannelRepository().channelData.findUnique(channelId))!.permissionOverwrites;
|
||||
return {
|
||||
everyone: overwrites.get(createRoleID(BigInt(guild.id))),
|
||||
target: overwrites.get(createRoleID(BigInt(target.id))),
|
||||
@@ -358,9 +358,14 @@ describe('thread permission bits across guild modes', () => {
|
||||
.execute();
|
||||
const repository = new ChannelRepository();
|
||||
const before = await repository.channelData.countGuildChannels(guildId);
|
||||
const source = (await repository.findUnique(createChannelID(BigInt(text.id))))!;
|
||||
const source = (await repository.channelData.findUnique(createChannelID(BigInt(text.id))))!;
|
||||
const forumId = createChannelID(BigInt(text.id) + 1n);
|
||||
await repository.upsert({...source.toRow(), channel_id: forumId, type: ChannelTypes.GUILD_FORUM, name: 'forum'});
|
||||
await repository.channelData.upsert({
|
||||
...source.toRow(),
|
||||
channel_id: forumId,
|
||||
type: ChannelTypes.GUILD_FORUM,
|
||||
name: 'forum',
|
||||
});
|
||||
expect(await repository.channelData.countGuildChannels(guildId)).toBe(before + 1);
|
||||
await createBuilder(harness, owner.token)
|
||||
.delete(`/channels/${forumId}`)
|
||||
@@ -383,9 +388,14 @@ describe('thread permission bits across guild modes', () => {
|
||||
.body({name: 'text', type: ChannelTypes.GUILD_TEXT, parent_id: category.id})
|
||||
.execute();
|
||||
const repository = new ChannelRepository();
|
||||
const source = (await repository.findUnique(createChannelID(BigInt(text.id))))!;
|
||||
const source = (await repository.channelData.findUnique(createChannelID(BigInt(text.id))))!;
|
||||
const forumId = createChannelID(BigInt(text.id) + 1n);
|
||||
await repository.upsert({...source.toRow(), channel_id: forumId, type: ChannelTypes.GUILD_FORUM, name: 'forum'});
|
||||
await repository.channelData.upsert({
|
||||
...source.toRow(),
|
||||
channel_id: forumId,
|
||||
type: ChannelTypes.GUILD_FORUM,
|
||||
name: 'forum',
|
||||
});
|
||||
expect(await fetchOne<GuildThreadStateRow>(FETCH_MARKER, {guild_id: guildId})).not.toBeNull();
|
||||
await setChannelThreadsConfig({enabled: false});
|
||||
const dispatchSpy = vi.spyOn(NoopGatewayService.prototype, 'dispatchGuild');
|
||||
@@ -398,12 +408,12 @@ describe('thread permission bits across guild modes', () => {
|
||||
.patch(`/channels/${category.id}`)
|
||||
.body({permission_overwrites: [{id: guild.id, type: 0, allow: '0', deny: Permissions.VIEW_CHANNEL.toString()}]})
|
||||
.execute();
|
||||
expect((await repository.findUnique(forumId))?.permissionOverwrites.size).toBe(1);
|
||||
expect((await repository.channelData.findUnique(forumId))?.permissionOverwrites.size).toBe(1);
|
||||
expect(updatedIds()).toContain(text.id);
|
||||
expect(updatedIds()).not.toContain(forumId.toString());
|
||||
dispatchSpy.mockClear();
|
||||
await createBuilder(harness, owner.token).delete(`/channels/${category.id}`).expect(204).execute();
|
||||
expect((await repository.findUnique(forumId))?.parentId).toBeNull();
|
||||
expect((await repository.channelData.findUnique(forumId))?.parentId).toBeNull();
|
||||
expect(updatedIds()).toContain(text.id);
|
||||
expect(updatedIds()).not.toContain(forumId.toString());
|
||||
} finally {
|
||||
@@ -429,7 +439,7 @@ describe('thread permission bits across guild modes', () => {
|
||||
const channelId = createChannelID(BigInt(channel.id));
|
||||
const everyoneId = createRoleID(BigInt(guild.id));
|
||||
const storedAllow = async () =>
|
||||
(await new ChannelRepository().findUnique(channelId))?.permissionOverwrites.get(everyoneId)?.allow;
|
||||
(await new ChannelRepository().channelData.findUnique(channelId))?.permissionOverwrites.get(everyoneId)?.allow;
|
||||
await createBuilder(harness, owner.token)
|
||||
.patch(`/channels/${channel.id}`)
|
||||
.body({permission_overwrites: [{id: guild.id, type: 0, allow: Permissions.VIEW_CHANNEL.toString(), deny: '0'}]})
|
||||
@@ -480,7 +490,7 @@ describe('thread permission bits across guild modes', () => {
|
||||
.body([{id: child.id, parent_id: category.id, lock_permissions: true}])
|
||||
.expect(204);
|
||||
await (capable ? builder.header(FEATURES, CAPABLE) : builder).execute();
|
||||
const stored = await new ChannelRepository().findUnique(createChannelID(BigInt(child.id)));
|
||||
const stored = await new ChannelRepository().channelData.findUnique(createChannelID(BigInt(child.id)));
|
||||
return stored?.permissionOverwrites.get(everyoneId)?.allow;
|
||||
};
|
||||
expect(await moveAndRead(true)).toBe(Permissions.SEND_MESSAGES | inThreads);
|
||||
|
||||
Reference in new issue
Block a user