mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat: add providers to compose
This commit is contained in:
parent
4c99e6000a
commit
d57bbff87c
@ -16,16 +16,9 @@ const baseAdmin: Admin = {
|
|||||||
createdAt: "",
|
createdAt: "",
|
||||||
authId: "",
|
authId: "",
|
||||||
adminId: "string",
|
adminId: "string",
|
||||||
githubAppId: null,
|
|
||||||
githubAppName: null,
|
|
||||||
serverIp: null,
|
serverIp: null,
|
||||||
certificateType: "none",
|
certificateType: "none",
|
||||||
host: null,
|
host: null,
|
||||||
githubClientId: null,
|
|
||||||
githubClientSecret: null,
|
|
||||||
githubInstallationId: null,
|
|
||||||
githubPrivateKey: null,
|
|
||||||
githubWebhookSecret: null,
|
|
||||||
letsEncryptEmail: null,
|
letsEncryptEmail: null,
|
||||||
sshPrivateKey: null,
|
sshPrivateKey: null,
|
||||||
enableDockerCleanup: false,
|
enableDockerCleanup: false,
|
||||||
|
@ -13,6 +13,18 @@ const baseApp: ApplicationNested = {
|
|||||||
buildArgs: null,
|
buildArgs: null,
|
||||||
buildPath: "/",
|
buildPath: "/",
|
||||||
buildType: "nixpacks",
|
buildType: "nixpacks",
|
||||||
|
bitbucketBranch: "",
|
||||||
|
bitbucketBuildPath: "",
|
||||||
|
bitbucketId: "",
|
||||||
|
bitbucketRepository: "",
|
||||||
|
bitbucketOwner: "",
|
||||||
|
githubId: "",
|
||||||
|
gitlabProjectId: 0,
|
||||||
|
gitlabBranch: "",
|
||||||
|
gitlabBuildPath: "",
|
||||||
|
gitlabId: "",
|
||||||
|
gitlabRepository: "",
|
||||||
|
gitlabOwner: "",
|
||||||
command: null,
|
command: null,
|
||||||
cpuLimit: null,
|
cpuLimit: null,
|
||||||
cpuReservation: null,
|
cpuReservation: null,
|
||||||
|
@ -44,6 +44,7 @@ const GitlabProviderSchema = 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"),
|
||||||
|
gitlabPathNamespace: z.string().min(1),
|
||||||
id: z.number().nullable(),
|
id: z.number().nullable(),
|
||||||
})
|
})
|
||||||
.required(),
|
.required(),
|
||||||
@ -70,6 +71,7 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => {
|
|||||||
repository: {
|
repository: {
|
||||||
owner: "",
|
owner: "",
|
||||||
repo: "",
|
repo: "",
|
||||||
|
gitlabPathNamespace: "",
|
||||||
id: null,
|
id: null,
|
||||||
},
|
},
|
||||||
gitlabId: "",
|
gitlabId: "",
|
||||||
@ -112,6 +114,7 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => {
|
|||||||
repository: {
|
repository: {
|
||||||
repo: data.gitlabRepository || "",
|
repo: data.gitlabRepository || "",
|
||||||
owner: data.gitlabOwner || "",
|
owner: data.gitlabOwner || "",
|
||||||
|
gitlabPathNamespace: data.gitlabPathNamespace || "",
|
||||||
id: data.gitlabProjectId,
|
id: data.gitlabProjectId,
|
||||||
},
|
},
|
||||||
buildPath: data.gitlabBuildPath || "/",
|
buildPath: data.gitlabBuildPath || "/",
|
||||||
@ -129,6 +132,7 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => {
|
|||||||
gitlabId: data.gitlabId,
|
gitlabId: data.gitlabId,
|
||||||
applicationId,
|
applicationId,
|
||||||
gitlabProjectId: data.repository.id,
|
gitlabProjectId: data.repository.id,
|
||||||
|
gitlabPathNamespace: data.repository.gitlabPathNamespace,
|
||||||
})
|
})
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
toast.success("Service Provided Saved");
|
toast.success("Service Provided Saved");
|
||||||
@ -161,6 +165,7 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => {
|
|||||||
owner: "",
|
owner: "",
|
||||||
repo: "",
|
repo: "",
|
||||||
id: null,
|
id: null,
|
||||||
|
gitlabPathNamespace: "",
|
||||||
});
|
});
|
||||||
form.setValue("branch", "");
|
form.setValue("branch", "");
|
||||||
}}
|
}}
|
||||||
@ -246,6 +251,7 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => {
|
|||||||
owner: repo.owner.username as string,
|
owner: repo.owner.username as string,
|
||||||
repo: repo.name,
|
repo: repo.name,
|
||||||
id: repo.id,
|
id: repo.id,
|
||||||
|
gitlabPathNamespace: repo.url,
|
||||||
});
|
});
|
||||||
form.setValue("branch", "");
|
form.setValue("branch", "");
|
||||||
}}
|
}}
|
||||||
|
@ -0,0 +1,375 @@
|
|||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
} from "@/components/ui/command";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { api } from "@/utils/api";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { CheckIcon, ChevronsUpDown } from "lucide-react";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const BitbucketProviderSchema = z.object({
|
||||||
|
composePath: z.string().min(1),
|
||||||
|
repository: z
|
||||||
|
.object({
|
||||||
|
repo: z.string().min(1, "Repo is required"),
|
||||||
|
owner: z.string().min(1, "Owner is required"),
|
||||||
|
})
|
||||||
|
.required(),
|
||||||
|
branch: z.string().min(1, "Branch is required"),
|
||||||
|
bitbucketId: z.string().min(1, "Bitbucket Provider is required"),
|
||||||
|
});
|
||||||
|
|
||||||
|
type BitbucketProvider = z.infer<typeof BitbucketProviderSchema>;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
composeId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SaveBitbucketProviderCompose = ({ composeId }: Props) => {
|
||||||
|
const { data: bitbucketProviders } =
|
||||||
|
api.gitProvider.bitbucketProviders.useQuery();
|
||||||
|
const { data, refetch } = api.compose.one.useQuery({ composeId });
|
||||||
|
|
||||||
|
const { mutateAsync, isLoading: isSavingBitbucketProvider } =
|
||||||
|
api.compose.update.useMutation();
|
||||||
|
|
||||||
|
const form = useForm<BitbucketProvider>({
|
||||||
|
defaultValues: {
|
||||||
|
composePath: "./docker-compose.yml",
|
||||||
|
repository: {
|
||||||
|
owner: "",
|
||||||
|
repo: "",
|
||||||
|
},
|
||||||
|
bitbucketId: "",
|
||||||
|
branch: "",
|
||||||
|
},
|
||||||
|
resolver: zodResolver(BitbucketProviderSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
const repository = form.watch("repository");
|
||||||
|
const bitbucketId = form.watch("bitbucketId");
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: repositories,
|
||||||
|
isLoading: isLoadingRepositories,
|
||||||
|
error,
|
||||||
|
isError,
|
||||||
|
} = api.gitProvider.getBitbucketRepositories.useQuery({
|
||||||
|
bitbucketId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: branches,
|
||||||
|
fetchStatus,
|
||||||
|
status,
|
||||||
|
} = api.gitProvider.getBitbucketBranches.useQuery(
|
||||||
|
{
|
||||||
|
owner: repository?.owner,
|
||||||
|
repo: repository?.repo,
|
||||||
|
bitbucketId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: !!repository?.owner && !!repository?.repo && !!bitbucketId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data) {
|
||||||
|
form.reset({
|
||||||
|
branch: data.bitbucketBranch || "",
|
||||||
|
repository: {
|
||||||
|
repo: data.bitbucketRepository || "",
|
||||||
|
owner: data.bitbucketOwner || "",
|
||||||
|
},
|
||||||
|
composePath: data.composePath,
|
||||||
|
bitbucketId: data.bitbucketId || "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [form.reset, data, form]);
|
||||||
|
|
||||||
|
const onSubmit = async (data: BitbucketProvider) => {
|
||||||
|
await mutateAsync({
|
||||||
|
bitbucketBranch: data.branch,
|
||||||
|
bitbucketRepository: data.repository.repo,
|
||||||
|
bitbucketOwner: data.repository.owner,
|
||||||
|
bitbucketId: data.bitbucketId,
|
||||||
|
composePath: data.composePath,
|
||||||
|
composeId,
|
||||||
|
sourceType: "bitbucket",
|
||||||
|
composeStatus: "idle",
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
toast.success("Service Provided Saved");
|
||||||
|
await refetch();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error to save the Bitbucket provider");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="grid w-full gap-4 py-3"
|
||||||
|
>
|
||||||
|
{error && (
|
||||||
|
<AlertBlock type="error">Repositories: {error.message}</AlertBlock>
|
||||||
|
)}
|
||||||
|
<div className="grid md:grid-cols-2 gap-4">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="bitbucketId"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="md:col-span-2 flex flex-col">
|
||||||
|
<FormLabel>Bitbucket Account</FormLabel>
|
||||||
|
<Select
|
||||||
|
onValueChange={(value) => {
|
||||||
|
field.onChange(value);
|
||||||
|
form.setValue("repository", {
|
||||||
|
owner: "",
|
||||||
|
repo: "",
|
||||||
|
});
|
||||||
|
form.setValue("branch", "");
|
||||||
|
}}
|
||||||
|
defaultValue={field.value}
|
||||||
|
value={field.value}
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select a Bitbucket Account" />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{bitbucketProviders?.map((bitbucketProvider) => (
|
||||||
|
<SelectItem
|
||||||
|
key={bitbucketProvider.bitbucketId}
|
||||||
|
value={bitbucketProvider.bitbucketId}
|
||||||
|
>
|
||||||
|
{bitbucketProvider.gitProvider.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="repository"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="md:col-span-2 flex flex-col">
|
||||||
|
<FormLabel>Repository</FormLabel>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<FormControl>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
className={cn(
|
||||||
|
"w-full justify-between !bg-input",
|
||||||
|
!field.value && "text-muted-foreground",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{isLoadingRepositories
|
||||||
|
? "Loading...."
|
||||||
|
: field.value.owner
|
||||||
|
? repositories?.find(
|
||||||
|
(repo) => repo.name === field.value.repo,
|
||||||
|
)?.name
|
||||||
|
: "Select repository"}
|
||||||
|
|
||||||
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</FormControl>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="p-0" align="start">
|
||||||
|
<Command>
|
||||||
|
<CommandInput
|
||||||
|
placeholder="Search repository..."
|
||||||
|
className="h-9"
|
||||||
|
/>
|
||||||
|
{isLoadingRepositories && (
|
||||||
|
<span className="py-6 text-center text-sm">
|
||||||
|
Loading Repositories....
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<CommandEmpty>No repositories found.</CommandEmpty>
|
||||||
|
<ScrollArea className="h-96">
|
||||||
|
<CommandGroup>
|
||||||
|
{repositories?.map((repo) => (
|
||||||
|
<CommandItem
|
||||||
|
value={repo.url}
|
||||||
|
key={repo.url}
|
||||||
|
onSelect={() => {
|
||||||
|
form.setValue("repository", {
|
||||||
|
owner: repo.owner.username as string,
|
||||||
|
repo: repo.name,
|
||||||
|
});
|
||||||
|
form.setValue("branch", "");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{repo.name}
|
||||||
|
<CheckIcon
|
||||||
|
className={cn(
|
||||||
|
"ml-auto h-4 w-4",
|
||||||
|
repo.name === field.value.repo
|
||||||
|
? "opacity-100"
|
||||||
|
: "opacity-0",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</ScrollArea>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
{form.formState.errors.repository && (
|
||||||
|
<p className={cn("text-sm font-medium text-destructive")}>
|
||||||
|
Repository is required
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="branch"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="block w-full">
|
||||||
|
<FormLabel>Branch</FormLabel>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<FormControl>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
className={cn(
|
||||||
|
" w-full justify-between !bg-input",
|
||||||
|
!field.value && "text-muted-foreground",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{status === "loading" && fetchStatus === "fetching"
|
||||||
|
? "Loading...."
|
||||||
|
: field.value
|
||||||
|
? branches?.find(
|
||||||
|
(branch) => branch.name === field.value,
|
||||||
|
)?.name
|
||||||
|
: "Select branch"}
|
||||||
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</FormControl>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="p-0" align="start">
|
||||||
|
<Command>
|
||||||
|
<CommandInput
|
||||||
|
placeholder="Search branch..."
|
||||||
|
className="h-9"
|
||||||
|
/>
|
||||||
|
{status === "loading" && fetchStatus === "fetching" && (
|
||||||
|
<span className="py-6 text-center text-sm text-muted-foreground">
|
||||||
|
Loading Branches....
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{!repository?.owner && (
|
||||||
|
<span className="py-6 text-center text-sm text-muted-foreground">
|
||||||
|
Select a repository
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<ScrollArea className="h-96">
|
||||||
|
<CommandEmpty>No branch found.</CommandEmpty>
|
||||||
|
|
||||||
|
<CommandGroup>
|
||||||
|
{branches?.map((branch) => (
|
||||||
|
<CommandItem
|
||||||
|
value={branch.name}
|
||||||
|
key={branch.commit.sha}
|
||||||
|
onSelect={() => {
|
||||||
|
form.setValue("branch", branch.name);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{branch.name}
|
||||||
|
<CheckIcon
|
||||||
|
className={cn(
|
||||||
|
"ml-auto h-4 w-4",
|
||||||
|
branch.name === field.value
|
||||||
|
? "opacity-100"
|
||||||
|
: "opacity-0",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</ScrollArea>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</Popover>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="composePath"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Compose Path</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="docker-compose.yml" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full justify-end">
|
||||||
|
<Button
|
||||||
|
isLoading={isSavingBitbucketProvider}
|
||||||
|
type="submit"
|
||||||
|
className="w-fit"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -21,6 +21,13 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
@ -39,6 +46,7 @@ const GithubProviderSchema = z.object({
|
|||||||
})
|
})
|
||||||
.required(),
|
.required(),
|
||||||
branch: z.string().min(1, "Branch is required"),
|
branch: z.string().min(1, "Branch is required"),
|
||||||
|
githubId: z.string().min(1, "Github Provider is required"),
|
||||||
});
|
});
|
||||||
|
|
||||||
type GithubProvider = z.infer<typeof GithubProviderSchema>;
|
type GithubProvider = z.infer<typeof GithubProviderSchema>;
|
||||||
@ -48,6 +56,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SaveGithubProviderCompose = ({ composeId }: Props) => {
|
export const SaveGithubProviderCompose = ({ composeId }: Props) => {
|
||||||
|
const { data: githubProviders } = api.gitProvider.githubProviders.useQuery();
|
||||||
const { data, refetch } = api.compose.one.useQuery({ composeId });
|
const { data, refetch } = api.compose.one.useQuery({ composeId });
|
||||||
|
|
||||||
const { mutateAsync, isLoading: isSavingGithubProvider } =
|
const { mutateAsync, isLoading: isSavingGithubProvider } =
|
||||||
@ -60,26 +69,33 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
|
|||||||
owner: "",
|
owner: "",
|
||||||
repo: "",
|
repo: "",
|
||||||
},
|
},
|
||||||
|
githubId: "",
|
||||||
branch: "",
|
branch: "",
|
||||||
},
|
},
|
||||||
resolver: zodResolver(GithubProviderSchema),
|
resolver: zodResolver(GithubProviderSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
const repository = form.watch("repository");
|
const repository = form.watch("repository");
|
||||||
|
const githubId = form.watch("githubId");
|
||||||
|
|
||||||
const { data: repositories, isLoading: isLoadingRepositories } =
|
const { data: repositories, isLoading: isLoadingRepositories } =
|
||||||
api.admin.getRepositories.useQuery();
|
api.gitProvider.getRepositories.useQuery({
|
||||||
|
githubId,
|
||||||
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: branches,
|
data: branches,
|
||||||
fetchStatus,
|
fetchStatus,
|
||||||
status,
|
status,
|
||||||
} = api.admin.getBranches.useQuery(
|
} = api.gitProvider.getBranches.useQuery(
|
||||||
{
|
{
|
||||||
owner: repository?.owner,
|
owner: repository?.owner,
|
||||||
repo: repository?.repo,
|
repo: repository?.repo,
|
||||||
|
githubId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: !!repository?.owner && !!repository?.repo && !!githubId,
|
||||||
},
|
},
|
||||||
{ enabled: !!repository?.owner && !!repository?.repo },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -91,19 +107,21 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
|
|||||||
owner: data.owner || "",
|
owner: data.owner || "",
|
||||||
},
|
},
|
||||||
composePath: data.composePath,
|
composePath: data.composePath,
|
||||||
|
githubId: data.githubId || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [form.reset, data, form]);
|
}, [form.reset, data, form]);
|
||||||
|
|
||||||
const onSubmit = async (data: GithubProvider) => {
|
const onSubmit = async (data: GithubProvider) => {
|
||||||
console.log(data);
|
|
||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
branch: data.branch,
|
branch: data.branch,
|
||||||
repository: data.repository.repo,
|
repository: data.repository.repo,
|
||||||
composeId: composeId,
|
composeId,
|
||||||
owner: data.repository.owner,
|
owner: data.repository.owner,
|
||||||
sourceType: "github",
|
|
||||||
composePath: data.composePath,
|
composePath: data.composePath,
|
||||||
|
githubId: data.githubId,
|
||||||
|
sourceType: "github",
|
||||||
|
composeStatus: "idle",
|
||||||
})
|
})
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
toast.success("Service Provided Saved");
|
toast.success("Service Provided Saved");
|
||||||
@ -122,6 +140,45 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
|
|||||||
className="grid w-full gap-4 py-3"
|
className="grid w-full gap-4 py-3"
|
||||||
>
|
>
|
||||||
<div className="grid md:grid-cols-2 gap-4">
|
<div className="grid md:grid-cols-2 gap-4">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="githubId"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="md:col-span-2 flex flex-col">
|
||||||
|
<FormLabel>Github Account</FormLabel>
|
||||||
|
<Select
|
||||||
|
onValueChange={(value) => {
|
||||||
|
field.onChange(value);
|
||||||
|
form.setValue("repository", {
|
||||||
|
owner: "",
|
||||||
|
repo: "",
|
||||||
|
});
|
||||||
|
form.setValue("branch", "");
|
||||||
|
}}
|
||||||
|
defaultValue={field.value}
|
||||||
|
value={field.value}
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select a Github Account" />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{githubProviders?.map((githubProvider) => (
|
||||||
|
<SelectItem
|
||||||
|
key={githubProvider.githubId}
|
||||||
|
value={githubProvider.githubId}
|
||||||
|
>
|
||||||
|
{githubProvider.gitProvider.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="repository"
|
name="repository"
|
||||||
@ -278,7 +335,6 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="composePath"
|
name="composePath"
|
||||||
|
@ -0,0 +1,391 @@
|
|||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
} from "@/components/ui/command";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/components/ui/popover";
|
||||||
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { api } from "@/utils/api";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { CheckIcon, ChevronsUpDown } from "lucide-react";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const GitlabProviderSchema = z.object({
|
||||||
|
composePath: z.string().min(1),
|
||||||
|
repository: z
|
||||||
|
.object({
|
||||||
|
repo: z.string().min(1, "Repo is required"),
|
||||||
|
owner: z.string().min(1, "Owner is required"),
|
||||||
|
id: z.number().nullable(),
|
||||||
|
gitlabPathNamespace: z.string().min(1),
|
||||||
|
})
|
||||||
|
.required(),
|
||||||
|
branch: z.string().min(1, "Branch is required"),
|
||||||
|
gitlabId: z.string().min(1, "Gitlab Provider is required"),
|
||||||
|
});
|
||||||
|
|
||||||
|
type GitlabProvider = z.infer<typeof GitlabProviderSchema>;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
composeId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SaveGitlabProviderCompose = ({ composeId }: Props) => {
|
||||||
|
const { data: gitlabProviders } = api.gitProvider.gitlabProviders.useQuery();
|
||||||
|
const { data, refetch } = api.compose.one.useQuery({ composeId });
|
||||||
|
|
||||||
|
const { mutateAsync, isLoading: isSavingGitlabProvider } =
|
||||||
|
api.compose.update.useMutation();
|
||||||
|
|
||||||
|
const form = useForm<GitlabProvider>({
|
||||||
|
defaultValues: {
|
||||||
|
composePath: "./docker-compose.yml",
|
||||||
|
repository: {
|
||||||
|
owner: "",
|
||||||
|
repo: "",
|
||||||
|
gitlabPathNamespace: "",
|
||||||
|
id: null,
|
||||||
|
},
|
||||||
|
gitlabId: "",
|
||||||
|
branch: "",
|
||||||
|
},
|
||||||
|
resolver: zodResolver(GitlabProviderSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
const repository = form.watch("repository");
|
||||||
|
const gitlabId = form.watch("gitlabId");
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: repositories,
|
||||||
|
isLoading: isLoadingRepositories,
|
||||||
|
error,
|
||||||
|
} = api.gitProvider.getGitlabRepositories.useQuery({
|
||||||
|
gitlabId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: branches,
|
||||||
|
fetchStatus,
|
||||||
|
status,
|
||||||
|
} = api.gitProvider.getGitlabBranches.useQuery(
|
||||||
|
{
|
||||||
|
owner: repository?.owner,
|
||||||
|
repo: repository?.repo,
|
||||||
|
id: repository?.id,
|
||||||
|
gitlabId: gitlabId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: !!repository?.owner && !!repository?.repo && !!gitlabId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data) {
|
||||||
|
form.reset({
|
||||||
|
branch: data.gitlabBranch || "",
|
||||||
|
repository: {
|
||||||
|
repo: data.gitlabRepository || "",
|
||||||
|
owner: data.gitlabOwner || "",
|
||||||
|
id: data.gitlabProjectId,
|
||||||
|
gitlabPathNamespace: data.gitlabPathNamespace || "",
|
||||||
|
},
|
||||||
|
composePath: data.composePath,
|
||||||
|
gitlabId: data.gitlabId || "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [form.reset, data, form]);
|
||||||
|
|
||||||
|
const onSubmit = async (data: GitlabProvider) => {
|
||||||
|
await mutateAsync({
|
||||||
|
gitlabBranch: data.branch,
|
||||||
|
gitlabRepository: data.repository.repo,
|
||||||
|
gitlabOwner: data.repository.owner,
|
||||||
|
composePath: data.composePath,
|
||||||
|
gitlabId: data.gitlabId,
|
||||||
|
composeId,
|
||||||
|
gitlabProjectId: data.repository.id,
|
||||||
|
gitlabPathNamespace: data.repository.gitlabPathNamespace,
|
||||||
|
sourceType: "gitlab",
|
||||||
|
composeStatus: "idle",
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
toast.success("Service Provided Saved");
|
||||||
|
await refetch();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error to save the gitlab provider");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="grid w-full gap-4 py-3"
|
||||||
|
>
|
||||||
|
{error && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||||
|
<div className="grid md:grid-cols-2 gap-4">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="gitlabId"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="md:col-span-2 flex flex-col">
|
||||||
|
<FormLabel>Gitlab Account</FormLabel>
|
||||||
|
<Select
|
||||||
|
onValueChange={(value) => {
|
||||||
|
field.onChange(value);
|
||||||
|
form.setValue("repository", {
|
||||||
|
owner: "",
|
||||||
|
repo: "",
|
||||||
|
gitlabPathNamespace: "",
|
||||||
|
id: null,
|
||||||
|
});
|
||||||
|
form.setValue("branch", "");
|
||||||
|
}}
|
||||||
|
defaultValue={field.value}
|
||||||
|
value={field.value}
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select a Gitlab Account" />
|
||||||
|
</SelectTrigger>
|
||||||
|
</FormControl>
|
||||||
|
<SelectContent>
|
||||||
|
{gitlabProviders?.map((gitlabProvider) => (
|
||||||
|
<SelectItem
|
||||||
|
key={gitlabProvider.gitlabId}
|
||||||
|
value={gitlabProvider.gitlabId}
|
||||||
|
>
|
||||||
|
{gitlabProvider.gitProvider.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="repository"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="md:col-span-2 flex flex-col">
|
||||||
|
<FormLabel>Repository</FormLabel>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<FormControl>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
className={cn(
|
||||||
|
"w-full justify-between !bg-input",
|
||||||
|
!field.value && "text-muted-foreground",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{isLoadingRepositories
|
||||||
|
? "Loading...."
|
||||||
|
: field.value.owner
|
||||||
|
? repositories?.find(
|
||||||
|
(repo) => repo.name === field.value.repo,
|
||||||
|
)?.name
|
||||||
|
: "Select repository"}
|
||||||
|
|
||||||
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</FormControl>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="p-0" align="start">
|
||||||
|
<Command>
|
||||||
|
<CommandInput
|
||||||
|
placeholder="Search repository..."
|
||||||
|
className="h-9"
|
||||||
|
/>
|
||||||
|
{isLoadingRepositories && (
|
||||||
|
<span className="py-6 text-center text-sm">
|
||||||
|
Loading Repositories....
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<CommandEmpty>No repositories found.</CommandEmpty>
|
||||||
|
<ScrollArea className="h-96">
|
||||||
|
<CommandGroup>
|
||||||
|
{repositories && repositories.length === 0 && (
|
||||||
|
<CommandEmpty>
|
||||||
|
No repositories found.
|
||||||
|
</CommandEmpty>
|
||||||
|
)}
|
||||||
|
{repositories?.map((repo) => {
|
||||||
|
return (
|
||||||
|
<CommandItem
|
||||||
|
value={repo.url}
|
||||||
|
key={repo.url}
|
||||||
|
onSelect={() => {
|
||||||
|
form.setValue("repository", {
|
||||||
|
owner: repo.owner.username as string,
|
||||||
|
repo: repo.name,
|
||||||
|
id: repo.id,
|
||||||
|
gitlabPathNamespace: repo.url,
|
||||||
|
});
|
||||||
|
form.setValue("branch", "");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{repo.name}
|
||||||
|
<CheckIcon
|
||||||
|
className={cn(
|
||||||
|
"ml-auto h-4 w-4",
|
||||||
|
repo.name === field.value.repo
|
||||||
|
? "opacity-100"
|
||||||
|
: "opacity-0",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</CommandGroup>
|
||||||
|
</ScrollArea>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
{form.formState.errors.repository && (
|
||||||
|
<p className={cn("text-sm font-medium text-destructive")}>
|
||||||
|
Repository is required
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="branch"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="block w-full">
|
||||||
|
<FormLabel>Branch</FormLabel>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<FormControl>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
className={cn(
|
||||||
|
" w-full justify-between !bg-input",
|
||||||
|
!field.value && "text-muted-foreground",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{status === "loading" && fetchStatus === "fetching"
|
||||||
|
? "Loading...."
|
||||||
|
: field.value
|
||||||
|
? branches?.find(
|
||||||
|
(branch) => branch.name === field.value,
|
||||||
|
)?.name
|
||||||
|
: "Select branch"}
|
||||||
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</FormControl>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="p-0" align="start">
|
||||||
|
<Command>
|
||||||
|
<CommandInput
|
||||||
|
placeholder="Search branch..."
|
||||||
|
className="h-9"
|
||||||
|
/>
|
||||||
|
{status === "loading" && fetchStatus === "fetching" && (
|
||||||
|
<span className="py-6 text-center text-sm text-muted-foreground">
|
||||||
|
Loading Branches....
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{!repository?.owner && (
|
||||||
|
<span className="py-6 text-center text-sm text-muted-foreground">
|
||||||
|
Select a repository
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<ScrollArea className="h-96">
|
||||||
|
<CommandEmpty>No branch found.</CommandEmpty>
|
||||||
|
|
||||||
|
<CommandGroup>
|
||||||
|
{branches?.map((branch) => (
|
||||||
|
<CommandItem
|
||||||
|
value={branch.name}
|
||||||
|
key={branch.commit.id}
|
||||||
|
onSelect={() => {
|
||||||
|
form.setValue("branch", branch.name);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{branch.name}
|
||||||
|
<CheckIcon
|
||||||
|
className={cn(
|
||||||
|
"ml-auto h-4 w-4",
|
||||||
|
branch.name === field.value
|
||||||
|
? "opacity-100"
|
||||||
|
: "opacity-0",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</ScrollArea>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</Popover>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="composePath"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Compose Path</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="docker-compose.yml" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex w-full justify-end">
|
||||||
|
<Button
|
||||||
|
isLoading={isSavingGitlabProvider}
|
||||||
|
type="submit"
|
||||||
|
className="w-fit"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -8,15 +8,25 @@ import { ComposeFileEditor } from "../compose-file-editor";
|
|||||||
import { ShowConvertedCompose } from "../show-converted-compose";
|
import { ShowConvertedCompose } from "../show-converted-compose";
|
||||||
import { SaveGitProviderCompose } from "./save-git-provider-compose";
|
import { SaveGitProviderCompose } from "./save-git-provider-compose";
|
||||||
import { SaveGithubProviderCompose } from "./save-github-provider-compose";
|
import { SaveGithubProviderCompose } from "./save-github-provider-compose";
|
||||||
|
import {
|
||||||
|
BitbucketIcon,
|
||||||
|
GithubIcon,
|
||||||
|
GitIcon,
|
||||||
|
GitlabIcon,
|
||||||
|
} from "@/components/icons/data-tools-icons";
|
||||||
|
import { SaveGitlabProviderCompose } from "./save-gitlab-provider-compose";
|
||||||
|
import { SaveBitbucketProviderCompose } from "./save-bitbucket-provider-compose";
|
||||||
|
|
||||||
type TabState = "github" | "git" | "raw";
|
type TabState = "github" | "git" | "raw" | "gitlab" | "bitbucket";
|
||||||
interface Props {
|
interface Props {
|
||||||
composeId: string;
|
composeId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ShowProviderFormCompose = ({ composeId }: Props) => {
|
export const ShowProviderFormCompose = ({ composeId }: Props) => {
|
||||||
const { data: haveGithubConfigured } =
|
const { data: githubProviders } = api.gitProvider.githubProviders.useQuery();
|
||||||
api.admin.haveGithubConfigured.useQuery();
|
const { data: gitlabProviders } = api.gitProvider.gitlabProviders.useQuery();
|
||||||
|
const { data: bitbucketProviders } =
|
||||||
|
api.gitProvider.bitbucketProviders.useQuery();
|
||||||
|
|
||||||
const { data: compose } = api.compose.one.useQuery({ composeId });
|
const { data: compose } = api.compose.one.useQuery({ composeId });
|
||||||
const [tab, setSab] = useState<TabState>(compose?.sourceType || "github");
|
const [tab, setSab] = useState<TabState>(compose?.sourceType || "github");
|
||||||
@ -44,38 +54,96 @@ export const ShowProviderFormCompose = ({ composeId }: Props) => {
|
|||||||
setSab(e as TabState);
|
setSab(e as TabState);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TabsList className="grid w-fit grid-cols-4 bg-transparent">
|
<div className="flex flex-row items-center justify-between w-full gap-4">
|
||||||
<TabsTrigger
|
<TabsList className="md:grid md:w-fit md:grid-cols-5 max-md:overflow-x-scroll justify-start bg-transparent overflow-y-hidden">
|
||||||
value="github"
|
<TabsTrigger
|
||||||
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
value="github"
|
||||||
>
|
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
||||||
Github
|
>
|
||||||
</TabsTrigger>
|
<GithubIcon className="size-4 text-current fill-current" />
|
||||||
|
Github
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger
|
||||||
|
value="gitlab"
|
||||||
|
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
||||||
|
>
|
||||||
|
<GitlabIcon className="size-4 text-current fill-current" />
|
||||||
|
Gitlab
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger
|
||||||
|
value="bitbucket"
|
||||||
|
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
||||||
|
>
|
||||||
|
<BitbucketIcon className="size-4 text-current fill-current" />
|
||||||
|
Bitbucket
|
||||||
|
</TabsTrigger>
|
||||||
|
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="git"
|
value="git"
|
||||||
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
className="rounded-none border-b-2 gap-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
||||||
>
|
>
|
||||||
Git
|
<GitIcon />
|
||||||
</TabsTrigger>
|
Git
|
||||||
<TabsTrigger
|
</TabsTrigger>
|
||||||
value="raw"
|
<TabsTrigger
|
||||||
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
value="raw"
|
||||||
>
|
className="rounded-none border-b-2 border-b-transparent data-[state=active]:border-b-2 data-[state=active]:border-b-border"
|
||||||
Raw
|
>
|
||||||
</TabsTrigger>
|
Raw
|
||||||
</TabsList>
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
</div>
|
||||||
<TabsContent value="github" className="w-full p-2">
|
<TabsContent value="github" className="w-full p-2">
|
||||||
{haveGithubConfigured ? (
|
{githubProviders && githubProviders?.length > 0 ? (
|
||||||
<SaveGithubProviderCompose composeId={composeId} />
|
<SaveGithubProviderCompose composeId={composeId} />
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col items-center gap-3">
|
<div className="flex flex-col items-center gap-3 min-h-[15vh] justify-center">
|
||||||
<LockIcon className="size-8 text-muted-foreground" />
|
<GithubIcon className="size-8 text-muted-foreground" />
|
||||||
<span className="text-base text-muted-foreground">
|
<span className="text-base text-muted-foreground">
|
||||||
To deploy using GitHub, you need to configure your account
|
To deploy using GitHub, you need to configure your account
|
||||||
first. Please, go to{" "}
|
first. Please, go to{" "}
|
||||||
<Link
|
<Link
|
||||||
href="/dashboard/settings/server"
|
href="/dashboard/settings/git-providers"
|
||||||
|
className="text-foreground"
|
||||||
|
>
|
||||||
|
Settings
|
||||||
|
</Link>{" "}
|
||||||
|
to do so.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="gitlab" className="w-full p-2">
|
||||||
|
{gitlabProviders && gitlabProviders?.length > 0 ? (
|
||||||
|
<SaveGitlabProviderCompose composeId={composeId} />
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col items-center gap-3 min-h-[15vh] justify-center">
|
||||||
|
<GitlabIcon className="size-8 text-muted-foreground" />
|
||||||
|
<span className="text-base text-muted-foreground">
|
||||||
|
To deploy using GitLab, you need to configure your account
|
||||||
|
first. Please, go to{" "}
|
||||||
|
<Link
|
||||||
|
href="/dashboard/settings/git-providers"
|
||||||
|
className="text-foreground"
|
||||||
|
>
|
||||||
|
Settings
|
||||||
|
</Link>{" "}
|
||||||
|
to do so.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="bitbucket" className="w-full p-2">
|
||||||
|
{bitbucketProviders && bitbucketProviders?.length > 0 ? (
|
||||||
|
<SaveBitbucketProviderCompose composeId={composeId} />
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col items-center gap-3 min-h-[15vh] justify-center">
|
||||||
|
<BitbucketIcon className="size-8 text-muted-foreground" />
|
||||||
|
<span className="text-base text-muted-foreground">
|
||||||
|
To deploy using Bitbucket, you need to configure your account
|
||||||
|
first. Please, go to{" "}
|
||||||
|
<Link
|
||||||
|
href="/dashboard/settings/git-providers"
|
||||||
className="text-foreground"
|
className="text-foreground"
|
||||||
>
|
>
|
||||||
Settings
|
Settings
|
||||||
|
@ -256,8 +256,8 @@ export const DockerIcon = ({ className }: Props) => {
|
|||||||
export const GitIcon = ({ className }: Props) => {
|
export const GitIcon = ({ className }: Props) => {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width="24"
|
width="20"
|
||||||
height="24"
|
height="20"
|
||||||
viewBox="0 0 256 256"
|
viewBox="0 0 256 256"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
preserveAspectRatio="xMinYMin meet"
|
preserveAspectRatio="xMinYMin meet"
|
||||||
|
31
apps/dokploy/drizzle/0049_futuristic_vampiro.sql
Normal file
31
apps/dokploy/drizzle/0049_futuristic_vampiro.sql
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
ALTER TYPE "sourceTypeCompose" ADD VALUE 'gitlab';--> statement-breakpoint
|
||||||
|
ALTER TYPE "sourceTypeCompose" ADD VALUE 'bitbucket';--> statement-breakpoint
|
||||||
|
ALTER TABLE "compose" ADD COLUMN "gitlabProjectId" integer;--> statement-breakpoint
|
||||||
|
ALTER TABLE "compose" ADD COLUMN "gitlabRepository" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "compose" ADD COLUMN "gitlabOwner" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "compose" ADD COLUMN "gitlabBranch" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "compose" ADD COLUMN "gitlabBuildPath" text DEFAULT '/';--> statement-breakpoint
|
||||||
|
ALTER TABLE "compose" ADD COLUMN "bitbucketRepository" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "compose" ADD COLUMN "bitbucketOwner" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "compose" ADD COLUMN "bitbucketBranch" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "compose" ADD COLUMN "bitbucketBuildPath" text DEFAULT '/';--> statement-breakpoint
|
||||||
|
ALTER TABLE "compose" ADD COLUMN "githubId" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "compose" ADD COLUMN "gitlabId" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "compose" ADD COLUMN "bitbucketId" text;--> statement-breakpoint
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "compose" ADD CONSTRAINT "compose_githubId_github_provider_githubId_fk" FOREIGN KEY ("githubId") REFERENCES "public"."github_provider"("githubId") ON DELETE set null ON UPDATE no action;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN duplicate_object THEN null;
|
||||||
|
END $$;
|
||||||
|
--> statement-breakpoint
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "compose" ADD CONSTRAINT "compose_gitlabId_gitlab_provider_gitlabId_fk" FOREIGN KEY ("gitlabId") REFERENCES "public"."gitlab_provider"("gitlabId") ON DELETE set null ON UPDATE no action;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN duplicate_object THEN null;
|
||||||
|
END $$;
|
||||||
|
--> statement-breakpoint
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "compose" ADD CONSTRAINT "compose_bitbucketId_bitbucket_provider_bitbucketId_fk" FOREIGN KEY ("bitbucketId") REFERENCES "public"."bitbucket_provider"("bitbucketId") ON DELETE set null ON UPDATE no action;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN duplicate_object THEN null;
|
||||||
|
END $$;
|
2
apps/dokploy/drizzle/0050_faithful_brood.sql
Normal file
2
apps/dokploy/drizzle/0050_faithful_brood.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE "compose" DROP COLUMN IF EXISTS "gitlabBuildPath";--> statement-breakpoint
|
||||||
|
ALTER TABLE "compose" DROP COLUMN IF EXISTS "bitbucketBuildPath";
|
1
apps/dokploy/drizzle/0051_powerful_ironclad.sql
Normal file
1
apps/dokploy/drizzle/0051_powerful_ironclad.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "compose" ADD COLUMN "gitlabHttpUrl" text;
|
1
apps/dokploy/drizzle/0052_condemned_khan.sql
Normal file
1
apps/dokploy/drizzle/0052_condemned_khan.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "compose" RENAME COLUMN "gitlabHttpUrl" TO "gitlabPath";
|
2
apps/dokploy/drizzle/0053_fearless_electro.sql
Normal file
2
apps/dokploy/drizzle/0053_fearless_electro.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE "compose" RENAME COLUMN "gitlabPath" TO "gitlabPathNamespace";--> statement-breakpoint
|
||||||
|
ALTER TABLE "application" ADD COLUMN "gitlabPathNamespace" text;
|
3534
apps/dokploy/drizzle/meta/0049_snapshot.json
Normal file
3534
apps/dokploy/drizzle/meta/0049_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
3520
apps/dokploy/drizzle/meta/0050_snapshot.json
Normal file
3520
apps/dokploy/drizzle/meta/0050_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
3526
apps/dokploy/drizzle/meta/0051_snapshot.json
Normal file
3526
apps/dokploy/drizzle/meta/0051_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
3526
apps/dokploy/drizzle/meta/0052_snapshot.json
Normal file
3526
apps/dokploy/drizzle/meta/0052_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
3532
apps/dokploy/drizzle/meta/0053_snapshot.json
Normal file
3532
apps/dokploy/drizzle/meta/0053_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -344,6 +344,41 @@
|
|||||||
"when": 1725206119154,
|
"when": 1725206119154,
|
||||||
"tag": "0048_unknown_radioactive_man",
|
"tag": "0048_unknown_radioactive_man",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 49,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1725225552834,
|
||||||
|
"tag": "0049_futuristic_vampiro",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 50,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1725226430293,
|
||||||
|
"tag": "0050_faithful_brood",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 51,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1725231466293,
|
||||||
|
"tag": "0051_powerful_ironclad",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 52,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1725231697187,
|
||||||
|
"tag": "0052_condemned_khan",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 53,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1725232936525,
|
||||||
|
"tag": "0053_fearless_electro",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -8,7 +8,6 @@ export default async function handler(
|
|||||||
req: NextApiRequest,
|
req: NextApiRequest,
|
||||||
res: NextApiResponse,
|
res: NextApiResponse,
|
||||||
) {
|
) {
|
||||||
console.log(req.body);
|
|
||||||
const { code, gitlabId } = req.query;
|
const { code, gitlabId } = req.query;
|
||||||
|
|
||||||
if (!code || Array.isArray(code)) {
|
if (!code || Array.isArray(code)) {
|
||||||
|
@ -5,7 +5,6 @@ import { type apiCreateCompose, compose } from "@/server/db/schema";
|
|||||||
import { generateAppName } from "@/server/db/schema/utils";
|
import { generateAppName } from "@/server/db/schema/utils";
|
||||||
import { buildCompose } from "@/server/utils/builders/compose";
|
import { buildCompose } from "@/server/utils/builders/compose";
|
||||||
import { cloneCompose, loadDockerCompose } from "@/server/utils/docker/domain";
|
import { cloneCompose, loadDockerCompose } from "@/server/utils/docker/domain";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
|
||||||
import { sendBuildErrorNotifications } from "@/server/utils/notifications/build-error";
|
import { sendBuildErrorNotifications } from "@/server/utils/notifications/build-error";
|
||||||
import { sendBuildSuccessNotifications } from "@/server/utils/notifications/build-success";
|
import { sendBuildSuccessNotifications } from "@/server/utils/notifications/build-success";
|
||||||
import { execAsync } from "@/server/utils/process/execAsync";
|
import { execAsync } from "@/server/utils/process/execAsync";
|
||||||
@ -15,9 +14,11 @@ import { createComposeFile } from "@/server/utils/providers/raw";
|
|||||||
import { generatePassword } from "@/templates/utils";
|
import { generatePassword } from "@/templates/utils";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { findAdmin, getDokployUrl } from "./admin";
|
import { getDokployUrl } from "./admin";
|
||||||
import { createDeploymentCompose, updateDeploymentStatus } from "./deployment";
|
import { createDeploymentCompose, updateDeploymentStatus } from "./deployment";
|
||||||
import { validUniqueServerAppName } from "./project";
|
import { validUniqueServerAppName } from "./project";
|
||||||
|
import { cloneBitbucketRepository } from "@/server/utils/providers/bitbucket";
|
||||||
|
import { cloneGitlabRepository } from "@/server/utils/providers/gitlab";
|
||||||
|
|
||||||
export type Compose = typeof compose.$inferSelect;
|
export type Compose = typeof compose.$inferSelect;
|
||||||
|
|
||||||
@ -92,6 +93,9 @@ export const findComposeById = async (composeId: string) => {
|
|||||||
deployments: true,
|
deployments: true,
|
||||||
mounts: true,
|
mounts: true,
|
||||||
domains: true,
|
domains: true,
|
||||||
|
githubProvider: true,
|
||||||
|
gitlabProvider: true,
|
||||||
|
bitbucketProvider: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!result) {
|
if (!result) {
|
||||||
@ -151,7 +155,6 @@ export const deployCompose = async ({
|
|||||||
descriptionLog: string;
|
descriptionLog: string;
|
||||||
}) => {
|
}) => {
|
||||||
const compose = await findComposeById(composeId);
|
const compose = await findComposeById(composeId);
|
||||||
const admin = await findAdmin();
|
|
||||||
const buildLink = `${await getDokployUrl()}/dashboard/project/${compose.projectId}/services/compose/${compose.composeId}?tab=deployments`;
|
const buildLink = `${await getDokployUrl()}/dashboard/project/${compose.projectId}/services/compose/${compose.composeId}?tab=deployments`;
|
||||||
const deployment = await createDeploymentCompose({
|
const deployment = await createDeploymentCompose({
|
||||||
composeId: composeId,
|
composeId: composeId,
|
||||||
@ -161,7 +164,11 @@ export const deployCompose = async ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (compose.sourceType === "github") {
|
if (compose.sourceType === "github") {
|
||||||
await cloneGithubRepository(admin, compose, deployment.logPath, true);
|
await cloneGithubRepository(compose, deployment.logPath);
|
||||||
|
} else if (compose.sourceType === "gitlab") {
|
||||||
|
await cloneGitlabRepository(compose, deployment.logPath);
|
||||||
|
} else if (compose.sourceType === "bitbucket") {
|
||||||
|
await cloneBitbucketRepository(compose, deployment.logPath);
|
||||||
} else if (compose.sourceType === "git") {
|
} else if (compose.sourceType === "git") {
|
||||||
await cloneGitRepository(compose, deployment.logPath, true);
|
await cloneGitRepository(compose, deployment.logPath, true);
|
||||||
} else if (compose.sourceType === "raw") {
|
} else if (compose.sourceType === "raw") {
|
||||||
|
@ -138,6 +138,7 @@ export const applications = pgTable("application", {
|
|||||||
gitlabOwner: text("gitlabOwner"),
|
gitlabOwner: text("gitlabOwner"),
|
||||||
gitlabBranch: text("gitlabBranch"),
|
gitlabBranch: text("gitlabBranch"),
|
||||||
gitlabBuildPath: text("gitlabBuildPath").default("/"),
|
gitlabBuildPath: text("gitlabBuildPath").default("/"),
|
||||||
|
gitlabPathNamespace: text("gitlabPathNamespace"),
|
||||||
// Bitbucket
|
// Bitbucket
|
||||||
bitbucketRepository: text("bitbucketRepository"),
|
bitbucketRepository: text("bitbucketRepository"),
|
||||||
bitbucketOwner: text("bitbucketOwner"),
|
bitbucketOwner: text("bitbucketOwner"),
|
||||||
@ -423,6 +424,7 @@ export const apiSaveGitlabProvider = createSchema
|
|||||||
gitlabRepository: true,
|
gitlabRepository: true,
|
||||||
gitlabId: true,
|
gitlabId: true,
|
||||||
gitlabProjectId: true,
|
gitlabProjectId: true,
|
||||||
|
gitlabPathNamespace: true,
|
||||||
})
|
})
|
||||||
.required();
|
.required();
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { sshKeys } from "@/server/db/schema/ssh-key";
|
import { sshKeys } from "@/server/db/schema/ssh-key";
|
||||||
import { relations } from "drizzle-orm";
|
import { relations } from "drizzle-orm";
|
||||||
import { boolean, pgEnum, pgTable, text } from "drizzle-orm/pg-core";
|
import { boolean, integer, pgEnum, pgTable, text } from "drizzle-orm/pg-core";
|
||||||
import { createInsertSchema } from "drizzle-zod";
|
import { createInsertSchema } from "drizzle-zod";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@ -10,10 +10,17 @@ import { mounts } from "./mount";
|
|||||||
import { projects } from "./project";
|
import { projects } from "./project";
|
||||||
import { applicationStatus } from "./shared";
|
import { applicationStatus } from "./shared";
|
||||||
import { generateAppName } from "./utils";
|
import { generateAppName } from "./utils";
|
||||||
|
import {
|
||||||
|
bitbucketProvider,
|
||||||
|
githubProvider,
|
||||||
|
gitlabProvider,
|
||||||
|
} from "./git-provider";
|
||||||
|
|
||||||
export const sourceTypeCompose = pgEnum("sourceTypeCompose", [
|
export const sourceTypeCompose = pgEnum("sourceTypeCompose", [
|
||||||
"git",
|
"git",
|
||||||
"github",
|
"github",
|
||||||
|
"gitlab",
|
||||||
|
"bitbucket",
|
||||||
"raw",
|
"raw",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -39,6 +46,16 @@ export const compose = pgTable("compose", {
|
|||||||
owner: text("owner"),
|
owner: text("owner"),
|
||||||
branch: text("branch"),
|
branch: text("branch"),
|
||||||
autoDeploy: boolean("autoDeploy").$defaultFn(() => true),
|
autoDeploy: boolean("autoDeploy").$defaultFn(() => true),
|
||||||
|
// Gitlab
|
||||||
|
gitlabProjectId: integer("gitlabProjectId"),
|
||||||
|
gitlabRepository: text("gitlabRepository"),
|
||||||
|
gitlabOwner: text("gitlabOwner"),
|
||||||
|
gitlabBranch: text("gitlabBranch"),
|
||||||
|
gitlabPathNamespace: text("gitlabPathNamespace"),
|
||||||
|
// Bitbucket
|
||||||
|
bitbucketRepository: text("bitbucketRepository"),
|
||||||
|
bitbucketOwner: text("bitbucketOwner"),
|
||||||
|
bitbucketBranch: text("bitbucketBranch"),
|
||||||
// Git
|
// Git
|
||||||
customGitUrl: text("customGitUrl"),
|
customGitUrl: text("customGitUrl"),
|
||||||
customGitBranch: text("customGitBranch"),
|
customGitBranch: text("customGitBranch"),
|
||||||
@ -48,7 +65,6 @@ export const compose = pgTable("compose", {
|
|||||||
onDelete: "set null",
|
onDelete: "set null",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
//
|
|
||||||
command: text("command").notNull().default(""),
|
command: text("command").notNull().default(""),
|
||||||
//
|
//
|
||||||
composePath: text("composePath").notNull().default("./docker-compose.yml"),
|
composePath: text("composePath").notNull().default("./docker-compose.yml"),
|
||||||
@ -59,6 +75,19 @@ export const compose = pgTable("compose", {
|
|||||||
createdAt: text("createdAt")
|
createdAt: text("createdAt")
|
||||||
.notNull()
|
.notNull()
|
||||||
.$defaultFn(() => new Date().toISOString()),
|
.$defaultFn(() => new Date().toISOString()),
|
||||||
|
|
||||||
|
githubId: text("githubId").references(() => githubProvider.githubId, {
|
||||||
|
onDelete: "set null",
|
||||||
|
}),
|
||||||
|
gitlabId: text("gitlabId").references(() => gitlabProvider.gitlabId, {
|
||||||
|
onDelete: "set null",
|
||||||
|
}),
|
||||||
|
bitbucketId: text("bitbucketId").references(
|
||||||
|
() => bitbucketProvider.bitbucketId,
|
||||||
|
{
|
||||||
|
onDelete: "set null",
|
||||||
|
},
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const composeRelations = relations(compose, ({ one, many }) => ({
|
export const composeRelations = relations(compose, ({ one, many }) => ({
|
||||||
@ -73,6 +102,18 @@ export const composeRelations = relations(compose, ({ one, many }) => ({
|
|||||||
references: [sshKeys.sshKeyId],
|
references: [sshKeys.sshKeyId],
|
||||||
}),
|
}),
|
||||||
domains: many(domains),
|
domains: many(domains),
|
||||||
|
githubProvider: one(githubProvider, {
|
||||||
|
fields: [compose.githubId],
|
||||||
|
references: [githubProvider.githubId],
|
||||||
|
}),
|
||||||
|
gitlabProvider: one(gitlabProvider, {
|
||||||
|
fields: [compose.gitlabId],
|
||||||
|
references: [gitlabProvider.gitlabId],
|
||||||
|
}),
|
||||||
|
bitbucketProvider: one(bitbucketProvider, {
|
||||||
|
fields: [compose.bitbucketId],
|
||||||
|
references: [bitbucketProvider.bitbucketId],
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const createSchema = createInsertSchema(compose, {
|
const createSchema = createInsertSchema(compose, {
|
||||||
|
@ -64,7 +64,7 @@ export const githubProvider = pgTable("github_provider", {
|
|||||||
|
|
||||||
export const githubProviderRelations = relations(
|
export const githubProviderRelations = relations(
|
||||||
githubProvider,
|
githubProvider,
|
||||||
({ one, many }) => ({
|
({ one, }) => ({
|
||||||
gitProvider: one(gitProvider, {
|
gitProvider: one(gitProvider, {
|
||||||
fields: [githubProvider.gitProviderId],
|
fields: [githubProvider.gitProviderId],
|
||||||
references: [gitProvider.gitProviderId],
|
references: [gitProvider.gitProviderId],
|
||||||
@ -91,7 +91,7 @@ export const gitlabProvider = pgTable("gitlab_provider", {
|
|||||||
|
|
||||||
export const gitlabProviderRelations = relations(
|
export const gitlabProviderRelations = relations(
|
||||||
gitlabProvider,
|
gitlabProvider,
|
||||||
({ one, many }) => ({
|
({ one}) => ({
|
||||||
gitProvider: one(gitProvider, {
|
gitProvider: one(gitProvider, {
|
||||||
fields: [gitlabProvider.gitProviderId],
|
fields: [gitlabProvider.gitProviderId],
|
||||||
references: [gitProvider.gitProviderId],
|
references: [gitProvider.gitProviderId],
|
||||||
@ -114,7 +114,7 @@ export const bitbucketProvider = pgTable("bitbucket_provider", {
|
|||||||
|
|
||||||
export const bitbucketProviderRelations = relations(
|
export const bitbucketProviderRelations = relations(
|
||||||
bitbucketProvider,
|
bitbucketProvider,
|
||||||
({ one, many }) => ({
|
({ one }) => ({
|
||||||
gitProvider: one(gitProvider, {
|
gitProvider: one(gitProvider, {
|
||||||
fields: [bitbucketProvider.gitProviderId],
|
fields: [bitbucketProvider.gitProviderId],
|
||||||
references: [gitProvider.gitProviderId],
|
references: [gitProvider.gitProviderId],
|
||||||
|
@ -18,15 +18,7 @@ export type ComposeNested = InferResultType<
|
|||||||
>;
|
>;
|
||||||
export const buildCompose = async (compose: ComposeNested, logPath: string) => {
|
export const buildCompose = async (compose: ComposeNested, logPath: string) => {
|
||||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
const writeStream = createWriteStream(logPath, { flags: "a" });
|
||||||
const {
|
const { sourceType, appName, mounts, composeType, domains } = compose;
|
||||||
sourceType,
|
|
||||||
appName,
|
|
||||||
mounts,
|
|
||||||
composeType,
|
|
||||||
env,
|
|
||||||
composePath,
|
|
||||||
domains,
|
|
||||||
} = compose;
|
|
||||||
try {
|
try {
|
||||||
const command = createCommand(compose);
|
const command = createCommand(compose);
|
||||||
await writeDomainsToCompose(compose, domains);
|
await writeDomainsToCompose(compose, domains);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import fs, { existsSync, readFileSync, writeSync } from "node:fs";
|
import fs, { existsSync, readFileSync } from "node:fs";
|
||||||
import { writeFile } from "node:fs/promises";
|
import { writeFile } from "node:fs/promises";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import type { Compose } from "@/server/api/services/compose";
|
import type { Compose } from "@/server/api/services/compose";
|
||||||
@ -13,10 +13,16 @@ import type {
|
|||||||
DefinitionsService,
|
DefinitionsService,
|
||||||
PropertiesNetworks,
|
PropertiesNetworks,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
import { cloneRawGitlabRepository } from "../providers/gitlab";
|
||||||
|
import { cloneRawBitbucketRepository } from "../providers/bitbucket";
|
||||||
|
|
||||||
export const cloneCompose = async (compose: Compose) => {
|
export const cloneCompose = async (compose: Compose) => {
|
||||||
if (compose.sourceType === "github") {
|
if (compose.sourceType === "github") {
|
||||||
await cloneRawGithubRepository(compose);
|
await cloneRawGithubRepository(compose);
|
||||||
|
} else if (compose.sourceType === "gitlab") {
|
||||||
|
await cloneRawGitlabRepository(compose);
|
||||||
|
} else if (compose.sourceType === "bitbucket") {
|
||||||
|
await cloneRawBitbucketRepository(compose);
|
||||||
} else if (compose.sourceType === "git") {
|
} else if (compose.sourceType === "git") {
|
||||||
await cloneGitRawRepository(compose);
|
await cloneGitRawRepository(compose);
|
||||||
} else if (compose.sourceType === "raw") {
|
} else if (compose.sourceType === "raw") {
|
||||||
|
@ -72,6 +72,10 @@ export const getBuildAppDirectory = (application: Application) => {
|
|||||||
|
|
||||||
if (sourceType === "github") {
|
if (sourceType === "github") {
|
||||||
buildPath = application?.buildPath || "";
|
buildPath = application?.buildPath || "";
|
||||||
|
} else if (sourceType === "gitlab") {
|
||||||
|
buildPath = application?.gitlabBuildPath || "";
|
||||||
|
} else if (sourceType === "bitbucket") {
|
||||||
|
buildPath = application?.bitbucketBuildPath || "";
|
||||||
} else if (sourceType === "drop") {
|
} else if (sourceType === "drop") {
|
||||||
buildPath = application?.dropBuildPath || "";
|
buildPath = application?.dropBuildPath || "";
|
||||||
} else if (sourceType === "git") {
|
} else if (sourceType === "git") {
|
||||||
|
@ -6,14 +6,20 @@ import { recreateDirectory } from "../filesystem/directory";
|
|||||||
import { spawnAsync } from "../process/spawnAsync";
|
import { spawnAsync } from "../process/spawnAsync";
|
||||||
import type { InferResultType } from "@/server/types/with";
|
import type { InferResultType } from "@/server/types/with";
|
||||||
import { getBitbucketProvider } from "@/server/api/services/git-provider";
|
import { getBitbucketProvider } from "@/server/api/services/git-provider";
|
||||||
|
import type { Compose } from "@/server/api/services/compose";
|
||||||
|
|
||||||
export type ApplicationWithBitbucket = InferResultType<
|
export type ApplicationWithBitbucket = InferResultType<
|
||||||
"applications",
|
"applications",
|
||||||
{ bitbucketProvider: true }
|
{ bitbucketProvider: true }
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
export type ComposeWithBitbucket = InferResultType<
|
||||||
|
"compose",
|
||||||
|
{ bitbucketProvider: true }
|
||||||
|
>;
|
||||||
|
|
||||||
export const cloneBitbucketRepository = async (
|
export const cloneBitbucketRepository = async (
|
||||||
entity: ApplicationWithBitbucket,
|
entity: ApplicationWithBitbucket | ComposeWithBitbucket,
|
||||||
logPath: string,
|
logPath: string,
|
||||||
isCompose = false,
|
isCompose = false,
|
||||||
) => {
|
) => {
|
||||||
@ -68,16 +74,13 @@ export const cloneBitbucketRepository = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cloneRawBitbucketRepository = async (
|
export const cloneRawBitbucketRepository = async (entity: Compose) => {
|
||||||
entity: ApplicationWithBitbucket,
|
|
||||||
) => {
|
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
bitbucketRepository,
|
bitbucketRepository,
|
||||||
bitbucketOwner,
|
bitbucketOwner,
|
||||||
bitbucketBranch,
|
bitbucketBranch,
|
||||||
bitbucketId,
|
bitbucketId,
|
||||||
bitbucketProvider,
|
|
||||||
} = entity;
|
} = entity;
|
||||||
|
|
||||||
if (!bitbucketId) {
|
if (!bitbucketId) {
|
||||||
@ -87,6 +90,7 @@ export const cloneRawBitbucketRepository = async (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bitbucketProvider = await getBitbucketProvider(bitbucketId);
|
||||||
const basePath = COMPOSE_PATH;
|
const basePath = COMPOSE_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
await recreateDirectory(outputPath);
|
await recreateDirectory(outputPath);
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
getGithubProvider,
|
getGithubProvider,
|
||||||
type GithubProvider,
|
type GithubProvider,
|
||||||
} from "@/server/api/services/git-provider";
|
} from "@/server/api/services/git-provider";
|
||||||
|
import type { Compose } from "@/server/api/services/compose";
|
||||||
|
|
||||||
export const authGithub = (githubProvider: GithubProvider) => {
|
export const authGithub = (githubProvider: GithubProvider) => {
|
||||||
if (!haveGithubRequirements(githubProvider)) {
|
if (!haveGithubRequirements(githubProvider)) {
|
||||||
@ -71,8 +72,13 @@ export type ApplicationWithGithub = InferResultType<
|
|||||||
"applications",
|
"applications",
|
||||||
{ githubProvider: true }
|
{ githubProvider: true }
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
export type ComposeWithGithub = InferResultType<
|
||||||
|
"compose",
|
||||||
|
{ githubProvider: true }
|
||||||
|
>;
|
||||||
export const cloneGithubRepository = async (
|
export const cloneGithubRepository = async (
|
||||||
entity: ApplicationWithGithub,
|
entity: ApplicationWithGithub | ComposeWithGithub,
|
||||||
logPath: string,
|
logPath: string,
|
||||||
isCompose = false,
|
isCompose = false,
|
||||||
) => {
|
) => {
|
||||||
@ -140,9 +146,7 @@ export const cloneGithubRepository = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cloneRawGithubRepository = async (
|
export const cloneRawGithubRepository = async (entity: Compose) => {
|
||||||
entity: ApplicationWithGithub,
|
|
||||||
) => {
|
|
||||||
const { appName, repository, owner, branch, githubId } = entity;
|
const { appName, repository, owner, branch, githubId } = entity;
|
||||||
|
|
||||||
if (!githubId) {
|
if (!githubId) {
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
updateGitlabProvider,
|
updateGitlabProvider,
|
||||||
} from "@/server/api/services/git-provider";
|
} from "@/server/api/services/git-provider";
|
||||||
import type { InferResultType } from "@/server/types/with";
|
import type { InferResultType } from "@/server/types/with";
|
||||||
|
import type { Compose } from "@/server/api/services/compose";
|
||||||
|
|
||||||
export const refreshGitlabToken = async (gitlabProviderId: string) => {
|
export const refreshGitlabToken = async (gitlabProviderId: string) => {
|
||||||
const gitlabProvider = await getGitlabProvider(gitlabProviderId);
|
const gitlabProvider = await getGitlabProvider(gitlabProviderId);
|
||||||
@ -79,8 +80,13 @@ export type ApplicationWithGitlab = InferResultType<
|
|||||||
{ gitlabProvider: true }
|
{ gitlabProvider: true }
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
export type ComposeWithGitlab = InferResultType<
|
||||||
|
"compose",
|
||||||
|
{ gitlabProvider: true }
|
||||||
|
>;
|
||||||
|
|
||||||
export const cloneGitlabRepository = async (
|
export const cloneGitlabRepository = async (
|
||||||
entity: ApplicationWithGitlab,
|
entity: ApplicationWithGitlab | ComposeWithGitlab,
|
||||||
logPath: string,
|
logPath: string,
|
||||||
isCompose = false,
|
isCompose = false,
|
||||||
) => {
|
) => {
|
||||||
@ -92,6 +98,7 @@ export const cloneGitlabRepository = async (
|
|||||||
gitlabBranch,
|
gitlabBranch,
|
||||||
gitlabId,
|
gitlabId,
|
||||||
gitlabProvider,
|
gitlabProvider,
|
||||||
|
gitlabPathNamespace,
|
||||||
} = entity;
|
} = entity;
|
||||||
|
|
||||||
if (!gitlabId) {
|
if (!gitlabId) {
|
||||||
@ -121,7 +128,7 @@ export const cloneGitlabRepository = async (
|
|||||||
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");
|
||||||
await recreateDirectory(outputPath);
|
await recreateDirectory(outputPath);
|
||||||
const repoclone = `gitlab.com/${gitlabOwner}/${gitlabRepository}.git`;
|
const repoclone = `gitlab.com/${gitlabPathNamespace}.git`;
|
||||||
const cloneUrl = `https://oauth2:${gitlabProvider?.accessToken}@${repoclone}`;
|
const cloneUrl = `https://oauth2:${gitlabProvider?.accessToken}@${repoclone}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -191,12 +198,11 @@ export const getGitlabRepositories = async (input: {
|
|||||||
}
|
}
|
||||||
return kind === "user";
|
return kind === "user";
|
||||||
});
|
});
|
||||||
|
|
||||||
const mappedRepositories = filteredRepos.map((repo: any) => {
|
const mappedRepositories = filteredRepos.map((repo: any) => {
|
||||||
return {
|
return {
|
||||||
id: repo.id,
|
id: repo.id,
|
||||||
name: repo.name,
|
name: repo.name,
|
||||||
url: repo.web_url,
|
url: repo.path_with_namespace,
|
||||||
owner: {
|
owner: {
|
||||||
username: repo.namespace.path,
|
username: repo.namespace.path,
|
||||||
},
|
},
|
||||||
@ -249,16 +255,14 @@ export const getGitlabBranches = async (input: {
|
|||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cloneRawGitlabRepository = async (
|
export const cloneRawGitlabRepository = async (entity: Compose) => {
|
||||||
entity: ApplicationWithGitlab,
|
|
||||||
) => {
|
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
gitlabRepository,
|
gitlabRepository,
|
||||||
gitlabOwner,
|
gitlabOwner,
|
||||||
gitlabBranch,
|
gitlabBranch,
|
||||||
gitlabId,
|
gitlabId,
|
||||||
gitlabProvider,
|
gitlabPathNamespace,
|
||||||
} = entity;
|
} = entity;
|
||||||
|
|
||||||
if (!gitlabId) {
|
if (!gitlabId) {
|
||||||
@ -268,12 +272,15 @@ export const cloneRawGitlabRepository = async (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gitlabProvider = await getGitlabProvider(gitlabId);
|
||||||
|
|
||||||
await refreshGitlabToken(gitlabId);
|
await refreshGitlabToken(gitlabId);
|
||||||
const basePath = COMPOSE_PATH;
|
const basePath = COMPOSE_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
await recreateDirectory(outputPath);
|
await recreateDirectory(outputPath);
|
||||||
const repoclone = `gitlab.com/${gitlabOwner}/${gitlabRepository}.git`;
|
const repoclone = `gitlab.com/${gitlabPathNamespace}.git`;
|
||||||
const cloneUrl = `https://oauth2:${gitlabProvider?.accessToken}@${repoclone}`;
|
const cloneUrl = `https://oauth2:${gitlabProvider?.accessToken}@${repoclone}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await spawnAsync("git", [
|
await spawnAsync("git", [
|
||||||
"clone",
|
"clone",
|
||||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user