From 9c6754c70f6e22a70f6d7c0f1f567cc97aa86d9d Mon Sep 17 00:00:00 2001 From: Balu Babu Date: Wed, 21 Feb 2024 21:15:47 +0530 Subject: [PATCH] feat: inital setup info route (#3847) --- docker-compose.yml | 2 +- .../src/admin/guards/rest-admin.guard.ts | 11 +++++ .../infra-config/infra-config.controller.ts | 47 +++++++++++++++++++ .../src/infra-config/infra-config.module.ts | 2 + .../src/infra-config/infra-config.service.ts | 30 ++++++++++-- .../src/types/InfraConfig.ts | 4 ++ 6 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 packages/hoppscotch-backend/src/admin/guards/rest-admin.guard.ts create mode 100644 packages/hoppscotch-backend/src/infra-config/infra-config.controller.ts diff --git a/docker-compose.yml b/docker-compose.yml index b7f3d2f52..f8e085502 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -118,7 +118,7 @@ services: restart: always environment: # Edit the below line to match your PostgresDB URL if you have an outside DB (make sure to update the .env file as well) - - DATABASE_URL=postgresql://postgres:testpass@hoppscotch-db:5432/hoppscotch?connect_timeout=300 + # - DATABASE_URL=postgresql://postgres:testpass@hoppscotch-db:5432/hoppscotch?connect_timeout=300 - PORT=3000 volumes: # Uncomment the line below when modifying code. Only applicable when using the "dev" target. diff --git a/packages/hoppscotch-backend/src/admin/guards/rest-admin.guard.ts b/packages/hoppscotch-backend/src/admin/guards/rest-admin.guard.ts new file mode 100644 index 000000000..c855dc74f --- /dev/null +++ b/packages/hoppscotch-backend/src/admin/guards/rest-admin.guard.ts @@ -0,0 +1,11 @@ +import { Injectable, ExecutionContext, CanActivate } from '@nestjs/common'; + +@Injectable() +export class RESTAdminGuard implements CanActivate { + canActivate(context: ExecutionContext): boolean { + const request = context.switchToHttp().getRequest(); + const user = request.user; + + return user.isAdmin; + } +} diff --git a/packages/hoppscotch-backend/src/infra-config/infra-config.controller.ts b/packages/hoppscotch-backend/src/infra-config/infra-config.controller.ts new file mode 100644 index 000000000..117008b22 --- /dev/null +++ b/packages/hoppscotch-backend/src/infra-config/infra-config.controller.ts @@ -0,0 +1,47 @@ +import { Controller, Get, HttpStatus, Put, UseGuards } from '@nestjs/common'; +import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.guard'; +import { InfraConfigService } from './infra-config.service'; +import * as E from 'fp-ts/Either'; +import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard'; +import { RESTAdminGuard } from 'src/admin/guards/rest-admin.guard'; +import { throwHTTPErr } from 'src/auth/helper'; +import { AuthError } from 'src/types/AuthError'; +import { InfraConfigEnumForClient } from 'src/types/InfraConfig'; + +@UseGuards(ThrottlerBehindProxyGuard) +@Controller({ path: 'site', version: '1' }) +export class SiteController { + constructor(private infraConfigService: InfraConfigService) {} + + @Get('setup') + @UseGuards(JwtAuthGuard, RESTAdminGuard) + async fetchSetupInfo() { + const status = await this.infraConfigService.get( + InfraConfigEnumForClient.IS_FIRST_TIME_INFRA_SETUP, + ); + + if (E.isLeft(status)) + throwHTTPErr({ + message: status.left, + statusCode: HttpStatus.NOT_FOUND, + }); + return status.right; + } + + @Put('setup') + @UseGuards(JwtAuthGuard, RESTAdminGuard) + async setSetupAsComplete() { + const res = await this.infraConfigService.update( + InfraConfigEnumForClient.IS_FIRST_TIME_INFRA_SETUP, + false.toString(), + false, + ); + + if (E.isLeft(res)) + throwHTTPErr({ + message: res.left, + statusCode: HttpStatus.FORBIDDEN, + }); + return res.right; + } +} diff --git a/packages/hoppscotch-backend/src/infra-config/infra-config.module.ts b/packages/hoppscotch-backend/src/infra-config/infra-config.module.ts index dafd25ae8..cfb0ee3a2 100644 --- a/packages/hoppscotch-backend/src/infra-config/infra-config.module.ts +++ b/packages/hoppscotch-backend/src/infra-config/infra-config.module.ts @@ -1,10 +1,12 @@ import { Module } from '@nestjs/common'; import { InfraConfigService } from './infra-config.service'; import { PrismaModule } from 'src/prisma/prisma.module'; +import { SiteController } from './infra-config.controller'; @Module({ imports: [PrismaModule], providers: [InfraConfigService], exports: [InfraConfigService], + controllers: [SiteController], }) export class InfraConfigModule {} diff --git a/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts b/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts index 92be7e9dd..c90b5ab10 100644 --- a/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts +++ b/packages/hoppscotch-backend/src/infra-config/infra-config.service.ts @@ -34,7 +34,9 @@ export class InfraConfigService implements OnModuleInit { await this.initializeInfraConfigTable(); } - getDefaultInfraConfigs(): { name: InfraConfigEnum; value: string }[] { + async getDefaultInfraConfigs(): Promise< + { name: InfraConfigEnum; value: string }[] + > { // Prepare rows for 'infra_config' table with default values (from .env) for each 'name' const infraConfigDefaultObjs: { name: InfraConfigEnum; value: string }[] = [ { @@ -73,6 +75,10 @@ export class InfraConfigService implements OnModuleInit { name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS, value: getConfiguredSSOProviders(), }, + { + name: InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP, + value: (await this.prisma.infraConfig.count()) === 0 ? 'true' : 'false', + }, ]; return infraConfigDefaultObjs; @@ -88,7 +94,7 @@ export class InfraConfigService implements OnModuleInit { const enumValues = Object.values(InfraConfigEnum); // Fetch the default values (value in .env) for configs to be saved in 'infra_config' table - const infraConfigDefaultObjs = this.getDefaultInfraConfigs(); + const infraConfigDefaultObjs = await this.getDefaultInfraConfigs(); // Check if all the 'names' are listed in the default values if (enumValues.length !== infraConfigDefaultObjs.length) { @@ -147,11 +153,13 @@ export class InfraConfigService implements OnModuleInit { * Update InfraConfig by name * @param name Name of the InfraConfig * @param value Value of the InfraConfig + * @param restartEnabled If true, restart the app after updating the InfraConfig * @returns InfraConfig model */ async update( name: InfraConfigEnumForClient | InfraConfigEnum, value: string, + restartEnabled = false, ) { const isValidate = this.validateEnvValues([{ name, value }]); if (E.isLeft(isValidate)) return E.left(isValidate.left); @@ -162,7 +170,7 @@ export class InfraConfigService implements OnModuleInit { data: { value }, }); - stopApp(); + if (restartEnabled) stopApp(); return E.right(this.cast(infraConfig)); } catch (e) { @@ -261,6 +269,7 @@ export class InfraConfigService implements OnModuleInit { const isUpdated = await this.update( InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS, updatedAuthProviders.join(','), + true, ); if (E.isLeft(isUpdated)) return E.left(isUpdated.left); @@ -316,13 +325,24 @@ export class InfraConfigService implements OnModuleInit { */ async reset() { try { - const infraConfigDefaultObjs = this.getDefaultInfraConfigs(); + const infraConfigDefaultObjs = await this.getDefaultInfraConfigs(); await this.prisma.infraConfig.deleteMany({ where: { name: { in: infraConfigDefaultObjs.map((p) => p.name) } }, }); + + // Hardcode t + const updatedInfraConfigDefaultObjs = infraConfigDefaultObjs.filter( + (obj) => obj.name !== InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP, + ); await this.prisma.infraConfig.createMany({ - data: infraConfigDefaultObjs, + data: [ + ...updatedInfraConfigDefaultObjs, + { + name: InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP, + value: 'true', + }, + ], }); stopApp(); diff --git a/packages/hoppscotch-backend/src/types/InfraConfig.ts b/packages/hoppscotch-backend/src/types/InfraConfig.ts index 5c47eb9e0..4980e0abd 100644 --- a/packages/hoppscotch-backend/src/types/InfraConfig.ts +++ b/packages/hoppscotch-backend/src/types/InfraConfig.ts @@ -12,6 +12,8 @@ export enum InfraConfigEnum { MICROSOFT_CLIENT_SECRET = 'MICROSOFT_CLIENT_SECRET', VITE_ALLOWED_AUTH_PROVIDERS = 'VITE_ALLOWED_AUTH_PROVIDERS', + + IS_FIRST_TIME_INFRA_SETUP = 'IS_FIRST_TIME_INFRA_SETUP', } export enum InfraConfigEnumForClient { @@ -26,4 +28,6 @@ export enum InfraConfigEnumForClient { MICROSOFT_CLIENT_ID = 'MICROSOFT_CLIENT_ID', MICROSOFT_CLIENT_SECRET = 'MICROSOFT_CLIENT_SECRET', + + IS_FIRST_TIME_INFRA_SETUP = 'IS_FIRST_TIME_INFRA_SETUP', }