Merge pull request #3631 from Dokploy/3223-dokploy-selfhosted-gitlab-ce-selfhosted-error-postgre

feat(gitlab): add optional internal URL for GitLab integration
This commit is contained in:
Mauricio Siu
2026-02-07 12:54:29 -06:00
committed by GitHub
11 changed files with 7322 additions and 2418 deletions

View File

@@ -21,6 +21,7 @@ import {
FormControl,
FormField,
FormItem,
FormDescription,
FormLabel,
FormMessage,
} from "@/components/ui/form";
@@ -35,6 +36,10 @@ const Schema = z.object({
gitlabUrl: z.string().min(1, {
message: "GitLab URL is required",
}),
gitlabInternalUrl: z
.union([z.string().url(), z.literal("")])
.optional()
.transform((v) => (v === "" ? undefined : v)),
applicationId: z.string().min(1, {
message: "Application ID is required",
}),
@@ -66,6 +71,7 @@ export const AddGitlabProvider = () => {
redirectUri: webhookUrl,
name: "",
gitlabUrl: "https://gitlab.com",
gitlabInternalUrl: "",
},
resolver: zodResolver(Schema),
});
@@ -80,6 +86,7 @@ export const AddGitlabProvider = () => {
redirectUri: webhookUrl,
name: "",
gitlabUrl: "https://gitlab.com",
gitlabInternalUrl: "",
});
}, [form, isOpen]);
@@ -92,6 +99,7 @@ export const AddGitlabProvider = () => {
name: data.name || "",
redirectUri: data.redirectUri || "",
gitlabUrl: data.gitlabUrl || "https://gitlab.com",
gitlabInternalUrl: data.gitlabInternalUrl || undefined,
})
.then(async () => {
await utils.gitProvider.getAll.invalidate();
@@ -192,6 +200,29 @@ export const AddGitlabProvider = () => {
)}
/>
<FormField
control={form.control}
name="gitlabInternalUrl"
render={({ field }) => (
<FormItem>
<FormLabel>Internal URL (Optional)</FormLabel>
<FormControl>
<Input
placeholder="http://gitlab:80"
{...field}
value={field.value ?? ""}
/>
</FormControl>
<FormDescription>
Use when GitLab runs on the same instance as Dokploy.
Used for OAuth token exchange to reach GitLab via
internal network (e.g. Docker service name).
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="redirectUri"

View File

@@ -20,6 +20,7 @@ import {
FormControl,
FormField,
FormItem,
FormDescription,
FormLabel,
FormMessage,
} from "@/components/ui/form";
@@ -33,6 +34,10 @@ const Schema = z.object({
gitlabUrl: z.string().url({
message: "Invalid Gitlab URL",
}),
gitlabInternalUrl: z
.union([z.string().url(), z.literal("")])
.optional()
.transform((v) => (v === "" ? undefined : v)),
groupName: z.string().optional(),
});
@@ -61,6 +66,7 @@ export const EditGitlabProvider = ({ gitlabId }: Props) => {
groupName: "",
name: "",
gitlabUrl: "https://gitlab.com",
gitlabInternalUrl: "",
},
resolver: zodResolver(Schema),
});
@@ -72,6 +78,7 @@ export const EditGitlabProvider = ({ gitlabId }: Props) => {
groupName: gitlab?.groupName || "",
name: gitlab?.gitProvider.name || "",
gitlabUrl: gitlab?.gitlabUrl || "",
gitlabInternalUrl: gitlab?.gitlabInternalUrl || "",
});
}, [form, isOpen]);
@@ -82,6 +89,7 @@ export const EditGitlabProvider = ({ gitlabId }: Props) => {
groupName: data.groupName || "",
name: data.name || "",
gitlabUrl: data.gitlabUrl || "",
gitlabInternalUrl: data.gitlabInternalUrl ?? null,
})
.then(async () => {
await utils.gitProvider.getAll.invalidate();
@@ -151,6 +159,29 @@ export const EditGitlabProvider = ({ gitlabId }: Props) => {
)}
/>
<FormField
control={form.control}
name="gitlabInternalUrl"
render={({ field }) => (
<FormItem>
<FormLabel>Internal URL (Optional)</FormLabel>
<FormControl>
<Input
placeholder="http://gitlab:80"
{...field}
value={field.value ?? ""}
/>
</FormControl>
<FormDescription>
Use when GitLab runs on the same instance as Dokploy.
Used for OAuth token exchange to reach GitLab via
internal network (e.g. Docker service name).
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="groupName"

View File

@@ -0,0 +1 @@
ALTER TABLE "gitlab" ADD COLUMN "gitlabInternalUrl" text;

File diff suppressed because it is too large Load Diff

View File

@@ -981,6 +981,13 @@
"when": 1770442690721,
"tag": "0139_brave_bloodstorm",
"breakpoints": true
},
{
"idx": 140,
"version": "7",
"when": 1770489900075,
"tag": "0140_lame_mattie_franklin",
"breakpoints": true
}
]
}

View File

@@ -12,7 +12,9 @@ export default async function handler(
}
const gitlab = await findGitlabById(gitlabId as string);
const gitlabUrl = new URL(gitlab.gitlabUrl);
// Use internal URL for token exchange when GitLab is on same instance as Dokploy
const baseUrl = gitlab.gitlabInternalUrl || gitlab.gitlabUrl;
const gitlabUrl = new URL(baseUrl);
const headers: HeadersInit = {
"Content-Type": "application/x-www-form-urlencoded",

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,7 @@ export const gitlab = pgTable("gitlab", {
.primaryKey()
.$defaultFn(() => nanoid()),
gitlabUrl: text("gitlabUrl").default("https://gitlab.com").notNull(),
gitlabInternalUrl: text("gitlabInternalUrl"),
applicationId: text("application_id"),
redirectUri: text("redirect_uri"),
secret: text("secret"),
@@ -41,6 +42,7 @@ export const apiCreateGitlab = createSchema.extend({
authId: z.string().min(1),
name: z.string().min(1),
gitlabUrl: z.string().min(1),
gitlabInternalUrl: z.string().optional().nullable(),
});
export const apiFindOneGitlab = createSchema
@@ -70,4 +72,5 @@ export const apiUpdateGitlab = createSchema.extend({
name: z.string().min(1),
gitlabId: z.string().min(1),
gitlabUrl: z.string().min(1),
gitlabInternalUrl: z.string().optional().nullable(),
});

View File

@@ -497,6 +497,7 @@ table github {
table gitlab {
gitlabId text [pk, not null]
gitlabUrl text [not null, default: 'https://gitlab.com']
gitlabInternalUrl text
application_id text
redirect_uri text
secret text

View File

@@ -21,7 +21,9 @@ export const refreshGitlabToken = async (gitlabProviderId: string) => {
return;
}
const response = await fetch(`${gitlabProvider.gitlabUrl}/oauth/token`, {
// Use internal URL for token refresh when GitLab is on same instance as Dokploy
const baseUrl = gitlabProvider.gitlabInternalUrl || gitlabProvider.gitlabUrl;
const response = await fetch(`${baseUrl}/oauth/token`, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",

File diff suppressed because it is too large Load Diff