import { OAuth2AccessToken } from '@icynet/oauth2-provider'; import { Controller, Get, NotFoundException, Param, StreamableFile, UseGuards, } from '@nestjs/common'; import { join } from 'path'; import { createReadStream } from 'fs'; import { Bearer } from 'src/decorators/bearer.decorator'; import { Scope } from 'src/decorators/scope.decorator'; import { CurrentUser } from 'src/decorators/user.decorator'; import { OAuth2Guard } from 'src/guards/oauth2.guard'; import { ScopesGuard } from 'src/guards/scopes.guard'; import { ConfigurationService } from '../config/config.service'; import { OAuth2ClientService } from '../objects/oauth2-client/oauth2-client.service'; import { User } from '../objects/user/user.entity'; import { FormUtilityService } from '../utility/services/form-utility.service'; import { UploadService } from '../objects/upload/upload.service'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; @ApiTags('api') @Controller({ path: '/api', }) export class ApiController { constructor( private _config: ConfigurationService, private _oaClient: OAuth2ClientService, private _form: FormUtilityService, private _upload: UploadService, ) {} @Get() welcome() { return { welcome: { to: 'Icy Network!' }, api: true, oauth2: { authorizationUrl: '/oauth2/authorize', tokenUrl: '/oauth2/token', userUrl: '/api/user', }, swaggerdoc: '/api/openapi', }; } @Get('/user') @ApiBearerAuth() @UseGuards(OAuth2Guard) getCurrentUser(@CurrentUser() user: User, @Scope() scopeStr: string) { const scope = scopeStr.split(' '); // Management scope allows access to basically everything anyway const scopelessAccess = scope.includes('management'); const userData: Record = { id: user.id, uuid: user.uuid, username: user.username, display_name: user.display_name, // Standard claims sub: user.uuid, name: user.display_name, preferred_username: user.username, nickname: user.display_name, }; if (scope.includes('email') || scopelessAccess) { userData.email = user.email; userData.email_verified = true; } if ((scope.includes('picture') || scopelessAccess) && user.picture) { userData.picture = `${this._config.get('app.base_url')}/uploads/${ user.picture.file }`; userData.picture_file = user.picture.file; } if ( (scope.includes('privileges') || scopelessAccess) && user.privileges?.length ) { userData.privileges = user.privileges.map(({ name }) => name); } return userData; } @Get('/client') @ApiBearerAuth() @UseGuards(OAuth2Guard, ScopesGuard) async getCurrentClient(@Bearer() token: OAuth2AccessToken) { const client = await this._oaClient.getById(token.client_id, [ 'urls', 'picture', ]); return this._form.pluckObject(client, [ 'title', 'description', 'client_id', 'urls', 'picture', 'verified', ]); } @Get('/upload/:file') @ApiBearerAuth() @UseGuards(OAuth2Guard) async sendFile(@Param('file') fileName: string) { const cleanFile = decodeURI(fileName).replace(/(\&(.*))/, ''); const file = await this._upload.getByFile(cleanFile); if (!file) { throw new NotFoundException('File not found'); } const path = join(this._upload.uploadPath, file.file); const stream = createReadStream(path); return new StreamableFile(stream); } }