diff --git a/apps/backend/src/api/routes/settings.controller.ts b/apps/backend/src/api/routes/settings.controller.ts index e195b242d..1a83699cd 100644 --- a/apps/backend/src/api/routes/settings.controller.ts +++ b/apps/backend/src/api/routes/settings.controller.ts @@ -8,6 +8,7 @@ import { Sections, } from '@gitroom/backend/services/auth/permissions/permissions.service'; import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service'; +import { ConfigurationVariableService } from '@gitroom/nestjs-libraries/database/prisma/configuration/configuration.variable.service'; import {AddTeamMemberDto} from "@gitroom/nestjs-libraries/dtos/settings/add.team.member.dto"; import {ApiTags} from "@nestjs/swagger"; @@ -16,9 +17,18 @@ import {ApiTags} from "@nestjs/swagger"; export class SettingsController { constructor( private _starsService: StarsService, - private _organizationService: OrganizationService + private _organizationService: OrganizationService, + private _configurationVariableService: ConfigurationVariableService ) {} + @Get('/cvars/all') + @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) + async getConfigurationVariables() { + return { + configurationVariables: await this._configurationVariableService.getAll(), + }; + } + @Get('/github') @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) async getConnectedGithubAccounts(@GetOrgFromRequest() org: Organization) { diff --git a/apps/frontend/src/components/layout/top.menu.tsx b/apps/frontend/src/components/layout/top.menu.tsx index e750963c6..606c5cf2d 100644 --- a/apps/frontend/src/components/layout/top.menu.tsx +++ b/apps/frontend/src/components/layout/top.menu.tsx @@ -33,16 +33,12 @@ export const useMenuItems = () => { }, ] : []), - ...(!isGeneral - ? [ - { - name: 'Settings', - icon: 'settings', - path: '/settings', - role: ['ADMIN', 'SUPERADMIN'], - }, - ] - : []), + { + name: 'Settings', + icon: 'settings', + path: '/settings', + role: ['ADMIN', 'SUPERADMIN'], + }, { name: 'Marketplace', icon: 'marketplace', diff --git a/apps/frontend/src/components/settings/configuration-variable-editor.component.tsx b/apps/frontend/src/components/settings/configuration-variable-editor.component.tsx new file mode 100644 index 000000000..a3b8c2405 --- /dev/null +++ b/apps/frontend/src/components/settings/configuration-variable-editor.component.tsx @@ -0,0 +1,79 @@ +'use client'; + +// import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; +// import { useMemo } from 'react'; +// import { classValidatorResolver } from '@hookform/resolvers/class-validator'; +// import { SaveConfigurationVariableDto, SaveConfigurationVariablesDto } from '@gitroom/nestjs-libraries/dtos/settings/configuration-variables.dto.ts'; +import { Button } from '@gitroom/react/form/button' +import { useCallback, useState } from 'react' +import useSWR from 'swr' +import { useFetch } from '@gitroom/helpers/utils/custom.fetch' + +export const ConfigurationVariableEditorComponent = () => { + // const resolver = useMemo(() => classValidatorResolver(SaveConfigurationVariableDto), []); + + const fetch = useFetch(); + + // const form = useForm({ resolver, values: { message: '' } }); + + const [state, setState] = useState(true); + + const fetchCvars = useCallback(async () => { + const cvars = await ( + await fetch('/settings/cvars/all', { + method: 'GET', + }) + ).json(); + + setState(cvars); + return cvars; + }, []) + + const { data, error, isLoading } = useSWR('/settings/cvars/all', fetchCvars) + + /* + const submit: SubmitHandler = async (data) => { + await fetch(`/settings/cvars/${params.id}`, { + method: 'POST', + body: JSON.stringify(data), + }); + mutate(); + form.reset(); + } + */ + + if (isLoading) return
Loading...
+ if (error) return
Error loading data
+ + return ( +
+

Configuration Variable Editor

+

This screen is only accessible and editable by super admins, it includes configuration that effects the entire app.

+ + {data.configurationVariables.map((cvar) => ( +
+
+ +

{cvar.description}

+ + {cvar.docs && ( + More docs... + )} +
+
+ {cvar.datatype === 'bool' ? ( + + ) : ( + + )} +
+
+ ))} + + + +
+ ) +} diff --git a/apps/frontend/src/components/settings/settings.component.tsx b/apps/frontend/src/components/settings/settings.component.tsx index 051604f12..0e8bb794e 100644 --- a/apps/frontend/src/components/settings/settings.component.tsx +++ b/apps/frontend/src/components/settings/settings.component.tsx @@ -4,6 +4,7 @@ import { GithubComponent } from '@gitroom/frontend/components/settings/github.co import { useCallback, useEffect } from 'react'; import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { TeamsComponent } from '@gitroom/frontend/components/settings/teams.component'; +import { ConfigurationVariableEditorComponent } from '@gitroom/frontend/components/settings/configuration-variable-editor.component'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import useSWR from 'swr'; import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; @@ -70,6 +71,9 @@ export const SettingsComponent = () => { {/**/} )} + + + {!!user?.tier?.team_members && } ); diff --git a/libraries/nestjs-libraries/src/database/prisma/configuration/configuration.variable.repository.ts b/libraries/nestjs-libraries/src/database/prisma/configuration/configuration.variable.repository.ts new file mode 100644 index 000000000..2d9ea53f2 --- /dev/null +++ b/libraries/nestjs-libraries/src/database/prisma/configuration/configuration.variable.repository.ts @@ -0,0 +1,44 @@ +import { PrismaRepository } from '@gitroom/nestjs-libraries/database/prisma/prisma.service'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class ConfigurationVariableRepository { + constructor( + private _configurationVariables: PrismaRepository<'configurationVariables'> + ) {} + + getOrDefault(key: string, defaultValue: string) { + const dbVal = this._configurationVariables.model.configurationVariables.findFirst({ + where: { + key, + }, + }); + + if (dbVal) { + return dbVal; + } else { + return defaultValue; + } + } + + isSet(key: string) { + return !!this._configurationVariables.model.configurationVariables.findFirst({ + where: { + key, + }, + }); + } + + set(key: string, value: string) { + return this._configurationVariables.model.configurationVariables.create({ + data: { + key, + value, + }, + }); + } + + getAll() { + return this._configurationVariables.model.configurationVariables.findMany(); + } +} diff --git a/libraries/nestjs-libraries/src/database/prisma/configuration/configuration.variable.service.ts b/libraries/nestjs-libraries/src/database/prisma/configuration/configuration.variable.service.ts new file mode 100644 index 000000000..b8e3376c1 --- /dev/null +++ b/libraries/nestjs-libraries/src/database/prisma/configuration/configuration.variable.service.ts @@ -0,0 +1,82 @@ +import { Injectable } from '@nestjs/common'; + +import { ConfigurationVariableRepository } from '@gitroom/nestjs-libraries/database/prisma/configuration/configuration.variable.repository'; + +@Injectable() +export class ConfigurationVariableService { + private cvars: { + key: string, + title: string, + description: string, + datatype: string, + default: string | null, + val: string | null, + section: string[], + docs?: string, + }[]; + + constructor( + private _configurationVariableRepository: ConfigurationVariableRepository, + ) { + this.cvars = [ + { + key: 'USER_REGISTRATION_DISABLED', + title: 'Disable user registration', + description: 'If user registration is disabled, only super admins can create new users', + datatype: 'bool', + default: 'true', + val: 'true', + section: ['Functionality'], + }, + { + key: 'MARKETPLACE_DISABLED', + title: 'Disable marketplace', + description: 'If the marketplace is disabled, users will not be able to buy or sell posts', + datatype: 'bool', + default: 'true', + val: 'true', + section: ['Functionality'], + }, + { + key: 'DISCORD_CLIENT_ID', + title: 'Discord client ID', + description: 'Used to authenticate with Discord with OAuth.', + docs: 'https://docs.postiz.com/providers/discord', + datatype: 'string', + default: null, + val: null, + section: ['Providers', 'Discord'], + }, + { + key: 'DISCORD_CLIENT_SECRET', + title: 'Discord client secret', + description: 'Used to authenticate with Discord with OAuth.', + datatype: 'string', + default: null, + val: null, + section: ['Providers', 'Discord'], + }, + ] + } + + getOrDefault(key: string, defaultValue: string) { + return this._configurationVariableRepository.getOrDefault(key, defaultValue); + } + + getOrEmpty(key: string) { + return this._configurationVariableRepository.getOrDefault(key, ''); + } + + isSet(key: string) { + return this._configurationVariableRepository.isSet(key); + } + + set(key: string, value: string) { + return this._configurationVariableRepository.set(key, value); + } + + getAll() { + return this.cvars; +// return this._configurationVariableRepository.getAll(); + } +} diff --git a/libraries/nestjs-libraries/src/database/prisma/database.module.ts b/libraries/nestjs-libraries/src/database/prisma/database.module.ts index 99b563858..fd4a3ef6d 100644 --- a/libraries/nestjs-libraries/src/database/prisma/database.module.ts +++ b/libraries/nestjs-libraries/src/database/prisma/database.module.ts @@ -2,6 +2,8 @@ import { Global, Module } from '@nestjs/common'; import { PrismaRepository, PrismaService } from './prisma.service'; import { OrganizationRepository } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.repository'; import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service'; +import { ConfigurationVariableRepository } from '@gitroom/nestjs-libraries/database/prisma/configuration/configuration.variable.repository'; +import { ConfigurationVariableService } from '@gitroom/nestjs-libraries/database/prisma/configuration/configuration.variable.service'; import { UsersService } from '@gitroom/nestjs-libraries/database/prisma/users/users.service'; import { UsersRepository } from '@gitroom/nestjs-libraries/database/prisma/users/users.repository'; import { StarsService } from '@gitroom/nestjs-libraries/database/prisma/stars/stars.service'; @@ -41,6 +43,8 @@ import { AgenciesRepository } from '@gitroom/nestjs-libraries/database/prisma/ag UsersRepository, OrganizationService, OrganizationRepository, + ConfigurationVariableService, + ConfigurationVariableRepository, StarsService, StarsRepository, SubscriptionService, diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index 7cd6ac59f..557372f7e 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -455,6 +455,13 @@ model Messages { @@index([deletedAt]) } +model ConfigurationVariables { + key String @unique + value String + + @@index([key]) +} + enum OrderStatus { PENDING ACCEPTED @@ -502,4 +509,4 @@ enum APPROVED_SUBMIT_FOR_ORDER { NO WAITING_CONFIRMATION YES -} \ No newline at end of file +} diff --git a/libraries/nestjs-libraries/src/dtos/settings/configuration-variables.dto.ts b/libraries/nestjs-libraries/src/dtos/settings/configuration-variables.dto.ts new file mode 100644 index 000000000..9901f6e23 --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/settings/configuration-variables.dto.ts @@ -0,0 +1,16 @@ +import {IsDefined, IsString, ValidateNested} from 'class-validator'; +import {Type} from 'class-transformer'; + +export class SaveConfigurationVariableDto { + @IsString() + key: string; + + @IsString() + val: string; +} + +export class SaveConfigurationVariablesDto { + @ValidateNested({each: true}) + @Type(() => ConfigurationVariableDto) + configurationVariables: Record; +}