mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
refactor: Update Gitea provider components and API handling
- Adjusted GiteaProviderSchema to ensure watchPaths are correctly initialized and validated. - Refactored SaveGiteaProvider and SaveGiteaProviderCompose components for improved state management and UI consistency. - Simplified API router methods for Gitea, enhancing readability and error handling. - Updated database schema and service functions for better clarity and maintainability. - Removed unnecessary comments and improved logging for better debugging.
This commit is contained in:
parent
fc7eff94b6
commit
9359ee7a04
@ -67,13 +67,13 @@ const GiteaProviderSchema = z.object({
|
|||||||
.object({
|
.object({
|
||||||
repo: z.string().min(1, "Repo is required"),
|
repo: z.string().min(1, "Repo is required"),
|
||||||
owner: z.string().min(1, "Owner is required"),
|
owner: z.string().min(1, "Owner is required"),
|
||||||
giteaPathNamespace: z.string().min(1),
|
giteaPathNamespace: z.string(),
|
||||||
id: z.number().nullable(),
|
id: z.number().nullable(),
|
||||||
watchPaths: z.array(z.string()).default([]),
|
|
||||||
})
|
})
|
||||||
.required(),
|
.required(),
|
||||||
branch: z.string().min(1, "Branch is required"),
|
branch: z.string().min(1, "Branch is required"),
|
||||||
giteaId: z.string().min(1, "Gitea Provider is required"),
|
giteaId: z.string().min(1, "Gitea Provider is required"),
|
||||||
|
watchPaths: z.array(z.string()).default([]),
|
||||||
});
|
});
|
||||||
|
|
||||||
type GiteaProvider = z.infer<typeof GiteaProviderSchema>;
|
type GiteaProvider = z.infer<typeof GiteaProviderSchema>;
|
||||||
@ -97,10 +97,10 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
|
|||||||
repo: "",
|
repo: "",
|
||||||
giteaPathNamespace: "",
|
giteaPathNamespace: "",
|
||||||
id: null,
|
id: null,
|
||||||
watchPaths: [],
|
|
||||||
},
|
},
|
||||||
giteaId: "",
|
giteaId: "",
|
||||||
branch: "",
|
branch: "",
|
||||||
|
watchPaths: [],
|
||||||
},
|
},
|
||||||
resolver: zodResolver(GiteaProviderSchema),
|
resolver: zodResolver(GiteaProviderSchema),
|
||||||
});
|
});
|
||||||
@ -146,10 +146,10 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
|
|||||||
owner: data.giteaOwner || "",
|
owner: data.giteaOwner || "",
|
||||||
giteaPathNamespace: data.giteaPathNamespace || "",
|
giteaPathNamespace: data.giteaPathNamespace || "",
|
||||||
id: data.giteaProjectId,
|
id: data.giteaProjectId,
|
||||||
watchPaths: data.watchPaths || [],
|
|
||||||
},
|
},
|
||||||
buildPath: data.giteaBuildPath || "/",
|
buildPath: data.giteaBuildPath || "/",
|
||||||
giteaId: data.giteaId || "",
|
giteaId: data.giteaId || "",
|
||||||
|
watchPaths: data.watchPaths || [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [form.reset, data, form]);
|
}, [form.reset, data, form]);
|
||||||
@ -164,7 +164,7 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
|
|||||||
applicationId,
|
applicationId,
|
||||||
giteaProjectId: data.repository.id,
|
giteaProjectId: data.repository.id,
|
||||||
giteaPathNamespace: data.repository.giteaPathNamespace,
|
giteaPathNamespace: data.repository.giteaPathNamespace,
|
||||||
watchPaths: data.repository.watchPaths,
|
watchPaths: data.watchPaths,
|
||||||
})
|
})
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
toast.success("Service Provider Saved");
|
toast.success("Service Provider Saved");
|
||||||
@ -198,7 +198,6 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
|
|||||||
repo: "",
|
repo: "",
|
||||||
id: null,
|
id: null,
|
||||||
giteaPathNamespace: "",
|
giteaPathNamespace: "",
|
||||||
watchPaths: [],
|
|
||||||
});
|
});
|
||||||
form.setValue("branch", "");
|
form.setValue("branch", "");
|
||||||
}}
|
}}
|
||||||
@ -285,7 +284,6 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
|
|||||||
repo: repo.name,
|
repo: repo.name,
|
||||||
id: repo.id,
|
id: repo.id,
|
||||||
giteaPathNamespace: repo.name,
|
giteaPathNamespace: repo.name,
|
||||||
watchPaths: [],
|
|
||||||
});
|
});
|
||||||
form.setValue("branch", "");
|
form.setValue("branch", "");
|
||||||
}}
|
}}
|
||||||
@ -413,7 +411,7 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
|
|||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="repository.watchPaths"
|
name="watchPaths"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="md:col-span-2">
|
<FormItem className="md:col-span-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
@ -41,7 +41,7 @@ import { cn } from "@/lib/utils";
|
|||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import type { Branch, Repository } from "@/utils/gitea-utils";
|
import type { Branch, Repository } from "@/utils/gitea-utils";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { CheckIcon, ChevronsUpDown, X } from "lucide-react";
|
import { CheckIcon, ChevronsUpDown, Plus, X } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
@ -55,7 +55,7 @@ const GiteaProviderSchema = z.object({
|
|||||||
repo: z.string().min(1, "Repo is required"),
|
repo: z.string().min(1, "Repo is required"),
|
||||||
owner: z.string().min(1, "Owner is required"),
|
owner: z.string().min(1, "Owner is required"),
|
||||||
id: z.number().nullable(),
|
id: z.number().nullable(),
|
||||||
giteaPathNamespace: z.string().min(1),
|
giteaPathNamespace: z.string(),
|
||||||
})
|
})
|
||||||
.required(),
|
.required(),
|
||||||
branch: z.string().min(1, "Branch is required"),
|
branch: z.string().min(1, "Branch is required"),
|
||||||
@ -95,6 +95,8 @@ export const SaveGiteaProviderCompose = ({ composeId }: Props) => {
|
|||||||
const repository = form.watch("repository");
|
const repository = form.watch("repository");
|
||||||
const giteaId = form.watch("giteaId");
|
const giteaId = form.watch("giteaId");
|
||||||
|
|
||||||
|
console.log(repository);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: repositories,
|
data: repositories,
|
||||||
isLoading: isLoadingRepositories,
|
isLoading: isLoadingRepositories,
|
||||||
@ -126,6 +128,7 @@ export const SaveGiteaProviderCompose = ({ composeId }: Props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
|
console.log(data.giteaRepository);
|
||||||
form.reset({
|
form.reset({
|
||||||
branch: data.giteaBranch || "",
|
branch: data.giteaBranch || "",
|
||||||
repository: {
|
repository: {
|
||||||
@ -452,20 +455,20 @@ export const SaveGiteaProviderCompose = ({ composeId }: Props) => {
|
|||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="secondary"
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const input = document.querySelector(
|
const input = document.querySelector(
|
||||||
'input[placeholder="Enter a path to watch (e.g., src/*, dist/*)"]',
|
'input[placeholder*="Enter a path"]',
|
||||||
) as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
const value = input.value.trim();
|
const path = input.value.trim();
|
||||||
if (value) {
|
if (path) {
|
||||||
const newPaths = [...(field.value || []), value];
|
field.onChange([...field.value, path]);
|
||||||
form.setValue("watchPaths", newPaths);
|
|
||||||
input.value = "";
|
input.value = "";
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Add
|
<Plus className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@ -475,13 +478,11 @@ export const SaveGiteaProviderCompose = ({ composeId }: Props) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<div className="flex justify-end">
|
||||||
type="submit"
|
<Button type="submit" isLoading={isSavingGiteaProvider}>
|
||||||
className="w-full"
|
Save
|
||||||
disabled={isSavingGiteaProvider || !form.formState.isDirty}
|
|
||||||
>
|
|
||||||
{isSavingGiteaProvider ? "Saving..." : "Save Gitea Provider"}
|
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,14 +22,9 @@ import {
|
|||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
export const giteaRouter = createTRPCRouter({
|
export const giteaRouter = createTRPCRouter({
|
||||||
// Create a new Gitea provider
|
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
.input(apiCreateGitea)
|
.input(apiCreateGitea)
|
||||||
.mutation(
|
.mutation(async ({ input, ctx }) => {
|
||||||
async ({
|
|
||||||
input,
|
|
||||||
ctx,
|
|
||||||
}: { input: typeof apiCreateGitea._input; ctx: any }) => {
|
|
||||||
try {
|
try {
|
||||||
return await createGitea(input, ctx.session.activeOrganizationId);
|
return await createGitea(input, ctx.session.activeOrganizationId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -39,17 +34,11 @@ export const giteaRouter = createTRPCRouter({
|
|||||||
cause: error,
|
cause: error,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
|
|
||||||
// Fetch a specific Gitea provider by ID
|
|
||||||
one: protectedProcedure
|
one: protectedProcedure
|
||||||
.input(apiFindOneGitea)
|
.input(apiFindOneGitea)
|
||||||
.query(
|
.query(async ({ input, ctx }) => {
|
||||||
async ({
|
|
||||||
input,
|
|
||||||
ctx,
|
|
||||||
}: { input: typeof apiFindOneGitea._input; ctx: any }) => {
|
|
||||||
const giteaProvider = await findGiteaById(input.giteaId);
|
const giteaProvider = await findGiteaById(input.giteaId);
|
||||||
if (
|
if (
|
||||||
giteaProvider.gitProvider.organizationId !==
|
giteaProvider.gitProvider.organizationId !==
|
||||||
@ -61,10 +50,8 @@ export const giteaRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return giteaProvider;
|
return giteaProvider;
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
|
|
||||||
// Fetch all Gitea providers for the active organization
|
|
||||||
giteaProviders: protectedProcedure.query(async ({ ctx }: { ctx: any }) => {
|
giteaProviders: protectedProcedure.query(async ({ ctx }: { ctx: any }) => {
|
||||||
let result = await db.query.gitea.findMany({
|
let result = await db.query.gitea.findMany({
|
||||||
with: {
|
with: {
|
||||||
@ -72,14 +59,12 @@ export const giteaRouter = createTRPCRouter({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Filter by organization ID
|
|
||||||
result = result.filter(
|
result = result.filter(
|
||||||
(provider) =>
|
(provider) =>
|
||||||
provider.gitProvider.organizationId ===
|
provider.gitProvider.organizationId ===
|
||||||
ctx.session.activeOrganizationId,
|
ctx.session.activeOrganizationId,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Filter providers that meet the requirements
|
|
||||||
const filtered = result
|
const filtered = result
|
||||||
.filter((provider) => haveGiteaRequirements(provider))
|
.filter((provider) => haveGiteaRequirements(provider))
|
||||||
.map((provider) => {
|
.map((provider) => {
|
||||||
@ -94,14 +79,9 @@ export const giteaRouter = createTRPCRouter({
|
|||||||
return filtered;
|
return filtered;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// Fetch repositories from Gitea provider
|
|
||||||
getGiteaRepositories: protectedProcedure
|
getGiteaRepositories: protectedProcedure
|
||||||
.input(apiFindOneGitea)
|
.input(apiFindOneGitea)
|
||||||
.query(
|
.query(async ({ input, ctx }) => {
|
||||||
async ({
|
|
||||||
input,
|
|
||||||
ctx,
|
|
||||||
}: { input: typeof apiFindOneGitea._input; ctx: any }) => {
|
|
||||||
const { giteaId } = input;
|
const { giteaId } = input;
|
||||||
|
|
||||||
if (!giteaId) {
|
if (!giteaId) {
|
||||||
@ -123,7 +103,9 @@ export const giteaRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await getGiteaRepositories(giteaId);
|
const repositories = await getGiteaRepositories(giteaId);
|
||||||
|
console.log(repositories);
|
||||||
|
return repositories;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching Gitea repositories:", error);
|
console.error("Error fetching Gitea repositories:", error);
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
@ -131,17 +113,11 @@ export const giteaRouter = createTRPCRouter({
|
|||||||
message: error instanceof Error ? error.message : String(error),
|
message: error instanceof Error ? error.message : String(error),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
|
|
||||||
// Fetch branches of a specific Gitea repository
|
|
||||||
getGiteaBranches: protectedProcedure
|
getGiteaBranches: protectedProcedure
|
||||||
.input(apiFindGiteaBranches)
|
.input(apiFindGiteaBranches)
|
||||||
.query(
|
.query(async ({ input, ctx }) => {
|
||||||
async ({
|
|
||||||
input,
|
|
||||||
ctx,
|
|
||||||
}: { input: typeof apiFindGiteaBranches._input; ctx: any }) => {
|
|
||||||
const { giteaId, owner, repositoryName } = input;
|
const { giteaId, owner, repositoryName } = input;
|
||||||
|
|
||||||
if (!giteaId || !owner || !repositoryName) {
|
if (!giteaId || !owner || !repositoryName) {
|
||||||
@ -168,7 +144,7 @@ export const giteaRouter = createTRPCRouter({
|
|||||||
giteaId,
|
giteaId,
|
||||||
owner,
|
owner,
|
||||||
repo: repositoryName,
|
repo: repositoryName,
|
||||||
id: 0, // Provide a default value for the optional id
|
id: 0,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching Gitea branches:", error);
|
console.error("Error fetching Gitea branches:", error);
|
||||||
@ -177,17 +153,11 @@ export const giteaRouter = createTRPCRouter({
|
|||||||
message: error instanceof Error ? error.message : String(error),
|
message: error instanceof Error ? error.message : String(error),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
|
|
||||||
// Test connection to Gitea provider
|
|
||||||
testConnection: protectedProcedure
|
testConnection: protectedProcedure
|
||||||
.input(apiGiteaTestConnection)
|
.input(apiGiteaTestConnection)
|
||||||
.mutation(
|
.mutation(async ({ input, ctx }) => {
|
||||||
async ({
|
|
||||||
input,
|
|
||||||
ctx,
|
|
||||||
}: { input: typeof apiGiteaTestConnection._input; ctx: any }) => {
|
|
||||||
const giteaId = input.giteaId ?? "";
|
const giteaId = input.giteaId ?? "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -214,17 +184,11 @@ export const giteaRouter = createTRPCRouter({
|
|||||||
message: error instanceof Error ? error.message : String(error),
|
message: error instanceof Error ? error.message : String(error),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
|
|
||||||
// Update an existing Gitea provider
|
|
||||||
update: protectedProcedure
|
update: protectedProcedure
|
||||||
.input(apiUpdateGitea)
|
.input(apiUpdateGitea)
|
||||||
.mutation(
|
.mutation(async ({ input, ctx }) => {
|
||||||
async ({
|
|
||||||
input,
|
|
||||||
ctx,
|
|
||||||
}: { input: typeof apiUpdateGitea._input; ctx: any }) => {
|
|
||||||
const giteaProvider = await findGiteaById(input.giteaId);
|
const giteaProvider = await findGiteaById(input.giteaId);
|
||||||
if (
|
if (
|
||||||
giteaProvider.gitProvider.organizationId !==
|
giteaProvider.gitProvider.organizationId !==
|
||||||
@ -236,8 +200,6 @@ export const giteaRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Updating Gitea provider:", input);
|
|
||||||
|
|
||||||
if (input.name) {
|
if (input.name) {
|
||||||
await updateGitProvider(input.gitProviderId, {
|
await updateGitProvider(input.gitProviderId, {
|
||||||
name: input.name,
|
name: input.name,
|
||||||
@ -254,6 +216,5 @@ export const giteaRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
@ -5,13 +5,12 @@ import { nanoid } from "nanoid";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { gitProvider } from "./git-provider";
|
import { gitProvider } from "./git-provider";
|
||||||
|
|
||||||
// Gitea table definition
|
|
||||||
export const gitea = pgTable("gitea", {
|
export const gitea = pgTable("gitea", {
|
||||||
giteaId: text("giteaId")
|
giteaId: text("giteaId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.primaryKey()
|
.primaryKey()
|
||||||
.$defaultFn(() => nanoid()), // Using nanoid for unique ID
|
.$defaultFn(() => nanoid()),
|
||||||
giteaUrl: text("giteaUrl").default("https://gitea.com").notNull(), // Default URL for Gitea
|
giteaUrl: text("giteaUrl").default("https://gitea.com").notNull(),
|
||||||
redirectUri: text("redirect_uri"),
|
redirectUri: text("redirect_uri"),
|
||||||
clientId: text("client_id"),
|
clientId: text("client_id"),
|
||||||
clientSecret: text("client_secret"),
|
clientSecret: text("client_secret"),
|
||||||
@ -26,7 +25,6 @@ export const gitea = pgTable("gitea", {
|
|||||||
lastAuthenticatedAt: integer("last_authenticated_at"),
|
lastAuthenticatedAt: integer("last_authenticated_at"),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Gitea relations with gitProvider
|
|
||||||
export const giteaProviderRelations = relations(gitea, ({ one }) => ({
|
export const giteaProviderRelations = relations(gitea, ({ one }) => ({
|
||||||
gitProvider: one(gitProvider, {
|
gitProvider: one(gitProvider, {
|
||||||
fields: [gitea.gitProviderId],
|
fields: [gitea.gitProviderId],
|
||||||
@ -34,10 +32,8 @@ export const giteaProviderRelations = relations(gitea, ({ one }) => ({
|
|||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Create schema for Gitea
|
|
||||||
const createSchema = createInsertSchema(gitea);
|
const createSchema = createInsertSchema(gitea);
|
||||||
|
|
||||||
// API schema for creating a Gitea instance
|
|
||||||
export const apiCreateGitea = createSchema.extend({
|
export const apiCreateGitea = createSchema.extend({
|
||||||
clientId: z.string().optional(),
|
clientId: z.string().optional(),
|
||||||
clientSecret: z.string().optional(),
|
clientSecret: z.string().optional(),
|
||||||
@ -54,14 +50,12 @@ export const apiCreateGitea = createSchema.extend({
|
|||||||
lastAuthenticatedAt: z.number().optional(),
|
lastAuthenticatedAt: z.number().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// API schema for finding one Gitea instance
|
|
||||||
export const apiFindOneGitea = createSchema
|
export const apiFindOneGitea = createSchema
|
||||||
.extend({
|
.extend({
|
||||||
giteaId: z.string().min(1),
|
giteaId: z.string().min(1),
|
||||||
})
|
})
|
||||||
.pick({ giteaId: true });
|
.pick({ giteaId: true });
|
||||||
|
|
||||||
// API schema for testing Gitea connection
|
|
||||||
export const apiGiteaTestConnection = createSchema
|
export const apiGiteaTestConnection = createSchema
|
||||||
.extend({
|
.extend({
|
||||||
organizationName: z.string().optional(),
|
organizationName: z.string().optional(),
|
||||||
@ -70,7 +64,6 @@ export const apiGiteaTestConnection = createSchema
|
|||||||
|
|
||||||
export type ApiGiteaTestConnection = z.infer<typeof apiGiteaTestConnection>;
|
export type ApiGiteaTestConnection = z.infer<typeof apiGiteaTestConnection>;
|
||||||
|
|
||||||
// API schema for finding branches in Gitea
|
|
||||||
export const apiFindGiteaBranches = z.object({
|
export const apiFindGiteaBranches = z.object({
|
||||||
id: z.number().optional(),
|
id: z.number().optional(),
|
||||||
owner: z.string().min(1),
|
owner: z.string().min(1),
|
||||||
@ -78,7 +71,6 @@ export const apiFindGiteaBranches = z.object({
|
|||||||
giteaId: z.string().optional(),
|
giteaId: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// API schema for updating Gitea instance
|
|
||||||
export const apiUpdateGitea = createSchema.extend({
|
export const apiUpdateGitea = createSchema.extend({
|
||||||
clientId: z.string().optional(),
|
clientId: z.string().optional(),
|
||||||
clientSecret: z.string().optional(),
|
clientSecret: z.string().optional(),
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
// @ts-ignore: Cannot find module errors
|
|
||||||
import { db } from "@dokploy/server/db";
|
import { db } from "@dokploy/server/db";
|
||||||
// @ts-ignore: Cannot find module errors
|
|
||||||
import {
|
import {
|
||||||
type apiCreateGitea,
|
type apiCreateGitea,
|
||||||
gitProvider,
|
gitProvider,
|
||||||
@ -15,7 +13,6 @@ export const createGitea = async (
|
|||||||
input: typeof apiCreateGitea._type,
|
input: typeof apiCreateGitea._type,
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
) => {
|
) => {
|
||||||
// @ts-ignore - Complex transaction type - Added because proper typing in Drizzle in not sufficient
|
|
||||||
return await db.transaction(async (tx) => {
|
return await db.transaction(async (tx) => {
|
||||||
const newGitProvider = await tx
|
const newGitProvider = await tx
|
||||||
.insert(gitProvider)
|
.insert(gitProvider)
|
||||||
@ -25,7 +22,7 @@ export const createGitea = async (
|
|||||||
name: input.name,
|
name: input.name,
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
.then((response: (typeof gitProvider.$inferSelect)[]) => response[0]);
|
.then((response) => response[0]);
|
||||||
|
|
||||||
if (!newGitProvider) {
|
if (!newGitProvider) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
@ -50,7 +47,6 @@ export const createGitea = async (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return just the essential data needed by the frontend
|
|
||||||
return {
|
return {
|
||||||
giteaId: giteaProvider.giteaId,
|
giteaId: giteaProvider.giteaId,
|
||||||
clientId: giteaProvider.clientId,
|
clientId: giteaProvider.clientId,
|
||||||
@ -69,7 +65,6 @@ export const findGiteaById = async (giteaId: string) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!giteaProviderResult) {
|
if (!giteaProviderResult) {
|
||||||
console.error("No Gitea Provider found:", { giteaId });
|
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
message: "Gitea Provider not found",
|
message: "Gitea Provider not found",
|
||||||
@ -78,21 +73,11 @@ export const findGiteaById = async (giteaId: string) => {
|
|||||||
|
|
||||||
return giteaProviderResult;
|
return giteaProviderResult;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error finding Gitea Provider:", error);
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateGitea = async (giteaId: string, input: Partial<Gitea>) => {
|
export const updateGitea = async (giteaId: string, input: Partial<Gitea>) => {
|
||||||
console.log("Updating Gitea Provider:", {
|
|
||||||
giteaId,
|
|
||||||
updateData: {
|
|
||||||
accessTokenPresent: !!input.accessToken,
|
|
||||||
refreshTokenPresent: !!input.refreshToken,
|
|
||||||
expiresAt: input.expiresAt,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const updateResult = await db
|
const updateResult = await db
|
||||||
.update(gitea)
|
.update(gitea)
|
||||||
@ -103,13 +88,11 @@ export const updateGitea = async (giteaId: string, input: Partial<Gitea>) => {
|
|||||||
const result = updateResult[0] as Gitea | undefined;
|
const result = updateResult[0] as Gitea | undefined;
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
console.error("No rows were updated", { giteaId, input });
|
|
||||||
throw new Error(`Failed to update Gitea provider with ID ${giteaId}`);
|
throw new Error(`Failed to update Gitea provider with ID ${giteaId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error updating Gitea provider:", error);
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,46 +1,18 @@
|
|||||||
import { createWriteStream } from "node:fs";
|
import { createWriteStream } from "node:fs";
|
||||||
import * as fs from "node:fs/promises";
|
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
// @ts-ignore: Cannot find module errors
|
|
||||||
import { paths } from "@dokploy/server/constants";
|
import { paths } from "@dokploy/server/constants";
|
||||||
// @ts-ignore: Cannot find module errors
|
import {
|
||||||
import { findGiteaById, updateGitea } from "@dokploy/server/services/gitea";
|
findGiteaById,
|
||||||
|
type Gitea,
|
||||||
|
updateGitea,
|
||||||
|
} from "@dokploy/server/services/gitea";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { recreateDirectory } from "../filesystem/directory";
|
import { recreateDirectory } from "../filesystem/directory";
|
||||||
import { execAsyncRemote } from "../process/execAsync";
|
import { execAsyncRemote } from "../process/execAsync";
|
||||||
import { spawnAsync } from "../process/spawnAsync";
|
import { spawnAsync } from "../process/spawnAsync";
|
||||||
|
import type { Compose } from "@dokploy/server/services/compose";
|
||||||
|
import type { InferResultType } from "@dokploy/server/types/with";
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper function to maintain compatibility with the existing implementation
|
|
||||||
*/
|
|
||||||
export const fetchGiteaBranches = async (
|
|
||||||
giteaId: string,
|
|
||||||
repoFullName: string,
|
|
||||||
) => {
|
|
||||||
// Ensure owner and repo are non-empty strings
|
|
||||||
const parts = repoFullName.split("/");
|
|
||||||
|
|
||||||
// Validate that we have exactly two parts
|
|
||||||
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
||||||
throw new Error(
|
|
||||||
`Invalid repository name format: ${repoFullName}. Expected format: owner/repo`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const [owner, repo] = parts;
|
|
||||||
|
|
||||||
// Call the existing getGiteaBranches function with the correct object structure
|
|
||||||
return await getGiteaBranches({
|
|
||||||
giteaId,
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
id: 0, // Provide a default value for optional id
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to check if the required fields are filled for Gitea repository operations
|
|
||||||
*/
|
|
||||||
export const getErrorCloneRequirements = (entity: {
|
export const getErrorCloneRequirements = (entity: {
|
||||||
giteaRepository?: string | null;
|
giteaRepository?: string | null;
|
||||||
giteaOwner?: string | null;
|
giteaOwner?: string | null;
|
||||||
@ -138,11 +110,15 @@ export const refreshGiteaToken = async (giteaProviderId: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
export type ApplicationWithGitea = InferResultType<
|
||||||
* Generate a secure Git clone command with proper validation
|
"applications",
|
||||||
*/
|
{ gitea: true }
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type ComposeWithGitea = InferResultType<"compose", { gitea: true }>;
|
||||||
|
|
||||||
export const getGiteaCloneCommand = async (
|
export const getGiteaCloneCommand = async (
|
||||||
entity: any,
|
entity: ApplicationWithGitea | ComposeWithGitea,
|
||||||
logPath: string,
|
logPath: string,
|
||||||
isCompose = false,
|
isCompose = false,
|
||||||
) => {
|
) => {
|
||||||
@ -153,6 +129,7 @@ export const getGiteaCloneCommand = async (
|
|||||||
giteaOwner,
|
giteaOwner,
|
||||||
giteaRepository,
|
giteaRepository,
|
||||||
serverId,
|
serverId,
|
||||||
|
gitea,
|
||||||
} = entity;
|
} = entity;
|
||||||
|
|
||||||
if (!serverId) {
|
if (!serverId) {
|
||||||
@ -163,6 +140,12 @@ export const getGiteaCloneCommand = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!giteaId) {
|
if (!giteaId) {
|
||||||
|
const command = `
|
||||||
|
echo "Error: ❌ Gitlab Provider not found" >> ${logPath};
|
||||||
|
exit 1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
await execAsyncRemote(serverId, command);
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
message: "Gitea Provider not found",
|
message: "Gitea Provider not found",
|
||||||
@ -171,64 +154,46 @@ export const getGiteaCloneCommand = async (
|
|||||||
|
|
||||||
// Use paths(true) for remote operations
|
// Use paths(true) for remote operations
|
||||||
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true);
|
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true);
|
||||||
|
await refreshGiteaToken(giteaId);
|
||||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
|
|
||||||
const giteaProvider = await findGiteaById(giteaId);
|
const baseUrl = gitea?.giteaUrl.replace(/^https?:\/\//, "");
|
||||||
const baseUrl = giteaProvider.giteaUrl.replace(/^https?:\/\//, "");
|
|
||||||
const repoClone = `${giteaOwner}/${giteaRepository}.git`;
|
const repoClone = `${giteaOwner}/${giteaRepository}.git`;
|
||||||
const cloneUrl = `https://oauth2:${giteaProvider.accessToken}@${baseUrl}/${repoClone}`;
|
const cloneUrl = `https://oauth2:${gitea?.accessToken}@${baseUrl}/${repoClone}`;
|
||||||
|
|
||||||
const cloneCommand = `
|
const cloneCommand = `
|
||||||
# Ensure output directory exists and is empty
|
|
||||||
rm -rf ${outputPath};
|
rm -rf ${outputPath};
|
||||||
mkdir -p ${outputPath};
|
mkdir -p ${outputPath};
|
||||||
|
|
||||||
# Clone with detailed logging
|
|
||||||
echo "Cloning repository to ${outputPath}" >> ${logPath};
|
|
||||||
echo "Repository: ${repoClone}" >> ${logPath};
|
|
||||||
|
|
||||||
if ! git clone --branch ${giteaBranch} --depth 1 --recurse-submodules ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
|
if ! git clone --branch ${giteaBranch} --depth 1 --recurse-submodules ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
|
||||||
echo "❌ [ERROR] Failed to clone the repository ${repoClone}" >> ${logPath};
|
echo "❌ [ERROR] Failed to clone the repository ${repoClone}" >> ${logPath};
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify clone
|
|
||||||
CLONE_COUNT=$(find ${outputPath} -type f | wc -l)
|
|
||||||
echo "Files cloned: $CLONE_COUNT" >> ${logPath};
|
|
||||||
|
|
||||||
if [ "$CLONE_COUNT" -eq 0 ]; then
|
|
||||||
echo "⚠️ WARNING: No files cloned" >> ${logPath};
|
|
||||||
exit 1;
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Cloned ${repoClone} to ${outputPath}: ✅" >> ${logPath};
|
echo "Cloned ${repoClone} to ${outputPath}: ✅" >> ${logPath};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return cloneCommand;
|
return cloneCommand;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
interface CloneGiteaRepository {
|
||||||
* Main function to clone a Gitea repository with improved validation and robust directory handling
|
appName: string;
|
||||||
*/
|
giteaBranch: string;
|
||||||
|
giteaId: string;
|
||||||
|
giteaOwner: string;
|
||||||
|
giteaRepository: string;
|
||||||
|
}
|
||||||
export const cloneGiteaRepository = async (
|
export const cloneGiteaRepository = async (
|
||||||
entity: any,
|
entity: CloneGiteaRepository,
|
||||||
logPath?: string,
|
logPath: string,
|
||||||
isCompose = false,
|
isCompose = false,
|
||||||
) => {
|
) => {
|
||||||
// If logPath is not provided, generate a default log path
|
const { APPLICATIONS_PATH, COMPOSE_PATH } = paths();
|
||||||
const actualLogPath =
|
|
||||||
logPath ||
|
|
||||||
join(
|
|
||||||
paths()[isCompose ? "COMPOSE_PATH" : "APPLICATIONS_PATH"],
|
|
||||||
entity.appName,
|
|
||||||
"clone.log",
|
|
||||||
);
|
|
||||||
|
|
||||||
const writeStream = createWriteStream(actualLogPath, { flags: "a" });
|
const writeStream = createWriteStream(logPath, { flags: "a" });
|
||||||
const { appName, giteaBranch, giteaId, giteaOwner, giteaRepository } = entity;
|
const { appName, giteaBranch, giteaId, giteaOwner, giteaRepository } = entity;
|
||||||
|
|
||||||
try {
|
|
||||||
if (!giteaId) {
|
if (!giteaId) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
@ -236,10 +201,7 @@ export const cloneGiteaRepository = async (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh the access token
|
|
||||||
await refreshGiteaToken(giteaId);
|
await refreshGiteaToken(giteaId);
|
||||||
|
|
||||||
// Fetch the Gitea provider
|
|
||||||
const giteaProvider = await findGiteaById(giteaId);
|
const giteaProvider = await findGiteaById(giteaId);
|
||||||
if (!giteaProvider) {
|
if (!giteaProvider) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
@ -248,58 +210,16 @@ export const cloneGiteaRepository = async (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths();
|
|
||||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
|
|
||||||
// Log path information
|
|
||||||
writeStream.write("\nPath Information:\n");
|
|
||||||
writeStream.write(`Base Path: ${basePath}\n`);
|
|
||||||
writeStream.write(`Output Path: ${outputPath}\n`);
|
|
||||||
|
|
||||||
writeStream.write(`\nRecreating directory: ${outputPath}\n`);
|
|
||||||
await recreateDirectory(outputPath);
|
await recreateDirectory(outputPath);
|
||||||
|
|
||||||
// Additional step - verify directory exists and is empty
|
|
||||||
try {
|
|
||||||
const filesCheck = await fs.readdir(outputPath);
|
|
||||||
writeStream.write(
|
|
||||||
`Directory after cleanup - files: ${filesCheck.length}\n`,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (filesCheck.length > 0) {
|
|
||||||
writeStream.write("WARNING: Directory not empty after cleanup!\n");
|
|
||||||
|
|
||||||
// Force remove with shell command if recreateDirectory didn't work
|
|
||||||
if (entity.serverId) {
|
|
||||||
writeStream.write("Attempting forceful cleanup via shell command\n");
|
|
||||||
await execAsyncRemote(
|
|
||||||
entity.serverId,
|
|
||||||
`rm -rf "${outputPath}" && mkdir -p "${outputPath}"`,
|
|
||||||
(data) => writeStream.write(`Cleanup output: ${data}\n`),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Fallback to direct fs operations if serverId not available
|
|
||||||
writeStream.write("Attempting direct fs removal\n");
|
|
||||||
await fs.rm(outputPath, { recursive: true, force: true });
|
|
||||||
await fs.mkdir(outputPath, { recursive: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (verifyError) {
|
|
||||||
writeStream.write(`Error verifying directory: ${verifyError}\n`);
|
|
||||||
// Continue anyway - the clone operation might handle this
|
|
||||||
}
|
|
||||||
|
|
||||||
const repoClone = `${giteaOwner}/${giteaRepository}.git`;
|
const repoClone = `${giteaOwner}/${giteaRepository}.git`;
|
||||||
const baseUrl = giteaProvider.giteaUrl.replace(/^https?:\/\//, "");
|
const baseUrl = giteaProvider.giteaUrl.replace(/^https?:\/\//, "");
|
||||||
const cloneUrl = `https://oauth2:${giteaProvider.accessToken}@${baseUrl}/${repoClone}`;
|
const cloneUrl = `https://oauth2:${giteaProvider.accessToken}@${baseUrl}/${repoClone}`;
|
||||||
|
|
||||||
writeStream.write(`\nCloning Repo ${repoClone} to ${outputPath}...\n`);
|
writeStream.write(`\nCloning Repo ${repoClone} to ${outputPath}...\n`);
|
||||||
writeStream.write(
|
|
||||||
`Clone URL (masked): https://oauth2:***@${baseUrl}/${repoClone}\n`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// First try standard git clone
|
|
||||||
try {
|
try {
|
||||||
await spawnAsync(
|
await spawnAsync(
|
||||||
"git",
|
"git",
|
||||||
@ -320,142 +240,26 @@ export const cloneGiteaRepository = async (
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
writeStream.write("\nStandard git clone succeeded\n");
|
writeStream.write(`\nCloned ${repoClone}: ✅\n`);
|
||||||
} catch (cloneError) {
|
|
||||||
writeStream.write(`\nStandard git clone failed: ${cloneError}\n`);
|
|
||||||
writeStream.write("Falling back to git init + fetch approach...\n");
|
|
||||||
|
|
||||||
// Retry cleanup one more time
|
|
||||||
if (entity.serverId) {
|
|
||||||
await execAsyncRemote(
|
|
||||||
entity.serverId,
|
|
||||||
`rm -rf "${outputPath}" && mkdir -p "${outputPath}"`,
|
|
||||||
(data) => writeStream.write(`Cleanup retry: ${data}\n`),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await fs.rm(outputPath, { recursive: true, force: true });
|
|
||||||
await fs.mkdir(outputPath, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize git repo
|
|
||||||
writeStream.write("Initializing git repository...\n");
|
|
||||||
await spawnAsync("git", ["init", outputPath], (data) =>
|
|
||||||
writeStream.write(data),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set remote origin
|
|
||||||
writeStream.write("Setting remote origin...\n");
|
|
||||||
await spawnAsync(
|
|
||||||
"git",
|
|
||||||
["-C", outputPath, "remote", "add", "origin", cloneUrl],
|
|
||||||
(data) => writeStream.write(data),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Fetch branch
|
|
||||||
writeStream.write(`Fetching branch: ${giteaBranch}...\n`);
|
|
||||||
await spawnAsync(
|
|
||||||
"git",
|
|
||||||
["-C", outputPath, "fetch", "--depth", "1", "origin", giteaBranch],
|
|
||||||
(data) => writeStream.write(data),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Checkout branch
|
|
||||||
writeStream.write(`Checking out branch: ${giteaBranch}...\n`);
|
|
||||||
await spawnAsync(
|
|
||||||
"git",
|
|
||||||
["-C", outputPath, "checkout", "FETCH_HEAD"],
|
|
||||||
(data) => writeStream.write(data),
|
|
||||||
);
|
|
||||||
|
|
||||||
writeStream.write("Git init and fetch completed successfully\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify clone
|
|
||||||
const files = await fs.readdir(outputPath);
|
|
||||||
writeStream.write("\nClone Verification:\n");
|
|
||||||
writeStream.write(`Files found: ${files.length}\n`);
|
|
||||||
if (files.length > 0) {
|
|
||||||
// Using a for loop instead of forEach
|
|
||||||
for (let i = 0; i < Math.min(files.length, 10); i++) {
|
|
||||||
writeStream.write(`- ${files[i]}\n`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.length === 0) {
|
|
||||||
throw new Error("Repository clone failed - directory is empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
writeStream.write(`\nCloned ${repoClone} successfully: ✅\n`);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
writeStream.write(`\nClone Error: ${error}\n`);
|
writeStream.write(`ERROR Clonning: ${error}: ❌`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
writeStream.end();
|
writeStream.end();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
export const cloneRawGiteaRepository = async (entity: Compose) => {
|
||||||
* Clone a Gitea repository locally for a Compose configuration
|
const { appName, giteaRepository, giteaOwner, giteaBranch, giteaId } = entity;
|
||||||
* Leverages the existing comprehensive cloneGiteaRepository function
|
const { COMPOSE_PATH } = paths();
|
||||||
*/
|
|
||||||
export const cloneRawGiteaRepository = async (entity: any) => {
|
|
||||||
// Merge the existing entity with compose-specific properties
|
|
||||||
const composeEntity = {
|
|
||||||
...entity,
|
|
||||||
sourceType: "compose",
|
|
||||||
isCompose: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Call cloneGiteaRepository with the modified entity
|
|
||||||
await cloneGiteaRepository(composeEntity);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clone a Gitea repository remotely for a Compose configuration
|
|
||||||
* Uses the existing getGiteaCloneCommand function for remote cloning
|
|
||||||
*/
|
|
||||||
export const cloneRawGiteaRepositoryRemote = async (compose: any) => {
|
|
||||||
const { COMPOSE_PATH } = paths(true);
|
|
||||||
const logPath = join(COMPOSE_PATH, compose.appName, "clone.log");
|
|
||||||
|
|
||||||
// Reuse the existing getGiteaCloneCommand function
|
|
||||||
const command = await getGiteaCloneCommand(
|
|
||||||
{
|
|
||||||
...compose,
|
|
||||||
isCompose: true,
|
|
||||||
},
|
|
||||||
logPath,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!compose.serverId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Server not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the clone command on the remote server
|
|
||||||
await execAsyncRemote(compose.serverId, command);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper function to check if a Gitea provider meets the necessary requirements
|
|
||||||
export const haveGiteaRequirements = (giteaProvider: any) => {
|
|
||||||
return !!(giteaProvider?.clientId && giteaProvider?.clientSecret);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to test the connection to a Gitea provider
|
|
||||||
*/
|
|
||||||
export const testGiteaConnection = async (input: { giteaId: string }) => {
|
|
||||||
try {
|
|
||||||
const { giteaId } = input;
|
|
||||||
|
|
||||||
if (!giteaId) {
|
if (!giteaId) {
|
||||||
throw new Error("Gitea provider not found");
|
throw new TRPCError({
|
||||||
|
code: "NOT_FOUND",
|
||||||
|
message: "Gitea Provider not found",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
await refreshGiteaToken(giteaId);
|
||||||
// Fetch the Gitea provider from the database
|
|
||||||
const giteaProvider = await findGiteaById(giteaId);
|
const giteaProvider = await findGiteaById(giteaId);
|
||||||
if (!giteaProvider) {
|
if (!giteaProvider) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
@ -464,16 +268,93 @@ export const testGiteaConnection = async (input: { giteaId: string }) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Gitea Provider Found:", {
|
const basePath = COMPOSE_PATH;
|
||||||
id: giteaProvider.giteaId,
|
const outputPath = join(basePath, appName, "code");
|
||||||
url: giteaProvider.giteaUrl,
|
await recreateDirectory(outputPath);
|
||||||
hasAccessToken: !!giteaProvider.accessToken,
|
|
||||||
});
|
const repoClone = `${giteaOwner}/${giteaRepository}.git`;
|
||||||
|
const baseUrl = giteaProvider.giteaUrl.replace(/^https?:\/\//, "");
|
||||||
|
const cloneUrl = `https://oauth2:${giteaProvider.accessToken}@${baseUrl}/${repoClone}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await spawnAsync("git", [
|
||||||
|
"clone",
|
||||||
|
"--branch",
|
||||||
|
giteaBranch!,
|
||||||
|
"--depth",
|
||||||
|
"1",
|
||||||
|
"--recurse-submodules",
|
||||||
|
cloneUrl,
|
||||||
|
outputPath,
|
||||||
|
"--progress",
|
||||||
|
]);
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const cloneRawGiteaRepositoryRemote = async (compose: Compose) => {
|
||||||
|
const {
|
||||||
|
appName,
|
||||||
|
giteaRepository,
|
||||||
|
giteaOwner,
|
||||||
|
giteaBranch,
|
||||||
|
giteaId,
|
||||||
|
serverId,
|
||||||
|
} = compose;
|
||||||
|
|
||||||
|
if (!serverId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "NOT_FOUND",
|
||||||
|
message: "Server not found",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!giteaId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "NOT_FOUND",
|
||||||
|
message: "Gitea Provider not found",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const { COMPOSE_PATH } = paths(true);
|
||||||
|
const giteaProvider = await findGiteaById(giteaId);
|
||||||
|
const basePath = COMPOSE_PATH;
|
||||||
|
const outputPath = join(basePath, appName, "code");
|
||||||
|
const repoClone = `${giteaOwner}/${giteaRepository}.git`;
|
||||||
|
const baseUrl = giteaProvider.giteaUrl.replace(/^https?:\/\//, "");
|
||||||
|
const cloneUrl = `https://oauth2:${giteaProvider.accessToken}@${baseUrl}/${repoClone}`;
|
||||||
|
try {
|
||||||
|
const command = `
|
||||||
|
rm -rf ${outputPath};
|
||||||
|
git clone --branch ${giteaBranch} --depth 1 ${cloneUrl} ${outputPath}
|
||||||
|
`;
|
||||||
|
await execAsyncRemote(serverId, command);
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const haveGiteaRequirements = (giteaProvider: Gitea) => {
|
||||||
|
return !!(giteaProvider?.clientId && giteaProvider?.clientSecret);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const testGiteaConnection = async (input: { giteaId: string }) => {
|
||||||
|
try {
|
||||||
|
const { giteaId } = input;
|
||||||
|
|
||||||
|
if (!giteaId) {
|
||||||
|
throw new Error("Gitea provider not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const giteaProvider = await findGiteaById(giteaId);
|
||||||
|
if (!giteaProvider) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "NOT_FOUND",
|
||||||
|
message: "Gitea provider not found in the database",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh the token if needed
|
|
||||||
await refreshGiteaToken(giteaId);
|
await refreshGiteaToken(giteaId);
|
||||||
|
|
||||||
// Fetch the provider again in case the token was refreshed
|
|
||||||
const provider = await findGiteaById(giteaId);
|
const provider = await findGiteaById(giteaId);
|
||||||
if (!provider || !provider.accessToken) {
|
if (!provider || !provider.accessToken) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
@ -482,15 +363,9 @@ export const testGiteaConnection = async (input: { giteaId: string }) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make API request to test connection
|
const baseUrl = provider.giteaUrl.replace(/\/+$/, "");
|
||||||
console.log("Making API request to test connection...");
|
|
||||||
|
|
||||||
// Construct proper URL for the API request
|
|
||||||
const baseUrl = provider.giteaUrl.replace(/\/+$/, ""); // Remove trailing slashes
|
|
||||||
const url = `${baseUrl}/api/v1/user/repos`;
|
const url = `${baseUrl}/api/v1/user/repos`;
|
||||||
|
|
||||||
console.log(`Testing connection to: ${url}`);
|
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
@ -499,45 +374,31 @@ export const testGiteaConnection = async (input: { giteaId: string }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorText = await response.text();
|
|
||||||
console.error("Repository API failed:", errorText);
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to connect to Gitea API: ${response.status} ${response.statusText}`,
|
`Failed to connect to Gitea API: ${response.status} ${response.statusText}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const repos = await response.json();
|
const repos = await response.json();
|
||||||
console.log(
|
|
||||||
`Successfully connected to Gitea API. Found ${repos.length} repositories.`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update lastAuthenticatedAt
|
|
||||||
await updateGitea(giteaId, {
|
await updateGitea(giteaId, {
|
||||||
lastAuthenticatedAt: Math.floor(Date.now() / 1000),
|
lastAuthenticatedAt: Math.floor(Date.now() / 1000),
|
||||||
});
|
});
|
||||||
|
|
||||||
return repos.length;
|
return repos.length;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Gitea Connection Test Error:", error);
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to fetch repositories from a Gitea provider
|
|
||||||
*/
|
|
||||||
export const getGiteaRepositories = async (giteaId?: string) => {
|
export const getGiteaRepositories = async (giteaId?: string) => {
|
||||||
if (!giteaId) {
|
if (!giteaId) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh the token
|
|
||||||
await refreshGiteaToken(giteaId);
|
await refreshGiteaToken(giteaId);
|
||||||
|
|
||||||
// Fetch the Gitea provider
|
|
||||||
const giteaProvider = await findGiteaById(giteaId);
|
const giteaProvider = await findGiteaById(giteaId);
|
||||||
|
|
||||||
// Construct the URL for fetching repositories
|
|
||||||
const baseUrl = giteaProvider.giteaUrl.replace(/\/+$/, "");
|
const baseUrl = giteaProvider.giteaUrl.replace(/\/+$/, "");
|
||||||
const url = `${baseUrl}/api/v1/user/repos`;
|
const url = `${baseUrl}/api/v1/user/repos`;
|
||||||
|
|
||||||
@ -557,7 +418,6 @@ export const getGiteaRepositories = async (giteaId?: string) => {
|
|||||||
|
|
||||||
const repositories = await response.json();
|
const repositories = await response.json();
|
||||||
|
|
||||||
// Map repositories to a consistent format
|
|
||||||
const mappedRepositories = repositories.map((repo: any) => ({
|
const mappedRepositories = repositories.map((repo: any) => ({
|
||||||
id: repo.id,
|
id: repo.id,
|
||||||
name: repo.name,
|
name: repo.name,
|
||||||
@ -570,9 +430,6 @@ export const getGiteaRepositories = async (giteaId?: string) => {
|
|||||||
return mappedRepositories;
|
return mappedRepositories;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to fetch branches for a specific Gitea repository
|
|
||||||
*/
|
|
||||||
export const getGiteaBranches = async (input: {
|
export const getGiteaBranches = async (input: {
|
||||||
id?: number;
|
id?: number;
|
||||||
giteaId?: string;
|
giteaId?: string;
|
||||||
@ -583,10 +440,8 @@ export const getGiteaBranches = async (input: {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the Gitea provider
|
|
||||||
const giteaProvider = await findGiteaById(input.giteaId);
|
const giteaProvider = await findGiteaById(input.giteaId);
|
||||||
|
|
||||||
// Construct the URL for fetching branches
|
|
||||||
const baseUrl = giteaProvider.giteaUrl.replace(/\/+$/, "");
|
const baseUrl = giteaProvider.giteaUrl.replace(/\/+$/, "");
|
||||||
const url = `${baseUrl}/api/v1/repos/${input.owner}/${input.repo}/branches`;
|
const url = `${baseUrl}/api/v1/repos/${input.owner}/${input.repo}/branches`;
|
||||||
|
|
||||||
@ -603,7 +458,6 @@ export const getGiteaBranches = async (input: {
|
|||||||
|
|
||||||
const branches = await response.json();
|
const branches = await response.json();
|
||||||
|
|
||||||
// Map branches to a consistent format
|
|
||||||
return branches.map((branch: any) => ({
|
return branches.map((branch: any) => ({
|
||||||
id: branch.name,
|
id: branch.name,
|
||||||
name: branch.name,
|
name: branch.name,
|
||||||
@ -612,15 +466,3 @@ export const getGiteaBranches = async (input: {
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
|
||||||
cloneGiteaRepository,
|
|
||||||
cloneRawGiteaRepository,
|
|
||||||
cloneRawGiteaRepositoryRemote,
|
|
||||||
refreshGiteaToken,
|
|
||||||
haveGiteaRequirements,
|
|
||||||
testGiteaConnection,
|
|
||||||
getGiteaRepositories,
|
|
||||||
getGiteaBranches,
|
|
||||||
fetchGiteaBranches,
|
|
||||||
};
|
|
||||||
|
Loading…
Reference in New Issue
Block a user