diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index ecdd37b..2e30ec2 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -20,7 +20,6 @@ import { BullConfigService } from './redis/service/bull-config.service'; import { RedisService } from './redis/service/redis.service'; import { OrganizationModule } from './organization/organization.module'; import { ClientModule } from './client/client.module'; -import { ViewModule } from './view/view.module'; @Module({ imports: [ @@ -72,7 +71,6 @@ import { ViewModule } from './view/view.module'; AuthModule, OrganizationModule, ClientModule, - ViewModule, ], controllers: [AppController], providers: [AppService], diff --git a/apps/backend/src/auth/controllers/interaction.controller.ts b/apps/backend/src/auth/controllers/interaction.controller.ts index 0363679..47a88c8 100644 --- a/apps/backend/src/auth/controllers/interaction.controller.ts +++ b/apps/backend/src/auth/controllers/interaction.controller.ts @@ -5,10 +5,10 @@ import { InternalServerErrorException, Logger, Post, + Query, Req, Res, } from '@nestjs/common'; -import { ApiExcludeController } from '@nestjs/swagger'; import { AuthService } from '../services/auth.service'; @@ -18,9 +18,10 @@ import { InteractionService } from '../oidc/service/interaction.service'; import { OidcService } from '../oidc/service/core.service'; import { InteractionParams, InteractionSession } from '../oidc/types/interaction.type'; import { User } from '../../database/models/user.model'; +import { ApiTags } from '@nestjs/swagger'; @Controller('interaction') -@ApiExcludeController() +@ApiTags('Interaction') export class InteractionController { constructor( private readonly authService: AuthService, @@ -31,7 +32,16 @@ export class InteractionController { logger = new Logger(InteractionController.name); @Get(':id') - async getUserInteraction(@Res() res: Response, @Req() req: Request, @CurrentUser() user: User) { + async getUserInteraction( + @Res() res: Response, + @Req() req: Request, + @CurrentUser() user: User, + @Query('id') interactionQuery: string, + ) { + if (!req.cookies['_interaction'] || !req.cookies['_interaction.sig'] || !interactionQuery) { + throw new BadRequestException('No Interaction found or invalid Interaction'); + } + const details: { session?: InteractionSession; uid: string; diff --git a/apps/backend/src/auth/controllers/view.controller.ts b/apps/backend/src/auth/controllers/view.controller.ts deleted file mode 100644 index 2986222..0000000 --- a/apps/backend/src/auth/controllers/view.controller.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class AuthViewController { - constructor() {} -} diff --git a/apps/backend/src/client/client.module.ts b/apps/backend/src/client/client.module.ts index 937d195..76a4271 100644 --- a/apps/backend/src/client/client.module.ts +++ b/apps/backend/src/client/client.module.ts @@ -2,12 +2,14 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { RedisModule } from '../redis/redis.module'; -import { DATABASE_ENTITIES } from '../database/database.entities'; +import { ClientController } from './controller/client.controller'; +import { ClientService } from './service/client.service'; +import { OidcClient } from '@server/database/models/oidc_client.model'; @Module({ - imports: [TypeOrmModule.forFeature(DATABASE_ENTITIES), RedisModule], - controllers: [], - providers: [], - exports: [], + imports: [TypeOrmModule.forFeature([OidcClient]), RedisModule], + controllers: [ClientController], + providers: [ClientService], + exports: [ClientService], }) export class ClientModule {} diff --git a/apps/backend/src/client/controller/client.controller.ts b/apps/backend/src/client/controller/client.controller.ts index e88320a..8c2d915 100644 --- a/apps/backend/src/client/controller/client.controller.ts +++ b/apps/backend/src/client/controller/client.controller.ts @@ -1,6 +1,14 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; +import { ClientService } from '../service/client.service'; -@Controller('organization') -@ApiTags('Clients') -export class ClientsController {} +@Controller('client') +@ApiTags('Client') +export class ClientController { + constructor(private readonly clientService: ClientService) {} + + @Get() + async getOrganizations() { + return this.clientService.getClients(); + } +} diff --git a/apps/backend/src/client/service/client.service.ts b/apps/backend/src/client/service/client.service.ts index 546fc15..3a492b0 100644 --- a/apps/backend/src/client/service/client.service.ts +++ b/apps/backend/src/client/service/client.service.ts @@ -11,5 +11,10 @@ export class ClientService { private readonly clientRepository: Repository, ) {} - public async getClients() {} + public async getClients() { + return await this.clientRepository.find(); + } + + /** Creates a new client, requires the user to be in an org*/ + public async createClient() {} } diff --git a/apps/backend/src/database/pagination/pagination.module.ts b/apps/backend/src/database/pagination/pagination.module.ts new file mode 100644 index 0000000..e69de29 diff --git a/apps/backend/src/database/pagination/paginator.const.ts b/apps/backend/src/database/pagination/paginator.const.ts new file mode 100644 index 0000000..b87971e --- /dev/null +++ b/apps/backend/src/database/pagination/paginator.const.ts @@ -0,0 +1,5 @@ +export const PAGINATOR_OPTIONS = Symbol('PAGINATOR_OPTIONS'); +export const PAGINATOR_REPOSITORY = Symbol('PAGINATOR_REPOSITORY'); +export const PAGINATOR_CURSOR_EXTRACTOR = Symbol('PAGINATOR_CURSOR_EXTRACTOR'); +export const PAGINATOR_CURSOR_MAKER = Symbol('PAGINATOR_CURSOR_MAKER'); +export const PAGINATOR_ORDER = Symbol('PAGINATOR_ORDER'); diff --git a/apps/backend/src/database/pagination/paginator.interface.ts b/apps/backend/src/database/pagination/paginator.interface.ts new file mode 100644 index 0000000..296d444 --- /dev/null +++ b/apps/backend/src/database/pagination/paginator.interface.ts @@ -0,0 +1,36 @@ +import type { + InjectionToken, + ModuleMetadata, + OptionalFactoryDependency, + Provider, + Type, +} from '@nestjs/common'; +import type { FindOptionsOrder, FindOptionsWhere, ObjectLiteral, Repository } from 'typeorm'; + +export type CursorMaker = (entity: T) => string | null; +export type CursorExtractor = (cursor: string) => FindOptionsWhere; + +export interface PaginatorModuleOptions { + repository: Repository; + cursorMaker: CursorMaker; + cursorExtractor: CursorExtractor; + order: FindOptionsOrder; +} + +export interface PaginatorModuleOptionsFactory { + createPaginatorOptions(): + | Promise> + | PaginatorModuleOptions; +} + +export interface PaginatorModuleAsyncOptions + extends Pick { + useExisting?: Type>; + useClass?: Type>; + useFactory?: ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...args: any[] + ) => Promise> | PaginatorModuleOptions; + inject?: (InjectionToken | OptionalFactoryDependency)[]; + extraProviders?: Provider[]; +} diff --git a/apps/backend/src/database/pagination/paginator.service.ts b/apps/backend/src/database/pagination/paginator.service.ts new file mode 100644 index 0000000..e69de29 diff --git a/apps/backend/src/organization/controller/organization.controller.ts b/apps/backend/src/organization/controller/organization.controller.ts index e37ce1d..b8ec80e 100644 --- a/apps/backend/src/organization/controller/organization.controller.ts +++ b/apps/backend/src/organization/controller/organization.controller.ts @@ -11,22 +11,50 @@ import { CurrentUser } from '../../auth/decorators/user.decorator'; export class OrganizationController { constructor(private readonly organizationService: OrganizationService) {} + @ApiOperation({ + summary: 'Create organization', + description: 'Create a new organization', + }) + @Post() + public async createOrganization( + @Body() body: CreateOrgDto, + @CurrentUser() user: User, + ): Promise { + return await this.organizationService.createOrganization(user.id, body); + } + + @ApiOperation({ + summary: 'Get organizations', + description: 'Get a paginated list of organizations', + }) @Get() // Admin: Paginated list of organizations. public async getOrganizations(): Promise { return await this.organizationService.getOrganizations(); } + @ApiOperation({ + summary: 'Get organization by ID', + description: 'Get an organization by its ID', + }) @Get(':id') public async getOrganization(@Param('id') id: string): Promise { return await this.organizationService.getOrganizationById(id); } + @ApiOperation({ + summary: 'Get organization members', + description: 'Get a list of members in an organization', + }) @Get(':id/members') public async getOrganizationMembers(@Param('id') id: string): Promise { return await this.organizationService.getOrgMembers(id); } + @ApiOperation({ + summary: 'Delete an organization', + description: 'Delete an organization and all associated data.', + }) @Delete(':id') public async deleteOrganization(@Param('id') id: string): Promise { return await this.organizationService.deleteOrganization(id); @@ -35,16 +63,14 @@ export class OrganizationController { @Patch(':id') public async partialUpdateOrganization(@Param('id') id: string): Promise {} + @ApiOperation({ + summary: "Update's an organization's details", + }) @Put(':id') public async updateOrganization(@Param('id') id: string): Promise {} - @Post() - public async createOrganization( - @Body() body: CreateOrgDto, - @CurrentUser() user: User, - ): Promise { - return await this.organizationService.createOrganization(user.id, body); - } + // === Flags === + // These are normally handled by Waterwolf to enable certain privileges. Ex. Portal Network /** * This sets a flag on the organization. diff --git a/apps/backend/src/view/controllers/view.controller.ts b/apps/backend/src/view/controllers/view.controller.ts deleted file mode 100644 index b7c6227..0000000 --- a/apps/backend/src/view/controllers/view.controller.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Controller, Get, Render, UseGuards } from '@nestjs/common'; -import { ApiExcludeController, ApiExcludeEndpoint } from '@nestjs/swagger'; - -import { ViewLoginGuard } from '../../auth/guard/viewLogin.guard'; -import { CurrentUser } from '../../auth/decorators/user.decorator'; -import { User } from '../../database/models/user.model'; - -@ApiExcludeController() -@Controller() -export class ViewController { - @Get('auth/login') - @Render('auth/login') - @ApiExcludeEndpoint() - @UseGuards(ViewLoginGuard) - public async loginView(): Promise { - return { - forgot_password: 'forgot-password', - register: 'register', - login_url: '/auth/login', - //background_image: 'https://waterwolf.club/static/img/portal/portal7.jpg', - }; - } - - @Get('auth/login/totp') - @Render('auth/login-totp') - @ApiExcludeEndpoint() - @UseGuards(ViewLoginGuard) - public async getLoginTotp(): Promise { - return { - login: 'login', - methods: ['authenticator', 'email'], - }; - } - - @Get('auth/register') - @Render('auth/register') - @ApiExcludeEndpoint() - @UseGuards(ViewLoginGuard) - public async getRegister(): Promise { - return { - login: 'login', - }; - } - - @Get('auth/forgot-password') - @UseGuards(ViewLoginGuard) - @Render('auth/forgot-password') - @ApiExcludeEndpoint() - public async getForgotPassword(): Promise { - return { - login: 'login', - }; - } - - @Get('home') - @Render('home/index') - @ApiExcludeEndpoint() - @UseGuards(ViewLoginGuard) - public async getHomeView(@CurrentUser() user: User): Promise { - return { - user: { - name: user.displayName ?? user.username, - avatar: user.avatar, - email: user.email, - }, - }; - } -} diff --git a/apps/backend/src/view/view.module.ts b/apps/backend/src/view/view.module.ts deleted file mode 100644 index b007405..0000000 --- a/apps/backend/src/view/view.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ViewController } from './controllers/view.controller'; - -@Module({ - imports: [], - controllers: [ViewController], - providers: [], - exports: [], -}) -export class ViewModule {}