mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Merge branch 'feat/add-gitea-repo' of https://github.com/jrparks/dokploy into feat/add-gitea-repo
This commit is contained in:
@@ -27,6 +27,7 @@ if (typeof window === "undefined") {
|
||||
const baseApp: ApplicationNested = {
|
||||
applicationId: "",
|
||||
herokuVersion: "",
|
||||
cleanCache: false,
|
||||
watchPaths: [],
|
||||
applicationStatus: "done",
|
||||
appName: "",
|
||||
|
||||
@@ -7,6 +7,7 @@ import { expect, test } from "vitest";
|
||||
const baseApp: ApplicationNested = {
|
||||
applicationId: "",
|
||||
herokuVersion: "",
|
||||
cleanCache: false,
|
||||
applicationStatus: "done",
|
||||
appName: "",
|
||||
autoDeploy: true,
|
||||
|
||||
@@ -40,7 +40,7 @@ interface Props {
|
||||
}
|
||||
|
||||
const AddRedirectchema = z.object({
|
||||
replicas: z.number(),
|
||||
replicas: z.number().min(1, "Replicas must be at least 1"),
|
||||
registryId: z.string(),
|
||||
});
|
||||
|
||||
@@ -130,9 +130,11 @@ export const ShowClusterSettings = ({ applicationId }: Props) => {
|
||||
placeholder="1"
|
||||
{...field}
|
||||
onChange={(e) => {
|
||||
field.onChange(Number(e.target.value));
|
||||
const value = e.target.value;
|
||||
field.onChange(value === "" ? 0 : Number(value));
|
||||
}}
|
||||
type="number"
|
||||
value={field.value || ""}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import { toast } from "sonner";
|
||||
import { domain } from "@/server/db/validations/domain";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Dices } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import type z from "zod";
|
||||
|
||||
type Domain = z.infer<typeof domain>;
|
||||
@@ -83,6 +84,13 @@ export const AddDomain = ({
|
||||
const { mutateAsync: generateDomain, isLoading: isLoadingGenerate } =
|
||||
api.domain.generateDomain.useMutation();
|
||||
|
||||
const { data: canGenerateTraefikMeDomains } =
|
||||
api.domain.canGenerateTraefikMeDomains.useQuery({
|
||||
serverId: application?.serverId || "",
|
||||
});
|
||||
|
||||
console.log("canGenerateTraefikMeDomains", canGenerateTraefikMeDomains);
|
||||
|
||||
const form = useForm<Domain>({
|
||||
resolver: zodResolver(domain),
|
||||
defaultValues: {
|
||||
@@ -186,6 +194,21 @@ export const AddDomain = ({
|
||||
name="host"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
{!canGenerateTraefikMeDomains &&
|
||||
field.value.includes("traefik.me") && (
|
||||
<AlertBlock type="warning">
|
||||
You need to set an IP address in your{" "}
|
||||
<Link
|
||||
href="/dashboard/settings/server"
|
||||
className="text-primary"
|
||||
>
|
||||
{application?.serverId
|
||||
? "Remote Servers -> Server -> Edit Server -> Update IP Address"
|
||||
: "Web Server -> Server -> Update Server IP"}
|
||||
</Link>{" "}
|
||||
to make your traefik.me domain work.
|
||||
</AlertBlock>
|
||||
)}
|
||||
<FormLabel>Host</FormLabel>
|
||||
<div className="flex gap-2">
|
||||
<FormControl>
|
||||
|
||||
@@ -16,8 +16,8 @@ import {
|
||||
Ban,
|
||||
CheckCircle2,
|
||||
Hammer,
|
||||
HelpCircle,
|
||||
RefreshCcw,
|
||||
Rocket,
|
||||
Terminal,
|
||||
} from "lucide-react";
|
||||
import { useRouter } from "next/router";
|
||||
@@ -55,7 +55,7 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
|
||||
<CardTitle className="text-xl">Deploy Settings</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-row gap-4 flex-wrap">
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<TooltipProvider delayDuration={0} disableHoverableContent={false}>
|
||||
<DialogAction
|
||||
title="Deploy Application"
|
||||
description="Are you sure you want to deploy this application?"
|
||||
@@ -79,12 +79,14 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
|
||||
<Button
|
||||
variant="default"
|
||||
isLoading={data?.applicationStatus === "running"}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Deploy
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Rocket className="size-4 mr-1" />
|
||||
Deploy
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -114,9 +116,24 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button variant="secondary" isLoading={isReloading}>
|
||||
Reload
|
||||
<RefreshCcw className="size-4" />
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={isReloading}
|
||||
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center">
|
||||
<RefreshCcw className="size-4 mr-1" />
|
||||
Reload
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
<p>Reload the application without rebuilding it</p>
|
||||
</TooltipContent>
|
||||
</TooltipPrimitive.Portal>
|
||||
</Tooltip>
|
||||
</Button>
|
||||
</DialogAction>
|
||||
<DialogAction
|
||||
@@ -139,13 +156,14 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={data?.applicationStatus === "running"}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Rebuild
|
||||
<Hammer className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Hammer className="size-4 mr-1" />
|
||||
Rebuild
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -180,13 +198,14 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={isStarting}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Start
|
||||
<CheckCircle2 className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<CheckCircle2 className="size-4 mr-1" />
|
||||
Start
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -219,13 +238,14 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
|
||||
<Button
|
||||
variant="destructive"
|
||||
isLoading={isStopping}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Stop
|
||||
<Ban className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Ban className="size-4 mr-1" />
|
||||
Stop
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -241,15 +261,18 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
|
||||
appName={data?.appName || ""}
|
||||
serverId={data?.serverId || ""}
|
||||
>
|
||||
<Button variant="outline">
|
||||
<Terminal />
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
<Terminal className="size-4 mr-1" />
|
||||
Open Terminal
|
||||
</Button>
|
||||
</DockerTerminalModal>
|
||||
<div className="flex flex-row items-center gap-2 rounded-md px-4 py-2 border">
|
||||
<span className="text-sm font-medium">Autodeploy</span>
|
||||
<Switch
|
||||
aria-label="Toggle italic"
|
||||
aria-label="Toggle autodeploy"
|
||||
checked={data?.autoDeploy || false}
|
||||
onCheckedChange={async (enabled) => {
|
||||
await update({
|
||||
@@ -264,14 +287,14 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
|
||||
toast.error("Error updating Auto Deploy");
|
||||
});
|
||||
}}
|
||||
className="flex flex-row gap-2 items-center"
|
||||
className="flex flex-row gap-2 items-center data-[state=checked]:bg-primary"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row items-center gap-2 rounded-md px-4 py-2 border">
|
||||
<span className="text-sm font-medium">Clean Cache</span>
|
||||
<Switch
|
||||
aria-label="Toggle italic"
|
||||
aria-label="Toggle clean cache"
|
||||
checked={data?.cleanCache || false}
|
||||
onCheckedChange={async (enabled) => {
|
||||
await update({
|
||||
@@ -286,7 +309,7 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
|
||||
toast.error("Error updating Clean Cache");
|
||||
});
|
||||
}}
|
||||
className="flex flex-row gap-2 items-center"
|
||||
className="flex flex-row gap-2 items-center data-[state=checked]:bg-primary"
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
import { domainCompose } from "@/server/db/validations/domain";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { DatabaseZap, Dices, RefreshCw } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import type z from "zod";
|
||||
|
||||
type Domain = z.infer<typeof domainCompose>;
|
||||
@@ -102,6 +103,11 @@ export const AddDomainCompose = ({
|
||||
? api.domain.update.useMutation()
|
||||
: api.domain.create.useMutation();
|
||||
|
||||
const { data: canGenerateTraefikMeDomains } =
|
||||
api.domain.canGenerateTraefikMeDomains.useQuery({
|
||||
serverId: compose?.serverId || "",
|
||||
});
|
||||
|
||||
const form = useForm<Domain>({
|
||||
resolver: zodResolver(domainCompose),
|
||||
defaultValues: {
|
||||
@@ -313,6 +319,21 @@ export const AddDomainCompose = ({
|
||||
name="host"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
{!canGenerateTraefikMeDomains &&
|
||||
field.value.includes("traefik.me") && (
|
||||
<AlertBlock type="warning">
|
||||
You need to set an IP address in your{" "}
|
||||
<Link
|
||||
href="/dashboard/settings/server"
|
||||
className="text-primary"
|
||||
>
|
||||
{compose?.serverId
|
||||
? "Remote Servers -> Server -> Edit Server -> Update IP Address"
|
||||
: "Web Server -> Server -> Update Server IP"}
|
||||
</Link>{" "}
|
||||
to make your traefik.me domain work.
|
||||
</AlertBlock>
|
||||
)}
|
||||
<FormLabel>Host</FormLabel>
|
||||
<div className="flex gap-2">
|
||||
<FormControl>
|
||||
|
||||
@@ -8,11 +8,11 @@ import {
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { api } from "@/utils/api";
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||
import { Ban, CheckCircle2, Hammer, HelpCircle, Terminal } from "lucide-react";
|
||||
import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
|
||||
import { useRouter } from "next/router";
|
||||
import { toast } from "sonner";
|
||||
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||
|
||||
interface Props {
|
||||
composeId: string;
|
||||
@@ -34,7 +34,7 @@ export const ComposeActions = ({ composeId }: Props) => {
|
||||
api.compose.stop.useMutation();
|
||||
return (
|
||||
<div className="flex flex-row gap-4 w-full flex-wrap ">
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<TooltipProvider delayDuration={0} disableHoverableContent={false}>
|
||||
<DialogAction
|
||||
title="Deploy Compose"
|
||||
description="Are you sure you want to deploy this compose?"
|
||||
@@ -58,12 +58,14 @@ export const ComposeActions = ({ composeId }: Props) => {
|
||||
<Button
|
||||
variant="default"
|
||||
isLoading={data?.composeStatus === "running"}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Deploy
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Rocket className="size-4 mr-1" />
|
||||
Deploy
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -74,36 +76,37 @@ export const ComposeActions = ({ composeId }: Props) => {
|
||||
</Button>
|
||||
</DialogAction>
|
||||
<DialogAction
|
||||
title="Rebuild Compose"
|
||||
description="Are you sure you want to rebuild this compose?"
|
||||
title="Reload Compose"
|
||||
description="Are you sure you want to reload this compose?"
|
||||
type="default"
|
||||
onClick={async () => {
|
||||
await redeploy({
|
||||
composeId: composeId,
|
||||
})
|
||||
.then(() => {
|
||||
toast.success("Compose rebuilt successfully");
|
||||
toast.success("Compose reloaded successfully");
|
||||
refetch();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error rebuilding compose");
|
||||
toast.error("Error reloading compose");
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={data?.composeStatus === "running"}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Rebuild
|
||||
<Hammer className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<RefreshCcw className="size-4 mr-1" />
|
||||
Reload
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
<p>Only rebuilds the compose without downloading new code</p>
|
||||
<p>Reload the compose without rebuilding it</p>
|
||||
</TooltipContent>
|
||||
</TooltipPrimitive.Portal>
|
||||
</Tooltip>
|
||||
@@ -131,13 +134,14 @@ export const ComposeActions = ({ composeId }: Props) => {
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={isStarting}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Start
|
||||
<CheckCircle2 className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<CheckCircle2 className="size-4 mr-1" />
|
||||
Start
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -169,13 +173,14 @@ export const ComposeActions = ({ composeId }: Props) => {
|
||||
<Button
|
||||
variant="destructive"
|
||||
isLoading={isStopping}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Stop
|
||||
<Ban className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Ban className="size-4 mr-1" />
|
||||
Stop
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -191,15 +196,18 @@ export const ComposeActions = ({ composeId }: Props) => {
|
||||
appName={data?.appName || ""}
|
||||
serverId={data?.serverId || ""}
|
||||
>
|
||||
<Button variant="outline">
|
||||
<Terminal />
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
<Terminal className="size-4 mr-1" />
|
||||
Open Terminal
|
||||
</Button>
|
||||
</DockerTerminalModal>
|
||||
<div className="flex flex-row items-center gap-2 rounded-md px-4 py-2 border">
|
||||
<span className="text-sm font-medium">Autodeploy</span>
|
||||
<Switch
|
||||
aria-label="Toggle italic"
|
||||
aria-label="Toggle autodeploy"
|
||||
checked={data?.autoDeploy || false}
|
||||
onCheckedChange={async (enabled) => {
|
||||
await update({
|
||||
@@ -214,7 +222,7 @@ export const ComposeActions = ({ composeId }: Props) => {
|
||||
toast.error("Error updating Auto Deploy");
|
||||
});
|
||||
}}
|
||||
className="flex flex-row gap-2 items-center"
|
||||
className="flex flex-row gap-2 items-center data-[state=checked]:bg-primary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
import { DrawerLogs } from "@/components/shared/drawer-logs";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
} from "@/components/ui/command";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
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 { cn } from "@/lib/utils";
|
||||
import { api } from "@/utils/api";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import copy from "copy-to-clipboard";
|
||||
import { debounce } from "lodash";
|
||||
import { CheckIcon, ChevronsUpDown, Copy, RotateCcw } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
import type { ServiceType } from "../../application/advanced/show-resources";
|
||||
import { type LogLine, parseLogs } from "../../docker/logs/utils";
|
||||
|
||||
interface Props {
|
||||
databaseId: string;
|
||||
databaseType: Exclude<ServiceType, "application" | "redis">;
|
||||
serverId: string | null;
|
||||
}
|
||||
|
||||
const RestoreBackupSchema = z.object({
|
||||
destinationId: z
|
||||
.string({
|
||||
required_error: "Please select a destination",
|
||||
})
|
||||
.min(1, {
|
||||
message: "Destination is required",
|
||||
}),
|
||||
backupFile: z
|
||||
.string({
|
||||
required_error: "Please select a backup file",
|
||||
})
|
||||
.min(1, {
|
||||
message: "Backup file is required",
|
||||
}),
|
||||
databaseName: z
|
||||
.string({
|
||||
required_error: "Please enter a database name",
|
||||
})
|
||||
.min(1, {
|
||||
message: "Database name is required",
|
||||
}),
|
||||
});
|
||||
|
||||
type RestoreBackup = z.infer<typeof RestoreBackupSchema>;
|
||||
|
||||
export const RestoreBackup = ({
|
||||
databaseId,
|
||||
databaseType,
|
||||
serverId,
|
||||
}: Props) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
const { data: destinations = [] } = api.destination.all.useQuery();
|
||||
|
||||
const form = useForm<RestoreBackup>({
|
||||
defaultValues: {
|
||||
destinationId: "",
|
||||
backupFile: "",
|
||||
databaseName: "",
|
||||
},
|
||||
resolver: zodResolver(RestoreBackupSchema),
|
||||
});
|
||||
|
||||
const destionationId = form.watch("destinationId");
|
||||
|
||||
const debouncedSetSearch = debounce((value: string) => {
|
||||
setSearch(value);
|
||||
}, 300);
|
||||
|
||||
const { data: files = [], isLoading } = api.backup.listBackupFiles.useQuery(
|
||||
{
|
||||
destinationId: destionationId,
|
||||
search,
|
||||
serverId: serverId ?? "",
|
||||
},
|
||||
{
|
||||
enabled: isOpen && !!destionationId,
|
||||
},
|
||||
);
|
||||
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]);
|
||||
const [isDeploying, setIsDeploying] = useState(false);
|
||||
|
||||
// const { mutateAsync: restore, isLoading: isRestoring } =
|
||||
// api.backup.restoreBackup.useMutation();
|
||||
|
||||
api.backup.restoreBackupWithLogs.useSubscription(
|
||||
{
|
||||
databaseId,
|
||||
databaseType,
|
||||
databaseName: form.watch("databaseName"),
|
||||
backupFile: form.watch("backupFile"),
|
||||
destinationId: form.watch("destinationId"),
|
||||
},
|
||||
{
|
||||
enabled: isDeploying,
|
||||
onData(log) {
|
||||
if (!isDrawerOpen) {
|
||||
setIsDrawerOpen(true);
|
||||
}
|
||||
|
||||
if (log === "Restore completed successfully!") {
|
||||
setIsDeploying(false);
|
||||
}
|
||||
const parsedLogs = parseLogs(log);
|
||||
setFilteredLogs((prev) => [...prev, ...parsedLogs]);
|
||||
},
|
||||
onError(error) {
|
||||
console.error("Restore logs error:", error);
|
||||
setIsDeploying(false);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const onSubmit = async (_data: RestoreBackup) => {
|
||||
setIsDeploying(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline">
|
||||
<RotateCcw className="mr-2 size-4" />
|
||||
Restore Backup
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center">
|
||||
<RotateCcw className="mr-2 size-4" />
|
||||
Restore Backup
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Select a destination and search for backup files
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<Form {...form}>
|
||||
<form
|
||||
id="hook-form-restore-backup"
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="grid w-full gap-4"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="destinationId"
|
||||
render={({ field }) => (
|
||||
<FormItem className="">
|
||||
<FormLabel>Destination</FormLabel>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<FormControl>
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
{field.value
|
||||
? destinations.find(
|
||||
(d) => d.destinationId === field.value,
|
||||
)?.name
|
||||
: "Select Destination"}
|
||||
<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 destinations..."
|
||||
className="h-9"
|
||||
/>
|
||||
<CommandEmpty>No destinations found.</CommandEmpty>
|
||||
<ScrollArea className="h-64">
|
||||
<CommandGroup>
|
||||
{destinations.map((destination) => (
|
||||
<CommandItem
|
||||
value={destination.destinationId}
|
||||
key={destination.destinationId}
|
||||
onSelect={() => {
|
||||
form.setValue(
|
||||
"destinationId",
|
||||
destination.destinationId,
|
||||
);
|
||||
}}
|
||||
>
|
||||
{destination.name}
|
||||
<CheckIcon
|
||||
className={cn(
|
||||
"ml-auto h-4 w-4",
|
||||
destination.destinationId === field.value
|
||||
? "opacity-100"
|
||||
: "opacity-0",
|
||||
)}
|
||||
/>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</ScrollArea>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="backupFile"
|
||||
render={({ field }) => (
|
||||
<FormItem className="">
|
||||
<FormLabel className="flex items-center justify-between">
|
||||
Search Backup Files
|
||||
{field.value && (
|
||||
<Badge variant="outline">
|
||||
{field.value}
|
||||
<Copy
|
||||
className="ml-2 size-4 cursor-pointer"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
copy(field.value);
|
||||
toast.success("Backup file copied to clipboard");
|
||||
}}
|
||||
/>
|
||||
</Badge>
|
||||
)}
|
||||
</FormLabel>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<FormControl>
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-full justify-between !bg-input",
|
||||
!field.value && "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
{field.value || "Search and select a backup file"}
|
||||
<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 backup files..."
|
||||
onValueChange={debouncedSetSearch}
|
||||
className="h-9"
|
||||
/>
|
||||
{isLoading ? (
|
||||
<div className="py-6 text-center text-sm">
|
||||
Loading backup files...
|
||||
</div>
|
||||
) : files.length === 0 && search ? (
|
||||
<div className="py-6 text-center text-sm text-muted-foreground">
|
||||
No backup files found for "{search}"
|
||||
</div>
|
||||
) : files.length === 0 ? (
|
||||
<div className="py-6 text-center text-sm text-muted-foreground">
|
||||
No backup files available
|
||||
</div>
|
||||
) : (
|
||||
<ScrollArea className="h-64">
|
||||
<CommandGroup>
|
||||
{files.map((file) => (
|
||||
<CommandItem
|
||||
value={file}
|
||||
key={file}
|
||||
onSelect={() => {
|
||||
form.setValue("backupFile", file);
|
||||
}}
|
||||
>
|
||||
<div className="flex w-full justify-between">
|
||||
<span>{file}</span>
|
||||
</div>
|
||||
<CheckIcon
|
||||
className={cn(
|
||||
"ml-auto h-4 w-4",
|
||||
file === field.value
|
||||
? "opacity-100"
|
||||
: "opacity-0",
|
||||
)}
|
||||
/>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</ScrollArea>
|
||||
)}
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="databaseName"
|
||||
render={({ field }) => (
|
||||
<FormItem className="">
|
||||
<FormLabel>Database Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="Enter database name" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
isLoading={isDeploying}
|
||||
form="hook-form-restore-backup"
|
||||
type="submit"
|
||||
disabled={!form.watch("backupFile")}
|
||||
>
|
||||
Restore
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
<DrawerLogs
|
||||
isOpen={isDrawerOpen}
|
||||
onClose={() => {
|
||||
setIsDrawerOpen(false);
|
||||
setFilteredLogs([]);
|
||||
setIsDeploying(false);
|
||||
// refetch();
|
||||
}}
|
||||
filteredLogs={filteredLogs}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -20,6 +20,7 @@ import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import type { ServiceType } from "../../application/advanced/show-resources";
|
||||
import { AddBackup } from "./add-backup";
|
||||
import { RestoreBackup } from "./restore-backup";
|
||||
import { UpdateBackup } from "./update-backup";
|
||||
|
||||
interface Props {
|
||||
@@ -71,7 +72,14 @@ export const ShowBackups = ({ id, type }: Props) => {
|
||||
</div>
|
||||
|
||||
{postgres && postgres?.backups?.length > 0 && (
|
||||
<AddBackup databaseId={id} databaseType={type} refetch={refetch} />
|
||||
<div className="flex flex-col lg:flex-row gap-4 w-full lg:w-auto">
|
||||
<AddBackup databaseId={id} databaseType={type} refetch={refetch} />
|
||||
<RestoreBackup
|
||||
databaseId={id}
|
||||
databaseType={type}
|
||||
serverId={postgres.serverId}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-4">
|
||||
@@ -98,11 +106,18 @@ export const ShowBackups = ({ id, type }: Props) => {
|
||||
<span className="text-base text-muted-foreground">
|
||||
No backups configured
|
||||
</span>
|
||||
<AddBackup
|
||||
databaseId={id}
|
||||
databaseType={type}
|
||||
refetch={refetch}
|
||||
/>
|
||||
<div className="flex flex-col sm:flex-row gap-4 w-full sm:w-auto">
|
||||
<AddBackup
|
||||
databaseId={id}
|
||||
databaseType={type}
|
||||
refetch={refetch}
|
||||
/>
|
||||
<RestoreBackup
|
||||
databaseId={id}
|
||||
databaseType={type}
|
||||
serverId={postgres.serverId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col pt-2">
|
||||
@@ -183,6 +198,7 @@ export const ShowBackups = ({ id, type }: Props) => {
|
||||
<TooltipContent>Run Manual Backup</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<UpdateBackup
|
||||
backupId={backup.backupId}
|
||||
refetch={refetch}
|
||||
|
||||
@@ -119,7 +119,6 @@ export const DockerLogsId: React.FC<Props> = ({
|
||||
const wsUrl = `${protocol}//${
|
||||
window.location.host
|
||||
}/docker-container-logs?${params.toString()}`;
|
||||
console.log("Connecting to WebSocket:", wsUrl);
|
||||
const ws = new WebSocket(wsUrl);
|
||||
|
||||
const resetNoDataTimeout = () => {
|
||||
@@ -136,7 +135,6 @@ export const DockerLogsId: React.FC<Props> = ({
|
||||
ws.close();
|
||||
return;
|
||||
}
|
||||
console.log("WebSocket connected");
|
||||
resetNoDataTimeout();
|
||||
};
|
||||
|
||||
|
||||
@@ -111,7 +111,10 @@ export const ShowExternalMariadbCredentials = ({ mariadbId }: Props) => {
|
||||
{!getIp && (
|
||||
<AlertBlock type="warning">
|
||||
You need to set an IP address in your{" "}
|
||||
<Link href="/dashboard/settings" className="text-primary">
|
||||
<Link
|
||||
href="/dashboard/settings/server"
|
||||
className="text-primary"
|
||||
>
|
||||
{data?.serverId
|
||||
? "Remote Servers -> Server -> Edit Server -> Update IP Address"
|
||||
: "Web Server -> Server -> Update Server IP"}
|
||||
|
||||
@@ -10,13 +10,7 @@ import {
|
||||
} from "@/components/ui/tooltip";
|
||||
import { api } from "@/utils/api";
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||
import {
|
||||
Ban,
|
||||
CheckCircle2,
|
||||
HelpCircle,
|
||||
RefreshCcw,
|
||||
Terminal,
|
||||
} from "lucide-react";
|
||||
import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { type LogLine, parseLogs } from "../../docker/logs/utils";
|
||||
@@ -92,12 +86,14 @@ export const ShowGeneralMariadb = ({ mariadbId }: Props) => {
|
||||
<Button
|
||||
variant="default"
|
||||
isLoading={data?.applicationStatus === "running"}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Deploy
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Rocket className="size-4 mr-1" />
|
||||
Deploy
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -107,6 +103,8 @@ export const ShowGeneralMariadb = ({ mariadbId }: Props) => {
|
||||
</Tooltip>
|
||||
</Button>
|
||||
</DialogAction>
|
||||
</TooltipProvider>
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<DialogAction
|
||||
title="Reload Mariadb"
|
||||
description="Are you sure you want to reload this mariadb?"
|
||||
@@ -128,13 +126,14 @@ export const ShowGeneralMariadb = ({ mariadbId }: Props) => {
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={isReloading}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Reload
|
||||
<RefreshCcw className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<RefreshCcw className="size-4 mr-1" />
|
||||
Reload
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -144,7 +143,9 @@ export const ShowGeneralMariadb = ({ mariadbId }: Props) => {
|
||||
</Tooltip>
|
||||
</Button>
|
||||
</DialogAction>
|
||||
{data?.applicationStatus === "idle" ? (
|
||||
</TooltipProvider>
|
||||
{data?.applicationStatus === "idle" ? (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<DialogAction
|
||||
title="Start Mariadb"
|
||||
description="Are you sure you want to start this mariadb?"
|
||||
@@ -165,13 +166,14 @@ export const ShowGeneralMariadb = ({ mariadbId }: Props) => {
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={isStarting}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Start
|
||||
<CheckCircle2 className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<CheckCircle2 className="size-4 mr-1" />
|
||||
Start
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -184,7 +186,9 @@ export const ShowGeneralMariadb = ({ mariadbId }: Props) => {
|
||||
</Tooltip>
|
||||
</Button>
|
||||
</DialogAction>
|
||||
) : (
|
||||
</TooltipProvider>
|
||||
) : (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<DialogAction
|
||||
title="Stop Mariadb"
|
||||
description="Are you sure you want to stop this mariadb?"
|
||||
@@ -204,13 +208,14 @@ export const ShowGeneralMariadb = ({ mariadbId }: Props) => {
|
||||
<Button
|
||||
variant="destructive"
|
||||
isLoading={isStopping}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Stop
|
||||
<Ban className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Ban className="size-4 mr-1" />
|
||||
Stop
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -220,15 +225,29 @@ export const ShowGeneralMariadb = ({ mariadbId }: Props) => {
|
||||
</Tooltip>
|
||||
</Button>
|
||||
</DialogAction>
|
||||
)}
|
||||
</TooltipProvider>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
<DockerTerminalModal
|
||||
appName={data?.appName || ""}
|
||||
serverId={data?.serverId || ""}
|
||||
>
|
||||
<Button variant="outline">
|
||||
<Terminal />
|
||||
Open Terminal
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center">
|
||||
<Terminal className="size-4 mr-1" />
|
||||
Open Terminal
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
<p>Open a terminal to the MariaDB container</p>
|
||||
</TooltipContent>
|
||||
</TooltipPrimitive.Portal>
|
||||
</Tooltip>
|
||||
</Button>
|
||||
</DockerTerminalModal>
|
||||
</CardContent>
|
||||
|
||||
@@ -111,7 +111,10 @@ export const ShowExternalMongoCredentials = ({ mongoId }: Props) => {
|
||||
{!getIp && (
|
||||
<AlertBlock type="warning">
|
||||
You need to set an IP address in your{" "}
|
||||
<Link href="/dashboard/settings" className="text-primary">
|
||||
<Link
|
||||
href="/dashboard/settings/server"
|
||||
className="text-primary"
|
||||
>
|
||||
{data?.serverId
|
||||
? "Remote Servers -> Server -> Edit Server -> Update IP Address"
|
||||
: "Web Server -> Server -> Update Server IP"}
|
||||
|
||||
@@ -10,13 +10,7 @@ import {
|
||||
} from "@/components/ui/tooltip";
|
||||
import { api } from "@/utils/api";
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||
import {
|
||||
Ban,
|
||||
CheckCircle2,
|
||||
HelpCircle,
|
||||
RefreshCcw,
|
||||
Terminal,
|
||||
} from "lucide-react";
|
||||
import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { type LogLine, parseLogs } from "../../docker/logs/utils";
|
||||
@@ -91,12 +85,14 @@ export const ShowGeneralMongo = ({ mongoId }: Props) => {
|
||||
<Button
|
||||
variant="default"
|
||||
isLoading={data?.applicationStatus === "running"}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Deploy
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Rocket className="size-4 mr-1" />
|
||||
Deploy
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -127,13 +123,14 @@ export const ShowGeneralMongo = ({ mongoId }: Props) => {
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={isReloading}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Reload
|
||||
<RefreshCcw className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<RefreshCcw className="size-4 mr-1" />
|
||||
Reload
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -164,13 +161,14 @@ export const ShowGeneralMongo = ({ mongoId }: Props) => {
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={isStarting}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Start
|
||||
<CheckCircle2 className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<CheckCircle2 className="size-4 mr-1" />
|
||||
Start
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -203,13 +201,14 @@ export const ShowGeneralMongo = ({ mongoId }: Props) => {
|
||||
<Button
|
||||
variant="destructive"
|
||||
isLoading={isStopping}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Stop
|
||||
<Ban className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Ban className="size-4 mr-1" />
|
||||
Stop
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -225,9 +224,23 @@ export const ShowGeneralMongo = ({ mongoId }: Props) => {
|
||||
appName={data?.appName || ""}
|
||||
serverId={data?.serverId || ""}
|
||||
>
|
||||
<Button variant="outline">
|
||||
<Terminal />
|
||||
Open Terminal
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center">
|
||||
<Terminal className="size-4 mr-1" />
|
||||
Open Terminal
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
<p>Open a terminal to the MongoDB container</p>
|
||||
</TooltipContent>
|
||||
</TooltipPrimitive.Portal>
|
||||
</Tooltip>
|
||||
</Button>
|
||||
</DockerTerminalModal>
|
||||
</CardContent>
|
||||
|
||||
@@ -111,7 +111,10 @@ export const ShowExternalMysqlCredentials = ({ mysqlId }: Props) => {
|
||||
{!getIp && (
|
||||
<AlertBlock type="warning">
|
||||
You need to set an IP address in your{" "}
|
||||
<Link href="/dashboard/settings" className="text-primary">
|
||||
<Link
|
||||
href="/dashboard/settings/server"
|
||||
className="text-primary"
|
||||
>
|
||||
{data?.serverId
|
||||
? "Remote Servers -> Server -> Edit Server -> Update IP Address"
|
||||
: "Web Server -> Server -> Update Server IP"}
|
||||
|
||||
@@ -10,13 +10,7 @@ import {
|
||||
} from "@/components/ui/tooltip";
|
||||
import { api } from "@/utils/api";
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||
import {
|
||||
Ban,
|
||||
CheckCircle2,
|
||||
HelpCircle,
|
||||
RefreshCcw,
|
||||
Terminal,
|
||||
} from "lucide-react";
|
||||
import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { type LogLine, parseLogs } from "../../docker/logs/utils";
|
||||
@@ -77,7 +71,7 @@ export const ShowGeneralMysql = ({ mysqlId }: Props) => {
|
||||
<CardContent className="flex flex-row gap-4 flex-wrap">
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<DialogAction
|
||||
title="Deploy Mysql"
|
||||
title="Deploy MySQL"
|
||||
description="Are you sure you want to deploy this mysql?"
|
||||
type="default"
|
||||
onClick={async () => {
|
||||
@@ -89,12 +83,14 @@ export const ShowGeneralMysql = ({ mysqlId }: Props) => {
|
||||
<Button
|
||||
variant="default"
|
||||
isLoading={data?.applicationStatus === "running"}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Deploy
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Rocket className="size-4 mr-1" />
|
||||
Deploy
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -105,7 +101,7 @@ export const ShowGeneralMysql = ({ mysqlId }: Props) => {
|
||||
</Button>
|
||||
</DialogAction>
|
||||
<DialogAction
|
||||
title="Reload Mysql"
|
||||
title="Reload MySQL"
|
||||
description="Are you sure you want to reload this mysql?"
|
||||
type="default"
|
||||
onClick={async () => {
|
||||
@@ -114,24 +110,25 @@ export const ShowGeneralMysql = ({ mysqlId }: Props) => {
|
||||
appName: data?.appName || "",
|
||||
})
|
||||
.then(() => {
|
||||
toast.success("Mysql reloaded successfully");
|
||||
toast.success("MySQL reloaded successfully");
|
||||
refetch();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error reloading Mysql");
|
||||
toast.error("Error reloading MySQL");
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={isReloading}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Reload
|
||||
<RefreshCcw className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<RefreshCcw className="size-4 mr-1" />
|
||||
Reload
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -143,7 +140,7 @@ export const ShowGeneralMysql = ({ mysqlId }: Props) => {
|
||||
</DialogAction>
|
||||
{data?.applicationStatus === "idle" ? (
|
||||
<DialogAction
|
||||
title="Start Mysql"
|
||||
title="Start MySQL"
|
||||
description="Are you sure you want to start this mysql?"
|
||||
type="default"
|
||||
onClick={async () => {
|
||||
@@ -151,24 +148,25 @@ export const ShowGeneralMysql = ({ mysqlId }: Props) => {
|
||||
mysqlId: mysqlId,
|
||||
})
|
||||
.then(() => {
|
||||
toast.success("Mysql started successfully");
|
||||
toast.success("MySQL started successfully");
|
||||
refetch();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error starting Mysql");
|
||||
toast.error("Error starting MySQL");
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={isStarting}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Start
|
||||
<CheckCircle2 className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<CheckCircle2 className="size-4 mr-1" />
|
||||
Start
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -183,31 +181,32 @@ export const ShowGeneralMysql = ({ mysqlId }: Props) => {
|
||||
</DialogAction>
|
||||
) : (
|
||||
<DialogAction
|
||||
title="Stop Mysql"
|
||||
title="Stop MySQL"
|
||||
description="Are you sure you want to stop this mysql?"
|
||||
onClick={async () => {
|
||||
await stop({
|
||||
mysqlId: mysqlId,
|
||||
})
|
||||
.then(() => {
|
||||
toast.success("Mysql stopped successfully");
|
||||
toast.success("MySQL stopped successfully");
|
||||
refetch();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error stopping Mysql");
|
||||
toast.error("Error stopping MySQL");
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="destructive"
|
||||
isLoading={isStopping}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Stop
|
||||
<Ban className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Ban className="size-4 mr-1" />
|
||||
Stop
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -223,9 +222,23 @@ export const ShowGeneralMysql = ({ mysqlId }: Props) => {
|
||||
appName={data?.appName || ""}
|
||||
serverId={data?.serverId || ""}
|
||||
>
|
||||
<Button variant="outline">
|
||||
<Terminal />
|
||||
Open Terminal
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center">
|
||||
<Terminal className="size-4 mr-1" />
|
||||
Open Terminal
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
<p>Open a terminal to the MySQL container</p>
|
||||
</TooltipContent>
|
||||
</TooltipPrimitive.Portal>
|
||||
</Tooltip>
|
||||
</Button>
|
||||
</DockerTerminalModal>
|
||||
</CardContent>
|
||||
|
||||
@@ -113,7 +113,10 @@ export const ShowExternalPostgresCredentials = ({ postgresId }: Props) => {
|
||||
{!getIp && (
|
||||
<AlertBlock type="warning">
|
||||
You need to set an IP address in your{" "}
|
||||
<Link href="/dashboard/settings" className="text-primary">
|
||||
<Link
|
||||
href="/dashboard/settings/server"
|
||||
className="text-primary"
|
||||
>
|
||||
{data?.serverId
|
||||
? "Remote Servers -> Server -> Edit Server -> Update IP Address"
|
||||
: "Web Server -> Server -> Update Server IP"}
|
||||
|
||||
@@ -10,13 +10,7 @@ import {
|
||||
} from "@/components/ui/tooltip";
|
||||
import { api } from "@/utils/api";
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||
import {
|
||||
Ban,
|
||||
CheckCircle2,
|
||||
HelpCircle,
|
||||
RefreshCcw,
|
||||
Terminal,
|
||||
} from "lucide-react";
|
||||
import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { type LogLine, parseLogs } from "../../docker/logs/utils";
|
||||
@@ -78,9 +72,9 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
|
||||
<CardTitle className="text-xl">Deploy Settings</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-row gap-4 flex-wrap">
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<TooltipProvider disableHoverableContent={false}>
|
||||
<DialogAction
|
||||
title="Deploy Postgres"
|
||||
title="Deploy PostgreSQL"
|
||||
description="Are you sure you want to deploy this postgres?"
|
||||
type="default"
|
||||
onClick={async () => {
|
||||
@@ -92,12 +86,14 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
|
||||
<Button
|
||||
variant="default"
|
||||
isLoading={data?.applicationStatus === "running"}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Deploy
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Rocket className="size-4 mr-1" />
|
||||
Deploy
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -108,7 +104,7 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
|
||||
</Button>
|
||||
</DialogAction>
|
||||
<DialogAction
|
||||
title="Reload Postgres"
|
||||
title="Reload PostgreSQL"
|
||||
description="Are you sure you want to reload this postgres?"
|
||||
type="default"
|
||||
onClick={async () => {
|
||||
@@ -117,24 +113,25 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
|
||||
appName: data?.appName || "",
|
||||
})
|
||||
.then(() => {
|
||||
toast.success("Postgres reloaded successfully");
|
||||
toast.success("PostgreSQL reloaded successfully");
|
||||
refetch();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error reloading Postgres");
|
||||
toast.error("Error reloading PostgreSQL");
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={isReloading}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Reload
|
||||
<RefreshCcw className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<RefreshCcw className="size-4 mr-1" />
|
||||
Reload
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -146,7 +143,7 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
|
||||
</DialogAction>
|
||||
{data?.applicationStatus === "idle" ? (
|
||||
<DialogAction
|
||||
title="Start Postgres"
|
||||
title="Start PostgreSQL"
|
||||
description="Are you sure you want to start this postgres?"
|
||||
type="default"
|
||||
onClick={async () => {
|
||||
@@ -154,24 +151,25 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
|
||||
postgresId: postgresId,
|
||||
})
|
||||
.then(() => {
|
||||
toast.success("Postgres started successfully");
|
||||
toast.success("PostgreSQL started successfully");
|
||||
refetch();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error starting Postgres");
|
||||
toast.error("Error starting PostgreSQL");
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={isStarting}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Start
|
||||
<CheckCircle2 className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<CheckCircle2 className="size-4 mr-1" />
|
||||
Start
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -186,31 +184,32 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
|
||||
</DialogAction>
|
||||
) : (
|
||||
<DialogAction
|
||||
title="Stop Postgres"
|
||||
title="Stop PostgreSQL"
|
||||
description="Are you sure you want to stop this postgres?"
|
||||
onClick={async () => {
|
||||
await stop({
|
||||
postgresId: postgresId,
|
||||
})
|
||||
.then(() => {
|
||||
toast.success("Postgres stopped successfully");
|
||||
toast.success("PostgreSQL stopped successfully");
|
||||
refetch();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error stopping Postgres");
|
||||
toast.error("Error stopping PostgreSQL");
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="destructive"
|
||||
isLoading={isStopping}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Stop
|
||||
<Ban className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Ban className="size-4 mr-1" />
|
||||
Stop
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -226,9 +225,23 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
|
||||
appName={data?.appName || ""}
|
||||
serverId={data?.serverId || ""}
|
||||
>
|
||||
<Button variant="outline">
|
||||
<Terminal />
|
||||
Open Terminal
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center">
|
||||
<Terminal className="size-4 mr-1" />
|
||||
Open Terminal
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
<p>Open a terminal to the PostgreSQL container</p>
|
||||
</TooltipContent>
|
||||
</TooltipPrimitive.Portal>
|
||||
</Tooltip>
|
||||
</Button>
|
||||
</DockerTerminalModal>
|
||||
</CardContent>
|
||||
|
||||
@@ -21,7 +21,7 @@ import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { api } from "@/utils/api";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { PenBoxIcon } from "lucide-react";
|
||||
import { PenBox } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
@@ -94,9 +94,9 @@ export const UpdatePostgres = ({ postgresId }: Props) => {
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="group hover:bg-blue-500/10 "
|
||||
className="group hover:bg-blue-500/10 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
<PenBoxIcon className="size-3.5 text-primary group-hover:text-blue-500" />
|
||||
<PenBox className="size-3.5 text-primary group-hover:text-blue-500" />
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">
|
||||
@@ -151,6 +151,7 @@ export const UpdatePostgres = ({ postgresId }: Props) => {
|
||||
isLoading={isLoading}
|
||||
form="hook-form-update-postgres"
|
||||
type="submit"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
|
||||
@@ -22,7 +22,7 @@ const examples = [
|
||||
"Sendgrid service opensource analogue",
|
||||
];
|
||||
|
||||
export const StepOne = ({ nextStep, setTemplateInfo, templateInfo }: any) => {
|
||||
export const StepOne = ({ setTemplateInfo, templateInfo }: any) => {
|
||||
// Get servers from the API
|
||||
const { data: servers } = api.server.withSSHKey.useQuery();
|
||||
|
||||
|
||||
@@ -105,7 +105,10 @@ export const ShowExternalRedisCredentials = ({ redisId }: Props) => {
|
||||
{!getIp && (
|
||||
<AlertBlock type="warning">
|
||||
You need to set an IP address in your{" "}
|
||||
<Link href="/dashboard/settings" className="text-primary">
|
||||
<Link
|
||||
href="/dashboard/settings/server"
|
||||
className="text-primary"
|
||||
>
|
||||
{data?.serverId
|
||||
? "Remote Servers -> Server -> Edit Server -> Update IP Address"
|
||||
: "Web Server -> Server -> Update Server IP"}
|
||||
|
||||
@@ -10,13 +10,7 @@ import {
|
||||
} from "@/components/ui/tooltip";
|
||||
import { api } from "@/utils/api";
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||
import {
|
||||
Ban,
|
||||
CheckCircle2,
|
||||
HelpCircle,
|
||||
RefreshCcw,
|
||||
Terminal,
|
||||
} from "lucide-react";
|
||||
import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { type LogLine, parseLogs } from "../../docker/logs/utils";
|
||||
@@ -91,12 +85,14 @@ export const ShowGeneralRedis = ({ redisId }: Props) => {
|
||||
<Button
|
||||
variant="default"
|
||||
isLoading={data?.applicationStatus === "running"}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Deploy
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Rocket className="size-4 mr-1" />
|
||||
Deploy
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -127,13 +123,14 @@ export const ShowGeneralRedis = ({ redisId }: Props) => {
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={isReloading}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Reload
|
||||
<RefreshCcw className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<RefreshCcw className="size-4 mr-1" />
|
||||
Reload
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -164,13 +161,14 @@ export const ShowGeneralRedis = ({ redisId }: Props) => {
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={isStarting}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Start
|
||||
<CheckCircle2 className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<CheckCircle2 className="size-4 mr-1" />
|
||||
Start
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -203,13 +201,14 @@ export const ShowGeneralRedis = ({ redisId }: Props) => {
|
||||
<Button
|
||||
variant="destructive"
|
||||
isLoading={isStopping}
|
||||
className="flex items-center gap-1.5"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
Stop
|
||||
<Ban className="size-4" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
<div className="flex items-center">
|
||||
<Ban className="size-4 mr-1" />
|
||||
Stop
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
@@ -225,9 +224,23 @@ export const ShowGeneralRedis = ({ redisId }: Props) => {
|
||||
appName={data?.appName || ""}
|
||||
serverId={data?.serverId || ""}
|
||||
>
|
||||
<Button variant="outline">
|
||||
<Terminal />
|
||||
Open Terminal
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center">
|
||||
<Terminal className="size-4 mr-1" />
|
||||
Open Terminal
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
<p>Open a terminal to the Redis container</p>
|
||||
</TooltipContent>
|
||||
</TooltipPrimitive.Portal>
|
||||
</Tooltip>
|
||||
</Button>
|
||||
</DockerTerminalModal>
|
||||
</CardContent>
|
||||
|
||||
@@ -56,10 +56,10 @@ export const AddNode = ({ serverId }: Props) => {
|
||||
<TabsTrigger value="worker">Worker</TabsTrigger>
|
||||
<TabsTrigger value="manager">Manager</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="worker" className="pt-4">
|
||||
<TabsContent value="worker" className="pt-4 overflow-hidden">
|
||||
<AddWorker serverId={serverId} />
|
||||
</TabsContent>
|
||||
<TabsContent value="manager" className="pt-4">
|
||||
<TabsContent value="manager" className="pt-4 overflow-hidden">
|
||||
<AddManager serverId={serverId} />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AlertBlock } from "@/components/shared/alert-block";
|
||||
import { CardContent } from "@/components/ui/card";
|
||||
import {
|
||||
DialogDescription,
|
||||
@@ -6,7 +7,7 @@ import {
|
||||
} from "@/components/ui/dialog";
|
||||
import { api } from "@/utils/api";
|
||||
import copy from "copy-to-clipboard";
|
||||
import { CopyIcon } from "lucide-react";
|
||||
import { CopyIcon, Loader2 } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface Props {
|
||||
@@ -14,56 +15,66 @@ interface Props {
|
||||
}
|
||||
|
||||
export const AddManager = ({ serverId }: Props) => {
|
||||
const { data } = api.cluster.addManager.useQuery({ serverId });
|
||||
const { data, isLoading, error, isError } = api.cluster.addManager.useQuery({
|
||||
serverId,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<CardContent className="sm:max-w-4xl max-h-screen overflow-y-auto flex flex-col gap-4 px-0">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Add a new manager</DialogTitle>
|
||||
<DialogDescription>Add a new manager</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col gap-2.5 text-sm">
|
||||
<span>1. Go to your new server and run the following command</span>
|
||||
<span className="bg-muted rounded-lg p-2 flex justify-between">
|
||||
curl https://get.docker.com | sh -s -- --version {data?.version}
|
||||
<button
|
||||
type="button"
|
||||
className="self-center"
|
||||
onClick={() => {
|
||||
copy(
|
||||
`curl https://get.docker.com | sh -s -- --version ${data?.version}`,
|
||||
);
|
||||
toast.success("Copied to clipboard");
|
||||
}}
|
||||
>
|
||||
<CopyIcon className="h-4 w-4 cursor-pointer" />
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<CardContent className="sm:max-w-4xl flex flex-col gap-4 px-0">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Add a new manager</DialogTitle>
|
||||
<DialogDescription>Add a new manager</DialogDescription>
|
||||
</DialogHeader>
|
||||
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||
{isLoading ? (
|
||||
<Loader2 className="w-full animate-spin text-muted-foreground" />
|
||||
) : (
|
||||
<>
|
||||
<div className="flex flex-col gap-2.5 text-sm">
|
||||
<span>
|
||||
1. Go to your new server and run the following command
|
||||
</span>
|
||||
<span className="bg-muted rounded-lg p-2 flex justify-between">
|
||||
curl https://get.docker.com | sh -s -- --version {data?.version}
|
||||
<button
|
||||
type="button"
|
||||
className="self-center"
|
||||
onClick={() => {
|
||||
copy(
|
||||
`curl https://get.docker.com | sh -s -- --version ${data?.version}`,
|
||||
);
|
||||
toast.success("Copied to clipboard");
|
||||
}}
|
||||
>
|
||||
<CopyIcon className="h-4 w-4 cursor-pointer" />
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2.5 text-sm">
|
||||
<span>
|
||||
2. Run the following command to add the node(manager) to your
|
||||
cluster
|
||||
</span>
|
||||
<span className="bg-muted rounded-lg p-2 flex">
|
||||
{data?.command}
|
||||
<button
|
||||
type="button"
|
||||
className="self-start"
|
||||
onClick={() => {
|
||||
copy(data?.command || "");
|
||||
toast.success("Copied to clipboard");
|
||||
}}
|
||||
>
|
||||
<CopyIcon className="h-4 w-4 cursor-pointer" />
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2.5 text-sm">
|
||||
<span>
|
||||
2. Run the following command to add the node(manager) to your
|
||||
cluster
|
||||
</span>
|
||||
|
||||
<span className="bg-muted rounded-lg p-2 flex">
|
||||
{data?.command}
|
||||
<button
|
||||
type="button"
|
||||
className="self-start"
|
||||
onClick={() => {
|
||||
copy(data?.command || "");
|
||||
toast.success("Copied to clipboard");
|
||||
}}
|
||||
>
|
||||
<CopyIcon className="h-4 w-4 cursor-pointer" />
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ export const ShowNodesModal = ({ serverId }: Props) => {
|
||||
className="w-full cursor-pointer "
|
||||
onSelect={(e) => e.preventDefault()}
|
||||
>
|
||||
Show Nodes
|
||||
Show Swarm Nodes
|
||||
</DropdownMenuItem>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-5xl overflow-y-auto max-h-screen ">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { AlertBlock } from "@/components/shared/alert-block";
|
||||
import { CardContent } from "@/components/ui/card";
|
||||
import {
|
||||
DialogDescription,
|
||||
@@ -6,7 +7,7 @@ import {
|
||||
} from "@/components/ui/dialog";
|
||||
import { api } from "@/utils/api";
|
||||
import copy from "copy-to-clipboard";
|
||||
import { CopyIcon } from "lucide-react";
|
||||
import { CopyIcon, Loader2 } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface Props {
|
||||
@@ -14,54 +15,62 @@ interface Props {
|
||||
}
|
||||
|
||||
export const AddWorker = ({ serverId }: Props) => {
|
||||
const { data } = api.cluster.addWorker.useQuery({ serverId });
|
||||
const { data, isLoading, error, isError } = api.cluster.addWorker.useQuery({
|
||||
serverId,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CardContent className="sm:max-w-4xl max-h-screen overflow-y-auto flex flex-col gap-4 px-0">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Add a new worker</DialogTitle>
|
||||
<DialogDescription>Add a new worker</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col gap-2.5 text-sm">
|
||||
<span>1. Go to your new server and run the following command</span>
|
||||
<span className="bg-muted rounded-lg p-2 flex justify-between">
|
||||
curl https://get.docker.com | sh -s -- --version {data?.version}
|
||||
<button
|
||||
type="button"
|
||||
className="self-center"
|
||||
onClick={() => {
|
||||
copy(
|
||||
`curl https://get.docker.com | sh -s -- --version ${data?.version}`,
|
||||
);
|
||||
toast.success("Copied to clipboard");
|
||||
}}
|
||||
>
|
||||
<CopyIcon className="h-4 w-4 cursor-pointer" />
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<CardContent className="sm:max-w-4xl flex flex-col gap-4 px-0">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Add a new worker</DialogTitle>
|
||||
<DialogDescription>Add a new worker</DialogDescription>
|
||||
</DialogHeader>
|
||||
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||
{isLoading ? (
|
||||
<Loader2 className="w-full animate-spin text-muted-foreground" />
|
||||
) : (
|
||||
<>
|
||||
<div className="flex flex-col gap-2.5 text-sm">
|
||||
<span>1. Go to your new server and run the following command</span>
|
||||
<span className="bg-muted rounded-lg p-2 flex justify-between">
|
||||
curl https://get.docker.com | sh -s -- --version {data?.version}
|
||||
<button
|
||||
type="button"
|
||||
className="self-center"
|
||||
onClick={() => {
|
||||
copy(
|
||||
`curl https://get.docker.com | sh -s -- --version ${data?.version}`,
|
||||
);
|
||||
toast.success("Copied to clipboard");
|
||||
}}
|
||||
>
|
||||
<CopyIcon className="h-4 w-4 cursor-pointer" />
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2.5 text-sm">
|
||||
<span>
|
||||
2. Run the following command to add the node(worker) to your cluster
|
||||
</span>
|
||||
<div className="flex flex-col gap-2.5 text-sm">
|
||||
<span>
|
||||
2. Run the following command to add the node(worker) to your
|
||||
cluster
|
||||
</span>
|
||||
|
||||
<span className="bg-muted rounded-lg p-2 flex">
|
||||
{data?.command}
|
||||
<button
|
||||
type="button"
|
||||
className="self-start"
|
||||
onClick={() => {
|
||||
copy(data?.command || "");
|
||||
toast.success("Copied to clipboard");
|
||||
}}
|
||||
>
|
||||
<CopyIcon className="h-4 w-4 cursor-pointer" />
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</div>
|
||||
<span className="bg-muted rounded-lg p-2 flex">
|
||||
{data?.command}
|
||||
<button
|
||||
type="button"
|
||||
className="self-start"
|
||||
onClick={() => {
|
||||
copy(data?.command || "");
|
||||
toast.success("Copied to clipboard");
|
||||
}}
|
||||
>
|
||||
<CopyIcon className="h-4 w-4 cursor-pointer" />
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -139,7 +139,10 @@ export const AddGiteaProvider = () => {
|
||||
<ExternalLink className="w-fit text-primary size-4" />
|
||||
</Link>
|
||||
</li>
|
||||
<li>Navigate to Applications</li>
|
||||
<li>
|
||||
Navigate to Applications {"->"} Create new OAuth2
|
||||
Application
|
||||
</li>
|
||||
<li>
|
||||
Create a new application with the following details:
|
||||
<ul className="list-disc list-inside ml-4">
|
||||
@@ -148,10 +151,6 @@ export const AddGiteaProvider = () => {
|
||||
Redirect URI:{" "}
|
||||
<span className="text-primary">{webhookUrl}</span>{" "}
|
||||
</li>
|
||||
<li>
|
||||
Select Permissions - organization: read, user: read,
|
||||
repository: read/write
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
@@ -124,10 +124,10 @@ export const ShowGitProviders = () => {
|
||||
gitProvider.gitlab?.accessToken &&
|
||||
gitProvider.gitlab?.refreshToken;
|
||||
|
||||
const haveGiteaRequirements =
|
||||
isGitea &&
|
||||
gitProvider.gitea?.accessToken &&
|
||||
gitProvider.gitea?.refreshToken;
|
||||
// const haveGiteaRequirements =
|
||||
// isGitea &&
|
||||
// gitProvider.gitea?.accessToken &&
|
||||
// gitProvider.gitea?.refreshToken;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -207,6 +207,7 @@ export const ShowGitProviders = () => {
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isGithub && haveGithubRequirements && (
|
||||
<EditGithubProvider
|
||||
githubId={gitProvider.github?.githubId}
|
||||
|
||||
@@ -663,13 +663,16 @@ export const HandleNotifications = ({ notificationId }: Props) => {
|
||||
{...field}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
if (value) {
|
||||
if (value === "") {
|
||||
field.onChange(undefined);
|
||||
} else {
|
||||
const port = Number.parseInt(value);
|
||||
if (port > 0 && port < 65536) {
|
||||
field.onChange(port);
|
||||
}
|
||||
}
|
||||
}}
|
||||
value={field.value || ""}
|
||||
type="number"
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
@@ -159,9 +159,11 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => {
|
||||
<Input
|
||||
type="number"
|
||||
{...field}
|
||||
onChange={(e) =>
|
||||
field.onChange(Number(e.target.value))
|
||||
}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
field.onChange(value === "" ? undefined : Number(value));
|
||||
}}
|
||||
value={field.value || ""}
|
||||
className="w-full dark:bg-black"
|
||||
placeholder="e.g. 8080"
|
||||
/>
|
||||
@@ -185,9 +187,11 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => {
|
||||
<Input
|
||||
type="number"
|
||||
{...field}
|
||||
onChange={(e) =>
|
||||
field.onChange(Number(e.target.value))
|
||||
}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
field.onChange(value === "" ? undefined : Number(value));
|
||||
}}
|
||||
value={field.value || ""}
|
||||
className="w-full dark:bg-black"
|
||||
placeholder="e.g. 80"
|
||||
/>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
ALTER TYPE "public"."gitProviderType" ADD VALUE 'gitea';--> statement-breakpoint
|
||||
CREATE TABLE "gitea" (
|
||||
"giteaId" text PRIMARY KEY NOT NULL,
|
||||
"giteaUrl" text DEFAULT 'https://gitea.com' NOT NULL,
|
||||
"application_id" text,
|
||||
"redirect_uri" text,
|
||||
"secret" text,
|
||||
"access_token" text,
|
||||
"refresh_token" text,
|
||||
"organization_name" text,
|
||||
"expires_at" integer,
|
||||
"gitProviderId" text NOT NULL,
|
||||
"gitea_username" text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "gitea" ADD CONSTRAINT "gitea_gitProviderId_git_provider_gitProviderId_fk" FOREIGN KEY ("gitProviderId") REFERENCES "public"."git_provider"("gitProviderId") ON DELETE cascade ON UPDATE no action;
|
||||
@@ -1,9 +0,0 @@
|
||||
ALTER TYPE "public"."sourceType" ADD VALUE 'gitea' BEFORE 'drop';--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "giteaProjectId" integer;--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "giteaRepository" text;--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "giteaOwner" text;--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "giteaBranch" text;--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "giteaBuildPath" text DEFAULT '/';--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "giteaPathNamespace" text;--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "giteaId" text;--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD CONSTRAINT "application_giteaId_gitea_giteaId_fk" FOREIGN KEY ("giteaId") REFERENCES "public"."gitea"("giteaId") ON DELETE set null ON UPDATE no action;
|
||||
@@ -1,6 +0,0 @@
|
||||
ALTER TABLE "gitea" ADD COLUMN "client_id" text;--> statement-breakpoint
|
||||
ALTER TABLE "gitea" ADD COLUMN "client_secret" text;--> statement-breakpoint
|
||||
ALTER TABLE "gitea" DROP COLUMN "application_id";--> statement-breakpoint
|
||||
ALTER TABLE "gitea" DROP COLUMN "secret";--> statement-breakpoint
|
||||
ALTER TABLE "gitea" DROP COLUMN "refresh_token";--> statement-breakpoint
|
||||
ALTER TABLE "gitea" DROP COLUMN "organization_name";
|
||||
@@ -1,4 +0,0 @@
|
||||
ALTER TABLE "gitea" ADD COLUMN "refresh_token" text;--> statement-breakpoint
|
||||
ALTER TABLE "gitea" ADD COLUMN "organization_name" text;--> statement-breakpoint
|
||||
ALTER TABLE "gitea" ADD COLUMN "scopes" text;--> statement-breakpoint
|
||||
ALTER TABLE "gitea" ADD COLUMN "last_authenticated_at" integer;
|
||||
@@ -1,6 +0,0 @@
|
||||
ALTER TYPE "public"."sourceTypeCompose" ADD VALUE 'gitea' BEFORE 'raw';--> statement-breakpoint
|
||||
ALTER TABLE "compose" ADD COLUMN "giteaRepository" text;--> statement-breakpoint
|
||||
ALTER TABLE "compose" ADD COLUMN "giteaOwner" text;--> statement-breakpoint
|
||||
ALTER TABLE "compose" ADD COLUMN "giteaBranch" text;--> statement-breakpoint
|
||||
ALTER TABLE "compose" ADD COLUMN "giteaId" text;--> statement-breakpoint
|
||||
ALTER TABLE "compose" ADD CONSTRAINT "compose_giteaId_gitea_giteaId_fk" FOREIGN KEY ("giteaId") REFERENCES "public"."gitea"("giteaId") ON DELETE set null ON UPDATE no action;
|
||||
@@ -1,157 +0,0 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumlabel = 'gitea' AND
|
||||
enumtypid = (SELECT oid FROM pg_type WHERE typname = 'sourceType')) THEN
|
||||
ALTER TYPE "public"."sourceType" ADD VALUE 'gitea' BEFORE 'drop';
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumlabel = 'gitea' AND
|
||||
enumtypid = (SELECT oid FROM pg_type WHERE typname = 'sourceTypeCompose')) THEN
|
||||
ALTER TYPE "public"."sourceTypeCompose" ADD VALUE 'gitea' BEFORE 'raw';
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumlabel = 'gitea' AND
|
||||
enumtypid = (SELECT oid FROM pg_type WHERE typname = 'gitProviderType')) THEN
|
||||
ALTER TYPE "public"."gitProviderType" ADD VALUE 'gitea';
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'gitea') THEN
|
||||
CREATE TABLE "gitea" (
|
||||
"giteaId" text PRIMARY KEY NOT NULL,
|
||||
"giteaUrl" text DEFAULT 'https://gitea.com' NOT NULL,
|
||||
"redirect_uri" text,
|
||||
"client_id" text,
|
||||
"client_secret" text,
|
||||
"gitProviderId" text NOT NULL,
|
||||
"gitea_username" text,
|
||||
"access_token" text,
|
||||
"refresh_token" text,
|
||||
"expires_at" integer,
|
||||
"scopes" text DEFAULT 'repo,repo:status,read:user,read:org',
|
||||
"last_authenticated_at" integer
|
||||
);
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
--> statement-breakpoint
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'application' AND column_name = 'giteaProjectId') THEN
|
||||
ALTER TABLE "application" ADD COLUMN "giteaProjectId" integer;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'application' AND column_name = 'giteaRepository') THEN
|
||||
ALTER TABLE "application" ADD COLUMN "giteaRepository" text;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'application' AND column_name = 'giteaOwner') THEN
|
||||
ALTER TABLE "application" ADD COLUMN "giteaOwner" text;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'application' AND column_name = 'giteaBranch') THEN
|
||||
ALTER TABLE "application" ADD COLUMN "giteaBranch" text;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'application' AND column_name = 'giteaBuildPath') THEN
|
||||
ALTER TABLE "application" ADD COLUMN "giteaBuildPath" text DEFAULT '/';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'application' AND column_name = 'giteaPathNamespace') THEN
|
||||
ALTER TABLE "application" ADD COLUMN "giteaPathNamespace" text;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'application' AND column_name = 'giteaId') THEN
|
||||
ALTER TABLE "application" ADD COLUMN "giteaId" text;
|
||||
END IF;
|
||||
END $$;
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'compose' AND column_name = 'giteaRepository') THEN
|
||||
ALTER TABLE "compose" ADD COLUMN "giteaRepository" text;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'compose' AND column_name = 'giteaOwner') THEN
|
||||
ALTER TABLE "compose" ADD COLUMN "giteaOwner" text;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'compose' AND column_name = 'giteaBranch') THEN
|
||||
ALTER TABLE "compose" ADD COLUMN "giteaBranch" text;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'compose' AND column_name = 'giteaId') THEN
|
||||
ALTER TABLE "compose" ADD COLUMN "giteaId" text;
|
||||
END IF;
|
||||
END $$;
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.table_constraints
|
||||
WHERE constraint_name = 'gitea_gitProviderId_git_provider_gitProviderId_fk'
|
||||
) THEN
|
||||
ALTER TABLE "gitea" ADD CONSTRAINT "gitea_gitProviderId_git_provider_gitProviderId_fk"
|
||||
FOREIGN KEY ("gitProviderId") REFERENCES "public"."git_provider"("gitProviderId")
|
||||
ON DELETE cascade ON UPDATE no action;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.table_constraints
|
||||
WHERE constraint_name = 'application_giteaId_gitea_giteaId_fk'
|
||||
) THEN
|
||||
ALTER TABLE "application" ADD CONSTRAINT "application_giteaId_gitea_giteaId_fk"
|
||||
FOREIGN KEY ("giteaId") REFERENCES "public"."gitea"("giteaId")
|
||||
ON DELETE set null ON UPDATE no action;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.table_constraints
|
||||
WHERE constraint_name = 'compose_giteaId_gitea_giteaId_fk'
|
||||
) THEN
|
||||
ALTER TABLE "compose" ADD CONSTRAINT "compose_giteaId_gitea_giteaId_fk"
|
||||
FOREIGN KEY ("giteaId") REFERENCES "public"."gitea"("giteaId")
|
||||
ON DELETE set null ON UPDATE no action;
|
||||
END IF;
|
||||
END $$;
|
||||
32
apps/dokploy/drizzle/0079_bizarre_wendell_rand.sql
Normal file
32
apps/dokploy/drizzle/0079_bizarre_wendell_rand.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
ALTER TYPE "public"."sourceType" ADD VALUE 'gitea' BEFORE 'drop';--> statement-breakpoint
|
||||
ALTER TYPE "public"."sourceTypeCompose" ADD VALUE 'gitea' BEFORE 'raw';--> statement-breakpoint
|
||||
ALTER TYPE "public"."gitProviderType" ADD VALUE 'gitea';--> statement-breakpoint
|
||||
CREATE TABLE "gitea" (
|
||||
"giteaId" text PRIMARY KEY NOT NULL,
|
||||
"giteaUrl" text DEFAULT 'https://gitea.com' NOT NULL,
|
||||
"redirect_uri" text,
|
||||
"client_id" text,
|
||||
"client_secret" text,
|
||||
"gitProviderId" text NOT NULL,
|
||||
"gitea_username" text,
|
||||
"access_token" text,
|
||||
"refresh_token" text,
|
||||
"expires_at" integer,
|
||||
"scopes" text DEFAULT 'repo,repo:status,read:user,read:org',
|
||||
"last_authenticated_at" integer
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "giteaProjectId" integer;--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "giteaRepository" text;--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "giteaOwner" text;--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "giteaBranch" text;--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "giteaBuildPath" text DEFAULT '/';--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "giteaPathNamespace" text;--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "giteaId" text;--> statement-breakpoint
|
||||
ALTER TABLE "compose" ADD COLUMN "giteaRepository" text;--> statement-breakpoint
|
||||
ALTER TABLE "compose" ADD COLUMN "giteaOwner" text;--> statement-breakpoint
|
||||
ALTER TABLE "compose" ADD COLUMN "giteaBranch" text;--> statement-breakpoint
|
||||
ALTER TABLE "compose" ADD COLUMN "giteaId" text;--> statement-breakpoint
|
||||
ALTER TABLE "gitea" ADD CONSTRAINT "gitea_gitProviderId_git_provider_gitProviderId_fk" FOREIGN KEY ("gitProviderId") REFERENCES "public"."git_provider"("gitProviderId") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD CONSTRAINT "application_giteaId_gitea_giteaId_fk" FOREIGN KEY ("giteaId") REFERENCES "public"."gitea"("giteaId") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "compose" ADD CONSTRAINT "compose_giteaId_gitea_giteaId_fk" FOREIGN KEY ("giteaId") REFERENCES "public"."gitea"("giteaId") ON DELETE set null ON UPDATE no action;
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"id": "436368c7-e9a2-4df0-8c8f-863a69c9432d",
|
||||
"id": "44cb886c-d31a-4b3d-b70e-da306c74dcf5",
|
||||
"prevId": "53fc370e-731b-4bfe-874f-9ce725650082",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
@@ -868,6 +868,12 @@
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"logCleanupCron": {
|
||||
"name": "logCleanupCron",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"enablePaidFeatures": {
|
||||
"name": "enablePaidFeatures",
|
||||
"type": "boolean",
|
||||
@@ -3914,100 +3920,6 @@
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.gitea": {
|
||||
"name": "gitea",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"giteaUrl": {
|
||||
"name": "giteaUrl",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'https://gitea.com'"
|
||||
},
|
||||
"application_id": {
|
||||
"name": "application_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"redirect_uri": {
|
||||
"name": "redirect_uri",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"secret": {
|
||||
"name": "secret",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"access_token": {
|
||||
"name": "access_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"refresh_token": {
|
||||
"name": "refresh_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"organization_name": {
|
||||
"name": "organization_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"gitProviderId": {
|
||||
"name": "gitProviderId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"gitea_username": {
|
||||
"name": "gitea_username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"gitea_gitProviderId_git_provider_gitProviderId_fk": {
|
||||
"name": "gitea_gitProviderId_git_provider_gitProviderId_fk",
|
||||
"tableFrom": "gitea",
|
||||
"tableTo": "git_provider",
|
||||
"columnsFrom": [
|
||||
"gitProviderId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"gitProviderId"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.server": {
|
||||
"name": "server",
|
||||
"schema": "",
|
||||
@@ -5195,8 +5107,7 @@
|
||||
"values": [
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea"
|
||||
"bitbucket"
|
||||
]
|
||||
},
|
||||
"public.serverStatus": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "4360a233-2c88-4d6c-834f-eef562e079e3",
|
||||
"prevId": "436368c7-e9a2-4df0-8c8f-863a69c9432d",
|
||||
"id": "ad43c733-01c3-4841-b600-252421350fb9",
|
||||
"prevId": "44cb886c-d31a-4b3d-b70e-da306c74dcf5",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
@@ -235,43 +235,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaProjectId": {
|
||||
"name": "giteaProjectId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaRepository": {
|
||||
"name": "giteaRepository",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaOwner": {
|
||||
"name": "giteaOwner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBranch": {
|
||||
"name": "giteaBranch",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBuildPath": {
|
||||
"name": "giteaBuildPath",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'/'"
|
||||
},
|
||||
"giteaPathNamespace": {
|
||||
"name": "giteaPathNamespace",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"bitbucketRepository": {
|
||||
"name": "bitbucketRepository",
|
||||
"type": "text",
|
||||
@@ -483,12 +446,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"bitbucketId": {
|
||||
"name": "bitbucketId",
|
||||
"type": "text",
|
||||
@@ -569,19 +526,6 @@
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"application_giteaId_gitea_giteaId_fk": {
|
||||
"name": "application_giteaId_gitea_giteaId_fk",
|
||||
"tableFrom": "application",
|
||||
"tableTo": "gitea",
|
||||
"columnsFrom": [
|
||||
"giteaId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"giteaId"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"application_bitbucketId_bitbucket_bitbucketId_fk": {
|
||||
"name": "application_bitbucketId_bitbucket_bitbucketId_fk",
|
||||
"tableFrom": "application",
|
||||
@@ -917,13 +861,6 @@
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"enableLogRotation": {
|
||||
"name": "enableLogRotation",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"logCleanupCron": {
|
||||
"name": "logCleanupCron",
|
||||
"type": "text",
|
||||
@@ -1153,6 +1090,12 @@
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'none'"
|
||||
},
|
||||
"customCertResolver": {
|
||||
"name": "customCertResolver",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
@@ -3976,100 +3919,6 @@
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.gitea": {
|
||||
"name": "gitea",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"giteaUrl": {
|
||||
"name": "giteaUrl",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'https://gitea.com'"
|
||||
},
|
||||
"application_id": {
|
||||
"name": "application_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"redirect_uri": {
|
||||
"name": "redirect_uri",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"secret": {
|
||||
"name": "secret",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"access_token": {
|
||||
"name": "access_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"refresh_token": {
|
||||
"name": "refresh_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"organization_name": {
|
||||
"name": "organization_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"gitProviderId": {
|
||||
"name": "gitProviderId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"gitea_username": {
|
||||
"name": "gitea_username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"gitea_gitProviderId_git_provider_gitProviderId_fk": {
|
||||
"name": "gitea_gitProviderId_git_provider_gitProviderId_fk",
|
||||
"tableFrom": "gitea",
|
||||
"tableTo": "git_provider",
|
||||
"columnsFrom": [
|
||||
"gitProviderId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"gitProviderId"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.server": {
|
||||
"name": "server",
|
||||
"schema": "",
|
||||
@@ -5134,7 +4983,6 @@
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea",
|
||||
"drop"
|
||||
]
|
||||
},
|
||||
@@ -5259,8 +5107,7 @@
|
||||
"values": [
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea"
|
||||
"bitbucket"
|
||||
]
|
||||
},
|
||||
"public.serverStatus": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "8946105c-e69c-49c7-8334-784ee2419a92",
|
||||
"prevId": "4360a233-2c88-4d6c-834f-eef562e079e3",
|
||||
"id": "5808aed9-4223-4ac4-ae22-6c0dae500d75",
|
||||
"prevId": "ad43c733-01c3-4841-b600-252421350fb9",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
@@ -241,43 +241,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaProjectId": {
|
||||
"name": "giteaProjectId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaRepository": {
|
||||
"name": "giteaRepository",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaOwner": {
|
||||
"name": "giteaOwner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBranch": {
|
||||
"name": "giteaBranch",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBuildPath": {
|
||||
"name": "giteaBuildPath",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'/'"
|
||||
},
|
||||
"giteaPathNamespace": {
|
||||
"name": "giteaPathNamespace",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"bitbucketRepository": {
|
||||
"name": "bitbucketRepository",
|
||||
"type": "text",
|
||||
@@ -489,12 +452,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"bitbucketId": {
|
||||
"name": "bitbucketId",
|
||||
"type": "text",
|
||||
@@ -575,19 +532,6 @@
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"application_giteaId_gitea_giteaId_fk": {
|
||||
"name": "application_giteaId_gitea_giteaId_fk",
|
||||
"tableFrom": "application",
|
||||
"tableTo": "gitea",
|
||||
"columnsFrom": [
|
||||
"giteaId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"giteaId"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"application_bitbucketId_bitbucket_bitbucketId_fk": {
|
||||
"name": "application_bitbucketId_bitbucket_bitbucketId_fk",
|
||||
"tableFrom": "application",
|
||||
@@ -923,13 +867,6 @@
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"enableLogRotation": {
|
||||
"name": "enableLogRotation",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"logCleanupCron": {
|
||||
"name": "logCleanupCron",
|
||||
"type": "text",
|
||||
@@ -3988,88 +3925,6 @@
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.gitea": {
|
||||
"name": "gitea",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"giteaUrl": {
|
||||
"name": "giteaUrl",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'https://gitea.com'"
|
||||
},
|
||||
"redirect_uri": {
|
||||
"name": "redirect_uri",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"client_id": {
|
||||
"name": "client_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"client_secret": {
|
||||
"name": "client_secret",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"gitProviderId": {
|
||||
"name": "gitProviderId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"gitea_username": {
|
||||
"name": "gitea_username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"access_token": {
|
||||
"name": "access_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"gitea_gitProviderId_git_provider_gitProviderId_fk": {
|
||||
"name": "gitea_gitProviderId_git_provider_gitProviderId_fk",
|
||||
"tableFrom": "gitea",
|
||||
"tableTo": "git_provider",
|
||||
"columnsFrom": [
|
||||
"gitProviderId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"gitProviderId"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.server": {
|
||||
"name": "server",
|
||||
"schema": "",
|
||||
@@ -5134,7 +4989,6 @@
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea",
|
||||
"drop"
|
||||
]
|
||||
},
|
||||
@@ -5259,8 +5113,7 @@
|
||||
"values": [
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea"
|
||||
"bitbucket"
|
||||
]
|
||||
},
|
||||
"public.serverStatus": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "85133e87-6886-493f-89d6-c83162f76d50",
|
||||
"prevId": "8946105c-e69c-49c7-8334-784ee2419a92",
|
||||
"id": "3584a243-55f6-418b-aece-985d878b30d7",
|
||||
"prevId": "5808aed9-4223-4ac4-ae22-6c0dae500d75",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
@@ -241,43 +241,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaProjectId": {
|
||||
"name": "giteaProjectId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaRepository": {
|
||||
"name": "giteaRepository",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaOwner": {
|
||||
"name": "giteaOwner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBranch": {
|
||||
"name": "giteaBranch",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBuildPath": {
|
||||
"name": "giteaBuildPath",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'/'"
|
||||
},
|
||||
"giteaPathNamespace": {
|
||||
"name": "giteaPathNamespace",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"bitbucketRepository": {
|
||||
"name": "bitbucketRepository",
|
||||
"type": "text",
|
||||
@@ -489,12 +452,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"bitbucketId": {
|
||||
"name": "bitbucketId",
|
||||
"type": "text",
|
||||
@@ -575,19 +532,6 @@
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"application_giteaId_gitea_giteaId_fk": {
|
||||
"name": "application_giteaId_gitea_giteaId_fk",
|
||||
"tableFrom": "application",
|
||||
"tableTo": "gitea",
|
||||
"columnsFrom": [
|
||||
"giteaId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"giteaId"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"application_bitbucketId_bitbucket_bitbucketId_fk": {
|
||||
"name": "application_bitbucketId_bitbucket_bitbucketId_fk",
|
||||
"tableFrom": "application",
|
||||
@@ -923,13 +867,6 @@
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"enableLogRotation": {
|
||||
"name": "enableLogRotation",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"logCleanupCron": {
|
||||
"name": "logCleanupCron",
|
||||
"type": "text",
|
||||
@@ -3988,112 +3925,6 @@
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.gitea": {
|
||||
"name": "gitea",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"giteaUrl": {
|
||||
"name": "giteaUrl",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'https://gitea.com'"
|
||||
},
|
||||
"redirect_uri": {
|
||||
"name": "redirect_uri",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"client_id": {
|
||||
"name": "client_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"client_secret": {
|
||||
"name": "client_secret",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"gitProviderId": {
|
||||
"name": "gitProviderId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"gitea_username": {
|
||||
"name": "gitea_username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"access_token": {
|
||||
"name": "access_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"refresh_token": {
|
||||
"name": "refresh_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"organization_name": {
|
||||
"name": "organization_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"scopes": {
|
||||
"name": "scopes",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"last_authenticated_at": {
|
||||
"name": "last_authenticated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"gitea_gitProviderId_git_provider_gitProviderId_fk": {
|
||||
"name": "gitea_gitProviderId_git_provider_gitProviderId_fk",
|
||||
"tableFrom": "gitea",
|
||||
"tableTo": "git_provider",
|
||||
"columnsFrom": [
|
||||
"gitProviderId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"gitProviderId"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.server": {
|
||||
"name": "server",
|
||||
"schema": "",
|
||||
@@ -5158,7 +4989,6 @@
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea",
|
||||
"drop"
|
||||
]
|
||||
},
|
||||
@@ -5283,8 +5113,7 @@
|
||||
"values": [
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea"
|
||||
"bitbucket"
|
||||
]
|
||||
},
|
||||
"public.serverStatus": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "00d7b47b-5341-4dda-b121-4a87cec39d95",
|
||||
"prevId": "85133e87-6886-493f-89d6-c83162f76d50",
|
||||
"id": "dd51ff04-a160-4d0d-b72f-4916493b740f",
|
||||
"prevId": "3584a243-55f6-418b-aece-985d878b30d7",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
@@ -247,43 +247,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaProjectId": {
|
||||
"name": "giteaProjectId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaRepository": {
|
||||
"name": "giteaRepository",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaOwner": {
|
||||
"name": "giteaOwner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBranch": {
|
||||
"name": "giteaBranch",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBuildPath": {
|
||||
"name": "giteaBuildPath",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'/'"
|
||||
},
|
||||
"giteaPathNamespace": {
|
||||
"name": "giteaPathNamespace",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"bitbucketRepository": {
|
||||
"name": "bitbucketRepository",
|
||||
"type": "text",
|
||||
@@ -495,12 +458,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"bitbucketId": {
|
||||
"name": "bitbucketId",
|
||||
"type": "text",
|
||||
@@ -581,19 +538,6 @@
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"application_giteaId_gitea_giteaId_fk": {
|
||||
"name": "application_giteaId_gitea_giteaId_fk",
|
||||
"tableFrom": "application",
|
||||
"tableTo": "gitea",
|
||||
"columnsFrom": [
|
||||
"giteaId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"giteaId"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"application_bitbucketId_bitbucket_bitbucketId_fk": {
|
||||
"name": "application_bitbucketId_bitbucket_bitbucketId_fk",
|
||||
"tableFrom": "application",
|
||||
@@ -929,13 +873,6 @@
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"enableLogRotation": {
|
||||
"name": "enableLogRotation",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"logCleanupCron": {
|
||||
"name": "logCleanupCron",
|
||||
"type": "text",
|
||||
@@ -2971,24 +2908,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaRepository": {
|
||||
"name": "giteaRepository",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaOwner": {
|
||||
"name": "giteaOwner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBranch": {
|
||||
"name": "giteaBranch",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"customGitUrl": {
|
||||
"name": "customGitUrl",
|
||||
"type": "text",
|
||||
@@ -3080,12 +2999,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"serverId": {
|
||||
"name": "serverId",
|
||||
"type": "text",
|
||||
@@ -3160,19 +3073,6 @@
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"compose_giteaId_gitea_giteaId_fk": {
|
||||
"name": "compose_giteaId_gitea_giteaId_fk",
|
||||
"tableFrom": "compose",
|
||||
"tableTo": "gitea",
|
||||
"columnsFrom": [
|
||||
"giteaId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"giteaId"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"compose_serverId_server_serverId_fk": {
|
||||
"name": "compose_serverId_server_serverId_fk",
|
||||
"tableFrom": "compose",
|
||||
@@ -4031,112 +3931,6 @@
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.gitea": {
|
||||
"name": "gitea",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"giteaUrl": {
|
||||
"name": "giteaUrl",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'https://gitea.com'"
|
||||
},
|
||||
"redirect_uri": {
|
||||
"name": "redirect_uri",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"client_id": {
|
||||
"name": "client_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"client_secret": {
|
||||
"name": "client_secret",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"gitProviderId": {
|
||||
"name": "gitProviderId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"gitea_username": {
|
||||
"name": "gitea_username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"access_token": {
|
||||
"name": "access_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"refresh_token": {
|
||||
"name": "refresh_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"organization_name": {
|
||||
"name": "organization_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"scopes": {
|
||||
"name": "scopes",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"last_authenticated_at": {
|
||||
"name": "last_authenticated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"gitea_gitProviderId_git_provider_gitProviderId_fk": {
|
||||
"name": "gitea_gitProviderId_git_provider_gitProviderId_fk",
|
||||
"tableFrom": "gitea",
|
||||
"tableTo": "git_provider",
|
||||
"columnsFrom": [
|
||||
"gitProviderId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"gitProviderId"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.server": {
|
||||
"name": "server",
|
||||
"schema": "",
|
||||
@@ -5201,7 +4995,6 @@
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea",
|
||||
"drop"
|
||||
]
|
||||
},
|
||||
@@ -5298,7 +5091,6 @@
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea",
|
||||
"raw"
|
||||
]
|
||||
},
|
||||
@@ -5327,8 +5119,7 @@
|
||||
"values": [
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea"
|
||||
"bitbucket"
|
||||
]
|
||||
},
|
||||
"public.serverStatus": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "7c8508ba-01eb-487e-88cf-0abe10916904",
|
||||
"prevId": "f74127dc-3cda-4091-988f-97b60eb22427",
|
||||
"id": "27ba0f3d-859f-4233-a179-8aee11ad9179",
|
||||
"prevId": "dd51ff04-a160-4d0d-b72f-4916493b740f",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
@@ -179,13 +179,6 @@
|
||||
"notNull": true,
|
||||
"default": "'github'"
|
||||
},
|
||||
"cleanCache": {
|
||||
"name": "cleanCache",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"repository": {
|
||||
"name": "repository",
|
||||
"type": "text",
|
||||
@@ -254,43 +247,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaProjectId": {
|
||||
"name": "giteaProjectId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaRepository": {
|
||||
"name": "giteaRepository",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaOwner": {
|
||||
"name": "giteaOwner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBranch": {
|
||||
"name": "giteaBranch",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBuildPath": {
|
||||
"name": "giteaBuildPath",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'/'"
|
||||
},
|
||||
"giteaPathNamespace": {
|
||||
"name": "giteaPathNamespace",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"bitbucketRepository": {
|
||||
"name": "bitbucketRepository",
|
||||
"type": "text",
|
||||
@@ -502,12 +458,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"bitbucketId": {
|
||||
"name": "bitbucketId",
|
||||
"type": "text",
|
||||
@@ -588,19 +538,6 @@
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"application_giteaId_gitea_giteaId_fk": {
|
||||
"name": "application_giteaId_gitea_giteaId_fk",
|
||||
"tableFrom": "application",
|
||||
"tableTo": "gitea",
|
||||
"columnsFrom": [
|
||||
"giteaId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"giteaId"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"application_bitbucketId_bitbucket_bitbucketId_fk": {
|
||||
"name": "application_bitbucketId_bitbucket_bitbucketId_fk",
|
||||
"tableFrom": "application",
|
||||
@@ -1766,12 +1703,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"keepLatestCount": {
|
||||
"name": "keepLatestCount",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"databaseType": {
|
||||
"name": "databaseType",
|
||||
"type": "databaseType",
|
||||
@@ -2977,24 +2908,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaRepository": {
|
||||
"name": "giteaRepository",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaOwner": {
|
||||
"name": "giteaOwner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBranch": {
|
||||
"name": "giteaBranch",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"customGitUrl": {
|
||||
"name": "customGitUrl",
|
||||
"type": "text",
|
||||
@@ -3092,12 +3005,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"serverId": {
|
||||
"name": "serverId",
|
||||
"type": "text",
|
||||
@@ -3172,19 +3079,6 @@
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"compose_giteaId_gitea_giteaId_fk": {
|
||||
"name": "compose_giteaId_gitea_giteaId_fk",
|
||||
"tableFrom": "compose",
|
||||
"tableTo": "gitea",
|
||||
"columnsFrom": [
|
||||
"giteaId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"giteaId"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"compose_serverId_server_serverId_fk": {
|
||||
"name": "compose_serverId_server_serverId_fk",
|
||||
"tableFrom": "compose",
|
||||
@@ -4043,107 +3937,6 @@
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.gitea": {
|
||||
"name": "gitea",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"giteaUrl": {
|
||||
"name": "giteaUrl",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'https://gitea.com'"
|
||||
},
|
||||
"redirect_uri": {
|
||||
"name": "redirect_uri",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"client_id": {
|
||||
"name": "client_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"client_secret": {
|
||||
"name": "client_secret",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"gitProviderId": {
|
||||
"name": "gitProviderId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"gitea_username": {
|
||||
"name": "gitea_username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"access_token": {
|
||||
"name": "access_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"refresh_token": {
|
||||
"name": "refresh_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"scopes": {
|
||||
"name": "scopes",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'repo,repo:status,read:user,read:org'"
|
||||
},
|
||||
"last_authenticated_at": {
|
||||
"name": "last_authenticated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"gitea_gitProviderId_git_provider_gitProviderId_fk": {
|
||||
"name": "gitea_gitProviderId_git_provider_gitProviderId_fk",
|
||||
"tableFrom": "gitea",
|
||||
"tableTo": "git_provider",
|
||||
"columnsFrom": [
|
||||
"gitProviderId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"gitProviderId"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.server": {
|
||||
"name": "server",
|
||||
"schema": "",
|
||||
@@ -5208,7 +5001,6 @@
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea",
|
||||
"drop"
|
||||
]
|
||||
},
|
||||
@@ -5305,7 +5097,6 @@
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea",
|
||||
"raw"
|
||||
]
|
||||
},
|
||||
@@ -5334,8 +5125,7 @@
|
||||
"values": [
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea"
|
||||
"bitbucket"
|
||||
]
|
||||
},
|
||||
"public.serverStatus": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"id": "c8f3ee9b-66c6-4a23-a151-78e04363bdf9",
|
||||
"prevId": "f74127dc-3cda-4091-988f-97b60eb22427",
|
||||
"id": "d4d95ab9-16bd-4583-949e-d6ae0a0bb7a0",
|
||||
"prevId": "27ba0f3d-859f-4233-a179-8aee11ad9179",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
@@ -179,13 +179,6 @@
|
||||
"notNull": true,
|
||||
"default": "'github'"
|
||||
},
|
||||
"cleanCache": {
|
||||
"name": "cleanCache",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"repository": {
|
||||
"name": "repository",
|
||||
"type": "text",
|
||||
@@ -254,43 +247,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaProjectId": {
|
||||
"name": "giteaProjectId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaRepository": {
|
||||
"name": "giteaRepository",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaOwner": {
|
||||
"name": "giteaOwner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBranch": {
|
||||
"name": "giteaBranch",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBuildPath": {
|
||||
"name": "giteaBuildPath",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'/'"
|
||||
},
|
||||
"giteaPathNamespace": {
|
||||
"name": "giteaPathNamespace",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"bitbucketRepository": {
|
||||
"name": "bitbucketRepository",
|
||||
"type": "text",
|
||||
@@ -502,12 +458,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"bitbucketId": {
|
||||
"name": "bitbucketId",
|
||||
"type": "text",
|
||||
@@ -588,19 +538,6 @@
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"application_giteaId_gitea_giteaId_fk": {
|
||||
"name": "application_giteaId_gitea_giteaId_fk",
|
||||
"tableFrom": "application",
|
||||
"tableTo": "gitea",
|
||||
"columnsFrom": [
|
||||
"giteaId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"giteaId"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"application_bitbucketId_bitbucket_bitbucketId_fk": {
|
||||
"name": "application_bitbucketId_bitbucket_bitbucketId_fk",
|
||||
"tableFrom": "application",
|
||||
@@ -2977,24 +2914,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaRepository": {
|
||||
"name": "giteaRepository",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaOwner": {
|
||||
"name": "giteaOwner",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaBranch": {
|
||||
"name": "giteaBranch",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"customGitUrl": {
|
||||
"name": "customGitUrl",
|
||||
"type": "text",
|
||||
@@ -3092,12 +3011,6 @@
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"serverId": {
|
||||
"name": "serverId",
|
||||
"type": "text",
|
||||
@@ -3172,19 +3085,6 @@
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"compose_giteaId_gitea_giteaId_fk": {
|
||||
"name": "compose_giteaId_gitea_giteaId_fk",
|
||||
"tableFrom": "compose",
|
||||
"tableTo": "gitea",
|
||||
"columnsFrom": [
|
||||
"giteaId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"giteaId"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"compose_serverId_server_serverId_fk": {
|
||||
"name": "compose_serverId_server_serverId_fk",
|
||||
"tableFrom": "compose",
|
||||
@@ -4043,107 +3943,6 @@
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.gitea": {
|
||||
"name": "gitea",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"giteaId": {
|
||||
"name": "giteaId",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"giteaUrl": {
|
||||
"name": "giteaUrl",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'https://gitea.com'"
|
||||
},
|
||||
"redirect_uri": {
|
||||
"name": "redirect_uri",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"client_id": {
|
||||
"name": "client_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"client_secret": {
|
||||
"name": "client_secret",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"gitProviderId": {
|
||||
"name": "gitProviderId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"gitea_username": {
|
||||
"name": "gitea_username",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"access_token": {
|
||||
"name": "access_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"refresh_token": {
|
||||
"name": "refresh_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"scopes": {
|
||||
"name": "scopes",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'repo,repo:status,read:user,read:org'"
|
||||
},
|
||||
"last_authenticated_at": {
|
||||
"name": "last_authenticated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"gitea_gitProviderId_git_provider_gitProviderId_fk": {
|
||||
"name": "gitea_gitProviderId_git_provider_gitProviderId_fk",
|
||||
"tableFrom": "gitea",
|
||||
"tableTo": "git_provider",
|
||||
"columnsFrom": [
|
||||
"gitProviderId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"gitProviderId"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.server": {
|
||||
"name": "server",
|
||||
"schema": "",
|
||||
@@ -5208,7 +5007,6 @@
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea",
|
||||
"drop"
|
||||
]
|
||||
},
|
||||
@@ -5305,7 +5103,6 @@
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea",
|
||||
"raw"
|
||||
]
|
||||
},
|
||||
@@ -5334,8 +5131,7 @@
|
||||
"values": [
|
||||
"github",
|
||||
"gitlab",
|
||||
"bitbucket",
|
||||
"gitea"
|
||||
"bitbucket"
|
||||
]
|
||||
},
|
||||
"public.serverStatus": {
|
||||
|
||||
5360
apps/dokploy/drizzle/meta/0079_snapshot.json
Normal file
5360
apps/dokploy/drizzle/meta/0079_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -502,64 +502,64 @@
|
||||
{
|
||||
"idx": 71,
|
||||
"version": "7",
|
||||
"when": 1741559743256,
|
||||
"tag": "0071_flimsy_plazm",
|
||||
"when": 1741460060541,
|
||||
"tag": "0071_flaky_black_queen",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 72,
|
||||
"version": "7",
|
||||
"when": 1741593124105,
|
||||
"tag": "0072_low_redwing",
|
||||
"when": 1741487009559,
|
||||
"tag": "0072_green_susan_delgado",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 73,
|
||||
"version": "7",
|
||||
"when": 1741645208694,
|
||||
"tag": "0073_dark_tigra",
|
||||
"when": 1741489681190,
|
||||
"tag": "0073_hot_domino",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 74,
|
||||
"version": "7",
|
||||
"when": 1741673569715,
|
||||
"tag": "0074_military_miss_america",
|
||||
"when": 1741490064139,
|
||||
"tag": "0074_black_quasar",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 75,
|
||||
"version": "7",
|
||||
"when": 1742018928109,
|
||||
"tag": "0075_wild_xorn",
|
||||
"when": 1741491527516,
|
||||
"tag": "0075_young_typhoid_mary",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 76,
|
||||
"version": "7",
|
||||
"when": 1742237840762,
|
||||
"tag": "0076_tough_iron_patriot",
|
||||
"when": 1741493754270,
|
||||
"tag": "0076_young_sharon_ventura",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 77,
|
||||
"version": "7",
|
||||
"when": 1742241730000,
|
||||
"tag": "0076_young_sharon_ventura",
|
||||
"when": 1741510086231,
|
||||
"tag": "0077_chemical_dreadnoughts",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 78,
|
||||
"version": "7",
|
||||
"when": 1742241730001,
|
||||
"tag": "0077_chemical_dreadnoughts",
|
||||
"when": 1742112194375,
|
||||
"tag": "0078_uneven_omega_sentinel",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 79,
|
||||
"version": "7",
|
||||
"when": 17422417300002,
|
||||
"tag": "0078_uneven_omega_sentinel",
|
||||
"when": 1742281690186,
|
||||
"tag": "0079_bizarre_wendell_rand",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dokploy",
|
||||
"version": "v0.20.4",
|
||||
"version": "v0.20.7",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
|
||||
@@ -93,6 +93,7 @@ export default async function handler(
|
||||
try {
|
||||
const branchName = githubBody?.ref?.replace("refs/heads/", "");
|
||||
const repository = githubBody?.repository?.name;
|
||||
|
||||
const deploymentTitle = extractCommitMessage(req.headers, req.body);
|
||||
const deploymentHash = extractHash(req.headers, req.body);
|
||||
const owner = githubBody?.repository?.owner?.name;
|
||||
@@ -107,6 +108,7 @@ export default async function handler(
|
||||
eq(applications.branch, branchName),
|
||||
eq(applications.repository, repository),
|
||||
eq(applications.owner, owner),
|
||||
eq(applications.githubId, githubResult.githubId),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -151,6 +153,7 @@ export default async function handler(
|
||||
eq(compose.branch, branchName),
|
||||
eq(compose.repository, repository),
|
||||
eq(compose.owner, owner),
|
||||
eq(compose.githubId, githubResult.githubId),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -240,6 +243,7 @@ export default async function handler(
|
||||
eq(applications.branch, branch),
|
||||
eq(applications.isPreviewDeploymentsActive, true),
|
||||
eq(applications.owner, owner),
|
||||
eq(applications.githubId, githubResult.githubId),
|
||||
),
|
||||
with: {
|
||||
previewDeployments: true,
|
||||
|
||||
@@ -11,9 +11,13 @@ import {
|
||||
createBackup,
|
||||
findBackupById,
|
||||
findMariadbByBackupId,
|
||||
findMariadbById,
|
||||
findMongoByBackupId,
|
||||
findMongoById,
|
||||
findMySqlByBackupId,
|
||||
findMySqlById,
|
||||
findPostgresByBackupId,
|
||||
findPostgresById,
|
||||
findServerById,
|
||||
removeBackupById,
|
||||
removeScheduleBackup,
|
||||
@@ -25,7 +29,21 @@ import {
|
||||
updateBackupById,
|
||||
} from "@dokploy/server";
|
||||
|
||||
import { findDestinationById } from "@dokploy/server/services/destination";
|
||||
import { getS3Credentials } from "@dokploy/server/utils/backups/utils";
|
||||
import {
|
||||
execAsync,
|
||||
execAsyncRemote,
|
||||
} from "@dokploy/server/utils/process/execAsync";
|
||||
import {
|
||||
restoreMariadbBackup,
|
||||
restoreMongoBackup,
|
||||
restoreMySqlBackup,
|
||||
restorePostgresBackup,
|
||||
} from "@dokploy/server/utils/restore";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { observable } from "@trpc/server/observable";
|
||||
import { z } from "zod";
|
||||
|
||||
export const backupRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
@@ -209,27 +227,146 @@ export const backupRouter = createTRPCRouter({
|
||||
});
|
||||
}
|
||||
}),
|
||||
listBackupFiles: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
destinationId: z.string(),
|
||||
search: z.string(),
|
||||
serverId: z.string().optional(),
|
||||
}),
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
try {
|
||||
const destination = await findDestinationById(input.destinationId);
|
||||
const rcloneFlags = getS3Credentials(destination);
|
||||
const bucketPath = `:s3:${destination.bucket}`;
|
||||
|
||||
const lastSlashIndex = input.search.lastIndexOf("/");
|
||||
const baseDir =
|
||||
lastSlashIndex !== -1
|
||||
? input.search.slice(0, lastSlashIndex + 1)
|
||||
: "";
|
||||
const searchTerm =
|
||||
lastSlashIndex !== -1
|
||||
? input.search.slice(lastSlashIndex + 1)
|
||||
: input.search;
|
||||
|
||||
const searchPath = baseDir ? `${bucketPath}/${baseDir}` : bucketPath;
|
||||
const listCommand = `rclone lsf ${rcloneFlags.join(" ")} "${searchPath}" | head -n 100`;
|
||||
|
||||
let stdout = "";
|
||||
|
||||
if (input.serverId) {
|
||||
const result = await execAsyncRemote(listCommand, input.serverId);
|
||||
stdout = result.stdout;
|
||||
} else {
|
||||
const result = await execAsync(listCommand);
|
||||
stdout = result.stdout;
|
||||
}
|
||||
|
||||
const files = stdout.split("\n").filter(Boolean);
|
||||
|
||||
const results = baseDir
|
||||
? files.map((file) => `${baseDir}${file}`)
|
||||
: files;
|
||||
|
||||
if (searchTerm) {
|
||||
return results.filter((file) =>
|
||||
file.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||
);
|
||||
}
|
||||
|
||||
return results;
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Error listing backup files",
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
restoreBackupWithLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
enabled: false,
|
||||
path: "/restore-backup-with-logs",
|
||||
method: "POST",
|
||||
override: true,
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
databaseId: z.string(),
|
||||
databaseType: z.enum(["postgres", "mysql", "mariadb", "mongo"]),
|
||||
databaseName: z.string().min(1),
|
||||
backupFile: z.string().min(1),
|
||||
destinationId: z.string().min(1),
|
||||
}),
|
||||
)
|
||||
.subscription(async ({ input }) => {
|
||||
const destination = await findDestinationById(input.destinationId);
|
||||
if (input.databaseType === "postgres") {
|
||||
const postgres = await findPostgresById(input.databaseId);
|
||||
|
||||
return observable<string>((emit) => {
|
||||
restorePostgresBackup(
|
||||
postgres,
|
||||
destination,
|
||||
input.databaseName,
|
||||
input.backupFile,
|
||||
(log) => {
|
||||
emit.next(log);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
if (input.databaseType === "mysql") {
|
||||
const mysql = await findMySqlById(input.databaseId);
|
||||
return observable<string>((emit) => {
|
||||
restoreMySqlBackup(
|
||||
mysql,
|
||||
destination,
|
||||
input.databaseName,
|
||||
input.backupFile,
|
||||
(log) => {
|
||||
emit.next(log);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
if (input.databaseType === "mariadb") {
|
||||
const mariadb = await findMariadbById(input.databaseId);
|
||||
return observable<string>((emit) => {
|
||||
restoreMariadbBackup(
|
||||
mariadb,
|
||||
destination,
|
||||
input.databaseName,
|
||||
input.backupFile,
|
||||
(log) => {
|
||||
emit.next(log);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
if (input.databaseType === "mongo") {
|
||||
const mongo = await findMongoById(input.databaseId);
|
||||
return observable<string>((emit) => {
|
||||
restoreMongoBackup(
|
||||
mongo,
|
||||
destination,
|
||||
input.databaseName,
|
||||
input.backupFile,
|
||||
(log) => {
|
||||
emit.next(log);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}),
|
||||
});
|
||||
|
||||
// export const getAdminId = async (backupId: string) => {
|
||||
// const backup = await findBackupById(backupId);
|
||||
|
||||
// if (backup.databaseType === "postgres" && backup.postgresId) {
|
||||
// const postgres = await findPostgresById(backup.postgresId);
|
||||
// return postgres.project.adminId;
|
||||
// }
|
||||
// if (backup.databaseType === "mariadb" && backup.mariadbId) {
|
||||
// const mariadb = await findMariadbById(backup.mariadbId);
|
||||
// return mariadb.project.adminId;
|
||||
// }
|
||||
// if (backup.databaseType === "mysql" && backup.mysqlId) {
|
||||
// const mysql = await findMySqlById(backup.mysqlId);
|
||||
// return mysql.project.adminId;
|
||||
// }
|
||||
// if (backup.databaseType === "mongo" && backup.mongoId) {
|
||||
// const mongo = await findMongoById(backup.mongoId);
|
||||
// return mongo.project.adminId;
|
||||
// }
|
||||
|
||||
// return null;
|
||||
// };
|
||||
|
||||
@@ -13,7 +13,9 @@ import {
|
||||
findDomainById,
|
||||
findDomainsByApplicationId,
|
||||
findDomainsByComposeId,
|
||||
findOrganizationById,
|
||||
findPreviewDeploymentById,
|
||||
findServerById,
|
||||
generateTraefikMeDomain,
|
||||
manageDomain,
|
||||
removeDomain,
|
||||
@@ -94,6 +96,19 @@ export const domainRouter = createTRPCRouter({
|
||||
input.serverId,
|
||||
);
|
||||
}),
|
||||
canGenerateTraefikMeDomains: protectedProcedure
|
||||
.input(z.object({ serverId: z.string() }))
|
||||
.query(async ({ input, ctx }) => {
|
||||
const organization = await findOrganizationById(
|
||||
ctx.session.activeOrganizationId,
|
||||
);
|
||||
|
||||
if (input.serverId) {
|
||||
const server = await findServerById(input.serverId);
|
||||
return server.ipAddress;
|
||||
}
|
||||
return organization?.owner.serverIp;
|
||||
}),
|
||||
|
||||
update: protectedProcedure
|
||||
.input(apiUpdateDomain)
|
||||
|
||||
@@ -2,12 +2,16 @@ import type { IncomingMessage } from "node:http";
|
||||
import * as bcrypt from "bcrypt";
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import { APIError } from "better-auth/api";
|
||||
import { apiKey, organization, twoFactor } from "better-auth/plugins";
|
||||
import { and, desc, eq } from "drizzle-orm";
|
||||
import { IS_CLOUD } from "../constants";
|
||||
import { db } from "../db";
|
||||
import * as schema from "../db/schema";
|
||||
import { getUserByToken } from "../services/admin";
|
||||
import { updateUser } from "../services/user";
|
||||
import { sendEmail } from "../verification/send-verification-email";
|
||||
import { getPublicIpWithFallback } from "../wss/utils";
|
||||
|
||||
const { handler, api } = betterAuth({
|
||||
database: drizzleAdapter(db, {
|
||||
@@ -88,11 +92,40 @@ const { handler, api } = betterAuth({
|
||||
databaseHooks: {
|
||||
user: {
|
||||
create: {
|
||||
before: async (_user, context) => {
|
||||
if (!IS_CLOUD) {
|
||||
const xDokployToken =
|
||||
context?.request?.headers?.get("x-dokploy-token");
|
||||
if (xDokployToken) {
|
||||
const user = await getUserByToken(xDokployToken);
|
||||
if (!user) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const isAdminPresent = await db.query.member.findFirst({
|
||||
where: eq(schema.member.role, "owner"),
|
||||
});
|
||||
if (isAdminPresent) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: "Admin is already created",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
after: async (user) => {
|
||||
const isAdminPresent = await db.query.member.findFirst({
|
||||
where: eq(schema.member.role, "owner"),
|
||||
});
|
||||
|
||||
if (!IS_CLOUD) {
|
||||
await updateUser(user.id, {
|
||||
serverIp: await getPublicIpWithFallback(),
|
||||
});
|
||||
}
|
||||
|
||||
if (IS_CLOUD || !isAdminPresent) {
|
||||
await db.transaction(async (tx) => {
|
||||
const organization = await tx
|
||||
|
||||
@@ -361,7 +361,7 @@ const installUtilities = () => `
|
||||
alpine)
|
||||
sed -i '/^#.*\/community/s/^#//' /etc/apk/repositories
|
||||
apk update >/dev/null
|
||||
apk add curl wget git jq openssl >/dev/null
|
||||
apk add curl wget git jq openssl sudo unzip tar >/dev/null
|
||||
;;
|
||||
ubuntu | debian | raspbian)
|
||||
DEBIAN_FRONTEND=noninteractive apt-get update -y >/dev/null
|
||||
|
||||
@@ -101,11 +101,11 @@ export const initializeTraefik = async ({
|
||||
console.log("Waiting for service cleanup...");
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
attempts++;
|
||||
} catch (e) {
|
||||
} catch (_e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
console.log("No existing service to remove");
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ export const initializeTraefik = async ({
|
||||
|
||||
await container.remove({ force: true });
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
} catch (error) {
|
||||
} catch (_err) {
|
||||
console.log("No existing container to remove");
|
||||
}
|
||||
|
||||
|
||||
4
packages/server/src/utils/restore/index.ts
Normal file
4
packages/server/src/utils/restore/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { restorePostgresBackup } from "./postgres";
|
||||
export { restoreMySqlBackup } from "./mysql";
|
||||
export { restoreMariadbBackup } from "./mariadb";
|
||||
export { restoreMongoBackup } from "./mongo";
|
||||
56
packages/server/src/utils/restore/mariadb.ts
Normal file
56
packages/server/src/utils/restore/mariadb.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import type { Destination } from "@dokploy/server/services/destination";
|
||||
import type { Mariadb } from "@dokploy/server/services/mariadb";
|
||||
import { getS3Credentials } from "../backups/utils";
|
||||
import {
|
||||
getRemoteServiceContainer,
|
||||
getServiceContainer,
|
||||
} from "../docker/utils";
|
||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||
|
||||
export const restoreMariadbBackup = async (
|
||||
mariadb: Mariadb,
|
||||
destination: Destination,
|
||||
database: string,
|
||||
backupFile: string,
|
||||
emit: (log: string) => void,
|
||||
) => {
|
||||
try {
|
||||
const { appName, databasePassword, databaseUser, serverId } = mariadb;
|
||||
|
||||
const rcloneFlags = getS3Credentials(destination);
|
||||
const bucketPath = `:s3:${destination.bucket}`;
|
||||
const backupPath = `${bucketPath}/${backupFile}`;
|
||||
|
||||
const { Id: containerName } = serverId
|
||||
? await getRemoteServiceContainer(serverId, appName)
|
||||
: await getServiceContainer(appName);
|
||||
|
||||
const restoreCommand = `
|
||||
rclone cat ${rcloneFlags.join(" ")} "${backupPath}" | gunzip | docker exec -i ${containerName} mariadb -u ${databaseUser} -p${databasePassword} ${database}
|
||||
`;
|
||||
|
||||
emit("Starting restore...");
|
||||
|
||||
emit(`Executing command: ${restoreCommand}`);
|
||||
|
||||
if (serverId) {
|
||||
await execAsyncRemote(serverId, restoreCommand);
|
||||
} else {
|
||||
await execAsync(restoreCommand);
|
||||
}
|
||||
|
||||
emit("Restore completed successfully!");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
emit(
|
||||
`Error: ${
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Error restoring mariadb backup"
|
||||
}`,
|
||||
);
|
||||
throw new Error(
|
||||
error instanceof Error ? error.message : "Error restoring mariadb backup",
|
||||
);
|
||||
}
|
||||
};
|
||||
64
packages/server/src/utils/restore/mongo.ts
Normal file
64
packages/server/src/utils/restore/mongo.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import type { Destination } from "@dokploy/server/services/destination";
|
||||
import type { Mongo } from "@dokploy/server/services/mongo";
|
||||
import { getS3Credentials } from "../backups/utils";
|
||||
import {
|
||||
getRemoteServiceContainer,
|
||||
getServiceContainer,
|
||||
} from "../docker/utils";
|
||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||
|
||||
export const restoreMongoBackup = async (
|
||||
mongo: Mongo,
|
||||
destination: Destination,
|
||||
database: string,
|
||||
backupFile: string,
|
||||
emit: (log: string) => void,
|
||||
) => {
|
||||
try {
|
||||
const { appName, databasePassword, databaseUser, serverId } = mongo;
|
||||
|
||||
const rcloneFlags = getS3Credentials(destination);
|
||||
const bucketPath = `:s3:${destination.bucket}`;
|
||||
const backupPath = `${bucketPath}/${backupFile}`;
|
||||
|
||||
const { Id: containerName } = serverId
|
||||
? await getRemoteServiceContainer(serverId, appName)
|
||||
: await getServiceContainer(appName);
|
||||
|
||||
// For MongoDB, we need to first download the backup file since mongorestore expects a directory
|
||||
const tempDir = "/tmp/dokploy-restore";
|
||||
const fileName = backupFile.split("/").pop() || "backup.dump.gz";
|
||||
const decompressedName = fileName.replace(".gz", "");
|
||||
|
||||
const downloadCommand = `\
|
||||
rm -rf ${tempDir} && \
|
||||
mkdir -p ${tempDir} && \
|
||||
rclone copy ${rcloneFlags.join(" ")} "${backupPath}" ${tempDir} && \
|
||||
cd ${tempDir} && \
|
||||
gunzip -f "${fileName}" && \
|
||||
docker exec -i ${containerName} mongorestore --username ${databaseUser} --password ${databasePassword} --authenticationDatabase admin --db ${database} --archive < "${decompressedName}" && \
|
||||
rm -rf ${tempDir}`;
|
||||
|
||||
emit("Starting restore...");
|
||||
|
||||
emit(`Executing command: ${downloadCommand}`);
|
||||
|
||||
if (serverId) {
|
||||
await execAsyncRemote(serverId, downloadCommand);
|
||||
} else {
|
||||
await execAsync(downloadCommand);
|
||||
}
|
||||
|
||||
emit("Restore completed successfully!");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
emit(
|
||||
`Error: ${
|
||||
error instanceof Error ? error.message : "Error restoring mongo backup"
|
||||
}`,
|
||||
);
|
||||
throw new Error(
|
||||
error instanceof Error ? error.message : "Error restoring mongo backup",
|
||||
);
|
||||
}
|
||||
};
|
||||
54
packages/server/src/utils/restore/mysql.ts
Normal file
54
packages/server/src/utils/restore/mysql.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import type { Destination } from "@dokploy/server/services/destination";
|
||||
import type { MySql } from "@dokploy/server/services/mysql";
|
||||
import { getS3Credentials } from "../backups/utils";
|
||||
import {
|
||||
getRemoteServiceContainer,
|
||||
getServiceContainer,
|
||||
} from "../docker/utils";
|
||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||
|
||||
export const restoreMySqlBackup = async (
|
||||
mysql: MySql,
|
||||
destination: Destination,
|
||||
database: string,
|
||||
backupFile: string,
|
||||
emit: (log: string) => void,
|
||||
) => {
|
||||
try {
|
||||
const { appName, databaseRootPassword, serverId } = mysql;
|
||||
|
||||
const rcloneFlags = getS3Credentials(destination);
|
||||
const bucketPath = `:s3:${destination.bucket}`;
|
||||
const backupPath = `${bucketPath}/${backupFile}`;
|
||||
|
||||
const { Id: containerName } = serverId
|
||||
? await getRemoteServiceContainer(serverId, appName)
|
||||
: await getServiceContainer(appName);
|
||||
|
||||
const restoreCommand = `
|
||||
rclone cat ${rcloneFlags.join(" ")} "${backupPath}" | gunzip | docker exec -i ${containerName} mysql -u root -p${databaseRootPassword} ${database}
|
||||
`;
|
||||
|
||||
emit("Starting restore...");
|
||||
|
||||
emit(`Executing command: ${restoreCommand}`);
|
||||
|
||||
if (serverId) {
|
||||
await execAsyncRemote(serverId, restoreCommand);
|
||||
} else {
|
||||
await execAsync(restoreCommand);
|
||||
}
|
||||
|
||||
emit("Restore completed successfully!");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
emit(
|
||||
`Error: ${
|
||||
error instanceof Error ? error.message : "Error restoring mysql backup"
|
||||
}`,
|
||||
);
|
||||
throw new Error(
|
||||
error instanceof Error ? error.message : "Error restoring mysql backup",
|
||||
);
|
||||
}
|
||||
};
|
||||
60
packages/server/src/utils/restore/postgres.ts
Normal file
60
packages/server/src/utils/restore/postgres.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { Destination } from "@dokploy/server/services/destination";
|
||||
import type { Postgres } from "@dokploy/server/services/postgres";
|
||||
import { getS3Credentials } from "../backups/utils";
|
||||
import {
|
||||
getRemoteServiceContainer,
|
||||
getServiceContainer,
|
||||
} from "../docker/utils";
|
||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||
|
||||
export const restorePostgresBackup = async (
|
||||
postgres: Postgres,
|
||||
destination: Destination,
|
||||
database: string,
|
||||
backupFile: string,
|
||||
emit: (log: string) => void,
|
||||
) => {
|
||||
try {
|
||||
const { appName, databaseUser, serverId } = postgres;
|
||||
|
||||
const rcloneFlags = getS3Credentials(destination);
|
||||
const bucketPath = `:s3:${destination.bucket}`;
|
||||
|
||||
const backupPath = `${bucketPath}/${backupFile}`;
|
||||
|
||||
const { Id: containerName } = serverId
|
||||
? await getRemoteServiceContainer(serverId, appName)
|
||||
: await getServiceContainer(appName);
|
||||
|
||||
emit("Starting restore...");
|
||||
emit(`Backup path: ${backupPath}`);
|
||||
|
||||
const command = `\
|
||||
rclone cat ${rcloneFlags.join(" ")} "${backupPath}" | gunzip | docker exec -i ${containerName} pg_restore -U ${databaseUser} -d ${database} --clean --if-exists`;
|
||||
|
||||
emit(`Executing command: ${command}`);
|
||||
|
||||
if (serverId) {
|
||||
const { stdout, stderr } = await execAsyncRemote(serverId, command);
|
||||
emit(stdout);
|
||||
emit(stderr);
|
||||
} else {
|
||||
const { stdout, stderr } = await execAsync(command);
|
||||
console.log("stdout", stdout);
|
||||
console.log("stderr", stderr);
|
||||
emit(stdout);
|
||||
emit(stderr);
|
||||
}
|
||||
|
||||
emit("Restore completed successfully!");
|
||||
} catch (error) {
|
||||
emit(
|
||||
`Error: ${
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Error restoring postgres backup"
|
||||
}`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user