mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 21:45:31 +09:00
feat(threads): thread and forum API, admin, schemas and docs (#3251)
This commit is contained in:
1 parent
5f4295e399
commit
3f4160b138
495 files changed
+39232
-1215
No files matched your search
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {Config} from '@app/api/Config';
|
||||
import {viewerFromCtx} from '@app/api/experiment/ChannelThreadsGate';
|
||||
import {DefaultUserOnly, LoginRequired} from '@app/api/middleware/AuthMiddleware';
|
||||
import {requireOAuth2BearerToken, requireOAuth2Scope} from '@app/api/middleware/OAuth2ScopeMiddleware';
|
||||
import {RateLimitMiddleware} from '@app/api/middleware/RateLimitMiddleware';
|
||||
@@ -124,6 +125,8 @@ export function OAuth2Controller(app: HonoApp) {
|
||||
await ctx.get('oauth2RequestService').authorizeConsent({
|
||||
body,
|
||||
userId: user.id,
|
||||
clientFeatures: ctx.get('clientFeatures'),
|
||||
viewer: viewerFromCtx(ctx),
|
||||
requestCache: ctx.get('requestCache'),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -5,7 +5,9 @@ import type {SudoVerificationBody} from '@app/api/auth/services/SudoVerification
|
||||
import {requireSudoMode} from '@app/api/auth/services/SudoVerificationService';
|
||||
import {createApplicationID, createChannelID, createGuildID, createRoleID, type UserID} from '@app/api/BrandedTypes';
|
||||
import type {ChannelService} from '@app/api/channel/services/ChannelService';
|
||||
import {guildActive, type ThreadViewer} from '@app/api/experiment/ChannelThreadsGate';
|
||||
import type {GuildService} from '@app/api/guild/services/GuildService';
|
||||
import {resolveProtectedBitActor, stripThreadPermissionBits} from '@app/api/guild/services/ThreadPermissionBits';
|
||||
import {Logger} from '@app/api/Logger';
|
||||
import type {RequestCache} from '@app/api/middleware/RequestCacheMiddleware';
|
||||
import type {ApplicationService} from '@app/api/oauth/ApplicationService';
|
||||
@@ -26,7 +28,9 @@ import {isSignInRefused} from '@app/api/user/UserHelpers';
|
||||
import {mapUserToOAuthResponse, mapUserToPartialResponse} from '@app/api/user/UserMappers';
|
||||
import {verifyPassword} from '@app/api/utils/PasswordUtils';
|
||||
import {canAuthorizeBotInvite, normalizeBotInvitePermissions} from '@fluxer/constants/src/BotPermissionUtils';
|
||||
import {ALL_PERMISSIONS} from '@fluxer/constants/src/ChannelConstants';
|
||||
import {JoinSourceTypes} from '@fluxer/constants/src/GuildConstants';
|
||||
import {THREAD_AWARE_ALL_PERMISSIONS, withImplicitThreadBits} from '@fluxer/constants/src/ThreadPermissionUtils';
|
||||
import {AccessDeniedError} from '@fluxer/errors/src/domains/core/AccessDeniedError';
|
||||
import {InputValidationError} from '@fluxer/errors/src/domains/core/InputValidationError';
|
||||
import {InvalidPermissionsIntegerError} from '@fluxer/errors/src/domains/core/InvalidPermissionsIntegerError';
|
||||
@@ -196,6 +200,8 @@ export class OAuth2RequestService {
|
||||
async authorizeConsent(params: {
|
||||
body: AuthorizeConsentRequest;
|
||||
userId: UserID;
|
||||
clientFeatures: ReadonlySet<string>;
|
||||
viewer: ThreadViewer;
|
||||
requestCache: RequestCache;
|
||||
}): Promise<OAuth2ConsentResponse> {
|
||||
const scopeStr = params.body.scope;
|
||||
@@ -207,6 +213,7 @@ export class OAuth2RequestService {
|
||||
if (guildId && channelId) {
|
||||
throw InputValidationError.create('channel_id', 'channel_id cannot be used with guild_id');
|
||||
}
|
||||
const botPermissionMask = guildId && guildActive(guildId) ? THREAD_AWARE_ALL_PERMISSIONS : ALL_PERMISSIONS;
|
||||
let requestedPermissions: bigint | null = null;
|
||||
if (params.body.permissions !== undefined) {
|
||||
try {
|
||||
@@ -217,7 +224,17 @@ export class OAuth2RequestService {
|
||||
if (requestedPermissions < 0) {
|
||||
throw new InvalidPermissionsNegativeError();
|
||||
}
|
||||
requestedPermissions = normalizeBotInvitePermissions(requestedPermissions);
|
||||
requestedPermissions = normalizeBotInvitePermissions(requestedPermissions, botPermissionMask);
|
||||
if (guildId && botPermissionMask !== ALL_PERMISSIONS) {
|
||||
const actor = await resolveProtectedBitActor({
|
||||
guildId,
|
||||
userId: params.userId,
|
||||
clientFeatures: params.clientFeatures,
|
||||
viewer: params.viewer,
|
||||
isBot: async () => false,
|
||||
});
|
||||
if (!actor.threadBits?.writer) requestedPermissions = stripThreadPermissionBits(requestedPermissions);
|
||||
}
|
||||
}
|
||||
if (!isBotOnly && responseType !== 'code') {
|
||||
throw new InvalidResponseTypeForNonBotError();
|
||||
@@ -258,8 +275,10 @@ export class OAuth2RequestService {
|
||||
});
|
||||
if (
|
||||
!canAuthorizeBotInvite({
|
||||
userPermissions,
|
||||
userPermissions:
|
||||
botPermissionMask === ALL_PERMISSIONS ? userPermissions : withImplicitThreadBits(userPermissions),
|
||||
requestedPermissions,
|
||||
mask: botPermissionMask,
|
||||
})
|
||||
) {
|
||||
throw new MissingPermissionsError();
|
||||
|
||||
Reference in new issue
Block a user