mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat: link field with application
This commit is contained in:
@@ -1,13 +1,5 @@
|
|||||||
|
import { AddSSHKey } from "@/components/dashboard/settings/ssh-keys/add-ssh-key";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
DialogTrigger,
|
|
||||||
} from "@/components/ui/dialog";
|
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -17,12 +9,24 @@ import {
|
|||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectLabel,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import copy from "copy-to-clipboard";
|
import { SelectSeparator } from "@radix-ui/react-select";
|
||||||
import { CopyIcon, LockIcon } from "lucide-react";
|
import { KeyRoundIcon, LockIcon } from "lucide-react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
import { flushSync } from "react-dom";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -33,6 +37,7 @@ const GitProviderSchema = z.object({
|
|||||||
}),
|
}),
|
||||||
branch: z.string().min(1, "Branch required"),
|
branch: z.string().min(1, "Branch required"),
|
||||||
buildPath: z.string().min(1, "Build Path required"),
|
buildPath: z.string().min(1, "Build Path required"),
|
||||||
|
sshKey: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
type GitProvider = z.infer<typeof GitProviderSchema>;
|
type GitProvider = z.infer<typeof GitProviderSchema>;
|
||||||
@@ -43,19 +48,22 @@ interface Props {
|
|||||||
|
|
||||||
export const SaveGitProvider = ({ applicationId }: Props) => {
|
export const SaveGitProvider = ({ applicationId }: Props) => {
|
||||||
const { data, refetch } = api.application.one.useQuery({ applicationId });
|
const { data, refetch } = api.application.one.useQuery({ applicationId });
|
||||||
|
const { data: sshKeys } = api.sshKey.all.useQuery();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const { mutateAsync, isLoading } =
|
const { mutateAsync, isLoading } =
|
||||||
api.application.saveGitProdiver.useMutation();
|
api.application.saveGitProdiver.useMutation();
|
||||||
const { mutateAsync: generateSSHKey, isLoading: isGeneratingSSHKey } =
|
// const { mutateAsync: generateSSHKey, isLoading: isGeneratingSSHKey } =
|
||||||
api.application.generateSSHKey.useMutation();
|
// api.application.generateSSHKey.useMutation();
|
||||||
|
|
||||||
const { mutateAsync: removeSSHKey, isLoading: isRemovingSSHKey } =
|
// const { mutateAsync: removeSSHKey, isLoading: isRemovingSSHKey } =
|
||||||
api.application.removeSSHKey.useMutation();
|
// api.application.removeSSHKey.useMutation();
|
||||||
const form = useForm<GitProvider>({
|
const form = useForm<GitProvider>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
branch: "",
|
branch: "",
|
||||||
buildPath: "/",
|
buildPath: "/",
|
||||||
repositoryURL: "",
|
repositoryURL: "",
|
||||||
|
sshKey: "",
|
||||||
},
|
},
|
||||||
resolver: zodResolver(GitProviderSchema),
|
resolver: zodResolver(GitProviderSchema),
|
||||||
});
|
});
|
||||||
@@ -63,6 +71,7 @@ export const SaveGitProvider = ({ applicationId }: Props) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
form.reset({
|
form.reset({
|
||||||
|
sshKey: data.customGitSSHKeyId || "",
|
||||||
branch: data.customGitBranch || "",
|
branch: data.customGitBranch || "",
|
||||||
buildPath: data.customGitBuildPath || "/",
|
buildPath: data.customGitBuildPath || "/",
|
||||||
repositoryURL: data.customGitUrl || "",
|
repositoryURL: data.customGitUrl || "",
|
||||||
@@ -75,6 +84,7 @@ export const SaveGitProvider = ({ applicationId }: Props) => {
|
|||||||
customGitBranch: values.branch,
|
customGitBranch: values.branch,
|
||||||
customGitBuildPath: values.buildPath,
|
customGitBuildPath: values.buildPath,
|
||||||
customGitUrl: values.repositoryURL,
|
customGitUrl: values.repositoryURL,
|
||||||
|
customGitSSHKeyId: values.sshKey,
|
||||||
applicationId,
|
applicationId,
|
||||||
})
|
})
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
@@ -92,160 +102,103 @@ export const SaveGitProvider = ({ applicationId }: Props) => {
|
|||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="flex flex-col gap-4"
|
className="flex flex-col gap-4"
|
||||||
>
|
>
|
||||||
<div className="grid md:grid-cols-2 gap-4 ">
|
<div className="grid md:grid-cols-2 gap-4">
|
||||||
<div className="md:col-span-2 space-y-4">
|
<div className="flex items-end col-span-2 gap-4">
|
||||||
<FormField
|
<div className="grow">
|
||||||
control={form.control}
|
<FormField
|
||||||
name="repositoryURL"
|
control={form.control}
|
||||||
render={({ field }) => (
|
name="repositoryURL"
|
||||||
<FormItem>
|
render={({ field }) => (
|
||||||
<FormLabel className="flex flex-row justify-between">
|
<FormItem>
|
||||||
Repository URL
|
<FormLabel>Repository URL</FormLabel>
|
||||||
<div className="flex gap-2">
|
<FormControl>
|
||||||
<Dialog>
|
<Input placeholder="git@bitbucket.org" {...field} />
|
||||||
<DialogTrigger className="flex flex-row gap-2">
|
</FormControl>
|
||||||
<LockIcon className="size-4 text-muted-foreground" />?
|
<FormMessage />
|
||||||
</DialogTrigger>
|
</FormItem>
|
||||||
<DialogContent className="sm:max-w-[425px]">
|
)}
|
||||||
<DialogHeader>
|
/>
|
||||||
<DialogTitle>Private Repository</DialogTitle>
|
</div>
|
||||||
<DialogDescription>
|
{sshKeys && sshKeys.length > 0 ? (
|
||||||
If your repository is private is necessary to
|
<FormField
|
||||||
generate SSH Keys to add to your git provider.
|
control={form.control}
|
||||||
</DialogDescription>
|
name="sshKey"
|
||||||
</DialogHeader>
|
render={({ field }) => (
|
||||||
<div className="grid gap-4 py-4">
|
<FormItem className="basis-40">
|
||||||
<div className="relative">
|
<FormLabel className="w-full inline-flex justify-between">
|
||||||
<Textarea
|
SSH Key
|
||||||
placeholder="Please click on Generate SSH Key"
|
<LockIcon className="size-4 text-muted-foreground" />
|
||||||
className="no-scrollbar h-64 text-muted-foreground"
|
</FormLabel>
|
||||||
disabled={!data?.customGitSSHKey}
|
<FormControl>
|
||||||
contentEditable={false}
|
<Select
|
||||||
value={
|
key={field.value}
|
||||||
data?.customGitSSHKey ||
|
onValueChange={field.onChange}
|
||||||
"Please click on Generate SSH Key"
|
defaultValue={field.value}
|
||||||
}
|
value={field.value}
|
||||||
/>
|
>
|
||||||
<button
|
<SelectTrigger>
|
||||||
type="button"
|
<SelectValue placeholder="Select a key" />
|
||||||
className="absolute right-2 top-2"
|
</SelectTrigger>
|
||||||
onClick={() => {
|
<SelectContent>
|
||||||
copy(
|
<SelectGroup>
|
||||||
data?.customGitSSHKey ||
|
{sshKeys?.map((sshKey) => (
|
||||||
"Generate a SSH Key",
|
<SelectItem
|
||||||
);
|
key={sshKey.sshKeyId}
|
||||||
toast.success("SSH Copied to clipboard");
|
value={sshKey.sshKeyId}
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<CopyIcon className="size-4" />
|
{sshKey.name}
|
||||||
</button>
|
</SelectItem>
|
||||||
</div>
|
))}
|
||||||
</div>
|
<SelectLabel>Keys ({sshKeys?.length})</SelectLabel>
|
||||||
<DialogFooter className="flex sm:justify-between gap-3.5 flex-col sm:flex-col w-full">
|
</SelectGroup>
|
||||||
<div className="flex flex-row gap-2 w-full justify-between flex-wrap">
|
<SelectSeparator />
|
||||||
{data?.customGitSSHKey && (
|
</SelectContent>
|
||||||
<Button
|
</Select>
|
||||||
variant="destructive"
|
</FormControl>
|
||||||
isLoading={
|
</FormItem>
|
||||||
isGeneratingSSHKey || isRemovingSSHKey
|
)}
|
||||||
}
|
/>
|
||||||
className="max-sm:w-full"
|
) : (
|
||||||
onClick={async () => {
|
<Button
|
||||||
await removeSSHKey({
|
variant="secondary"
|
||||||
applicationId,
|
onClick={() => router.push("/dashboard/settings/ssh-keys")}
|
||||||
})
|
type="button"
|
||||||
.then(async () => {
|
>
|
||||||
toast.success("SSH Key Removed");
|
<KeyRoundIcon className="size-4" /> Add SSH Key
|
||||||
await refetch();
|
</Button>
|
||||||
})
|
)}
|
||||||
.catch(() => {
|
|
||||||
toast.error(
|
|
||||||
"Error to remove the SSH Key",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
Remove SSH Key
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Button
|
|
||||||
isLoading={
|
|
||||||
isGeneratingSSHKey || isRemovingSSHKey
|
|
||||||
}
|
|
||||||
className="max-sm:w-full"
|
|
||||||
onClick={async () => {
|
|
||||||
await generateSSHKey({
|
|
||||||
applicationId,
|
|
||||||
})
|
|
||||||
.then(async () => {
|
|
||||||
toast.success("SSH Key Generated");
|
|
||||||
await refetch();
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
toast.error(
|
|
||||||
"Error to generate the SSH Key",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
Generate SSH Key
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<span className="text-sm text-muted-foreground">
|
|
||||||
Is recommended to remove the SSH Key if you want
|
|
||||||
to deploy a public repository.
|
|
||||||
</span>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</div>
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder="git@bitbucket.org" {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-4">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="branch"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Branch</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder="Branch" {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-4">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="buildPath"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>Build Path</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder="/" {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="branch"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Branch</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="Branch" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="buildPath"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Build Path</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="/" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-row justify-end">
|
<div className="flex flex-row justify-end">
|
||||||
<Button type="submit" className="w-fit" isLoading={isLoading}>
|
<Button type="submit" className="w-fit" isLoading={isLoading}>
|
||||||
Save{" "}
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ export const AddSSHKey = ({ children }: Props) => {
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Textarea
|
||||||
placeholder={"-----BEGIN RSA PRIVATE KEY-----"}
|
placeholder={"-----BEGIN RSA PRIVATE KEY-----"}
|
||||||
|
rows={5}
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@@ -143,16 +144,13 @@ export const AddSSHKey = ({ children }: Props) => {
|
|||||||
<FormLabel>Public Key</FormLabel>
|
<FormLabel>Public Key</FormLabel>
|
||||||
</div>
|
</div>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Textarea
|
<Input placeholder={"ssh-rsa AAAAB3NzaC1yc2E"} {...field} />
|
||||||
placeholder={"ssh-rsa AAAAB3NzaC1yc2E"}
|
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<DialogFooter className="flex w-full flex-row !justify-between pt-3">
|
<DialogFooter>
|
||||||
<Button isLoading={isLoading} type="submit">
|
<Button isLoading={isLoading} type="submit">
|
||||||
Create
|
Create
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -39,54 +39,54 @@ export const ShowDestinations = () => {
|
|||||||
</AddSSHKey>
|
</AddSSHKey>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="space-y-8">
|
||||||
<div className="flex gap-4 text-xs px-3.5">
|
<div className="flex flex-col gap-4">
|
||||||
<div className="col-span-2 basis-4/12">Key</div>
|
<div className="flex gap-4 text-xs px-3.5">
|
||||||
<div className="basis-3/12">Added</div>
|
<div className="col-span-2 basis-4/12">Key</div>
|
||||||
<div>Last Used</div>
|
<div className="basis-3/12">Added</div>
|
||||||
</div>
|
<div>Last Used</div>
|
||||||
{data?.map((sshKey) => (
|
|
||||||
<div
|
|
||||||
key={sshKey.sshKeyId}
|
|
||||||
className="flex gap-4 items-center border p-3.5 rounded-lg text-sm"
|
|
||||||
>
|
|
||||||
<div className="flex flex-col basis-4/12">
|
|
||||||
<span>{sshKey.name}</span>
|
|
||||||
{sshKey.description && (
|
|
||||||
<span className="text-xs text-muted-foreground">
|
|
||||||
{sshKey.description}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="basis-3/12">
|
|
||||||
{formatDistanceToNow(new Date(sshKey.createdAt), {
|
|
||||||
addSuffix: true,
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div className="grow">
|
|
||||||
{sshKey.lastUsedAt
|
|
||||||
? formatDistanceToNow(new Date(sshKey.lastUsedAt), {
|
|
||||||
addSuffix: true,
|
|
||||||
})
|
|
||||||
: "Never"}
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-row gap-1">
|
|
||||||
<UpdateSSHKey sshKeyId={sshKey.sshKeyId}>
|
|
||||||
<Button variant="ghost">
|
|
||||||
<PenBoxIcon className="size-4 text-muted-foreground" />
|
|
||||||
</Button>
|
|
||||||
</UpdateSSHKey>
|
|
||||||
<DeleteSSHKey sshKeyId={sshKey.sshKeyId} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
{data?.map((sshKey) => (
|
||||||
<div>
|
<div
|
||||||
<AddSSHKey>
|
key={sshKey.sshKeyId}
|
||||||
<Button>
|
className="flex gap-4 items-center border p-3.5 rounded-lg text-sm"
|
||||||
<KeyRoundIcon className="size-4" /> Add SSH Key
|
>
|
||||||
</Button>
|
<div className="flex flex-col basis-4/12">
|
||||||
</AddSSHKey>
|
<span>{sshKey.name}</span>
|
||||||
|
{sshKey.description && (
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
{sshKey.description}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="basis-3/12">
|
||||||
|
{formatDistanceToNow(new Date(sshKey.createdAt), {
|
||||||
|
addSuffix: true,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="grow">
|
||||||
|
{sshKey.lastUsedAt
|
||||||
|
? formatDistanceToNow(new Date(sshKey.lastUsedAt), {
|
||||||
|
addSuffix: true,
|
||||||
|
})
|
||||||
|
: "Never"}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row gap-1">
|
||||||
|
<UpdateSSHKey sshKeyId={sshKey.sshKeyId}>
|
||||||
|
<Button variant="ghost">
|
||||||
|
<PenBoxIcon className="size-4 text-muted-foreground" />
|
||||||
|
</Button>
|
||||||
|
</UpdateSSHKey>
|
||||||
|
<DeleteSSHKey sshKeyId={sshKey.sshKeyId} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<AddSSHKey>
|
||||||
|
<Button>
|
||||||
|
<KeyRoundIcon className="size-4" /> Add SSH Key
|
||||||
|
</Button>
|
||||||
|
</AddSSHKey>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { DeleteSSHKey } from "@/components/dashboard/settings/ssh-keys/delete-ssh-key";
|
||||||
import { AlertBlock } from "@/components/shared/alert-block";
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
@@ -135,7 +136,7 @@ export const UpdateSSHKey = ({ children, sshKeyId = "" }: Props) => {
|
|||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<DialogFooter className="flex w-full flex-row !justify-between pt-3">
|
<DialogFooter>
|
||||||
<Button isLoading={isLoading} type="submit">
|
<Button isLoading={isLoading} type="submit">
|
||||||
Update
|
Update
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
15
drizzle/0026_yielding_king_cobra.sql
Normal file
15
drizzle/0026_yielding_king_cobra.sql
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS "ssh-key" (
|
||||||
|
"sshKeyId" text PRIMARY KEY NOT NULL,
|
||||||
|
"publicKey" text NOT NULL,
|
||||||
|
"name" text NOT NULL,
|
||||||
|
"description" text,
|
||||||
|
"createdAt" text NOT NULL,
|
||||||
|
"lastUsedAt" text
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "application" RENAME COLUMN "customGitSSHKey" TO "customGitSSHKeyId";--> statement-breakpoint
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "application" ADD CONSTRAINT "application_customGitSSHKeyId_ssh-key_sshKeyId_fk" FOREIGN KEY ("customGitSSHKeyId") REFERENCES "public"."ssh-key"("sshKeyId") ON DELETE set null ON UPDATE no action;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN duplicate_object THEN null;
|
||||||
|
END $$;
|
||||||
2997
drizzle/meta/0026_snapshot.json
Normal file
2997
drizzle/meta/0026_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -183,6 +183,13 @@
|
|||||||
"when": 1721633853118,
|
"when": 1721633853118,
|
||||||
"tag": "0025_lying_mephisto",
|
"tag": "0025_lying_mephisto",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 26,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1721928706816,
|
||||||
|
"tag": "0026_yielding_king_cobra",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -235,6 +235,7 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
customGitBranch: input.customGitBranch,
|
customGitBranch: input.customGitBranch,
|
||||||
customGitBuildPath: input.customGitBuildPath,
|
customGitBuildPath: input.customGitBuildPath,
|
||||||
customGitUrl: input.customGitUrl,
|
customGitUrl: input.customGitUrl,
|
||||||
|
customGitSSHKeyId: input.customGitSSHKeyId,
|
||||||
sourceType: "git",
|
sourceType: "git",
|
||||||
applicationStatus: "idle",
|
applicationStatus: "idle",
|
||||||
});
|
});
|
||||||
@@ -249,9 +250,9 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
await generateSSHKey(application.appName);
|
await generateSSHKey(application.appName);
|
||||||
const file = await readRSAFile(application.appName);
|
const file = await readRSAFile(application.appName);
|
||||||
|
|
||||||
await updateApplication(input.applicationId, {
|
// await updateApplication(input.applicationId, {
|
||||||
customGitSSHKey: file,
|
// customGitSSHKey: file,
|
||||||
});
|
// });
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -261,9 +262,9 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
const application = await findApplicationById(input.applicationId);
|
const application = await findApplicationById(input.applicationId);
|
||||||
await removeRSAFiles(application.appName);
|
await removeRSAFiles(application.appName);
|
||||||
await updateApplication(input.applicationId, {
|
// await updateApplication(input.applicationId, {
|
||||||
customGitSSHKey: null,
|
// customGitSSHKey: null,
|
||||||
});
|
// });
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { redirects } from "./redirects";
|
|||||||
import { registry } from "./registry";
|
import { registry } from "./registry";
|
||||||
import { security } from "./security";
|
import { security } from "./security";
|
||||||
import { applicationStatus } from "./shared";
|
import { applicationStatus } from "./shared";
|
||||||
|
import { sshKeys } from "./ssh-key";
|
||||||
import { generateAppName } from "./utils";
|
import { generateAppName } from "./utils";
|
||||||
|
|
||||||
export const sourceType = pgEnum("sourceType", [
|
export const sourceType = pgEnum("sourceType", [
|
||||||
@@ -132,7 +133,12 @@ export const applications = pgTable("application", {
|
|||||||
customGitUrl: text("customGitUrl"),
|
customGitUrl: text("customGitUrl"),
|
||||||
customGitBranch: text("customGitBranch"),
|
customGitBranch: text("customGitBranch"),
|
||||||
customGitBuildPath: text("customGitBuildPath"),
|
customGitBuildPath: text("customGitBuildPath"),
|
||||||
customGitSSHKey: text("customGitSSHKey"),
|
customGitSSHKeyId: text("customGitSSHKeyId").references(
|
||||||
|
() => sshKeys.sshKeyId,
|
||||||
|
{
|
||||||
|
onDelete: "set null",
|
||||||
|
},
|
||||||
|
),
|
||||||
dockerfile: text("dockerfile"),
|
dockerfile: text("dockerfile"),
|
||||||
// Drop
|
// Drop
|
||||||
dropBuildPath: text("dropBuildPath"),
|
dropBuildPath: text("dropBuildPath"),
|
||||||
@@ -170,6 +176,10 @@ export const applicationsRelations = relations(
|
|||||||
references: [projects.projectId],
|
references: [projects.projectId],
|
||||||
}),
|
}),
|
||||||
deployments: many(deployments),
|
deployments: many(deployments),
|
||||||
|
customGitSSHKey: one(sshKeys, {
|
||||||
|
fields: [applications.customGitSSHKeyId],
|
||||||
|
references: [sshKeys.sshKeyId],
|
||||||
|
}),
|
||||||
domains: many(domains),
|
domains: many(domains),
|
||||||
mounts: many(mounts),
|
mounts: many(mounts),
|
||||||
redirects: many(redirects),
|
redirects: many(redirects),
|
||||||
@@ -289,7 +299,7 @@ const createSchema = createInsertSchema(applications, {
|
|||||||
dockerImage: z.string().optional(),
|
dockerImage: z.string().optional(),
|
||||||
username: z.string().optional(),
|
username: z.string().optional(),
|
||||||
password: z.string().optional(),
|
password: z.string().optional(),
|
||||||
customGitSSHKey: z.string().optional(),
|
customGitSSHKeyId: z.string().optional(),
|
||||||
repository: z.string().optional(),
|
repository: z.string().optional(),
|
||||||
dockerfile: z.string().optional(),
|
dockerfile: z.string().optional(),
|
||||||
branch: z.string().optional(),
|
branch: z.string().optional(),
|
||||||
@@ -371,7 +381,12 @@ export const apiSaveGitProvider = createSchema
|
|||||||
customGitBuildPath: true,
|
customGitBuildPath: true,
|
||||||
customGitUrl: true,
|
customGitUrl: true,
|
||||||
})
|
})
|
||||||
.required();
|
.required()
|
||||||
|
.merge(
|
||||||
|
createSchema.pick({
|
||||||
|
customGitSSHKeyId: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const apiSaveEnvironmentVariables = createSchema
|
export const apiSaveEnvironmentVariables = createSchema
|
||||||
.pick({
|
.pick({
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import { applications } from "@/server/db/schema/application";
|
||||||
import { sshKeyCreate } from "@/server/db/validations";
|
import { sshKeyCreate } from "@/server/db/validations";
|
||||||
|
import { relations } from "drizzle-orm";
|
||||||
import { pgTable, text, time } from "drizzle-orm/pg-core";
|
import { pgTable, text, time } from "drizzle-orm/pg-core";
|
||||||
import { createInsertSchema } from "drizzle-zod";
|
import { createInsertSchema } from "drizzle-zod";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
@@ -17,6 +19,10 @@ export const sshKeys = pgTable("ssh-key", {
|
|||||||
lastUsedAt: text("lastUsedAt"),
|
lastUsedAt: text("lastUsedAt"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const sshKeysRelations = relations(sshKeys, ({ many }) => ({
|
||||||
|
applications: many(applications),
|
||||||
|
}));
|
||||||
|
|
||||||
const createSchema = createInsertSchema(
|
const createSchema = createInsertSchema(
|
||||||
sshKeys,
|
sshKeys,
|
||||||
/* Private key is not stored in the DB */
|
/* Private key is not stored in the DB */
|
||||||
|
|||||||
Reference in New Issue
Block a user