This repository has no description
Something went wrong. Try again.
6.4 kB · 185 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186// SPDX-License-Identifier: AGPL-3.0-or-later
import {MAX_APPLICATIONS_PER_USER} from '@fluxer/constants/src/LimitConstants';import {AccessDeniedError} from '@fluxer/errors/src/domains/core/AccessDeniedError';import {BotUserNotFoundError} from '@fluxer/errors/src/domains/oauth/BotUserNotFoundError';import {InvalidClientError} from '@fluxer/errors/src/domains/oauth/InvalidClientError';import {MaxApplicationsError} from '@fluxer/errors/src/domains/oauth/MaxApplicationsError';import {UnknownApplicationError} from '@fluxer/errors/src/domains/oauth/UnknownApplicationError';import type { ApplicationCreateRequest, ApplicationUpdateRequest, BotProfileResponse, BotProfileUpdateRequest,} from '@fluxer/schema/src/domains/oauth/OAuthSchemas';import type {Context} from 'hono';import type {ApiContext} from '../ApiContext';import type {SudoVerificationBody} from '../auth/services/SudoVerificationService';import {requireSudoMode} from '../auth/services/SudoVerificationService';import {createApplicationID, type UserID} from '../BrandedTypes';import {UsernameNotAvailableError} from '../infrastructure/DiscriminatorService';import type {Application} from '../models/Application';import type {User} from '../models/User';import type {ApplicationService} from './ApplicationService';import {ApplicationNotOwnedError} from './ApplicationService';import {mapApplicationToResponse, mapBotProfileToResponse} from './OAuth2Mappers';import type {IApplicationRepository} from './repositories/IApplicationRepository';
export class OAuth2ApplicationsRequestService { constructor( private readonly apiContext: ApiContext, private readonly applicationService: ApplicationService, private readonly applicationRepository: IApplicationRepository, ) {}
async listApplications(userId: UserID) { const applications: Array<Application> = await this.applicationService.listApplicationsByOwner(userId); const botUserMap = new Map<string, User>(); const botUserFetches: Array<{ id: string; promise: Promise<User | null>; }> = []; for (const app of applications) { if (app.hasBotUser()) { const botUserId = app.getBotUserId(); if (botUserId) { botUserFetches.push({ id: botUserId.toString(), promise: this.apiContext.services.users.findUnique(botUserId), }); } } } const botUsers = await Promise.all(botUserFetches.map((f) => f.promise)); for (let i = 0; i < botUsers.length; i++) { const user = botUsers[i]; if (user !== null) { botUserMap.set(botUserFetches[i].id, user); } } return applications.map((app: Application) => { const botUserId = app.hasBotUser() ? app.getBotUserId() : null; const botUser = botUserId ? botUserMap.get(botUserId.toString()) : null; return mapApplicationToResponse(app, {botUser: botUser ?? undefined}); }); }
async createApplication(userId: UserID, body: ApplicationCreateRequest) { const existingApps = await this.applicationService.listApplicationsByOwner(userId); if (existingApps.length >= MAX_APPLICATIONS_PER_USER) { throw new MaxApplicationsError(MAX_APPLICATIONS_PER_USER); } const result = await this.applicationService.createApplication({ ownerUserId: userId, name: body.name, redirectUris: body.redirect_uris, botPublic: body.bot_public, botRequireCodeGrant: body.bot_require_code_grant, }); return mapApplicationToResponse(result.application, { botUser: result.botUser, botToken: result.botToken, clientSecret: result.clientSecret, }); }
async getApplication(userId: UserID, applicationId: bigint) { const appId = createApplicationID(applicationId); const application = await this.applicationRepository.getApplication(appId); if (!application) { throw new UnknownApplicationError(); } if (application.ownerUserId !== userId) { throw new AccessDeniedError(); } let botUser = null; if (application.hasBotUser()) { const botUserId = application.getBotUserId(); if (botUserId) { botUser = await this.apiContext.services.users.findUnique(botUserId); } } return mapApplicationToResponse(application, {botUser}); }
async updateApplication(userId: UserID, applicationId: bigint, body: ApplicationUpdateRequest) { try { const updated = await this.applicationService.updateApplication({ userId, applicationId: createApplicationID(applicationId), name: body.name, redirectUris: body.redirect_uris, botPublic: body.bot_public, botRequireCodeGrant: body.bot_require_code_grant, }); let botUser = null; if (updated.hasBotUser()) { const botUserId = updated.getBotUserId(); if (botUserId) { botUser = await this.apiContext.services.users.findUnique(botUserId); } } return mapApplicationToResponse(updated, {botUser: botUser ?? undefined}); } catch (err) { if (err instanceof ApplicationNotOwnedError) { throw new AccessDeniedError(); } if (err instanceof UnknownApplicationError) { throw err; } throw err; } }
async deleteApplication(params: { ctx: Context; userId: UserID; body: SudoVerificationBody; applicationId: bigint; }): Promise<void> { await requireSudoMode(params.ctx, params.ctx.get('user'), params.body); try { await this.applicationService.deleteApplication(params.userId, createApplicationID(params.applicationId)); } catch (err) { if (err instanceof ApplicationNotOwnedError) { throw new AccessDeniedError(); } if (err instanceof UnknownApplicationError) { throw err; } throw err; } }
async updateBotProfile( userId: UserID, applicationId: bigint, body: BotProfileUpdateRequest, ): Promise<BotProfileResponse> { try { const result = await this.applicationService.updateBotProfile(userId, createApplicationID(applicationId), { username: body.username, discriminator: body.discriminator, avatar: body.avatar, banner: body.banner, bio: body.bio, botFlags: body.bot_flags, }); return mapBotProfileToResponse(result.user); } catch (err) { if (err instanceof ApplicationNotOwnedError) { throw new AccessDeniedError(); } if (err instanceof BotUserNotFoundError) { throw err; } if (err instanceof InvalidClientError || err instanceof UnknownApplicationError) { throw new UnknownApplicationError(); } if (err instanceof UsernameNotAvailableError) { throw err; } throw err; } }}