diff --git a/apps/backend/.gitignore b/apps/backend/.gitignore
index f45ea2d91..de36198c5 100644
--- a/apps/backend/.gitignore
+++ b/apps/backend/.gitignore
@@ -66,5 +66,4 @@ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
!/uploads/temp/index.html
# Configuration
-/src/plugins/*/admin/database/migrations
/src/plugins/core/
\ No newline at end of file
diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts
index 26920ec19..fca7573e1 100644
--- a/apps/backend/src/app.module.ts
+++ b/apps/backend/src/app.module.ts
@@ -17,6 +17,11 @@ import { PluginsModule } from './plugins/plugins.module';
config: DATABASE_ENVS,
schemaDatabase,
},
+ // captcha: {
+ // type: 'cloudflare_turnstile',
+ // secret_key: '',
+ // site_key: '',
+ // },
// email: emailResend({
// api_key: process.env.EMAIL_RESEND_API_KEY,
// from: process.env.EMAIL_RESEND_FROM,
diff --git a/apps/frontend/src/plugins/admin/langs/en.json b/apps/frontend/src/plugins/admin/langs/en.json
index 024bd5f63..484edfdba 100644
--- a/apps/frontend/src/plugins/admin/langs/en.json
+++ b/apps/frontend/src/plugins/admin/langs/en.json
@@ -141,28 +141,6 @@
"send_testing_email": "Send Testing Email"
}
},
- "security": {
- "captcha": {
- "title": "Captcha",
- "desc": "Captcha is a security feature that requires users to complete a challenge to prove they are human. This helps prevent spam and abuse.",
- "type": {
- "title": "Type",
- "none": {
- "title": "None"
- },
- "recaptcha_desc": "Read more on Google reCAPTCHA.",
- "recaptcha_v2_invisible": "reCAPTCHA v2 Invisible",
- "recaptcha_v2_checkbox": "reCAPTCHA v2 Checkbox",
- "recaptcha_v3": "reCAPTCHA v3",
- "cloudflare_turnstile": {
- "title": "Cloudflare Turnstile",
- "desc": "Read more on Cloudflare Turnstile."
- }
- },
- "site_key": "Site Key",
- "secret_key": "Secret Key"
- }
- },
"authorization": {
"title": "Authorization",
"desc": "Manage authorization settings for your website.",
@@ -569,30 +547,6 @@
"submit": "Yes, delete file"
}
}
- },
- "security": {
- "spam": {
- "title": "Spam Protection",
- "captcha": {
- "title": "Captcha",
- "type": {
- "title": "Type",
- "none": {
- "title": "None"
- },
- "recaptcha_desc": "Read more on Google reCAPTCHA.",
- "recaptcha_v2_invisible": "reCAPTCHA v2 Invisible",
- "recaptcha_v2_checkbox": "reCAPTCHA v2 Checkbox",
- "recaptcha_v3": "reCAPTCHA v3",
- "cloudflare_turnstile": {
- "title": "Cloudflare Turnstile",
- "desc": "Read more on Cloudflare Turnstile."
- }
- },
- "site_key": "Site Key",
- "secret_key": "Secret Key"
- }
- }
}
},
"members": {
diff --git a/apps/frontend/src/plugins/core/langs/en.json b/apps/frontend/src/plugins/core/langs/en.json
index d2ed2d3a3..72dc11b6c 100644
--- a/apps/frontend/src/plugins/core/langs/en.json
+++ b/apps/frontend/src/plugins/core/langs/en.json
@@ -358,8 +358,6 @@
"settings_email": "Email",
"settings_authorization": "Authorization",
"settings_legal": "Legal & Policies",
- "security": "Security",
- "security_spam": "Spam Protection",
"plugins": "Plugins",
"styles": "Styles",
"styles_theme-editor": "Theme Editor",
@@ -379,8 +377,6 @@
"can_manage_settings_authorization": "Can manage authorization settings?",
"can_manage_settings_legal": "Can manage legal settings?",
"can_manage_plugins": "Can manage plugins?",
- "security": "Security",
- "can_manage_security_spam": "Can manage spam protection?",
"styles": "Styles",
"can_manage_styles_theme-editor": "Can manage theme editor?",
"can_manage_styles_nav": "Can manage navigation?",
diff --git a/packages/backend/scripts/generate-config.ts b/packages/backend/scripts/generate-config.ts
deleted file mode 100644
index 7f5791469..000000000
--- a/packages/backend/scripts/generate-config.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { existsSync } from 'fs';
-import { mkdir, writeFile } from 'fs/promises';
-import { join } from 'path';
-
-import {
- ConfigType,
- DEFAULT_CONFIG_DATA,
- getConfigFile,
-} from '../src/helpers/config';
-import { updateObject } from './helpers/update-object';
-
-export const generateConfig = async ({
- pluginsPath,
-}: {
- pluginsPath: string;
-}) => {
- const folderPath = join(pluginsPath, 'core', 'utils');
- if (!existsSync(folderPath)) {
- await mkdir(folderPath, { recursive: true });
- }
-
- const configPath = join(folderPath, 'config.json');
- if (!existsSync(configPath)) {
- await writeFile(
- configPath,
- JSON.stringify(DEFAULT_CONFIG_DATA, null, 2),
- 'utf8',
- );
-
- return;
- }
-
- const config = getConfigFile();
- const updatedConfig: ConfigType = updateObject(config, DEFAULT_CONFIG_DATA);
-
- await writeFile(configPath, JSON.stringify(updatedConfig, null, 2), 'utf8');
-};
diff --git a/packages/backend/scripts/generate-manifest.ts b/packages/backend/scripts/generate-manifest.ts
index d847911d2..89dee322f 100644
--- a/packages/backend/scripts/generate-manifest.ts
+++ b/packages/backend/scripts/generate-manifest.ts
@@ -88,22 +88,6 @@ export const generateManifest = async ({
process.exit(1);
}
- const configPath = join(
- process.cwd(),
- 'src',
- 'plugins',
- 'core',
- 'utils',
- 'config.json',
- );
-
- if (!existsSync(configPath)) {
- console.log(
- `⛔️ Config file not found in 'backend/utils/config.json' directory. "${configPath}"`,
- );
- process.exit(1);
- }
-
const languages = await db.query.core_languages.findMany({
columns: {
code: true,
diff --git a/packages/backend/scripts/setup.ts b/packages/backend/scripts/setup.ts
index b904f6ba0..ef41b7628 100644
--- a/packages/backend/scripts/setup.ts
+++ b/packages/backend/scripts/setup.ts
@@ -9,7 +9,6 @@ import coreSchemaDatabase from '../src/database';
import { checkUpdateSchemaDatabase } from './check-update-schema-database';
import { copyFiles } from './copy-files';
import { generateDatabaseMigrations, runMigrations } from './database';
-import { generateConfig } from './generate-config';
import { generateManifest } from './generate-manifest';
import { updatePlugins } from './update-plugins';
@@ -37,12 +36,7 @@ const init = async () => {
const pluginsPath = getPluginsPath();
console.log(
- `${initConsole} [1/${skipDatabase ? 2 : 7}] Setup the project. Generating the config file...`,
- );
- await generateConfig({ pluginsPath });
-
- console.log(
- `${initConsole} [2/${skipDatabase ? 2 : 7}] Copying files into backend...`,
+ `${initConsole} [1/${skipDatabase ? 1 : 6}] Copying files into backend...`,
);
await copyFiles({ pluginsPath });
@@ -51,7 +45,7 @@ const init = async () => {
process.exit(0);
}
- console.log(`${initConsole} [3/7] Generating database migrations...`);
+ console.log(`${initConsole} [2/6] Generating database migrations...`);
await generateDatabaseMigrations();
const database = createClientDatabase({
@@ -60,17 +54,17 @@ const init = async () => {
});
console.log(
- `${initConsole} [4/7] Create tables in database using migrations...`,
+ `${initConsole} [3/6] Create tables in database using migrations...`,
);
await runMigrations();
- console.log(`${initConsole} [5/7] Updating plugins...`);
+ console.log(`${initConsole} [4/6] Updating plugins...`);
await updatePlugins({ pluginsPath, db: database.db });
- console.log(`${initConsole} [6/7] Checking and updating schema database...`);
+ console.log(`${initConsole} [5/6] Checking and updating schema database...`);
await checkUpdateSchemaDatabase({ db: database.db });
- console.log(`${initConsole} [7/7] Generating the manifest files...`);
+ console.log(`${initConsole} [6/6] Generating the manifest files...`);
await generateManifest({ db: database.db });
console.log(`${initConsole} ✅ Project setup complete.`);
diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts
index 06f34f82e..3058ac195 100644
--- a/packages/backend/src/app.module.ts
+++ b/packages/backend/src/app.module.ts
@@ -9,6 +9,7 @@ import { join } from 'path';
import { CoreModule } from './core/core.module';
import { SSOAuthItem } from './helpers/auth/sso/sso.service';
+import { CaptchaConfig } from './helpers/captcha/captcha.service';
import { EmailSenderFunction } from './helpers/email/email-helpers.type';
import { GlobalHelpersModule } from './helpers/helpers.module';
import {
@@ -171,7 +172,9 @@ export class VitNodeCoreModule {
database,
email,
ssoLoginMethod,
+ captcha,
}: {
+ captcha?: CaptchaConfig;
database: DatabaseModuleArgs;
email?: EmailSenderFunction;
ssoLoginMethod?: SSOAuthItem[];
@@ -207,7 +210,7 @@ export class VitNodeCoreModule {
maxAge: 31536000,
},
}),
- GlobalHelpersModule.register({ email, ssoLoginMethod }),
+ GlobalHelpersModule.register({ email, ssoLoginMethod, captcha }),
],
};
}
diff --git a/packages/backend/src/core/admin/admin.module.ts b/packages/backend/src/core/admin/admin.module.ts
index e8ac64157..62d555f3a 100644
--- a/packages/backend/src/core/admin/admin.module.ts
+++ b/packages/backend/src/core/admin/admin.module.ts
@@ -6,7 +6,6 @@ import { DashboardAdminModule } from './dashboard/dashboard.module';
import { LanguagesAdminModule } from './languages/languages.module';
import { MembersAdminModule } from './members/members.module';
import { PluginsAdminModule } from './plugins/plugins.module';
-import { SecurityAdminModule } from './security/security.module';
import { SettingsAdminModule } from './settings/settings.module';
import { StylesAdminModule } from './styles/styles.module';
@@ -19,7 +18,6 @@ import { StylesAdminModule } from './styles/styles.module';
PluginsAdminModule,
StylesAdminModule,
AdvancedAdminModule,
- SecurityAdminModule,
DashboardAdminModule,
],
})
diff --git a/packages/backend/src/core/admin/auth/services/nav/core.nav.ts b/packages/backend/src/core/admin/auth/services/nav/core.nav.ts
index fc2e1bb80..fde071acb 100644
--- a/packages/backend/src/core/admin/auth/services/nav/core.nav.ts
+++ b/packages/backend/src/core/admin/auth/services/nav/core.nav.ts
@@ -53,17 +53,6 @@ export const coreNav: ShowAuthAdminObj['nav'] = [
},
],
},
- {
- code: 'security',
- icon: 'shield',
- keywords: [],
- children: [
- {
- code: 'spam',
- keywords: ['spam', 'report', 'reporting', 'recaptcha'],
- },
- ],
- },
{
code: 'styles',
icon: 'paintbrush',
diff --git a/packages/backend/src/core/admin/members/staff/admin/helpers/core-admin-permissions.ts b/packages/backend/src/core/admin/members/staff/admin/helpers/core-admin-permissions.ts
index 12bf7ac24..e986f4349 100644
--- a/packages/backend/src/core/admin/members/staff/admin/helpers/core-admin-permissions.ts
+++ b/packages/backend/src/core/admin/members/staff/admin/helpers/core-admin-permissions.ts
@@ -19,10 +19,6 @@ export const coreAdminPermissions: PermissionsStaffObj[] = [
'can_manage_settings_legal',
],
},
- {
- id: 'security',
- permissions: ['can_manage_security_spam'],
- },
{
id: 'can_manage_plugins',
permissions: [],
diff --git a/packages/backend/src/core/admin/security/captcha/captcha.controller.ts b/packages/backend/src/core/admin/security/captcha/captcha.controller.ts
deleted file mode 100644
index 31021ef69..000000000
--- a/packages/backend/src/core/admin/security/captcha/captcha.controller.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Controllers } from '@/helpers/controller.decorator';
-import { Body, Get, Put } from '@nestjs/common';
-import { ApiOkResponse } from '@nestjs/swagger';
-import { ShowCaptchaSecurityAdminObj } from 'vitnode-shared/admin/security/captcha.dto';
-
-import { EditCaptchaSecurityAdminService } from './service/edit.service';
-import { ShowCaptchaSecurityAdminService } from './service/show.service';
-
-@Controllers({
- plugin_name: 'Core',
- plugin_code: 'security',
- isAdmin: true,
- route: 'captcha',
-})
-export class CaptchaSecurityAdminController {
- constructor(
- private readonly showService: ShowCaptchaSecurityAdminService,
- private readonly editService: EditCaptchaSecurityAdminService,
- ) {}
-
- @ApiOkResponse({
- description: 'Edit captcha settings',
- type: ShowCaptchaSecurityAdminObj,
- })
- @Put()
- async edit(
- @Body() body: ShowCaptchaSecurityAdminObj,
- ): Promise {
- return await this.editService.edit(body);
- }
-
- @ApiOkResponse({
- description: 'Show captcha settings',
- type: ShowCaptchaSecurityAdminObj,
- })
- @Get()
- async show(): Promise {
- return await this.showService.show();
- }
-}
diff --git a/packages/backend/src/core/admin/security/captcha/captcha.module.ts b/packages/backend/src/core/admin/security/captcha/captcha.module.ts
deleted file mode 100644
index dac69dbea..000000000
--- a/packages/backend/src/core/admin/security/captcha/captcha.module.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Module } from '@nestjs/common';
-
-import { CaptchaSecurityAdminController } from './captcha.controller';
-import { EditCaptchaSecurityAdminService } from './service/edit.service';
-import { ShowCaptchaSecurityAdminService } from './service/show.service';
-
-@Module({
- providers: [ShowCaptchaSecurityAdminService, EditCaptchaSecurityAdminService],
- controllers: [CaptchaSecurityAdminController],
-})
-export class CaptchaSecurityAdminModule {}
diff --git a/packages/backend/src/core/admin/security/captcha/helpers.service.ts b/packages/backend/src/core/admin/security/captcha/helpers.service.ts
deleted file mode 100644
index a2ebb884d..000000000
--- a/packages/backend/src/core/admin/security/captcha/helpers.service.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { ABSOLUTE_PATHS } from '@/app.module';
-import { Injectable } from '@nestjs/common';
-import { join } from 'path';
-
-export interface CaptchaSecurityConfig {
- secret_key: string;
-}
-
-@Injectable()
-export class HelpersCaptchaSecurityAdminService {
- protected readonly path: string = join(
- ABSOLUTE_PATHS.plugin({ code: 'core' }).root,
- 'utils',
- 'captcha.config.json',
- );
-}
diff --git a/packages/backend/src/core/admin/security/captcha/service/edit.service.ts b/packages/backend/src/core/admin/security/captcha/service/edit.service.ts
deleted file mode 100644
index dfa8a6871..000000000
--- a/packages/backend/src/core/admin/security/captcha/service/edit.service.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { configPath, getConfigFile } from '@/helpers/config';
-import { Injectable } from '@nestjs/common';
-import { writeFile } from 'fs/promises';
-import { ShowCaptchaSecurityAdminObj } from 'vitnode-shared/admin/security/captcha.dto';
-
-import {
- CaptchaSecurityConfig,
- HelpersCaptchaSecurityAdminService,
-} from '../helpers.service';
-
-@Injectable()
-export class EditCaptchaSecurityAdminService extends HelpersCaptchaSecurityAdminService {
- async edit({
- secret_key,
- ...rest
- }: ShowCaptchaSecurityAdminObj): Promise {
- const config = getConfigFile();
- const captchaSecurityConfig: CaptchaSecurityConfig = {
- secret_key,
- };
- config.security.captcha = {
- ...rest,
- };
-
- // Write public config to file
- await writeFile(configPath, JSON.stringify(config, null, 2));
- // Write default config to file
- await writeFile(this.path, JSON.stringify(captchaSecurityConfig, null, 2));
-
- return {
- ...config.security.captcha,
- ...captchaSecurityConfig,
- };
- }
-}
diff --git a/packages/backend/src/core/admin/security/captcha/service/show.service.ts b/packages/backend/src/core/admin/security/captcha/service/show.service.ts
deleted file mode 100644
index 9a2fdde07..000000000
--- a/packages/backend/src/core/admin/security/captcha/service/show.service.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { getConfigFile } from '@/helpers/config';
-import { Injectable } from '@nestjs/common';
-import { existsSync } from 'fs';
-import { readFile } from 'fs/promises';
-import { ShowCaptchaSecurityAdminObj } from 'vitnode-shared/admin/security/captcha.dto';
-
-import {
- CaptchaSecurityConfig,
- HelpersCaptchaSecurityAdminService,
-} from '../helpers.service';
-
-@Injectable()
-export class ShowCaptchaSecurityAdminService extends HelpersCaptchaSecurityAdminService {
- async show(): Promise {
- const config = getConfigFile();
-
- if (!existsSync(this.path)) {
- return {
- ...config.security.captcha,
- secret_key: '',
- };
- }
-
- const captchaSecurityConfig: CaptchaSecurityConfig = JSON.parse(
- await readFile(this.path, 'utf8'),
- );
-
- return {
- ...config.security.captcha,
- ...captchaSecurityConfig,
- };
- }
-}
diff --git a/packages/backend/src/core/admin/security/security.module.ts b/packages/backend/src/core/admin/security/security.module.ts
deleted file mode 100644
index c0ea204c7..000000000
--- a/packages/backend/src/core/admin/security/security.module.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { Module } from '@nestjs/common';
-
-import { CaptchaSecurityAdminModule } from './captcha/captcha.module';
-
-@Module({
- imports: [CaptchaSecurityAdminModule],
-})
-export class SecurityAdminModule {}
diff --git a/packages/backend/src/core/admin/settings/auth/methods/services/create.service.ts b/packages/backend/src/core/admin/settings/auth/methods/services/create.service.ts
index 77749cd9c..43a1825ce 100644
--- a/packages/backend/src/core/admin/settings/auth/methods/services/create.service.ts
+++ b/packages/backend/src/core/admin/settings/auth/methods/services/create.service.ts
@@ -1,11 +1,11 @@
+import { core_users_sso } from '@/database/schema/users';
import { SSOAuthConfig, SSOAuthHelper } from '@/helpers/auth/sso/sso.service';
+import { InternalDatabaseService } from '@/utils/database/internal_database.service';
import {
ConflictException,
Injectable,
NotFoundException,
} from '@nestjs/common';
-import { existsSync } from 'fs';
-import { writeFile } from 'fs/promises';
import {
CreateMethodAuthSettingsAdminBody,
ShowMethodAuthSettingsAdmin,
@@ -13,7 +13,10 @@ import {
@Injectable()
export class CreateMethodsAuthSettingsAdminService {
- constructor(private readonly ssoAuthHelper: SSOAuthHelper) {}
+ constructor(
+ private readonly ssoAuthHelper: SSOAuthHelper,
+ private readonly databaseService: InternalDatabaseService,
+ ) {}
async create({
code,
@@ -32,35 +35,21 @@ export class CreateMethodsAuthSettingsAdminService {
enabled: true,
};
- if (!existsSync(this.ssoAuthHelper.path)) {
- const dataToSave: SSOAuthConfig = {
- sso: [dataSSO],
- };
-
- await writeFile(
- this.ssoAuthHelper.path,
- JSON.stringify(dataToSave, null, 2),
- );
-
- return {
- ...dataSSO,
- name: sso.name,
- };
- }
-
- const ssoConfig = await this.ssoAuthHelper.getSSOConfig();
- const checkIfSSOExists = ssoConfig.sso.find(item => item.code === code);
+ const ssoConfig =
+ await this.databaseService.db.query.core_users_sso.findMany();
+ const checkIfSSOExists = ssoConfig.find(item => item.code === code);
if (checkIfSSOExists) {
throw new ConflictException(
`SSO method with ${code} code already exists`,
);
}
- ssoConfig.sso.push(dataSSO);
- await writeFile(
- this.ssoAuthHelper.path,
- JSON.stringify(ssoConfig, null, 2),
- );
+ await this.databaseService.db.insert(core_users_sso).values({
+ code,
+ client_id,
+ client_secret,
+ enabled: true,
+ });
return {
...dataSSO,
diff --git a/packages/backend/src/core/admin/settings/auth/methods/services/delete.service.ts b/packages/backend/src/core/admin/settings/auth/methods/services/delete.service.ts
index f91ba67c8..68e83451b 100644
--- a/packages/backend/src/core/admin/settings/auth/methods/services/delete.service.ts
+++ b/packages/backend/src/core/admin/settings/auth/methods/services/delete.service.ts
@@ -1,10 +1,13 @@
import { SSOAuthHelper } from '@/helpers/auth/sso/sso.service';
+import { InternalDatabaseService } from '@/utils/database/internal_database.service';
import { Injectable, NotFoundException } from '@nestjs/common';
-import { rm, writeFile } from 'fs/promises';
@Injectable()
export class DeleteMethodsAuthSettingsAdminService {
- constructor(private readonly ssoAuthHelper: SSOAuthHelper) {}
+ constructor(
+ private readonly ssoAuthHelper: SSOAuthHelper,
+ private readonly databaseService: InternalDatabaseService,
+ ) {}
async delete(code: string): Promise {
const sso = await this.ssoAuthHelper.getActiveSSO(code);
@@ -12,17 +15,12 @@ export class DeleteMethodsAuthSettingsAdminService {
throw new NotFoundException(`SSO method with ${code} code not found`);
}
- const ssoConfigFile = await this.ssoAuthHelper.getSSOConfig();
- ssoConfigFile.sso = ssoConfigFile.sso.filter(item => item.code !== code);
- if (ssoConfigFile.sso.length === 0) {
- await rm(this.ssoAuthHelper.path);
-
- return;
+ const ssoConfig =
+ await this.databaseService.db.query.core_users_sso.findFirst({
+ where: (table, { eq }) => eq(table.code, code),
+ });
+ if (!ssoConfig) {
+ throw new NotFoundException(`SSO method with ${code} code not found`);
}
-
- await writeFile(
- this.ssoAuthHelper.path,
- JSON.stringify(ssoConfigFile, null, 2),
- );
}
}
diff --git a/packages/backend/src/core/admin/settings/auth/methods/services/edit.service.ts b/packages/backend/src/core/admin/settings/auth/methods/services/edit.service.ts
index eeee5a992..125b462f8 100644
--- a/packages/backend/src/core/admin/settings/auth/methods/services/edit.service.ts
+++ b/packages/backend/src/core/admin/settings/auth/methods/services/edit.service.ts
@@ -1,6 +1,8 @@
+import { core_users_sso } from '@/database/schema/users';
import { SSOAuthHelper } from '@/helpers/auth/sso/sso.service';
+import { InternalDatabaseService } from '@/utils/database/internal_database.service';
import { Injectable, NotFoundException } from '@nestjs/common';
-import { writeFile } from 'fs/promises';
+import { eq } from 'drizzle-orm';
import {
EditMethodAuthSettingsAdminBody,
ShowMethodAuthSettingsAdmin,
@@ -8,7 +10,10 @@ import {
@Injectable()
export class EditMethodsAuthSettingsAdminService {
- constructor(private readonly ssoAuthHelper: SSOAuthHelper) {}
+ constructor(
+ private readonly ssoAuthHelper: SSOAuthHelper,
+ private readonly databaseService: InternalDatabaseService,
+ ) {}
async edit({
code,
@@ -21,26 +26,26 @@ export class EditMethodsAuthSettingsAdminService {
if (!sso) {
throw new NotFoundException(`SSO method with ${code} code not found`);
}
- const ssoConfigFile = await this.ssoAuthHelper.getSSOConfig();
- const ssoConfig = ssoConfigFile.sso;
- const ssoIndex = ssoConfig.findIndex(item => item.code === code);
- if (ssoIndex === -1) {
+ const ssoConfig =
+ await this.databaseService.db.query.core_users_sso.findFirst({
+ where: (table, { eq }) => eq(table.code, code),
+ });
+ if (!ssoConfig) {
throw new NotFoundException(`SSO method with ${code} code not found`);
}
- ssoConfig[ssoIndex] = {
- ...ssoConfig[ssoIndex],
- client_id,
- client_secret,
- enabled,
- };
- await writeFile(
- this.ssoAuthHelper.path,
- JSON.stringify(ssoConfigFile, null, 2),
- );
+ const [data] = await this.databaseService.db
+ .update(core_users_sso)
+ .set({
+ client_id,
+ client_secret,
+ enabled,
+ })
+ .where(eq(core_users_sso.code, code))
+ .returning();
return {
- ...ssoConfig[ssoIndex],
+ ...data,
name: sso.name,
};
}
diff --git a/packages/backend/src/core/middleware/services/show.service.ts b/packages/backend/src/core/middleware/services/show.service.ts
index 42badd608..3b2dd821f 100644
--- a/packages/backend/src/core/middleware/services/show.service.ts
+++ b/packages/backend/src/core/middleware/services/show.service.ts
@@ -1,10 +1,15 @@
+import type { CaptchaConfig } from '@/helpers/captcha/captcha.service';
+
import { ABSOLUTE_PATHS } from '@/app.module';
import { SSOAuthHelper } from '@/helpers/auth/sso/sso.service';
-import { getConfigFile } from '@/helpers/config';
import { ConfigHelperService } from '@/helpers/config.service';
import { EmailHelperService } from '@/helpers/email/email.service';
import { InternalDatabaseService } from '@/utils/database/internal_database.service';
-import { Injectable, InternalServerErrorException } from '@nestjs/common';
+import {
+ Inject,
+ Injectable,
+ InternalServerErrorException,
+} from '@nestjs/common';
import { readFile } from 'fs/promises';
import { join } from 'path';
import { AppTypeMainSettingsAdmin } from 'vitnode-shared/admin/settings/main.enum';
@@ -21,6 +26,8 @@ export class ShowMiddlewareService {
private readonly navService: NavMiddlewareService,
private readonly ssoHelper: SSOAuthHelper,
private readonly configService: ConfigHelperService,
+ @Inject('VITNODE_CAPTCHA_CONFIG')
+ private readonly captchaConfig?: CaptchaConfig,
) {}
protected async getManifests({
@@ -47,7 +54,6 @@ export class ShowMiddlewareService {
async show(): Promise {
// TODO: Add cache
- const config = getConfigFile();
const [plugins, langs] = await Promise.all([
this.databaseService.db.query.core_plugins.findMany({
columns: {
@@ -109,8 +115,8 @@ export class ShowMiddlewareService {
site_short_name: configFromDb.site_short_name,
security: {
captcha: {
- site_key: config.security.captcha.site_key,
- type: config.security.captcha.type,
+ site_key: this.captchaConfig?.site_key ?? '',
+ type: this.captchaConfig?.type ?? '',
},
},
editor: {
diff --git a/packages/backend/src/database/schema/users.ts b/packages/backend/src/database/schema/users.ts
index 5dd949968..63a12af72 100644
--- a/packages/backend/src/database/schema/users.ts
+++ b/packages/backend/src/database/schema/users.ts
@@ -61,6 +61,13 @@ export const core_users_relations = relations(core_users, ({ one, many }) => ({
}),
}));
+export const core_users_sso = pgTable('core_users_sso', t => ({
+ code: t.varchar({ length: 100 }).notNull().unique(),
+ client_id: t.varchar({ length: 255 }).notNull(),
+ client_secret: t.varchar({ length: 255 }).notNull(),
+ enabled: t.boolean().notNull().default(false),
+}));
+
export const core_users_sso_tokens = pgTable(
'core_users_sso_tokens',
t => ({
@@ -71,7 +78,12 @@ export const core_users_sso_tokens = pgTable(
onDelete: 'cascade',
})
.notNull(),
- provider: t.varchar({ length: 100 }).notNull(),
+ provider: t
+ .varchar({ length: 100 })
+ .references(() => core_users_sso.code, {
+ onDelete: 'no action',
+ })
+ .notNull(),
provider_id: t.varchar({ length: 255 }).notNull(),
created_at: t.timestamp().notNull().defaultNow(),
updated_at: t.timestamp().notNull().defaultNow(),
diff --git a/packages/backend/src/helpers/auth/sso/sso.service.ts b/packages/backend/src/helpers/auth/sso/sso.service.ts
index ec2362bba..ce7538afc 100644
--- a/packages/backend/src/helpers/auth/sso/sso.service.ts
+++ b/packages/backend/src/helpers/auth/sso/sso.service.ts
@@ -1,8 +1,5 @@
-import { ABSOLUTE_PATHS } from '@/app.module';
+import { InternalDatabaseService } from '@/utils/database/internal_database.service';
import { Inject, Injectable, NotFoundException } from '@nestjs/common';
-import { existsSync } from 'fs';
-import { readFile } from 'fs/promises';
-import { join } from 'path';
import { ShowMethodAuthSettingsAdmin } from 'vitnode-shared/admin/settings/auth.dto';
import { SSOUrlAuthObj } from 'vitnode-shared/auth/sso.dto';
@@ -50,19 +47,14 @@ export class SSOAuthHelper {
constructor(
@Inject('VITNODE_SSO_LOGIN_METHODS')
private readonly loginMethods: SSOAuthItem[],
+ private readonly databaseService: InternalDatabaseService,
) {}
- path = join(
- ABSOLUTE_PATHS.plugin({ code: 'core' }).root,
- 'utils',
- 'sso.config.json',
- );
-
async getActiveSSO(
code: string,
): Promise {
const item = this.getSSO(code);
- if (!item || !existsSync(this.path)) {
+ if (!item) {
throw new NotFoundException(`SSO provider with ${code} code not found`);
}
@@ -77,14 +69,11 @@ export class SSOAuthHelper {
}
async getActiveSSOs(): Promise {
- if (!existsSync(this.path)) {
- return [];
- }
-
- const ssoConfig = await this.getSSOConfig();
+ const ssoConfig =
+ await this.databaseService.db.query.core_users_sso.findMany();
const SSOs = this.getSSOs();
const activeSSOs: ShowMethodAuthSettingsAdmin[] = [];
- ssoConfig.sso.forEach(sso => {
+ ssoConfig.forEach(sso => {
const ssoItem = SSOs.find(item => item.code === sso.code);
if (!ssoItem) return;
@@ -101,14 +90,6 @@ export class SSOAuthHelper {
return this.getSSOs().find(sso => sso.code === code);
}
- async getSSOConfig(): Promise {
- if (!existsSync(this.path)) {
- return { sso: [] };
- }
-
- return JSON.parse(await readFile(this.path, 'utf8'));
- }
-
getSSOs(): SSOAuthItem[] {
return [googleSSO, facebookSSO, ...this.loginMethods];
}
diff --git a/packages/backend/src/helpers/captcha/captcha.service.ts b/packages/backend/src/helpers/captcha/captcha.service.ts
index fc4fcc567..9e9eff2ae 100644
--- a/packages/backend/src/helpers/captcha/captcha.service.ts
+++ b/packages/backend/src/helpers/captcha/captcha.service.ts
@@ -1,19 +1,23 @@
-import { ABSOLUTE_PATHS } from '@/app.module';
import { getUserIp } from '@/functions';
-import { getConfigFile } from '@/helpers/config';
-import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
+import { HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
import { Request } from 'express';
-import * as fs from 'fs';
-import { join } from 'path';
-import { CaptchaTypeEnum } from 'vitnode-shared/utils/global';
+
+export interface CaptchaConfig {
+ secret_key: string;
+ site_key: string;
+ type:
+ | 'cloudflare_turnstile'
+ | 'recaptcha_v2_checkbox'
+ | 'recaptcha_v2_invisible'
+ | 'recaptcha_v3';
+}
@Injectable()
export class CaptchaHelper {
- protected readonly path: string = join(
- ABSOLUTE_PATHS.plugin({ code: 'core' }).root,
- 'utils',
- 'captcha.config.json',
- );
+ constructor(
+ @Inject('VITNODE_CAPTCHA_CONFIG')
+ private readonly captchaConfig?: CaptchaConfig,
+ ) {}
private async getResFromReCaptcha({
captchaKey,
@@ -22,35 +26,20 @@ export class CaptchaHelper {
captchaKey: string | string[];
userIp: string;
}): Promise<{ 'error-codes'?: string[]; score: number; success: boolean }> {
- const {
- security: { captcha: config },
- } = getConfigFile();
- // If captcha is disabled, return success
- if (config.type === CaptchaTypeEnum.none) {
+ if (!this.captchaConfig) {
return {
success: true,
score: 1,
};
}
- // Get secret key from file
- if (!fs.existsSync(this.path)) {
- return {
- success: false,
- score: 0,
- };
- }
- const captchaSecurityConfig: { secret_key: string } = JSON.parse(
- fs.readFileSync(this.path, 'utf8'),
- );
-
- if (config.type === CaptchaTypeEnum.cloudflare_turnstile) {
+ if (this.captchaConfig.type === 'cloudflare_turnstile') {
const res = await fetch(
'https://challenges.cloudflare.com/turnstile/v0/siteverify',
{
method: 'POST',
body: JSON.stringify({
- secret: captchaSecurityConfig.secret_key,
+ secret: this.captchaConfig.secret_key,
response: captchaKey,
remoteip: userIp,
}),
@@ -65,13 +54,13 @@ export class CaptchaHelper {
return data;
} else if (
[
- CaptchaTypeEnum.recaptcha_v2_checkbox,
- CaptchaTypeEnum.recaptcha_v2_invisible,
- CaptchaTypeEnum.recaptcha_v3,
- ].includes(config.type)
+ 'recaptcha_v2_checkbox',
+ 'recaptcha_v2_invisible',
+ 'recaptcha_v3',
+ ].includes(this.captchaConfig.type)
) {
const res = await fetch(
- `https://www.google.com/recaptcha/api/siteverify?secret=${captchaSecurityConfig.secret_key}&response=${captchaKey}&remoteip=${userIp}`,
+ `https://www.google.com/recaptcha/api/siteverify?secret=${this.captchaConfig.secret_key}&response=${captchaKey}&remoteip=${userIp}`,
{
method: 'POST',
},
@@ -90,11 +79,7 @@ export class CaptchaHelper {
async validateCaptcha({ req }: { req: Request }) {
const captchaKey = req.headers['x-vitnode-captcha-token'];
-
- const {
- security: { captcha: config },
- } = getConfigFile();
- if (!captchaKey && config.type !== CaptchaTypeEnum.none) {
+ if (!captchaKey && this.captchaConfig) {
throw new HttpException(
'Captcha token not provided',
HttpStatus.BAD_REQUEST,
diff --git a/packages/backend/src/helpers/config.ts b/packages/backend/src/helpers/config.ts
deleted file mode 100644
index 36f000067..000000000
--- a/packages/backend/src/helpers/config.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import * as fs from 'fs';
-import { join } from 'path';
-import { CaptchaTypeEnum } from 'vitnode-shared/utils/global';
-
-export interface ConfigType {
- security: {
- captcha: {
- site_key: string;
- type: CaptchaTypeEnum;
- };
- };
-}
-
-export const DEFAULT_CONFIG_DATA: ConfigType = {
- security: {
- captcha: {
- type: CaptchaTypeEnum.none,
- site_key: '',
- },
- },
-};
-
-export const configPath = join(
- process.cwd(),
- 'src',
- 'plugins',
- 'core',
- 'utils',
- 'config.json',
-);
-
-export const getConfigFile = () => {
- const file = fs.readFileSync(configPath, 'utf-8');
-
- return JSON.parse(file) as ConfigType;
-};
-
-export const updateConfigFile = (config: ConfigType) => {
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
-};
diff --git a/packages/backend/src/helpers/helpers.module.ts b/packages/backend/src/helpers/helpers.module.ts
index db59c70fe..c22da472d 100644
--- a/packages/backend/src/helpers/helpers.module.ts
+++ b/packages/backend/src/helpers/helpers.module.ts
@@ -10,7 +10,7 @@ import {
import { DeviceAuthService } from './auth/device.service';
import { InternalAuthAdminService } from './auth/internal_auth_admin.service';
import { SSOAuthHelper, SSOAuthItem } from './auth/sso/sso.service';
-import { CaptchaHelper } from './captcha/captcha.service';
+import { CaptchaConfig, CaptchaHelper } from './captcha/captcha.service';
import { ConfigHelperService } from './config.service';
import { EmailHelpersService } from './email/email-helpers.service';
import {
@@ -26,6 +26,7 @@ import { UserHelper } from './user.service';
@Module({})
export class GlobalHelpersModule {
static register(options: {
+ captcha?: CaptchaConfig;
email?: EmailSenderFunction;
ssoLoginMethod?: SSOAuthItem[];
}): DynamicModule {
@@ -45,6 +46,10 @@ export class GlobalHelpersModule {
await options.email(params);
},
},
+ {
+ provide: 'VITNODE_CAPTCHA_CONFIG',
+ useValue: options.captcha,
+ },
{
provide: 'VITNODE_SSO_LOGIN_METHODS',
useValue: options.ssoLoginMethod ?? [],
@@ -81,6 +86,7 @@ export class GlobalHelpersModule {
'EmailHelpersService',
'IOAuthGuards',
'IOAdminAuthGuards',
+ 'VITNODE_CAPTCHA_CONFIG',
DeviceAuthService,
UserHelper,
FilesHelperService,
diff --git a/packages/frontend/src/hooks/use-captcha.ts b/packages/frontend/src/hooks/use-captcha.ts
index 68a29ef98..f322f3422 100644
--- a/packages/frontend/src/hooks/use-captcha.ts
+++ b/packages/frontend/src/hooks/use-captcha.ts
@@ -1,8 +1,7 @@
+/* eslint-disable @typescript-eslint/ban-ts-comment */
import { useLocale } from 'next-intl';
import { useTheme } from 'next-themes';
-/* eslint-disable @typescript-eslint/ban-ts-comment */
import React from 'react';
-import { CaptchaTypeEnum } from 'vitnode-shared/utils/global';
import { useMiddlewareData } from './use-middleware-data';
@@ -13,15 +12,13 @@ export const useCaptcha = () => {
const {
security: { captcha: config },
} = useMiddlewareData();
- const [token, setToken] = React.useState(
- config.type === CaptchaTypeEnum.none ? 'none' : '',
- );
+ const [token, setToken] = React.useState(!config.type ? 'none' : '');
const handleLoaded = () => {
const elementId = 'vitnode_captcha';
if (
- config.type === CaptchaTypeEnum.recaptcha_v2_checkbox ||
- config.type === CaptchaTypeEnum.recaptcha_v2_invisible
+ config.type === 'recaptcha_v2_checkbox' ||
+ config.type === 'recaptcha_v2_invisible'
) {
// @ts-expect-error
window.grecaptcha.ready(() => {
@@ -30,16 +27,13 @@ export const useCaptcha = () => {
sitekey: config.site_key,
theme: resolvedTheme,
locale,
- size:
- config.type === CaptchaTypeEnum.recaptcha_v2_invisible
- ? 'invisible'
- : null,
+ size: config.type === 'recaptcha_v2_invisible' ? 'invisible' : null,
callback: (token: string) => {
setToken(token);
},
});
});
- } else if (config.type === CaptchaTypeEnum.cloudflare_turnstile) {
+ } else if (config.type === 'cloudflare_turnstile') {
// @ts-expect-error
window.turnstile.render(`#${elementId}`, {
sitekey: config.site_key,
@@ -55,7 +49,7 @@ export const useCaptcha = () => {
};
React.useEffect(() => {
- if (config.type === CaptchaTypeEnum.none) {
+ if (!config.type) {
setIsReady(true);
return;
@@ -69,11 +63,11 @@ export const useCaptcha = () => {
const script = document.createElement('script');
if (
- config.type === CaptchaTypeEnum.recaptcha_v2_checkbox ||
- config.type === CaptchaTypeEnum.recaptcha_v2_invisible
+ config.type === 'recaptcha_v2_checkbox' ||
+ config.type === 'recaptcha_v2_invisible'
) {
script.src = `${googleCaptchaDomain}&render=explicit`;
- } else if (config.type === CaptchaTypeEnum.recaptcha_v3) {
+ } else if (config.type === 'recaptcha_v3') {
script.src = `${googleCaptchaDomain}&render=${config.site_key}`;
} else {
// window[functionCF] = handleLoaded;
@@ -93,7 +87,7 @@ export const useCaptcha = () => {
}, []);
const getTokenFromCaptcha = async (): Promise => {
- if (config.type === CaptchaTypeEnum.recaptcha_v3) {
+ if (config.type === 'recaptcha_v3') {
// Captcha
return new Promise(resolve => {
// @ts-expect-error
diff --git a/packages/frontend/src/views/admin/views/core/security/spam/captcha/captcha-spam-security-admin-view.tsx b/packages/frontend/src/views/admin/views/core/security/spam/captcha/captcha-spam-security-admin-view.tsx
deleted file mode 100644
index 594833540..000000000
--- a/packages/frontend/src/views/admin/views/core/security/spam/captcha/captcha-spam-security-admin-view.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { fetcher } from '@/api/fetcher';
-import { TranslationsProvider } from '@/components/translations-provider';
-import { Metadata } from 'next';
-import { getTranslations } from 'next-intl/server';
-import { ShowCaptchaSecurityAdminObj } from 'vitnode-shared/admin/security/captcha.dto';
-
-import { ContentCaptchaSpamSecurityAdmin } from './content';
-
-const getData = async () => {
- const { data } = await fetcher({
- url: '/admin/security/captcha',
- cache: 'force-cache',
- });
-
- return data;
-};
-
-export const generateMetadataCaptchaSpamSecurityAdmin =
- async (): Promise => {
- const t = await getTranslations('admin.core.security.spam.captcha');
-
- return {
- title: t('title'),
- };
- };
-
-export const CaptchaSpamSecurityAdminView = async () => {
- const data = await getData();
-
- return (
-
-
-
- );
-};
diff --git a/packages/frontend/src/views/admin/views/core/security/spam/captcha/content.tsx b/packages/frontend/src/views/admin/views/core/security/spam/captcha/content.tsx
deleted file mode 100644
index fa98ac5dc..000000000
--- a/packages/frontend/src/views/admin/views/core/security/spam/captcha/content.tsx
+++ /dev/null
@@ -1,142 +0,0 @@
-'use client';
-
-import { AutoForm, DependencyType } from '@/components/form/auto-form';
-import { AutoFormInput } from '@/components/form/fields/input';
-import { AutoFormRadioGroup } from '@/components/form/fields/radio-group';
-import { Link } from '@/navigation';
-import { SquareArrowOutUpRightIcon } from 'lucide-react';
-import { useTranslations } from 'next-intl';
-import { ShowCaptchaSecurityAdminObj } from 'vitnode-shared/admin/security/captcha.dto';
-
-import { useCaptchaSecurityAdmin } from './hooks/use-captcha-security-admin';
-
-export const ContentCaptchaSpamSecurityAdmin = (
- data: ShowCaptchaSecurityAdminObj,
-) => {
- const { formSchema, onSubmit } = useCaptchaSecurityAdmin(data);
- const t = useTranslations('admin.core.security.spam.captcha');
-
- return (
- type === 'none',
- },
- {
- sourceField: 'type',
- type: DependencyType.HIDES,
- targetField: 'secret_key',
- when: (type: string) => type === 'none',
- },
- {
- sourceField: 'type',
- type: DependencyType.REQUIRES,
- targetField: 'site_key',
- when: (type: string) => type !== 'none',
- },
- {
- sourceField: 'type',
- type: DependencyType.REQUIRES,
- targetField: 'secret_key',
- when: (type: string) => type !== 'none',
- },
- ]}
- fields={[
- {
- id: 'type',
- label: t('type.title'),
- component: props => (
- (
-
- {text}
-
-
- ),
- }),
- },
- recaptcha_v3: {
- title: t('type.recaptcha_v3'),
- description: t.rich(`type.recaptcha_desc`, {
- link: text => (
-
- {text}
-
-
- ),
- }),
- },
- recaptcha_v2_invisible: {
- title: t('type.recaptcha_v2_invisible'),
- description: t.rich(`type.recaptcha_desc`, {
- link: text => (
-
- {text}
-
-
- ),
- }),
- },
- recaptcha_v2_checkbox: {
- title: t('type.recaptcha_v2_checkbox'),
- description: t.rich(`type.recaptcha_desc`, {
- link: text => (
-
- {text}
-
-
- ),
- }),
- },
- }}
- />
- ),
- },
- {
- id: 'site_key',
- label: t('site_key'),
- component: AutoFormInput,
- },
- {
- id: 'secret_key',
- label: t('secret_key'),
- component: AutoFormInput,
- },
- ]}
- formSchema={formSchema}
- onSubmit={onSubmit}
- theme="horizontal"
- />
- );
-};
diff --git a/packages/frontend/src/views/admin/views/core/security/spam/captcha/hooks/mutation-api.ts b/packages/frontend/src/views/admin/views/core/security/spam/captcha/hooks/mutation-api.ts
deleted file mode 100644
index d3775e5d3..000000000
--- a/packages/frontend/src/views/admin/views/core/security/spam/captcha/hooks/mutation-api.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-'use server';
-
-import { fetcher } from '@/api/fetcher';
-import { revalidatePath } from 'next/cache';
-import { ShowCaptchaSecurityAdminObj } from 'vitnode-shared/admin/security/captcha.dto';
-
-export const mutationApi = async (body: ShowCaptchaSecurityAdminObj) => {
- await fetcher({
- url: '/admin/security/captcha',
- method: 'PUT',
- body,
- });
-
- revalidatePath('/', 'layout');
-};
diff --git a/packages/frontend/src/views/admin/views/core/security/spam/captcha/hooks/use-captcha-security-admin.ts b/packages/frontend/src/views/admin/views/core/security/spam/captcha/hooks/use-captcha-security-admin.ts
deleted file mode 100644
index 539194e97..000000000
--- a/packages/frontend/src/views/admin/views/core/security/spam/captcha/hooks/use-captcha-security-admin.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { useTranslations } from 'next-intl';
-import { UseFormReturn } from 'react-hook-form';
-import { toast } from 'sonner';
-import { CaptchaTypeEnum } from 'vitnode-shared/utils/global';
-import { z } from 'zod';
-
-import { ContentCaptchaSpamSecurityAdmin } from '../content';
-import { mutationApi } from './mutation-api';
-
-export const useCaptchaSecurityAdmin = ({
- type,
- secret_key,
- site_key,
-}: React.ComponentProps) => {
- const t = useTranslations('core.global');
- const formSchema = z
- .object({
- type: z
- .enum([
- CaptchaTypeEnum.none,
- CaptchaTypeEnum.cloudflare_turnstile,
- CaptchaTypeEnum.recaptcha_v3,
- CaptchaTypeEnum.recaptcha_v2_invisible,
- CaptchaTypeEnum.recaptcha_v2_checkbox,
- ])
- .default(type),
- secret_key: z.string().default(secret_key),
- site_key: z.string().default(site_key),
- })
- .refine(input => {
- if (input.type === CaptchaTypeEnum.none) {
- return true;
- }
-
- return input.secret_key !== '' && input.site_key !== '';
- });
-
- const onSubmit = async (
- values: z.infer,
- form: UseFormReturn>,
- ) => {
- try {
- await mutationApi(values);
- toast.success(t('saved_success'));
- form.reset(values);
- } catch (_) {
- toast.error(t('errors.title'), {
- description: t('errors.internal_server_error'),
- });
- }
- };
-
- return { onSubmit, formSchema };
-};
diff --git a/packages/frontend/src/views/admin/views/core/security/spam/layout.tsx b/packages/frontend/src/views/admin/views/core/security/spam/layout.tsx
deleted file mode 100644
index 9eaf04843..000000000
--- a/packages/frontend/src/views/admin/views/core/security/spam/layout.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Card } from '@/components/ui/card';
-import { HeaderContent } from '@/components/ui/header-content';
-import { getTranslations } from 'next-intl/server';
-
-export const SpamSecurityAdminLayout = async ({
- children,
-}: {
- children: React.ReactNode;
-}) => {
- const t = await getTranslations('admin.core.security.spam');
-
- return (
- <>
-
- {children}
- >
- );
-};
diff --git a/packages/frontend/src/views/admin/views/dynamic-admin-view.tsx b/packages/frontend/src/views/admin/views/dynamic-admin-view.tsx
index 558bf653e..3779e26e8 100644
--- a/packages/frontend/src/views/admin/views/dynamic-admin-view.tsx
+++ b/packages/frontend/src/views/admin/views/dynamic-admin-view.tsx
@@ -28,11 +28,6 @@ import {
generateMetadataPluginsAdmin,
PluginsAdminView,
} from './core/plugins/plugins-admin-view';
-import {
- CaptchaSpamSecurityAdminView,
- generateMetadataCaptchaSpamSecurityAdmin,
-} from './core/security/spam/captcha/captcha-spam-security-admin-view';
-import { SpamSecurityAdminLayout } from './core/security/spam/layout';
import { AuthorizationSettingsAdminView } from './core/settings/authorization/authorization-settings-admin-view';
import { LayoutAuthorizationSettingsAdmin } from './core/settings/authorization/layout/layout';
import { MethodsAuthSettingsAdminView } from './core/settings/authorization/methods/methods-auth-settings-admin-view';
@@ -149,23 +144,6 @@ export const generateMetadataDynamic = async (props: {
}
}
- if (slug[1] === 'security' && !slug[4]) {
- const t = await getTranslations('admin.core.security.spam');
- const primary: Metadata = {
- title: t('title'),
- };
-
- if (slug[2] === 'spam') {
- const current = await generateMetadataCaptchaSpamSecurityAdmin();
-
- return {
- ...primary,
- ...current,
- title: `${current.title} - ${primary.title}`,
- };
- }
- }
-
if (slug[1] === 'plugins' && !slug[5]) {
if (!slug[2]) {
return generateMetadataPluginsAdmin();
@@ -297,24 +275,6 @@ export const DynamicAdminView = async (props: {
}
}
- if (slug[1] === 'security' && !slug[4]) {
- if (slug[2] === 'spam') {
- return (
-
- {(() => {
- if (slug[3] === 'captcha' || !slug[3]) {
- return ;
- }
-
- notFound();
- })()}
-
- );
- }
-
- notFound();
- }
-
if (slug[1] === 'plugins' && !slug[5]) {
return (
diff --git a/packages/shared/src/middleware.dto.ts b/packages/shared/src/middleware.dto.ts
index 9ff1b0e37..7cc0b86e8 100644
--- a/packages/shared/src/middleware.dto.ts
+++ b/packages/shared/src/middleware.dto.ts
@@ -1,7 +1,6 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import {
IsBoolean,
- IsEnum,
IsNumber,
IsObject,
IsOptional,
@@ -11,7 +10,6 @@ import {
import { MainSettingsAdminBody } from './admin/settings/main.dto';
import { ShowNavStyles } from './nav.dto';
import { FileObj } from './utils/files.dto';
-import { CaptchaTypeEnum } from './utils/global';
class EditorMiddleware {
@ApiProperty()
@@ -50,9 +48,9 @@ export class CaptchaSecurityMiddleware {
@IsString()
site_key: string;
- @ApiProperty({ enum: CaptchaTypeEnum })
- @IsEnum(CaptchaTypeEnum)
- type: CaptchaTypeEnum;
+ @ApiProperty()
+ @IsString()
+ type: string;
}
export class LanguagesMiddleware {
diff --git a/packages/shared/src/utils/global.ts b/packages/shared/src/utils/global.ts
deleted file mode 100644
index d0bb83e4d..000000000
--- a/packages/shared/src/utils/global.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export enum CaptchaTypeEnum {
- cloudflare_turnstile = 'cloudflare_turnstile',
- none = 'none',
- recaptcha_v2_checkbox = 'recaptcha_v2_checkbox',
- recaptcha_v2_invisible = 'recaptcha_v2_invisible',
- recaptcha_v3 = 'recaptcha_v3',
-}