Merge pull request #835 from Dokploy/560-self-hosted-gitlab

560 self hosted gitlab
This commit is contained in:
Mauricio Siu
2024-12-10 22:07:59 -06:00
committed by GitHub
10 changed files with 4312 additions and 13 deletions

View File

@@ -33,6 +33,9 @@ const Schema = z.object({
name: z.string().min(1, {
message: "Name is required",
}),
gitlabUrl: z.string().min(1, {
message: "GitLab URL is required",
}),
applicationId: z.string().min(1, {
message: "Application ID is required",
}),
@@ -62,16 +65,22 @@ export const AddGitlabProvider = () => {
applicationSecret: "",
groupName: "",
redirectUri: webhookUrl,
name: "",
gitlabUrl: "https://gitlab.com",
},
resolver: zodResolver(Schema),
});
const gitlabUrl = form.watch("gitlabUrl");
useEffect(() => {
form.reset({
applicationId: "",
applicationSecret: "",
groupName: "",
redirectUri: webhookUrl,
name: "",
gitlabUrl: "https://gitlab.com",
});
}, [form, isOpen]);
@@ -83,6 +92,7 @@ export const AddGitlabProvider = () => {
authId: auth?.id || "",
name: data.name || "",
redirectUri: data.redirectUri || "",
gitlabUrl: data.gitlabUrl || "https://gitlab.com",
})
.then(async () => {
await utils.gitProvider.getAll.invalidate();
@@ -129,7 +139,7 @@ export const AddGitlabProvider = () => {
<li className="flex flex-row gap-2 items-center">
Go to your GitLab profile settings{" "}
<Link
href="https://gitlab.com/-/profile/applications"
href={`${gitlabUrl}/-/profile/applications`}
target="_blank"
>
<ExternalLink className="w-fit text-primary size-4" />
@@ -169,6 +179,20 @@ export const AddGitlabProvider = () => {
)}
/>
<FormField
control={form.control}
name="gitlabUrl"
render={({ field }) => (
<FormItem>
<FormLabel>Gitlab URL</FormLabel>
<FormControl>
<Input placeholder="https://gitlab.com/" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="redirectUri"

View File

@@ -30,6 +30,9 @@ const Schema = z.object({
name: z.string().min(1, {
message: "Name is required",
}),
gitlabUrl: z.string().url({
message: "Invalid Gitlab URL",
}),
groupName: z.string().optional(),
});
@@ -40,7 +43,7 @@ interface Props {
}
export const EditGitlabProvider = ({ gitlabId }: Props) => {
const { data: gitlab } = api.gitlab.one.useQuery(
const { data: gitlab, refetch } = api.gitlab.one.useQuery(
{
gitlabId,
},
@@ -57,6 +60,7 @@ export const EditGitlabProvider = ({ gitlabId }: Props) => {
defaultValues: {
groupName: "",
name: "",
gitlabUrl: "https://gitlab.com",
},
resolver: zodResolver(Schema),
});
@@ -67,6 +71,7 @@ export const EditGitlabProvider = ({ gitlabId }: Props) => {
form.reset({
groupName: gitlab?.groupName || "",
name: gitlab?.gitProvider.name || "",
gitlabUrl: gitlab?.gitlabUrl || "",
});
}, [form, isOpen]);
@@ -76,11 +81,13 @@ export const EditGitlabProvider = ({ gitlabId }: Props) => {
gitProviderId: gitlab?.gitProviderId || "",
groupName: data.groupName || "",
name: data.name || "",
gitlabUrl: data.gitlabUrl || "",
})
.then(async () => {
await utils.gitProvider.getAll.invalidate();
toast.success("Gitlab updated successfully");
setIsOpen(false);
refetch();
})
.catch(() => {
toast.error("Error to update Gitlab");
@@ -126,6 +133,19 @@ export const EditGitlabProvider = ({ gitlabId }: Props) => {
</FormItem>
)}
/>
<FormField
control={form.control}
name="gitlabUrl"
render={({ field }) => (
<FormItem>
<FormLabel>Gitlab Url</FormLabel>
<FormControl>
<Input placeholder="https://gitlab.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}

View File

@@ -23,12 +23,16 @@ export const ShowGitProviders = () => {
const url = useUrl();
const getGitlabUrl = (clientId: string, gitlabId: string) => {
const getGitlabUrl = (
clientId: string,
gitlabId: string,
gitlabUrl: string,
) => {
const redirectUri = `${url}/api/providers/gitlab/callback?gitlabId=${gitlabId}`;
const scope = "api read_user read_repository";
const authUrl = `https://gitlab.com/oauth/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${encodeURIComponent(scope)}`;
const authUrl = `${gitlabUrl}/oauth/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${encodeURIComponent(scope)}`;
return authUrl;
};
@@ -142,6 +146,7 @@ export const ShowGitProviders = () => {
href={getGitlabUrl(
gitProvider.gitlab?.applicationId || "",
gitProvider.gitlab?.gitlabId || "",
gitProvider.gitlab?.gitlabUrl,
)}
target="_blank"
className={buttonVariants({

View File

@@ -0,0 +1 @@
ALTER TABLE "gitlab" ADD COLUMN "gitlabUrl" text DEFAULT 'https://gitlab.com' NOT NULL;

File diff suppressed because it is too large Load Diff

View File

@@ -351,6 +351,13 @@
"when": 1733628762978,
"tag": "0049_dark_leopardon",
"breakpoints": true
},
{
"idx": 50,
"version": "6",
"when": 1733889104203,
"tag": "0050_nappy_wrecker",
"breakpoints": true
}
]
}

View File

@@ -13,7 +13,7 @@ export default async function handler(
const gitlab = await findGitlabById(gitlabId as string);
const response = await fetch("https://gitlab.com/oauth/token", {
const response = await fetch(`${gitlab.gitlabUrl}/oauth/token`, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",

View File

@@ -142,6 +142,10 @@ export const gitlabRouter = createTRPCRouter({
name: input.name,
adminId: ctx.user.adminId,
});
await updateGitlab(input.gitlabId, {
...input,
});
} else {
await updateGitlab(input.gitlabId, {
...input,

View File

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

View File

@@ -26,7 +26,7 @@ export const refreshGitlabToken = async (gitlabProviderId: string) => {
return;
}
const response = await fetch("https://gitlab.com/oauth/token", {
const response = await fetch(`${gitlabProvider.gitlabUrl}/oauth/token`, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
@@ -122,7 +122,7 @@ export const cloneGitlabRepository = async (
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
const outputPath = join(basePath, appName, "code");
await recreateDirectory(outputPath);
const repoclone = `gitlab.com/${gitlabPathNamespace}.git`;
const repoclone = `${gitlab?.gitlabUrl.replace(/^https?:\/\//, "")}/${gitlabPathNamespace}.git`;
const cloneUrl = `https://oauth2:${gitlab?.accessToken}@${repoclone}`;
try {
@@ -218,7 +218,7 @@ export const getGitlabCloneCommand = async (
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
const outputPath = join(basePath, appName, "code");
await recreateDirectory(outputPath);
const repoclone = `gitlab.com/${gitlabPathNamespace}.git`;
const repoclone = `${gitlab?.gitlabUrl.replace(/^https?:\/\//, "")}/${gitlabPathNamespace}.git`;
const cloneUrl = `https://oauth2:${gitlab?.accessToken}@${repoclone}`;
const cloneCommand = `
@@ -244,7 +244,7 @@ export const getGitlabRepositories = async (gitlabId?: string) => {
const gitlabProvider = await findGitlabById(gitlabId);
const response = await fetch(
`https://gitlab.com/api/v4/projects?membership=true&owned=true&page=${0}&per_page=${100}`,
`${gitlabProvider.gitlabUrl}/api/v4/projects?membership=true&owned=true&page=${0}&per_page=${100}`,
{
headers: {
Authorization: `Bearer ${gitlabProvider.accessToken}`,
@@ -304,7 +304,7 @@ export const getGitlabBranches = async (input: {
const gitlabProvider = await findGitlabById(input.gitlabId);
const branchesResponse = await fetch(
`https://gitlab.com/api/v4/projects/${input.id}/repository/branches`,
`${gitlabProvider.gitlabUrl}/api/v4/projects/${input.id}/repository/branches`,
{
headers: {
Authorization: `Bearer ${gitlabProvider.accessToken}`,
@@ -350,7 +350,9 @@ export const cloneRawGitlabRepository = async (entity: Compose) => {
const basePath = COMPOSE_PATH;
const outputPath = join(basePath, appName, "code");
await recreateDirectory(outputPath);
const repoclone = `gitlab.com/${gitlabPathNamespace}.git`;
const gitlabUrl = gitlabProvider.gitlabUrl;
// What happen with oauth in self hosted instances?
const repoclone = `${gitlabUrl.replace(/^https?:\/\//, "")}/${gitlabPathNamespace}.git`;
const cloneUrl = `https://oauth2:${gitlabProvider?.accessToken}@${repoclone}`;
try {
@@ -390,7 +392,7 @@ export const cloneRawGitlabRepositoryRemote = async (compose: Compose) => {
await refreshGitlabToken(gitlabId);
const basePath = COMPOSE_PATH;
const outputPath = join(basePath, appName, "code");
const repoclone = `gitlab.com/${gitlabPathNamespace}.git`;
const repoclone = `${gitlabProvider.gitlabUrl.replace(/^https?:\/\//, "")}/${gitlabPathNamespace}.git`;
const cloneUrl = `https://oauth2:${gitlabProvider?.accessToken}@${repoclone}`;
try {
const command = `
@@ -417,7 +419,7 @@ export const testGitlabConnection = async (
const gitlabProvider = await findGitlabById(gitlabId);
const response = await fetch(
`https://gitlab.com/api/v4/projects?membership=true&owned=true&page=${0}&per_page=${100}`,
`${gitlabProvider.gitlabUrl}/api/v4/projects?membership=true&owned=true&page=${0}&per_page=${100}`,
{
headers: {
Authorization: `Bearer ${gitlabProvider.accessToken}`,