From 8121db69f2d045a6aa5e050ad7d5aa7d83166e6f Mon Sep 17 00:00:00 2001
From: Victor Santos
Date: Fri, 14 Nov 2025 20:26:21 -0300
Subject: [PATCH 001/307] Add MongoDB connection support
- Introduced MongoDB connection router and schemas.
- Updated app connection enums and maps to include MongoDB.
- Implemented validation and connection functions for MongoDB.
- Enhanced app connection service to handle MongoDB credentials.
---
.../routes/v1/app-connection-routers/index.ts | 2 +
.../mongodb-connection-router.ts | 18 ++++
.../app-connection/app-connection-enums.ts | 1 +
.../app-connection/app-connection-fns.ts | 7 +-
.../app-connection/app-connection-maps.ts | 2 +
.../app-connection/app-connection-service.ts | 2 +
.../app-connection/app-connection-types.ts | 10 +++
.../services/app-connection/mongodb/index.ts | 4 +
.../mongodb/mongodb-connection-enums.ts | 4 +
.../mongodb/mongodb-connection-fns.ts | 70 +++++++++++++++
.../mongodb/mongodb-connection-schemas.ts | 89 +++++++++++++++++++
.../mongodb/mongodb-connection-types.ts | 22 +++++
.../src/hooks/api/appConnections/enums.ts | 1 +
13 files changed, 231 insertions(+), 1 deletion(-)
create mode 100644 backend/src/server/routes/v1/app-connection-routers/mongodb-connection-router.ts
create mode 100644 backend/src/services/app-connection/mongodb/index.ts
create mode 100644 backend/src/services/app-connection/mongodb/mongodb-connection-enums.ts
create mode 100644 backend/src/services/app-connection/mongodb/mongodb-connection-fns.ts
create mode 100644 backend/src/services/app-connection/mongodb/mongodb-connection-schemas.ts
create mode 100644 backend/src/services/app-connection/mongodb/mongodb-connection-types.ts
diff --git a/backend/src/server/routes/v1/app-connection-routers/index.ts b/backend/src/server/routes/v1/app-connection-routers/index.ts
index aa1d671b62..1c3eade580 100644
--- a/backend/src/server/routes/v1/app-connection-routers/index.ts
+++ b/backend/src/server/routes/v1/app-connection-routers/index.ts
@@ -27,6 +27,7 @@ import { registerHerokuConnectionRouter } from "./heroku-connection-router";
import { registerHumanitecConnectionRouter } from "./humanitec-connection-router";
import { registerLaravelForgeConnectionRouter } from "./laravel-forge-connection-router";
import { registerLdapConnectionRouter } from "./ldap-connection-router";
+import { registerMongoDBConnectionRouter } from "./mongodb-connection-router";
import { registerMsSqlConnectionRouter } from "./mssql-connection-router";
import { registerMySqlConnectionRouter } from "./mysql-connection-router";
import { registerNetlifyConnectionRouter } from "./netlify-connection-router";
@@ -88,5 +89,6 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record {
+ registerAppConnectionEndpoints({
+ app: AppConnection.MongoDB,
+ server,
+ sanitizedResponseSchema: SanitizedMongoDBConnectionSchema,
+ createSchema: CreateMongoDBConnectionSchema,
+ updateSchema: UpdateMongoDBConnectionSchema
+ });
+};
diff --git a/backend/src/services/app-connection/app-connection-enums.ts b/backend/src/services/app-connection/app-connection-enums.ts
index 1c184a436a..5f09d61a8d 100644
--- a/backend/src/services/app-connection/app-connection-enums.ts
+++ b/backend/src/services/app-connection/app-connection-enums.ts
@@ -38,6 +38,7 @@ export enum AppConnection {
Netlify = "netlify",
Okta = "okta",
Redis = "redis",
+ MongoDB = "mongodb",
LaravelForge = "laravel-forge",
Chef = "chef",
Northflank = "northflank"
diff --git a/backend/src/services/app-connection/app-connection-fns.ts b/backend/src/services/app-connection/app-connection-fns.ts
index 863fa75f9a..f8e73db806 100644
--- a/backend/src/services/app-connection/app-connection-fns.ts
+++ b/backend/src/services/app-connection/app-connection-fns.ts
@@ -114,6 +114,7 @@ import {
validateLaravelForgeConnectionCredentials
} from "./laravel-forge";
import { getLdapConnectionListItem, LdapConnectionMethod, validateLdapConnectionCredentials } from "./ldap";
+import { getMongoDBConnectionListItem, MongoDBConnectionMethod, validateMongoDBConnectionCredentials } from "./mongodb";
import { getMsSqlConnectionListItem, MsSqlConnectionMethod } from "./mssql";
import { MySqlConnectionMethod } from "./mysql/mysql-connection-enums";
import { getMySqlConnectionListItem } from "./mysql/mysql-connection-fns";
@@ -216,6 +217,7 @@ export const listAppConnectionOptions = (projectType?: ProjectType) => {
getNorthflankConnectionListItem(),
getOktaConnectionListItem(),
getRedisConnectionListItem(),
+ getMongoDBConnectionListItem(),
getChefConnectionListItem()
]
.filter((option) => {
@@ -348,7 +350,8 @@ export const validateAppConnectionCredentials = async (
[AppConnection.Northflank]: validateNorthflankConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Okta]: validateOktaConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Chef]: validateChefConnectionCredentials as TAppConnectionCredentialsValidator,
- [AppConnection.Redis]: validateRedisConnectionCredentials as TAppConnectionCredentialsValidator
+ [AppConnection.Redis]: validateRedisConnectionCredentials as TAppConnectionCredentialsValidator,
+ [AppConnection.MongoDB]: validateMongoDBConnectionCredentials as TAppConnectionCredentialsValidator
};
return VALIDATE_APP_CONNECTION_CREDENTIALS_MAP[appConnection.app](appConnection, gatewayService, gatewayV2Service);
@@ -400,6 +403,7 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
case OracleDBConnectionMethod.UsernameAndPassword:
case AzureADCSConnectionMethod.UsernamePassword:
case RedisConnectionMethod.UsernameAndPassword:
+ case MongoDBConnectionMethod.UsernameAndPassword:
return "Username & Password";
case WindmillConnectionMethod.AccessToken:
case HCVaultConnectionMethod.AccessToken:
@@ -492,6 +496,7 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record<
[AppConnection.Northflank]: platformManagedCredentialsNotSupported,
[AppConnection.Okta]: platformManagedCredentialsNotSupported,
[AppConnection.Redis]: platformManagedCredentialsNotSupported,
+ [AppConnection.MongoDB]: platformManagedCredentialsNotSupported,
[AppConnection.LaravelForge]: platformManagedCredentialsNotSupported,
[AppConnection.Chef]: platformManagedCredentialsNotSupported
};
diff --git a/backend/src/services/app-connection/app-connection-maps.ts b/backend/src/services/app-connection/app-connection-maps.ts
index 5b8cc3fc1c..49b337a2c4 100644
--- a/backend/src/services/app-connection/app-connection-maps.ts
+++ b/backend/src/services/app-connection/app-connection-maps.ts
@@ -41,6 +41,7 @@ export const APP_CONNECTION_NAME_MAP: Record = {
[AppConnection.Netlify]: "Netlify",
[AppConnection.Okta]: "Okta",
[AppConnection.Redis]: "Redis",
+ [AppConnection.MongoDB]: "MongoDB",
[AppConnection.Chef]: "Chef",
[AppConnection.Northflank]: "Northflank"
};
@@ -86,6 +87,7 @@ export const APP_CONNECTION_PLAN_MAP: Record {
+ return {
+ name: "MongoDB" as const,
+ app: AppConnection.MongoDB as const,
+ methods: Object.values(MongoDBConnectionMethod) as [MongoDBConnectionMethod.UsernameAndPassword],
+ supportsPlatformManagement: false as const
+ };
+};
+
+export const validateMongoDBConnectionCredentials = async (config: TMongoDBConnectionConfig) => {
+ const [hostIp] = await verifyHostInputValidity(config.credentials.host);
+
+ let client: MongoClient | null = null;
+ try {
+ const isSrv = !config.credentials.port;
+ const uri = isSrv ? `mongodb+srv://${hostIp}` : `mongodb://${hostIp}:${config.credentials.port}`;
+
+ const clientOptions: {
+ auth?: { username: string; password?: string };
+ tls?: boolean;
+ tlsInsecure?: boolean;
+ ca?: string;
+ directConnection?: boolean;
+ } = {
+ auth: {
+ username: config.credentials.username,
+ password: config.credentials.password
+ },
+ directConnection: !isSrv
+ };
+
+ if (config.credentials.sslEnabled || isSrv) {
+ clientOptions.tls = true;
+ clientOptions.tlsInsecure = !config.credentials.sslRejectUnauthorized;
+ if (config.credentials.sslCertificate) {
+ clientOptions.ca = config.credentials.sslCertificate;
+ }
+ }
+
+ client = new MongoClient(uri, clientOptions);
+
+ // Validate connection by running ping command
+ await client
+ .db(config.credentials.database)
+ .command({ ping: 1 })
+ .then(() => true);
+
+ if (client) await client.close();
+
+ return config.credentials;
+ } catch (err) {
+ if (err instanceof BadRequestError) {
+ throw err;
+ }
+ throw new BadRequestError({
+ message: `Unable to validate connection: ${(err as Error)?.message || "verify credentials"}`
+ });
+ } finally {
+ if (client) await client.close();
+ }
+};
diff --git a/backend/src/services/app-connection/mongodb/mongodb-connection-schemas.ts b/backend/src/services/app-connection/mongodb/mongodb-connection-schemas.ts
new file mode 100644
index 0000000000..1caba50715
--- /dev/null
+++ b/backend/src/services/app-connection/mongodb/mongodb-connection-schemas.ts
@@ -0,0 +1,89 @@
+import z from "zod";
+
+import { AppConnections } from "@app/lib/api-docs";
+import {
+ BaseAppConnectionSchema,
+ GenericCreateAppConnectionFieldsSchema,
+ GenericUpdateAppConnectionFieldsSchema
+} from "@app/services/app-connection/app-connection-schemas";
+
+import { AppConnection } from "../app-connection-enums";
+import { MongoDBConnectionMethod } from "./mongodb-connection-enums";
+
+export const BaseMongoDBUsernameAndPasswordConnectionSchema = z.object({
+ host: z.string().toLowerCase().min(1),
+ port: z.coerce.number(),
+ username: z.string().min(1),
+ password: z.string().min(1),
+ database: z.string().min(1).trim(),
+
+ sslRejectUnauthorized: z.boolean(),
+ sslEnabled: z.boolean(),
+ sslCertificate: z
+ .string()
+ .trim()
+ .transform((value) => value || undefined)
+ .optional()
+});
+
+export const MongoDBConnectionAccessTokenCredentialsSchema = BaseMongoDBUsernameAndPasswordConnectionSchema;
+
+const BaseMongoDBConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.MongoDB) });
+
+export const MongoDBConnectionSchema = BaseMongoDBConnectionSchema.extend({
+ method: z.literal(MongoDBConnectionMethod.UsernameAndPassword),
+ credentials: MongoDBConnectionAccessTokenCredentialsSchema
+});
+
+export const SanitizedMongoDBConnectionSchema = z.discriminatedUnion("method", [
+ BaseMongoDBConnectionSchema.extend({
+ method: z.literal(MongoDBConnectionMethod.UsernameAndPassword),
+ credentials: MongoDBConnectionAccessTokenCredentialsSchema.pick({
+ host: true,
+ port: true,
+ username: true,
+ database: true,
+ sslEnabled: true,
+ sslRejectUnauthorized: true,
+ sslCertificate: true
+ })
+ })
+]);
+
+export const ValidateMongoDBConnectionCredentialsSchema = z.discriminatedUnion("method", [
+ z.object({
+ method: z
+ .literal(MongoDBConnectionMethod.UsernameAndPassword)
+ .describe(AppConnections.CREATE(AppConnection.MongoDB).method),
+ credentials: MongoDBConnectionAccessTokenCredentialsSchema.describe(
+ AppConnections.CREATE(AppConnection.MongoDB).credentials
+ )
+ })
+]);
+
+export const CreateMongoDBConnectionSchema = ValidateMongoDBConnectionCredentialsSchema.and(
+ GenericCreateAppConnectionFieldsSchema(AppConnection.MongoDB, {
+ supportsPlatformManagedCredentials: false,
+ supportsGateways: false
+ })
+);
+
+export const UpdateMongoDBConnectionSchema = z
+ .object({
+ credentials: MongoDBConnectionAccessTokenCredentialsSchema.optional().describe(
+ AppConnections.UPDATE(AppConnection.MongoDB).credentials
+ )
+ })
+ .and(
+ GenericUpdateAppConnectionFieldsSchema(AppConnection.MongoDB, {
+ supportsPlatformManagedCredentials: false,
+ supportsGateways: false
+ })
+ );
+
+export const MongoDBConnectionListItemSchema = z.object({
+ name: z.literal("MongoDB"),
+ app: z.literal(AppConnection.MongoDB),
+ methods: z.nativeEnum(MongoDBConnectionMethod).array(),
+ supportsPlatformManagement: z.literal(false)
+});
diff --git a/backend/src/services/app-connection/mongodb/mongodb-connection-types.ts b/backend/src/services/app-connection/mongodb/mongodb-connection-types.ts
new file mode 100644
index 0000000000..52212545a4
--- /dev/null
+++ b/backend/src/services/app-connection/mongodb/mongodb-connection-types.ts
@@ -0,0 +1,22 @@
+import z from "zod";
+
+import { DiscriminativePick } from "@app/lib/types";
+
+import { AppConnection } from "../app-connection-enums";
+import {
+ CreateMongoDBConnectionSchema,
+ MongoDBConnectionSchema,
+ ValidateMongoDBConnectionCredentialsSchema
+} from "./mongodb-connection-schemas";
+
+export type TMongoDBConnection = z.infer;
+
+export type TMongoDBConnectionInput = z.infer & {
+ app: AppConnection.MongoDB;
+};
+
+export type TValidateMongoDBConnectionCredentialsSchema = typeof ValidateMongoDBConnectionCredentialsSchema;
+
+export type TMongoDBConnectionConfig = DiscriminativePick & {
+ orgId: string;
+};
diff --git a/frontend/src/hooks/api/appConnections/enums.ts b/frontend/src/hooks/api/appConnections/enums.ts
index fba0cbb4bc..d04b42d4c9 100644
--- a/frontend/src/hooks/api/appConnections/enums.ts
+++ b/frontend/src/hooks/api/appConnections/enums.ts
@@ -39,6 +39,7 @@ export enum AppConnection {
Northflank = "northflank",
Okta = "okta",
Redis = "redis",
+ MongoDB = "mongodb",
LaravelForge = "laravel-forge",
Chef = "chef"
}
From 055a663d24d9da0052d828ab7a6b539307cef738 Mon Sep 17 00:00:00 2001
From: Victor Santos
Date: Mon, 17 Nov 2025 09:21:44 -0300
Subject: [PATCH 002/307] Add MongoDB connection form and related updates
- Implemented MongoDB connection form with validation and credential handling.
- Updated enums and maps to include MongoDB support across backend and frontend.
- Enhanced app connection schemas to accommodate MongoDB connection options.
- Integrated MongoDB connection handling in relevant components and hooks.
---
.../secret-rotation-v2-enums.ts | 3 +-
.../secret-rotation-v2-maps.ts | 6 +-
.../app-connection-router.ts | 6 +
frontend/src/helpers/appConnections.ts | 3 +
.../api/appConnections/types/app-options.ts | 7 +
.../hooks/api/appConnections/types/index.ts | 3 +
.../types/mongodb-connection.ts | 22 ++
.../AppConnectionForm/AppConnectionForm.tsx | 5 +
.../MongoDBConnectionForm.tsx | 334 ++++++++++++++++++
9 files changed, 386 insertions(+), 3 deletions(-)
create mode 100644 frontend/src/hooks/api/appConnections/types/mongodb-connection.ts
create mode 100644 frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/MongoDBConnectionForm.tsx
diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-enums.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-enums.ts
index 661a2399ae..470a638498 100644
--- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-enums.ts
+++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-enums.ts
@@ -8,7 +8,8 @@ export enum SecretRotation {
AwsIamUserSecret = "aws-iam-user-secret",
LdapPassword = "ldap-password",
OktaClientSecret = "okta-client-secret",
- RedisCredentials = "redis-credentials"
+ RedisCredentials = "redis-credentials",
+ MongoDBCredentials = "mongodb-credentials"
}
export enum SecretRotationStatus {
diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-maps.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-maps.ts
index 2087fa1957..c2b0714ab0 100644
--- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-maps.ts
+++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-maps.ts
@@ -11,7 +11,8 @@ export const SECRET_ROTATION_NAME_MAP: Record = {
[SecretRotation.AwsIamUserSecret]: "AWS IAM User Secret",
[SecretRotation.LdapPassword]: "LDAP Password",
[SecretRotation.OktaClientSecret]: "Okta Client Secret",
- [SecretRotation.RedisCredentials]: "Redis Credentials"
+ [SecretRotation.RedisCredentials]: "Redis Credentials",
+ [SecretRotation.MongoDBCredentials]: "MongoDB Credentials"
};
export const SECRET_ROTATION_CONNECTION_MAP: Record = {
@@ -24,5 +25,6 @@ export const SECRET_ROTATION_CONNECTION_MAP: Record;
diff --git a/frontend/src/hooks/api/appConnections/types/mongodb-connection.ts b/frontend/src/hooks/api/appConnections/types/mongodb-connection.ts
new file mode 100644
index 0000000000..151c676a54
--- /dev/null
+++ b/frontend/src/hooks/api/appConnections/types/mongodb-connection.ts
@@ -0,0 +1,22 @@
+import { AppConnection } from "@app/hooks/api/appConnections/enums";
+import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
+
+export enum MongoDBConnectionMethod {
+ UsernameAndPassword = "username-and-password"
+}
+
+export type TMongoDBConnectionCredentials = {
+ host: string;
+ port: number;
+ username: string;
+ password: string;
+ database: string;
+ sslEnabled: boolean;
+ sslRejectUnauthorized: boolean;
+ sslCertificate?: string;
+};
+
+export type TMongoDBConnection = TRootAppConnection & { app: AppConnection.MongoDB } & {
+ method: MongoDBConnectionMethod.UsernameAndPassword;
+ credentials: TMongoDBConnectionCredentials;
+};
diff --git a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/AppConnectionForm.tsx b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/AppConnectionForm.tsx
index aca33ffa28..3b1710fdb5 100644
--- a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/AppConnectionForm.tsx
+++ b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/AppConnectionForm.tsx
@@ -34,6 +34,7 @@ import { HerokuConnectionForm } from "./HerokuAppConnectionForm";
import { HumanitecConnectionForm } from "./HumanitecConnectionForm";
import { LaravelForgeConnectionForm } from "./LaravelForgeConnectionForm";
import { LdapConnectionForm } from "./LdapConnectionForm";
+import { MongoDBConnectionForm } from "./MongoDBConnectionForm";
import { MsSqlConnectionForm } from "./MsSqlConnectionForm";
import { MySqlConnectionForm } from "./MySqlConnectionForm";
import { NetlifyConnectionForm } from "./NetlifyConnectionForm";
@@ -170,6 +171,8 @@ const CreateForm = ({ app, onComplete, projectId }: CreateFormProps) => {
return ;
case AppConnection.Redis:
return ;
+ case AppConnection.MongoDB:
+ return ;
default:
throw new Error(`Unhandled App ${app}`);
}
@@ -326,6 +329,8 @@ const UpdateForm = ({ appConnection, onComplete }: UpdateFormProps) => {
return ;
case AppConnection.Redis:
return ;
+ case AppConnection.MongoDB:
+ return ;
default:
throw new Error(`Unhandled App ${(appConnection as TAppConnection).app}`);
}
diff --git a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/MongoDBConnectionForm.tsx b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/MongoDBConnectionForm.tsx
new file mode 100644
index 0000000000..c9b3421cee
--- /dev/null
+++ b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/MongoDBConnectionForm.tsx
@@ -0,0 +1,334 @@
+import { useEffect, useState } from "react";
+import { Controller, FormProvider, useForm } from "react-hook-form";
+import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tab } from "@headlessui/react";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { z } from "zod";
+
+import {
+ Button,
+ FormControl,
+ Input,
+ ModalClose,
+ SecretInput,
+ Select,
+ SelectItem,
+ Switch,
+ TextArea,
+ Tooltip
+} from "@app/components/v2";
+import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
+import { MongoDBConnectionMethod, TMongoDBConnection } from "@app/hooks/api/appConnections";
+import { AppConnection } from "@app/hooks/api/appConnections/enums";
+
+import {
+ genericAppConnectionFieldsSchema,
+ GenericAppConnectionsFields
+} from "./GenericAppConnectionFields";
+
+type Props = {
+ appConnection?: TMongoDBConnection;
+ onSubmit: (formData: FormData) => Promise;
+};
+
+const rootSchema = genericAppConnectionFieldsSchema.extend({
+ app: z.literal(AppConnection.MongoDB)
+});
+
+const formSchema = z.discriminatedUnion("method", [
+ rootSchema.extend({
+ method: z.literal(MongoDBConnectionMethod.UsernameAndPassword),
+ credentials: z.object({
+ host: z.string().trim().min(1, "Host required"),
+ port: z.coerce.number().default(27017),
+ username: z.string().trim().min(1, "Username required"),
+ password: z.string().trim().min(1, "Password required"),
+ database: z.string().trim().min(1, "Database required"),
+ sslEnabled: z.boolean().default(false),
+ sslRejectUnauthorized: z.boolean().default(true),
+ sslCertificate: z
+ .string()
+ .trim()
+ .transform((value) => value || undefined)
+ .optional()
+ })
+ })
+]);
+
+type FormData = z.infer;
+
+export const MongoDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
+ const isUpdate = Boolean(appConnection);
+ const [selectedTabIndex, setSelectedTabIndex] = useState(0);
+
+ const form = useForm({
+ resolver: zodResolver(formSchema),
+ defaultValues: appConnection ?? {
+ app: AppConnection.MongoDB,
+ method: MongoDBConnectionMethod.UsernameAndPassword,
+ credentials: {
+ host: "",
+ port: 27017,
+ username: "",
+ password: "",
+ database: "",
+ sslEnabled: false,
+ sslRejectUnauthorized: true,
+ sslCertificate: undefined
+ }
+ }
+ });
+
+ const {
+ handleSubmit,
+ watch,
+ control,
+ setValue,
+ formState: { isSubmitting, isDirty }
+ } = form;
+
+ const host = watch("credentials.host");
+ const sslEnabled = watch("credentials.sslEnabled");
+
+ useEffect(() => {
+ if (host && host.includes("+srv") && !sslEnabled) {
+ setValue("credentials.sslEnabled", true, { shouldDirty: true });
+ }
+ }, [host, sslEnabled, setValue]);
+
+ return (
+
+
+
+
+ )}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
From ed475f0ed8ff18fd0d9661f447a269c1db6fae85 Mon Sep 17 00:00:00 2001
From: Victor Santos
Date: Mon, 17 Nov 2025 17:39:37 -0300
Subject: [PATCH 003/307] Add MongoDB integration and documentation updates
- Enhanced MongoDB connection handling in backend with improved host validation.
- Updated documentation to include MongoDB connection options and API references.
- Added new images for MongoDB connection forms and processes.
- Implemented MongoDB connection form in the frontend with necessary adjustments.
---
.../mongodb/mongodb-connection-fns.ts | 12 +-
docs/docs.json | 13 ++
.../general/add-connection.png | Bin 952486 -> 99133 bytes
.../mongodb/mongodb-app-connection-form.png | Bin 0 -> 135672 bytes
.../mongodb-app-connection-generated.png | Bin 0 -> 99017 bytes
.../mongodb/mongodb-app-connection-option.png | Bin 0 -> 164660 bytes
docs/integrations/app-connections/mongodb.mdx | 141 ++++++++++++++++++
.../MongoDBConnectionForm.tsx | 10 +-
.../AppConnectionForm/RedisConnectionForm.tsx | 2 +-
9 files changed, 166 insertions(+), 12 deletions(-)
create mode 100644 docs/images/app-connections/mongodb/mongodb-app-connection-form.png
create mode 100644 docs/images/app-connections/mongodb/mongodb-app-connection-generated.png
create mode 100644 docs/images/app-connections/mongodb/mongodb-app-connection-option.png
create mode 100644 docs/integrations/app-connections/mongodb.mdx
diff --git a/backend/src/services/app-connection/mongodb/mongodb-connection-fns.ts b/backend/src/services/app-connection/mongodb/mongodb-connection-fns.ts
index 18aac82252..5587fc388b 100644
--- a/backend/src/services/app-connection/mongodb/mongodb-connection-fns.ts
+++ b/backend/src/services/app-connection/mongodb/mongodb-connection-fns.ts
@@ -17,11 +17,19 @@ export const getMongoDBConnectionListItem = () => {
};
export const validateMongoDBConnectionCredentials = async (config: TMongoDBConnectionConfig) => {
- const [hostIp] = await verifyHostInputValidity(config.credentials.host);
+ let normalizedHost = config.credentials.host.trim();
+ const isSrvFromHost = normalizedHost.startsWith("mongodb+srv://");
+ if (isSrvFromHost) {
+ normalizedHost = normalizedHost.replace(/^mongodb\+srv:\/\//, "");
+ } else if (normalizedHost.startsWith("mongodb://")) {
+ normalizedHost = normalizedHost.replace(/^mongodb:\/\//, "");
+ }
+
+ const [hostIp] = await verifyHostInputValidity(normalizedHost);
let client: MongoClient | null = null;
try {
- const isSrv = !config.credentials.port;
+ const isSrv = !config.credentials.port || isSrvFromHost;
const uri = isSrv ? `mongodb+srv://${hostIp}` : `mongodb://${hostIp}:${config.credentials.port}`;
const clientOptions: {
diff --git a/docs/docs.json b/docs/docs.json
index aea022fd4b..295a81bbb3 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -129,6 +129,7 @@
"integrations/app-connections/laravel-forge",
"integrations/app-connections/ldap",
"integrations/app-connections/mssql",
+ "integrations/app-connections/mongodb",
"integrations/app-connections/mysql",
"integrations/app-connections/netlify",
"integrations/app-connections/northflank",
@@ -1889,6 +1890,18 @@
"api-reference/endpoints/app-connections/mssql/delete"
]
},
+ {
+ "group": "MongoDB",
+ "pages": [
+ "api-reference/endpoints/app-connections/mongodb/list",
+ "api-reference/endpoints/app-connections/mongodb/available",
+ "api-reference/endpoints/app-connections/mongodb/get-by-id",
+ "api-reference/endpoints/app-connections/mongodb/get-by-name",
+ "api-reference/endpoints/app-connections/mongodb/create",
+ "api-reference/endpoints/app-connections/mongodb/update",
+ "api-reference/endpoints/app-connections/mongodb/delete"
+ ]
+ },
{
"group": "MySQL",
"pages": [
diff --git a/docs/images/app-connections/general/add-connection.png b/docs/images/app-connections/general/add-connection.png
index b6dce69ac1a6044d2b6080812340e27defc55993..5ebadae098225dc06560997a06bd89b0833a01d1 100644
GIT binary patch
literal 99133
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