* feat: add schema for registry and routes

* feat: add docker registry upload

* feat: add show cluster

* refactor: set the registry url in image in case we have a registry asociated

* feat: add update registry and fix the docker url markup

* chore: remove --advertise-ip on swarm script

* refactor: remove listen address of swarm initialize

* feat: add table to show nodes and add dropdown to add manager & workers

* refactor: improve interface for cluster

* refactor: improve UI

* feat: add experimental swarm settings

* refactor: remove comments

* refactor: prettify json of each setting

* refactor: add interface tooltip

* refactor: delete static form self registry

* refactor: allow to se a empty registry

* fix: remove text area warnings

* feat: add network swarm json

* refactor: update ui

* revert: go back to swarm init config

* refactor: remove initialization on server, only on setup script

* Update LICENSE.MD

* feat: appearance theme support system config

* refactor: remove logs

* fix(README-ru): hyperlink-ed docs url

* feat: (#107) webhook listener filter docker events based on image tag.

Fixes #107

* refactor: simplify comparison docker tags

* refactor: remove return in res status

* refactor: prevent to updates download automatically

* feat: support code editor (#105)

* feat: support code editor

* Update codeblock

* refactor: remove unused class

---------

Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>

* fix: select the right image from sourcetype (#109)

* chore: bump minor version

---------

Co-authored-by: hehehai <riverhohai@gmail.com>
Co-authored-by: Bayram Tagiev <bayram.tagiev.a@gmail.com>
Co-authored-by: Paulo Santana <30875229+hikinine@users.noreply.github.com>
This commit is contained in:
Mauricio Siu
2024-05-29 21:05:22 -06:00
committed by GitHub
parent 56a94ad14a
commit 7cb299a4bb
124 changed files with 26520 additions and 1525 deletions

View File

@@ -26,7 +26,7 @@ FROM node:18-slim AS production
# Install dependencies only for production
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable && apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
RUN corepack enable && apt-get update && apt-get install -y curl && apt-get install -y apache2-utils && rm -rf /var/lib/apt/lists/*
WORKDIR /app
@@ -47,7 +47,6 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-l
# Install docker
RUN curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh && rm get-docker.sh
# Install Nixpacks and tsx
# | VERBOSE=1 VERSION=1.21.0 bash
RUN curl -sSL https://nixpacks.com/install.sh -o install.sh \

View File

@@ -1,5 +1,7 @@
# License
## Core License (Apache License 2.0)
Copyright 2024 Mauricio Siu.
Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,11 +15,12 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.
## Appendix
## Additional Terms for Specific Features
In the event of a conflict, the provisions in this appendix shall take precedence over those in the Apache License.
The following additional terms apply to the multi-node support and Docker Compose file support features of Dokploy. In the event of a conflict, these provisions shall take precedence over those in the Apache License:
- **Modification Distribution:** Any modifications to the software must be distributed freely.
- **Future Paid Features:** Any future paid features of Dokploy cannot be sold or offered as a service by any party other than the copyright holder without prior written consent.
- **Self-Hosted Version Free**: All features of Dokploy, including multi-node support and Docker Compose file support, will always be free to use in the self-hosted version.
- **Restriction on Resale**: The multi-node support and Docker Compose file support features cannot be sold or offered as a service by any party other than the copyright holder without prior written consent.
- **Modification Distribution**: Any modifications to the multi-node support and Docker Compose file support features must be distributed freely and cannot be sold or offered as a service.
For further inquiries or permissions, please contact us directly.

View File

@@ -46,4 +46,4 @@ curl -sSL https://dokploy.com/install.sh | sh
- Centos 9
## 📄 Документация
Для подробной документации посетите docs.dokploy.com/docs.
Для подробной документации посетите [docs.dokploy.com/docs](https://docs.dokploy.com).

View File

@@ -0,0 +1,756 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { api } from "@/utils/api";
import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { Textarea } from "@/components/ui/textarea";
import { HelpCircle, Settings } from "lucide-react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
const HealthCheckSwarmSchema = z
.object({
Test: z.array(z.string()).optional(),
Interval: z.number().optional(),
Timeout: z.number().optional(),
StartPeriod: z.number().optional(),
Retries: z.number().optional(),
})
.strict();
const RestartPolicySwarmSchema = z
.object({
Condition: z.string().optional(),
Delay: z.number().optional(),
MaxAttempts: z.number().optional(),
Window: z.number().optional(),
})
.strict();
const PreferenceSchema = z
.object({
Spread: z.object({
SpreadDescriptor: z.string(),
}),
})
.strict();
const PlatformSchema = z
.object({
Architecture: z.string(),
OS: z.string(),
})
.strict();
const PlacementSwarmSchema = z
.object({
Constraints: z.array(z.string()).optional(),
Preferences: z.array(PreferenceSchema).optional(),
MaxReplicas: z.number().optional(),
Platforms: z.array(PlatformSchema).optional(),
})
.strict();
const UpdateConfigSwarmSchema = z
.object({
Parallelism: z.number(),
Delay: z.number().optional(),
FailureAction: z.string().optional(),
Monitor: z.number().optional(),
MaxFailureRatio: z.number().optional(),
Order: z.string(),
})
.strict();
const ReplicatedSchema = z
.object({
Replicas: z.number().optional(),
})
.strict();
const ReplicatedJobSchema = z
.object({
MaxConcurrent: z.number().optional(),
TotalCompletions: z.number().optional(),
})
.strict();
const ServiceModeSwarmSchema = z
.object({
Replicated: ReplicatedSchema.optional(),
Global: z.object({}).optional(),
ReplicatedJob: ReplicatedJobSchema.optional(),
GlobalJob: z.object({}).optional(),
})
.strict();
const NetworkSwarmSchema = z.array(
z
.object({
Target: z.string().optional(),
Aliases: z.array(z.string()).optional(),
DriverOpts: z.object({}).optional(),
})
.strict(),
);
const LabelsSwarmSchema = z.record(z.string());
const createStringToJSONSchema = (schema: z.ZodTypeAny) => {
return z
.string()
.transform((str, ctx) => {
if (str === null || str === "") {
return null;
}
try {
return JSON.parse(str);
} catch (e) {
ctx.addIssue({ code: "custom", message: "Invalid JSON format" });
return z.NEVER;
}
})
.superRefine((data, ctx) => {
if (data === null) {
return;
}
if (Object.keys(data).length === 0) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Object cannot be empty",
});
return;
}
const parseResult = schema.safeParse(data);
if (!parseResult.success) {
for (const error of parseResult.error.issues) {
const path = error.path.join(".");
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `${path} ${error.message}`,
});
}
}
});
};
const addSwarmSettings = z.object({
healthCheckSwarm: createStringToJSONSchema(HealthCheckSwarmSchema).nullable(),
restartPolicySwarm: createStringToJSONSchema(
RestartPolicySwarmSchema,
).nullable(),
placementSwarm: createStringToJSONSchema(PlacementSwarmSchema).nullable(),
updateConfigSwarm: createStringToJSONSchema(
UpdateConfigSwarmSchema,
).nullable(),
rollbackConfigSwarm: createStringToJSONSchema(
UpdateConfigSwarmSchema,
).nullable(),
modeSwarm: createStringToJSONSchema(ServiceModeSwarmSchema).nullable(),
labelsSwarm: createStringToJSONSchema(LabelsSwarmSchema).nullable(),
networkSwarm: createStringToJSONSchema(NetworkSwarmSchema).nullable(),
});
type AddSwarmSettings = z.infer<typeof addSwarmSettings>;
interface Props {
applicationId: string;
}
export const AddSwarmSettings = ({ applicationId }: Props) => {
const { data, refetch } = api.application.one.useQuery(
{
applicationId,
},
{
enabled: !!applicationId,
},
);
const { mutateAsync, isError, error, isLoading } =
api.application.update.useMutation();
const form = useForm<AddSwarmSettings>({
defaultValues: {
healthCheckSwarm: null,
restartPolicySwarm: null,
placementSwarm: null,
updateConfigSwarm: null,
rollbackConfigSwarm: null,
modeSwarm: null,
labelsSwarm: null,
networkSwarm: null,
},
resolver: zodResolver(addSwarmSettings),
});
useEffect(() => {
if (data) {
form.reset({
healthCheckSwarm: data.healthCheckSwarm
? JSON.stringify(data.healthCheckSwarm, null, 2)
: null,
restartPolicySwarm: data.restartPolicySwarm
? JSON.stringify(data.restartPolicySwarm, null, 2)
: null,
placementSwarm: data.placementSwarm
? JSON.stringify(data.placementSwarm, null, 2)
: null,
updateConfigSwarm: data.updateConfigSwarm
? JSON.stringify(data.updateConfigSwarm, null, 2)
: null,
rollbackConfigSwarm: data.rollbackConfigSwarm
? JSON.stringify(data.rollbackConfigSwarm, null, 2)
: null,
modeSwarm: data.modeSwarm
? JSON.stringify(data.modeSwarm, null, 2)
: null,
labelsSwarm: data.labelsSwarm
? JSON.stringify(data.labelsSwarm, null, 2)
: null,
networkSwarm: data.networkSwarm
? JSON.stringify(data.networkSwarm, null, 2)
: null,
});
}
}, [form, form.reset, data]);
const onSubmit = async (data: AddSwarmSettings) => {
await mutateAsync({
applicationId,
healthCheckSwarm: data.healthCheckSwarm,
restartPolicySwarm: data.restartPolicySwarm,
placementSwarm: data.placementSwarm,
updateConfigSwarm: data.updateConfigSwarm,
rollbackConfigSwarm: data.rollbackConfigSwarm,
modeSwarm: data.modeSwarm,
labelsSwarm: data.labelsSwarm,
networkSwarm: data.networkSwarm,
})
.then(async () => {
toast.success("Swarm settings updated");
refetch();
})
.catch(() => {
toast.error("Error to update the swarm settings");
});
};
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="secondary" className="cursor-pointer w-fit">
<Settings className="size-4 text-muted-foreground" />
Swarm Settings
</Button>
</DialogTrigger>
<DialogContent className="max-h-[85vh] overflow-y-auto sm:max-w-5xl p-0">
<DialogHeader className="p-6">
<DialogTitle>Swarm Settings</DialogTitle>
<DialogDescription>
Update certain settings using a json object.
</DialogDescription>
</DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
<Form {...form}>
<form
id="hook-form-add-permissions"
onSubmit={form.handleSubmit(onSubmit)}
className="grid grid-cols-1 md:grid-cols-2 w-full gap-4 relative"
>
<FormField
control={form.control}
name="healthCheckSwarm"
render={({ field }) => (
<FormItem className="relative max-lg:px-4 lg:pl-6 ">
<FormLabel>Health Check</FormLabel>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<FormDescription className="break-all w-fit flex flex-row gap-1 items-center">
Check the interface
<HelpCircle className="size-4 text-muted-foreground" />
</FormDescription>
</TooltipTrigger>
<TooltipContent
className="w-full z-[999]"
align="start"
side="bottom"
>
<code>
<pre>
{`{
Test?: string[] | undefined;
Interval?: number | undefined;
Timeout?: number | undefined;
StartPeriod?: number | undefined;
Retries?: number | undefined;
}`}
</pre>
</code>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<FormControl>
<Textarea
className="font-mono [field-sizing:content;] min-h-[11.2rem]"
placeholder={`{
"Test" : ["CMD-SHELL", "curl -f http://localhost:3000/health"],
"Interval" : 10000,
"Timeout" : 10000,
"StartPeriod" : 10000,
"Retries" : 10
}`}
{...field}
value={field?.value || ""}
/>
</FormControl>
<pre>
<FormMessage />
</pre>
</FormItem>
)}
/>
<FormField
control={form.control}
name="restartPolicySwarm"
render={({ field }) => (
<FormItem className="relative max-lg:px-4 lg:pr-6 ">
<FormLabel>Restart Policy</FormLabel>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<FormDescription className="break-all w-fit flex flex-row gap-1 items-center">
Check the interface
<HelpCircle className="size-4 text-muted-foreground" />
</FormDescription>
</TooltipTrigger>
<TooltipContent
className="w-full z-[999]"
align="start"
side="bottom"
>
<code>
<pre>
{`{
Condition?: string | undefined;
Delay?: number | undefined;
MaxAttempts?: number | undefined;
Window?: number | undefined;
}`}
</pre>
</code>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<FormControl>
<Textarea
className="font-mono [field-sizing:content;] min-h-[11.2rem]"
placeholder={`{
"Condition" : "on-failure",
"Delay" : 10000,
"MaxAttempts" : 10,
"Window" : 10000
} `}
{...field}
value={field?.value || ""}
/>
</FormControl>
<pre>
<FormMessage />
</pre>
</FormItem>
)}
/>
<FormField
control={form.control}
name="placementSwarm"
render={({ field }) => (
<FormItem className="relative max-lg:px-4 lg:pl-6 ">
<FormLabel>Placement</FormLabel>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<FormDescription className="break-all w-fit flex flex-row gap-1 items-center">
Check the interface
<HelpCircle className="size-4 text-muted-foreground" />
</FormDescription>
</TooltipTrigger>
<TooltipContent
className="w-full z-[999]"
align="start"
side="bottom"
>
<code>
<pre>
{`{
Constraints?: string[] | undefined;
Preferences?: Array<{ Spread: { SpreadDescriptor: string } }> | undefined;
MaxReplicas?: number | undefined;
Platforms?:
| Array<{
Architecture: string;
OS: string;
}>
| undefined;
}`}
</pre>
</code>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<FormControl>
<Textarea
className="font-mono [field-sizing:content;] min-h-[18.7rem]"
placeholder={`{
"Constraints" : ["node.role==manager"],
"Preferences" : [{
"Spread" : {
"SpreadDescriptor" : "node.labels.region"
}
}],
"MaxReplicas" : 10,
"Platforms" : [{
"Architecture" : "amd64",
"OS" : "linux"
}]
} `}
{...field}
value={field?.value || ""}
/>
</FormControl>
<pre>
<FormMessage />
</pre>
</FormItem>
)}
/>
<FormField
control={form.control}
name="updateConfigSwarm"
render={({ field }) => (
<FormItem className="relative max-lg:px-4 lg:pr-6 ">
<FormLabel>Update Config</FormLabel>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<FormDescription className="break-all w-fit flex flex-row gap-1 items-center">
Check the interface
<HelpCircle className="size-4 text-muted-foreground" />
</FormDescription>
</TooltipTrigger>
<TooltipContent
className="w-full z-[999]"
align="start"
side="bottom"
>
<code>
<pre>
{`{
Parallelism?: number;
Delay?: number | undefined;
FailureAction?: string | undefined;
Monitor?: number | undefined;
MaxFailureRatio?: number | undefined;
Order: string;
}`}
</pre>
</code>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<FormControl>
<Textarea
className="font-mono [field-sizing:content;] min-h-[18.7rem]"
placeholder={`{
"Parallelism" : 1,
"Delay" : 10000,
"FailureAction" : "continue",
"Monitor" : 10000,
"MaxFailureRatio" : 10,
"Order" : "start-first"
}`}
{...field}
value={field?.value || ""}
/>
</FormControl>
<pre>
<FormMessage />
</pre>
</FormItem>
)}
/>
<FormField
control={form.control}
name="rollbackConfigSwarm"
render={({ field }) => (
<FormItem className="relative max-lg:px-4 lg:pl-6 ">
<FormLabel>Rollback Config</FormLabel>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<FormDescription className="break-all w-fit flex flex-row gap-1 items-center">
Check the interface
<HelpCircle className="size-4 text-muted-foreground" />
</FormDescription>
</TooltipTrigger>
<TooltipContent
className="w-full z-[999]"
align="start"
side="bottom"
>
<code>
<pre>
{`{
Parallelism?: number;
Delay?: number | undefined;
FailureAction?: string | undefined;
Monitor?: number | undefined;
MaxFailureRatio?: number | undefined;
Order: string;
}`}
</pre>
</code>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<FormControl>
<Textarea
className="font-mono [field-sizing:content;] min-h-[14.8rem]"
placeholder={`{
"Parallelism" : 1,
"Delay" : 10000,
"FailureAction" : "continue",
"Monitor" : 10000,
"MaxFailureRatio" : 10,
"Order" : "start-first"
}`}
{...field}
value={field?.value || ""}
/>
</FormControl>
<pre>
<FormMessage />
</pre>
</FormItem>
)}
/>
<FormField
control={form.control}
name="modeSwarm"
render={({ field }) => (
<FormItem className="relative max-lg:px-4 lg:pr-6 ">
<FormLabel>Mode</FormLabel>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<FormDescription className="break-all w-fit flex flex-row gap-1 items-center">
Check the interface
<HelpCircle className="size-4 text-muted-foreground" />
</FormDescription>
</TooltipTrigger>
<TooltipContent
className="w-full z-[999]"
align="center"
side="bottom"
>
<code>
<pre>
{`{
Replicated?: { Replicas?: number | undefined } | undefined;
Global?: {} | undefined;
ReplicatedJob?:
| {
MaxConcurrent?: number | undefined;
TotalCompletions?: number | undefined;
}
| undefined;
GlobalJob?: {} | undefined;
}`}
</pre>
</code>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<FormControl>
<Textarea
className="font-mono [field-sizing:content;] min-h-[14.8rem]"
placeholder={`{
"Replicated" : {
"Replicas" : 1
},
"Global" : {},
"ReplicatedJob" : {
"MaxConcurrent" : 1,
"TotalCompletions" : 1
},
"GlobalJob" : {}
}`}
{...field}
value={field?.value || ""}
/>
</FormControl>
<pre>
<FormMessage />
</pre>
</FormItem>
)}
/>
<FormField
control={form.control}
name="networkSwarm"
render={({ field }) => (
<FormItem className="relative max-lg:px-4 lg:pl-6 ">
<FormLabel>Network</FormLabel>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<FormDescription className="break-all w-fit flex flex-row gap-1 items-center">
Check the interface
<HelpCircle className="size-4 text-muted-foreground" />
</FormDescription>
</TooltipTrigger>
<TooltipContent
className="w-full z-[999]"
align="start"
side="bottom"
>
<code>
<pre>
{`[
{
"Target" : string | undefined;
"Aliases" : string[] | undefined;
"DriverOpts" : { [key: string]: string } | undefined;
}
]`}
</pre>
</code>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<FormControl>
<Textarea
className="font-mono [field-sizing:content;] min-h-[18.5rem]"
placeholder={`[
{
"Target" : "dokploy-network",
"Aliases" : ["dokploy-network"],
"DriverOpts" : {
"com.docker.network.driver.mtu" : "1500",
"com.docker.network.driver.host_binding" : "true",
"com.docker.network.driver.mtu" : "1500",
"com.docker.network.driver.host_binding" : "true"
}
}
]`}
{...field}
value={field?.value || ""}
/>
</FormControl>
<pre>
<FormMessage />
</pre>
</FormItem>
)}
/>
<FormField
control={form.control}
name="labelsSwarm"
render={({ field }) => (
<FormItem className="relative max-lg:px-4 lg:pr-6 ">
<FormLabel>Labels</FormLabel>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<FormDescription className="break-all w-fit flex flex-row gap-1 items-center">
Check the interface
<HelpCircle className="size-4 text-muted-foreground" />
</FormDescription>
</TooltipTrigger>
<TooltipContent
className="w-full z-[999]"
align="start"
side="bottom"
>
<code>
<pre>
{`{
[name: string]: string;
}`}
</pre>
</code>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<FormControl>
<Textarea
className="font-mono [field-sizing:content;] min-h-[18.5rem]"
placeholder={`{
"com.example.app.name" : "my-app",
"com.example.app.version" : "1.0.0"
}`}
{...field}
value={field?.value || ""}
/>
</FormControl>
<pre>
<FormMessage />
</pre>
</FormItem>
)}
/>
<DialogFooter className="flex w-full flex-row justify-end md:col-span-2 m-0 sticky bottom-0 right-0 bg-muted border p-2 ">
<Button
isLoading={isLoading}
form="hook-form-add-permissions"
type="submit"
>
Update
</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,203 @@
import React from "react";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { api } from "@/utils/api";
import { z } from "zod";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { toast } from "sonner";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import Link from "next/link";
import { Server } from "lucide-react";
import { AddSwarmSettings } from "./modify-swarm-settings";
interface Props {
applicationId: string;
}
const AddRedirectchema = z.object({
replicas: z.number(),
registryId: z.string(),
});
type AddCommand = z.infer<typeof AddRedirectchema>;
export const ShowClusterSettings = ({ applicationId }: Props) => {
const { data } = api.application.one.useQuery(
{
applicationId,
},
{ enabled: !!applicationId },
);
const { data: registries } = api.registry.all.useQuery();
const utils = api.useUtils();
const { mutateAsync, isLoading } = api.application.update.useMutation();
const form = useForm<AddCommand>({
defaultValues: {
registryId: data?.registryId || "",
replicas: data?.replicas || 1,
},
resolver: zodResolver(AddRedirectchema),
});
useEffect(() => {
if (data?.command) {
form.reset({
registryId: data?.registryId || "",
replicas: data?.replicas || 1,
});
}
}, [form, form.reset, form.formState.isSubmitSuccessful, data?.command]);
const onSubmit = async (data: AddCommand) => {
await mutateAsync({
applicationId,
registryId: data?.registryId === "none" ? null : data?.registryId,
replicas: data?.replicas,
})
.then(async () => {
toast.success("Command Updated");
await utils.application.one.invalidate({
applicationId,
});
})
.catch(() => {
toast.error("Error to update the command");
});
};
return (
<Card className="bg-background">
<CardHeader className="flex flex-row justify-between">
<div>
<CardTitle className="text-xl">Cluster Settings</CardTitle>
<CardDescription>
Add the registry and the replicas of the application
</CardDescription>
</div>
<AddSwarmSettings applicationId={applicationId} />
</CardHeader>
<CardContent className="flex flex-col gap-4">
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full gap-4"
>
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="replicas"
render={({ field }) => (
<FormItem>
<FormLabel>Replicas</FormLabel>
<FormControl>
<Input
placeholder="1"
{...field}
onChange={(e) => {
field.onChange(Number(e.target.value));
}}
type="number"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{registries && registries?.length === 0 ? (
<div className="pt-10">
<div className="flex flex-col items-center gap-3">
<Server className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground">
To use a cluster feature, you need to configure at least a
registry first. Please, go to{" "}
<Link
href="/dashboard/settings/cluster"
className="text-foreground"
>
Settings
</Link>{" "}
to do so.
</span>
</div>
</div>
) : (
<>
<FormField
control={form.control}
name="registryId"
render={({ field }) => (
<FormItem>
<FormLabel>Select a registry</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<SelectTrigger>
<SelectValue placeholder="Select a registry" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{registries?.map((registry) => (
<SelectItem
key={registry.registryId}
value={registry.registryId}
>
{registry.registryName}
</SelectItem>
))}
<SelectItem value={"none"}>None</SelectItem>
<SelectLabel>
Registries ({registries?.length})
</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
</FormItem>
)}
/>
</>
)}
<div className="flex justify-end">
<Button isLoading={isLoading} type="submit" className="w-fit">
Save
</Button>
</div>
</form>
</Form>
</CardContent>
</Card>
);
};

View File

@@ -20,7 +20,7 @@ import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
import { Pencil } from "lucide-react";
import { PenBoxIcon, Pencil } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
@@ -99,7 +99,7 @@ export const UpdatePort = ({ portId }: Props) => {
<Dialog>
<DialogTrigger asChild>
<Button variant="ghost" isLoading={isLoading}>
<Pencil className="size-4 text-muted-foreground" />
<PenBoxIcon className="size-4 text-muted-foreground" />
</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -21,7 +21,7 @@ import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
import { Pencil } from "lucide-react";
import { PenBoxIcon, Pencil } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
@@ -94,7 +94,7 @@ export const UpdateRedirect = ({ redirectId }: Props) => {
<Dialog>
<DialogTrigger asChild>
<Button variant="ghost" isLoading={isLoading}>
<Pencil className="size-4 text-muted-foreground" />
<PenBoxIcon className="size-4 text-muted-foreground" />
</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -20,7 +20,7 @@ import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
import { Pencil } from "lucide-react";
import { PenBoxIcon, Pencil } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
@@ -89,7 +89,7 @@ export const UpdateSecurity = ({ securityId }: Props) => {
<Dialog>
<DialogTrigger asChild>
<Button variant="ghost" isLoading={isLoading}>
<Pencil className="size-4 text-muted-foreground" />
<PenBoxIcon className="size-4 text-muted-foreground" />
</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -9,6 +9,7 @@ import {
import { api } from "@/utils/api";
import { File } from "lucide-react";
import { UpdateTraefikConfig } from "./update-traefik-config";
import { CodeEditor } from "@/components/shared/code-editor";
interface Props {
applicationId: string;
}
@@ -43,11 +44,13 @@ export const ShowTraefikConfig = ({ applicationId }: Props) => {
</div>
) : (
<div className="flex flex-col pt-2 relative">
<div className="flex flex-col gap-6 bg-input p-4 rounded-md max-h-[35rem] min-h-[10rem] overflow-y-auto">
<div>
<pre className="font-sans">{data || "Empty"}</pre>
</div>
<div className="flex justify-end absolute z-50 right-6">
<div className="flex flex-col gap-6 max-h-[35rem] min-h-[10rem] overflow-y-auto">
<CodeEditor
value={data || "Empty"}
disabled
className="font-mono"
/>
<div className="flex justify-end absolute z-50 right-6 top-6">
<UpdateTraefikConfig applicationId={applicationId} />
</div>
</div>

View File

@@ -16,7 +16,6 @@ import {
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Textarea } from "@/components/ui/textarea";
import { api } from "@/utils/api";
import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -25,6 +24,7 @@ import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import jsyaml from "js-yaml";
import { CodeEditor } from "@/components/shared/code-editor";
const UpdateTraefikConfigSchema = z.object({
traefikConfig: z.string(),
@@ -122,7 +122,7 @@ export const UpdateTraefikConfig = ({ applicationId }: Props) => {
<form
id="hook-form-update-traefik-config"
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full py-4"
className="grid w-full py-4 overflow-auto"
>
<div className="flex flex-col">
<FormField
@@ -132,8 +132,8 @@ export const UpdateTraefikConfig = ({ applicationId }: Props) => {
<FormItem>
<FormLabel>Traefik config</FormLabel>
<FormControl>
<Textarea
className="h-[35rem] font-mono"
<CodeEditor
wrapperClassName="h-[35rem] font-mono"
placeholder={`http:
routers:
router-name:

View File

@@ -25,8 +25,8 @@ export const DeleteApplication = ({ applicationId }: Props) => {
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}>
<TrashIcon className="size-4 " />
<Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 text-muted-foreground" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>

View File

@@ -24,8 +24,8 @@ export const DeleteDomain = ({ domainId }: Props) => {
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}>
<TrashIcon className="size-4 " />
<Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 text-muted-foreground " />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>

View File

@@ -40,7 +40,9 @@ export const ShowDomains = ({ applicationId }: Props) => {
</div>
{data && data?.length > 0 && (
<AddDomain applicationId={applicationId} />
<AddDomain applicationId={applicationId}>
<GlobeIcon className="size-4" /> Add Domain
</AddDomain>
)}
</CardHeader>
<CardContent className="flex w-full flex-row gap-4">
@@ -51,7 +53,9 @@ export const ShowDomains = ({ applicationId }: Props) => {
To access to the application is required to set at least 1
domain
</span>
<AddDomain applicationId={applicationId}>Add Domain</AddDomain>
<AddDomain applicationId={applicationId}>
<GlobeIcon className="size-4" /> Add Domain
</AddDomain>
</div>
) : (
<div className="flex w-full flex-col gap-4">
@@ -75,8 +79,10 @@ export const ShowDomains = ({ applicationId }: Props) => {
<Button variant="outline" disabled>
{item.https ? "HTTPS" : "HTTP"}
</Button>
<UpdateDomain domainId={item.domainId} />
<DeleteDomain domainId={item.domainId} />
<div className="flex flex-row gap-1">
<UpdateDomain domainId={item.domainId} />
<DeleteDomain domainId={item.domainId} />
</div>
</div>
);
})}

View File

@@ -115,8 +115,8 @@ export const UpdateDomain = ({ domainId }: Props) => {
return (
<Dialog>
<DialogTrigger className="" asChild>
<Button>
<PenBoxIcon className="size-4" />
<Button variant="ghost">
<PenBoxIcon className="size-4 text-muted-foreground" />
</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl">

View File

@@ -90,7 +90,7 @@ export const UpdateApplication = ({ applicationId }: Props) => {
<Dialog>
<DialogTrigger asChild>
<Button variant="ghost">
<SquarePen className="size-4" />
<SquarePen className="size-4 text-muted-foreground" />
</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -20,7 +20,7 @@ import {
import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { Pencil, CheckIcon, ChevronsUpDown } from "lucide-react";
import { Pencil, CheckIcon, ChevronsUpDown, PenBoxIcon } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
@@ -115,7 +115,7 @@ export const UpdateBackup = ({ backupId, refetch }: Props) => {
<Dialog>
<DialogTrigger asChild>
<Button variant="ghost">
<Pencil className="size-4 text-muted-foreground" />
<PenBoxIcon className="size-4 text-muted-foreground" />
</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -9,16 +9,15 @@ import {
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Textarea } from "@/components/ui/textarea";
import { api } from "@/utils/api";
import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
import { AlertTriangle } from "lucide-react";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { validateAndFormatYAML } from "../application/advanced/traefik/update-traefik-config";
import { CodeEditor } from "@/components/shared/code-editor";
const UpdateServerMiddlewareConfigSchema = z.object({
traefikConfig: z.string(),
@@ -88,13 +87,12 @@ export const ShowTraefikFile = ({ path }: Props) => {
return (
<div>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full relative"
className="grid w-full relative z-[5]"
>
<div className="flex flex-col">
<div className="flex flex-col overflow-auto">
<FormField
control={form.control}
name="traefikConfig"
@@ -105,8 +103,8 @@ export const ShowTraefikFile = ({ path }: Props) => {
{path}
</FormDescription>
<FormControl>
<Textarea
className="h-[35rem] font-mono"
<CodeEditor
wrapperClassName="h-[35rem] font-mono"
placeholder={`http:
routers:
router-name:

View File

@@ -14,7 +14,7 @@ export const ShowTraefikSystem = () => {
return (
<div className={cn("mt-6 md:grid gap-4")}>
<div className="flex flex-col md:flex-row gap-4 md:gap-10 w-full">
<div className="flex flex-col lg:flex-row gap-4 md:gap-10 w-full">
{directories?.length === 0 && (
<div className="w-full flex-col gap-2 flex items-center justify-center h-[55vh]">
<span className="text-muted-foreground text-lg font-medium">
@@ -27,7 +27,7 @@ export const ShowTraefikSystem = () => {
<>
<Tree
data={directories}
className="md:max-w-[19rem] w-full md:h-[660px] border rounded-lg"
className="lg:max-w-[19rem] w-full lg:h-[660px] border rounded-lg"
onSelectChange={(item) => setFile(item?.id || null)}
folderIcon={Folder}
itemIcon={Workflow}

View File

@@ -1,13 +1,13 @@
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { api } from "@/utils/api";
@@ -16,47 +16,47 @@ import { useRouter } from "next/router";
import { toast } from "sonner";
interface Props {
mariadbId: string;
mariadbId: string;
}
export const DeleteMariadb = ({ mariadbId }: Props) => {
const { mutateAsync, isLoading } = api.mariadb.remove.useMutation();
const { push } = useRouter();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}>
<TrashIcon className="size-4 " />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete the
database
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync({
mariadbId,
})
.then((data) => {
push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully");
})
.catch(() => {
toast.error("Error to delete the database");
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
const { mutateAsync, isLoading } = api.mariadb.remove.useMutation();
const { push } = useRouter();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 text-muted-foreground " />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete the
database
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync({
mariadbId,
})
.then((data) => {
push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully");
})
.catch(() => {
toast.error("Error to delete the database");
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};

View File

@@ -90,7 +90,7 @@ export const UpdateMariadb = ({ mariadbId }: Props) => {
<Dialog>
<DialogTrigger asChild>
<Button variant="ghost">
<SquarePen className="size-4" />
<SquarePen className="size-4 text-muted-foreground" />
</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -1,13 +1,13 @@
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { api } from "@/utils/api";
@@ -16,47 +16,47 @@ import { useRouter } from "next/router";
import { toast } from "sonner";
interface Props {
mongoId: string;
mongoId: string;
}
export const DeleteMongo = ({ mongoId }: Props) => {
const { mutateAsync, isLoading } = api.mongo.remove.useMutation();
const { push } = useRouter();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}>
<TrashIcon className="size-4 " />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete the
database
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync({
mongoId,
})
.then((data) => {
push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully");
})
.catch(() => {
toast.error("Error to delete the database");
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
const { mutateAsync, isLoading } = api.mongo.remove.useMutation();
const { push } = useRouter();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 text-muted-foreground " />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete the
database
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync({
mongoId,
})
.then((data) => {
push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully");
})
.catch(() => {
toast.error("Error to delete the database");
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};

View File

@@ -90,7 +90,7 @@ export const UpdateMongo = ({ mongoId }: Props) => {
<Dialog>
<DialogTrigger asChild>
<Button variant="ghost">
<SquarePen className="size-4" />
<SquarePen className="size-4 text-muted-foreground" />
</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -1,13 +1,13 @@
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { api } from "@/utils/api";
@@ -16,47 +16,47 @@ import { useRouter } from "next/router";
import { toast } from "sonner";
interface Props {
mysqlId: string;
mysqlId: string;
}
export const DeleteMysql = ({ mysqlId }: Props) => {
const { mutateAsync, isLoading } = api.mysql.remove.useMutation();
const { push } = useRouter();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}>
<TrashIcon className="size-4 " />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete the
database
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync({
mysqlId,
})
.then((data) => {
push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully");
})
.catch(() => {
toast.error("Error to delete the database");
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
const { mutateAsync, isLoading } = api.mysql.remove.useMutation();
const { push } = useRouter();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 text-muted-foreground " />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete the
database
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync({
mysqlId,
})
.then((data) => {
push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully");
})
.catch(() => {
toast.error("Error to delete the database");
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};

View File

@@ -90,7 +90,7 @@ export const UpdateMysql = ({ mysqlId }: Props) => {
<Dialog>
<DialogTrigger asChild>
<Button variant="ghost">
<SquarePen className="size-4" />
<SquarePen className="size-4 text-muted-foreground" />
</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -1,13 +1,13 @@
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { api } from "@/utils/api";
@@ -16,47 +16,47 @@ import { useRouter } from "next/router";
import { toast } from "sonner";
interface Props {
postgresId: string;
postgresId: string;
}
export const DeletePostgres = ({ postgresId }: Props) => {
const { mutateAsync, isLoading } = api.postgres.remove.useMutation();
const { push } = useRouter();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}>
<TrashIcon className="size-4 " />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete the
database
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync({
postgresId,
})
.then((data) => {
push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully");
})
.catch(() => {
toast.error("Error to delete the database");
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
const { mutateAsync, isLoading } = api.postgres.remove.useMutation();
const { push } = useRouter();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 text-muted-foreground " />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete the
database
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync({
postgresId,
})
.then((data) => {
push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully");
})
.catch(() => {
toast.error("Error to delete the database");
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};

View File

@@ -90,7 +90,7 @@ export const UpdatePostgres = ({ postgresId }: Props) => {
<Dialog>
<DialogTrigger asChild>
<Button variant="ghost">
<SquarePen className="size-4" />
<SquarePen className="size-4 text-muted-foreground" />
</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -89,9 +89,9 @@ export const ShowProjects = () => {
<span className="flex flex-col gap-1.5">
<div className="flex items-center gap-2">
<BookIcon className="size-4 text-muted-foreground" />
<span className="text-base font-medium leading-none">
<Link className="text-base font-medium leading-none" href={`/dashboard/project/${project.projectId}`}>
{project.name}
</span>
</Link>
</div>
<span className="text-sm font-medium text-muted-foreground">

View File

@@ -1,13 +1,13 @@
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { api } from "@/utils/api";
@@ -16,47 +16,47 @@ import { useRouter } from "next/router";
import { toast } from "sonner";
interface Props {
redisId: string;
redisId: string;
}
export const DeleteRedis = ({ redisId }: Props) => {
const { mutateAsync, isLoading } = api.redis.remove.useMutation();
const { push } = useRouter();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}>
<TrashIcon className="size-4 " />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete the
database
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync({
redisId,
})
.then((data) => {
push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully");
})
.catch(() => {
toast.error("Error to delete the database");
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
const { mutateAsync, isLoading } = api.redis.remove.useMutation();
const { push } = useRouter();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 text-muted-foreground " />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete the
database
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync({
redisId,
})
.then((data) => {
push(`/dashboard/project/${data?.projectId}`);
toast.success("Database delete succesfully");
})
.catch(() => {
toast.error("Error to delete the database");
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};

View File

@@ -90,7 +90,7 @@ export const UpdateRedis = ({ redisId }: Props) => {
<Dialog>
<DialogTrigger asChild>
<Button variant="ghost">
<SquarePen className="size-4" />
<SquarePen className="size-4 text-muted-foreground" />
</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">

View File

@@ -25,7 +25,7 @@ import {
} from "@/components/ui/card";
const appearanceFormSchema = z.object({
theme: z.enum(["light", "dark"], {
theme: z.enum(["light", "dark", "system"], {
required_error: "Please select a theme.",
}),
});
@@ -34,7 +34,7 @@ type AppearanceFormValues = z.infer<typeof appearanceFormSchema>;
// This can come from your database or API.
const defaultValues: Partial<AppearanceFormValues> = {
theme: "light",
theme: "system",
};
export function AppearanceForm() {
@@ -46,7 +46,7 @@ export function AppearanceForm() {
useEffect(() => {
form.reset({
theme: theme === "light" ? "light" : "dark",
theme: (theme ?? "system") as AppearanceFormValues["theme"],
});
}, [form, theme]);
function onSubmit(data: AppearanceFormValues) {
@@ -81,28 +81,15 @@ export function AppearanceForm() {
onValueChange={field.onChange}
defaultValue={field.value}
value={field.value}
className="grid max-w-md grid-cols-1 sm:grid-cols-2 gap-8 pt-2"
className="grid max-w-md md:max-w-lg grid-cols-1 sm:grid-cols-3 gap-8 pt-2"
>
<FormItem>
<FormLabel className="[&:has([data-state=checked])>div]:border-primary">
<FormControl>
<RadioGroupItem value="light" className="sr-only" />
</FormControl>
<div className="items-center rounded-md border-2 border-muted p-1 hover:border-accent">
<div className="space-y-2 rounded-sm bg-[#ecedef] p-2">
<div className="space-y-2 rounded-md bg-white p-2 shadow-sm">
<div className="h-2 w-[80px] rounded-lg bg-[#ecedef]" />
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
</div>
<div className="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm">
<div className="h-4 w-4 rounded-full bg-[#ecedef]" />
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
</div>
<div className="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm">
<div className="h-4 w-4 rounded-full bg-[#ecedef]" />
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
</div>
</div>
<div className="items-center rounded-md border-2 border-muted p-1 hover:bg-accent transition-colors cursor-pointer">
<img src="/images/theme-light.svg" alt="light" />
</div>
<span className="block w-full p-2 text-center font-normal">
Light
@@ -114,27 +101,30 @@ export function AppearanceForm() {
<FormControl>
<RadioGroupItem value="dark" className="sr-only" />
</FormControl>
<div className="items-center rounded-md border-2 border-muted bg-popover p-1 hover:bg-accent hover:text-accent-foreground">
<div className="space-y-2 rounded-sm bg-slate-950 p-2">
<div className="space-y-2 rounded-md bg-slate-800 p-2 shadow-sm">
<div className="h-2 w-[80px] rounded-lg bg-slate-400" />
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
</div>
<div className="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm">
<div className="h-4 w-4 rounded-full bg-slate-400" />
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
</div>
<div className="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm">
<div className="h-4 w-4 rounded-full bg-slate-400" />
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
</div>
</div>
<div className="items-center rounded-md border-2 border-muted bg-popover p-1 transition-colors hover:bg-accent hover:text-accent-foreground cursor-pointer">
<img src="/images/theme-dark.svg" alt="dark" />
</div>
<span className="block w-full p-2 text-center font-normal">
Dark
</span>
</FormLabel>
</FormItem>
<FormItem>
<FormLabel className="[&:has([data-state=checked])>div]:border-primary">
<FormControl>
<RadioGroupItem
value="system"
className="sr-only"
/>
</FormControl>
<div className="items-center rounded-md border-2 border-muted bg-popover p-1 transition-colors hover:bg-accent hover:text-accent-foreground cursor-pointer">
<img src="/images/theme-system.svg" alt="system" />
</div>
<span className="block w-full p-2 text-center font-normal">
System
</span>
</FormLabel>
</FormItem>
</RadioGroup>
</FormItem>
);

View File

@@ -33,21 +33,23 @@ export const ShowCertificates = () => {
</div>
) : (
<div className="flex flex-col gap-6">
{data?.map((destination, index) => (
<div
key={destination.certificateId}
className="flex items-center justify-between"
>
<span className="text-sm text-muted-foreground">
{index + 1}. {destination.name}
</span>
<div className="flex flex-row gap-3">
<DeleteCertificate
certificateId={destination.certificateId}
/>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
{data?.map((destination, index) => (
<div
key={destination.certificateId}
className="flex items-center justify-between border p-4 rounded-lg"
>
<span className="text-sm text-muted-foreground">
{index + 1}. {destination.name}
</span>
<div className="flex flex-row gap-3">
<DeleteCertificate
certificateId={destination.certificateId}
/>
</div>
</div>
</div>
))}
))}
</div>
<div>
<AddCertificate />
</div>

View File

@@ -0,0 +1,66 @@
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
import { ExternalLink, PlusIcon } from "lucide-react";
import { AddWorker } from "./workers/add-worker";
import { AddManager } from "./manager/add-manager";
import Link from "next/link";
export const AddNode = () => {
return (
<Dialog>
<DialogTrigger asChild>
<Button className="w-full cursor-pointer space-x-3">
<PlusIcon className="h-4 w-4" />
Add Node
</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-4xl">
<DialogHeader>
<DialogTitle>Add Node</DialogTitle>
<DialogDescription className="flex flex-col gap-2">
Follow the steps to add a new node to your cluster, before you start
using this feature, you need to understand how docker swarm works.{" "}
<Link
href="https://docs.docker.com/engine/swarm/"
target="_blank"
className="text-primary flex flex-row gap-2 items-center"
>
Docker Swarm
<ExternalLink className="h-4 w-4" />
</Link>
<Link
href="https://docs.docker.com/engine/swarm/how-swarm-mode-works/nodes/"
target="_blank"
className="text-primary flex flex-row gap-2 items-center"
>
Architecture
<ExternalLink className="h-4 w-4" />
</Link>
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-2">
<Tabs defaultValue="worker">
<TabsList>
<TabsTrigger value="worker">Worker</TabsTrigger>
<TabsTrigger value="manager">Manager</TabsTrigger>
</TabsList>
<TabsContent value="worker" className="pt-4">
<AddWorker />
</TabsContent>
<TabsContent value="manager" className="pt-4">
<AddManager />
</TabsContent>
</Tabs>
</div>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,63 @@
import {
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { api } from "@/utils/api";
import { CardContent } from "@/components/ui/card";
import { CopyIcon } from "lucide-react";
import copy from "copy-to-clipboard";
import { toast } from "sonner";
export const AddManager = () => {
const { data } = api.cluster.addManager.useQuery();
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 24.0
<button
type="button"
className="self-center"
onClick={() => {
copy("curl https://get.docker.com | sh -s -- --version 24.0");
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}
<button
type="button"
className="self-start"
onClick={() => {
copy(data || "");
toast.success("Copied to clipboard");
}}
>
<CopyIcon className="h-4 w-4 cursor-pointer" />
</button>
</span>
</div>
</CardContent>
</div>
</>
);
};

View File

@@ -0,0 +1,43 @@
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
interface Props {
data: unknown;
}
export const ShowNodeData = ({ data }: Props) => {
return (
<Dialog>
<DialogTrigger asChild>
<DropdownMenuItem
className="w-full cursor-pointer"
onSelect={(e) => e.preventDefault()}
>
View Config
</DropdownMenuItem>
</DialogTrigger>
<DialogContent className={"sm:max-w-5xl overflow-y-auto max-h-screen"}>
<DialogHeader>
<DialogTitle>Node Config</DialogTitle>
<DialogDescription>
See in detail the metadata of this node
</DialogDescription>
</DialogHeader>
<div className="text-wrap rounded-lg border p-4 text-sm sm:max-w-[59rem] bg-card">
<code>
<pre className="whitespace-pre-wrap break-words">
{JSON.stringify(data, null, 2)}
</pre>
</code>
</div>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,162 @@
import React from "react";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { api } from "@/utils/api";
import { DateTooltip } from "@/components/shared/date-tooltip";
import { Badge } from "@/components/ui/badge";
import { DeleteWorker } from "./workers/delete-worker";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { HelpCircle, LockIcon, MoreHorizontal } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { ShowNodeData } from "./show-node-data";
import { AddNode } from "./add-node";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
export const ShowNodes = () => {
const { data, isLoading } = api.cluster.getNodes.useQuery();
const { data: registry } = api.registry.all.useQuery();
const haveAtLeastOneRegistry = !!(registry && registry?.length > 0);
return (
<Card className="bg-transparent h-full">
<CardHeader className="flex flex-row gap-2 justify-between w-full items-center flex-wrap">
<div className="flex flex-col gap-2">
<CardTitle className="text-xl">Cluster</CardTitle>
<CardDescription>Add nodes to your cluster</CardDescription>
</div>
{haveAtLeastOneRegistry && (
<div className="flex flex-row gap-2">
<AddNode />
</div>
)}
</CardHeader>
<CardContent className="flex flex-col gap-4">
{haveAtLeastOneRegistry ? (
<div className="grid md:grid-cols-1 gap-4">
{isLoading && <div>Loading...</div>}
<Table>
<TableCaption>A list of your managers / workers.</TableCaption>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Hostname</TableHead>
<TableHead className="text-right">Status</TableHead>
<TableHead className="text-right">Role</TableHead>
<TableHead className="text-right">Availability</TableHead>
<TableHead className="text-right">Engine Version</TableHead>
<TableHead className="text-right">Created</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data?.map((node) => {
const isManager = node.Spec.Role === "manager";
return (
<TableRow key={node.ID}>
<TableCell className="w-[100px]">
{node.Description.Hostname}
</TableCell>
<TableCell className="text-right">
{node.Status.State}
</TableCell>
<TableCell className="text-right">
<Badge variant={isManager ? "default" : "secondary"}>
{node?.Spec?.Role}
</Badge>
</TableCell>
<TableCell className="text-right">
{node.Spec.Availability}
</TableCell>
<TableCell className="text-right">
{node?.Description.Engine.EngineVersion}
</TableCell>
<TableCell className="text-right">
<DateTooltip date={node.CreatedAt} className="text-sm">
Created{" "}
</DateTooltip>
</TableCell>
<TableCell className="text-right flex justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<ShowNodeData data={node} />
{!node?.ManagerStatus?.Leader && (
<DeleteWorker nodeId={node.ID} />
)}
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
) : (
<div className="flex flex-col items-center gap-3">
<LockIcon className="size-8 text-muted-foreground" />
<div className="flex flex-row gap-2">
<span className="text-base text-muted-foreground ">
To add nodes to your cluster, you need to configure at least one
registry.
</span>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger className="self-center">
<HelpCircle className="size-5 text-muted-foreground " />
</TooltipTrigger>
<TooltipContent>
Nodes need a registry to pull images from.
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<ul className="list-disc list-inside text-sm text-muted-foreground border p-4 rounded-lg flex flex-col gap-1.5 mt-2.5">
<li>
<strong>Docker Registry:</strong> Use custom registries like
Docker Hub, DigitalOcean Registry, etc.
</li>
<li>
<strong>Self-Hosted Docker Registry:</strong> Automatically set
up a local registry to store all images.
</li>
</ul>
</div>
)}
</CardContent>
</Card>
);
};

View File

@@ -0,0 +1,61 @@
import { CardContent } from "@/components/ui/card";
import {
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { api } from "@/utils/api";
import copy from "copy-to-clipboard";
import { CopyIcon } from "lucide-react";
import { toast } from "sonner";
export const AddWorker = () => {
const { data } = api.cluster.addWorker.useQuery();
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 24.0
<button
type="button"
className="self-center"
onClick={() => {
copy("curl https://get.docker.com | sh -s -- --version 24.0");
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>
<span className="bg-muted rounded-lg p-2 flex">
{data}
<button
type="button"
className="self-start"
onClick={() => {
copy(data || "");
toast.success("Copied to clipboard");
}}
>
<CopyIcon className="h-4 w-4 cursor-pointer" />
</button>
</span>
</div>
</CardContent>
</div>
);
};

View File

@@ -0,0 +1,62 @@
import React from "react";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { api } from "@/utils/api";
import { TrashIcon } from "lucide-react";
import { toast } from "sonner";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
interface Props {
nodeId: string;
}
export const DeleteWorker = ({ nodeId }: Props) => {
const { mutateAsync, isLoading } = api.cluster.removeWorker.useMutation();
const utils = api.useUtils();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
Delete
</DropdownMenuItem>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete the
worker.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync({
nodeId,
})
.then(async () => {
utils.cluster.getNodes.invalidate();
toast.success("Worker deleted succesfully");
})
.catch(() => {
toast.error("Error to delete the worker");
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};

View File

@@ -0,0 +1,251 @@
import { Button } from "@/components/ui/button";
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 { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { AlertTriangle, Container } from "lucide-react";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const AddRegistrySchema = z.object({
registryName: z.string().min(1, {
message: "Registry name is required",
}),
username: z.string().min(1, {
message: "Username is required",
}),
password: z.string().min(1, {
message: "Password is required",
}),
registryUrl: z.string().min(1, {
message: "Registry URL is required",
}),
imagePrefix: z.string(),
});
type AddRegistry = z.infer<typeof AddRegistrySchema>;
export const AddRegistry = () => {
const utils = api.useUtils();
const [isOpen, setIsOpen] = useState(false);
const { mutateAsync, error, isError } = api.registry.create.useMutation();
const { mutateAsync: testRegistry, isLoading } =
api.registry.testRegistry.useMutation();
const router = useRouter();
const form = useForm<AddRegistry>({
defaultValues: {
username: "",
password: "",
registryUrl: "",
imagePrefix: "",
registryName: "",
},
resolver: zodResolver(AddRegistrySchema),
});
const password = form.watch("password");
const username = form.watch("username");
const registryUrl = form.watch("registryUrl");
const registryName = form.watch("registryName");
const imagePrefix = form.watch("imagePrefix");
useEffect(() => {
form.reset({
username: "",
password: "",
registryUrl: "",
imagePrefix: "",
});
}, [form, form.reset, form.formState.isSubmitSuccessful]);
const onSubmit = async (data: AddRegistry) => {
await mutateAsync({
password: data.password,
registryName: data.registryName,
username: data.username,
registryUrl: data.registryUrl,
registryType: "cloud",
imagePrefix: data.imagePrefix,
})
.then(async (data) => {
await utils.registry.all.invalidate();
toast.success("Registry added");
setIsOpen(false);
})
.catch(() => {
toast.error("Error to add a registry");
});
};
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button>
<Container className="h-4 w-4" />
Create Registry
</Button>
</DialogTrigger>
<DialogContent className="sm:m:max-w-lg ">
<DialogHeader>
<DialogTitle>Add a external registry</DialogTitle>
<DialogDescription>
Fill the next fields to add a external registry.
</DialogDescription>
</DialogHeader>
{isError && (
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
<AlertTriangle className="text-red-600 dark:text-red-400" />
<span className="text-sm text-red-600 dark:text-red-400">
{error?.message}
</span>
</div>
)}
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full gap-4"
>
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="registryName"
render={({ field }) => (
<FormItem>
<FormLabel>Registry Name</FormLabel>
<FormControl>
<Input placeholder="Registry Name" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="Username" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input
placeholder="Password"
{...field}
type="password"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="imagePrefix"
render={({ field }) => (
<FormItem>
<FormLabel>Image Prefix</FormLabel>
<FormControl>
<Input {...field} placeholder="Image Prefix" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="registryUrl"
render={({ field }) => (
<FormItem>
<FormLabel>Registry URL</FormLabel>
<FormControl>
<Input
placeholder="aws_account_id.dkr.ecr.us-west-2.amazonaws.com"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<DialogFooter className="flex flex-row w-full sm:justify-between gap-4 flex-wrap">
<Button
type="button"
variant={"secondary"}
isLoading={isLoading}
onClick={async () => {
await testRegistry({
username: username,
password: password,
registryUrl: registryUrl,
registryName: registryName,
registryType: "cloud",
imagePrefix: imagePrefix,
})
.then((data) => {
if (data) {
toast.success("Registry Tested Successfully");
} else {
toast.error("Registry Test Failed");
}
})
.catch(() => {
toast.error("Error to test the registry");
});
}}
>
Test Registry
</Button>
<Button isLoading={form.formState.isSubmitting} type="submit">
Create
</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,181 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { AlertTriangle, Container } from "lucide-react";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const AddRegistrySchema = z.object({
username: z
.string()
.min(1, {
message: "Username is required",
})
.regex(/^[a-zA-Z0-9]+$/, {
message: "Username can only contain letters and numbers",
}),
password: z.string().min(1, {
message: "Password is required",
}),
registryUrl: z.string().min(1, {
message: "Registry URL is required",
}),
});
type AddRegistry = z.infer<typeof AddRegistrySchema>;
export const AddSelfHostedRegistry = () => {
const utils = api.useUtils();
const [isOpen, setIsOpen] = useState(false);
const { mutateAsync, error, isError, isLoading } =
api.registry.enableSelfHostedRegistry.useMutation();
const router = useRouter();
const form = useForm<AddRegistry>({
defaultValues: {
username: "",
password: "",
registryUrl: "",
},
resolver: zodResolver(AddRegistrySchema),
});
useEffect(() => {
form.reset({
registryUrl: "",
username: "",
password: "",
});
}, [form, form.reset, form.formState.isSubmitSuccessful]);
const onSubmit = async (data: AddRegistry) => {
await mutateAsync({
registryUrl: data.registryUrl,
username: data.username,
password: data.password,
})
.then(async (data) => {
await utils.registry.all.invalidate();
toast.success("Self Hosted Registry Created");
setIsOpen(false);
})
.catch(() => {
toast.error("Error to create a self hosted registry");
});
};
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button>
<Container className="h-4 w-4" />
Enable Self Hosted Registry
</Button>
</DialogTrigger>
<DialogContent className="sm:m:max-w-lg ">
<DialogHeader>
<DialogTitle>Add a self hosted registry</DialogTitle>
<DialogDescription>
Fill the next fields to add a self hosted registry.
</DialogDescription>
</DialogHeader>
{isError && (
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
<AlertTriangle className="text-red-600 dark:text-red-400" />
<span className="text-sm text-red-600 dark:text-red-400">
{error?.message}
</span>
</div>
)}
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full gap-4"
>
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="Username" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input
placeholder="Password"
{...field}
type="password"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="flex flex-col gap-4">
<FormField
control={form.control}
name="registryUrl"
render={({ field }) => (
<FormItem>
<FormLabel>Registry URL</FormLabel>
<FormControl>
<Input placeholder="registry.dokploy.com" {...field} />
</FormControl>
<FormDescription>
Point a DNS record to the VPS IP address.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</div>
<DialogFooter>
<Button isLoading={isLoading} type="submit">
Create
</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,61 @@
import React from "react";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { api } from "@/utils/api";
import { TrashIcon } from "lucide-react";
import { toast } from "sonner";
interface Props {
registryId: string;
}
export const DeleteRegistry = ({ registryId }: Props) => {
const { mutateAsync, isLoading } = api.registry.remove.useMutation();
const utils = api.useUtils();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 text-muted-foreground " />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete the
registry.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync({
registryId,
})
.then(async () => {
utils.registry.all.invalidate();
toast.success("Registry deleted");
})
.catch(() => {
toast.error("Error to delete the registry");
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};

View File

@@ -0,0 +1,76 @@
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { api } from "@/utils/api";
import { Server } from "lucide-react";
import { AddRegistry } from "./add-docker-registry";
import { AddSelfHostedRegistry } from "./add-self-docker-registry";
import { DeleteRegistry } from "./delete-registry";
import { UpdateDockerRegistry } from "./update-docker-registry";
export const ShowRegistry = () => {
const { data } = api.registry.all.useQuery();
const haveSelfHostedRegistry = data?.some(
(registry) => registry.registryType === "selfHosted",
);
return (
<div className="h-full">
<Card className="bg-transparent h-full">
<CardHeader className="flex flex-row gap-2 flex-wrap justify-between w-full items-center">
<div className="flex flex-col gap-2">
<CardTitle className="text-xl">Registry</CardTitle>
<CardDescription>Add registry to your application.</CardDescription>
</div>
<div className="flex flex-row gap-2">
{data && data?.length > 0 && (
<>
{!haveSelfHostedRegistry && <AddSelfHostedRegistry />}
<AddRegistry />
</>
)}
</div>
</CardHeader>
<CardContent className="space-y-2 pt-4 h-full">
{data?.length === 0 ? (
<div className="flex flex-col items-center gap-3">
<Server className="size-8 self-center text-muted-foreground" />
<span className="text-base text-muted-foreground">
To create a cluster is required to set a registry.
</span>
<div className="flex flex-row gap-2">
<AddSelfHostedRegistry />
<AddRegistry />
</div>
</div>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-1 gap-6">
{data?.map((registry, index) => (
<div
key={registry.registryId}
className="flex items-center justify-between border p-4 rounded-lg"
>
<span className="text-sm text-muted-foreground">
{index + 1}. {registry.registryName}
</span>
<div className="flex flex-row gap-1">
<UpdateDockerRegistry registryId={registry.registryId} />
<DeleteRegistry registryId={registry.registryId} />
</div>
</div>
))}
</div>
)}
</CardContent>
</Card>
</div>
);
};

View File

@@ -0,0 +1,275 @@
import { Button } from "@/components/ui/button";
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 { cn } from "@/lib/utils";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { AlertTriangle, PenBoxIcon } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const updateRegistry = z.object({
registryName: z.string().min(1, {
message: "Registry name is required",
}),
username: z.string().min(1, {
message: "Username is required",
}),
password: z.string(),
registryUrl: z.string().min(1, {
message: "Registry URL is required",
}),
imagePrefix: z.string(),
});
type UpdateRegistry = z.infer<typeof updateRegistry>;
interface Props {
registryId: string;
}
export const UpdateDockerRegistry = ({ registryId }: Props) => {
const utils = api.useUtils();
const { mutateAsync: testRegistry, isLoading } =
api.registry.testRegistry.useMutation();
const { data, refetch } = api.registry.one.useQuery(
{
registryId,
},
{
enabled: !!registryId,
},
);
const isCloud = data?.registryType === "cloud";
const { mutateAsync, isError, error } = api.registry.update.useMutation();
const form = useForm<UpdateRegistry>({
defaultValues: {
imagePrefix: "",
registryName: "",
username: "",
password: "",
registryUrl: "",
},
resolver: zodResolver(updateRegistry),
});
const password = form.watch("password");
const username = form.watch("username");
const registryUrl = form.watch("registryUrl");
const registryName = form.watch("registryName");
const imagePrefix = form.watch("imagePrefix");
useEffect(() => {
if (data) {
form.reset({
imagePrefix: data.imagePrefix || "",
registryName: data.registryName || "",
username: data.username || "",
password: "",
registryUrl: data.registryUrl || "",
});
}
}, [form, form.reset, data]);
const onSubmit = async (data: UpdateRegistry) => {
await mutateAsync({
registryId,
...(data.password ? { password: data.password } : {}),
registryName: data.registryName,
username: data.username,
registryUrl: data.registryUrl,
imagePrefix: data.imagePrefix,
})
.then(async (data) => {
toast.success("Registry Updated");
await refetch();
await utils.registry.all.invalidate();
})
.catch(() => {
toast.error("Error to update the registry");
});
};
return (
<Dialog>
<DialogTrigger className="" asChild>
<Button variant="ghost">
<PenBoxIcon className="size-4 text-muted-foreground" />
</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl">
<DialogHeader>
<DialogTitle>Registry</DialogTitle>
<DialogDescription>Update the registry information</DialogDescription>
</DialogHeader>
{isError && (
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
<AlertTriangle className="text-red-600 dark:text-red-400" />
<span className="text-sm text-red-600 dark:text-red-400">
{error?.message}
</span>
</div>
)}
<Form {...form}>
<form
id="hook-form"
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full gap-8 "
>
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<FormField
control={form.control}
name="registryName"
render={({ field }) => (
<FormItem>
<FormLabel>Registry Name</FormLabel>
<FormControl>
<Input placeholder="Registry Name" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="Username" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input
placeholder="Password"
{...field}
type="password"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{isCloud && (
<FormField
control={form.control}
name="imagePrefix"
render={({ field }) => (
<FormItem>
<FormLabel>Image Prefix</FormLabel>
<FormControl>
<Input {...field} placeholder="Image Prefix" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
<FormField
control={form.control}
name="registryUrl"
render={({ field }) => (
<FormItem>
<FormLabel>Registry URL</FormLabel>
<FormControl>
<Input
placeholder="https://aws_account_id.dkr.ecr.us-west-2.amazonaws.com"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
</form>
<DialogFooter
className={cn(
isCloud ? "sm:justify-between " : "",
"flex flex-row w-full gap-4 flex-wrap",
)}
>
{isCloud && (
<Button
type="button"
variant={"secondary"}
isLoading={isLoading}
onClick={async () => {
await testRegistry({
username: username,
password: password,
registryUrl: registryUrl,
registryName: registryName,
registryType: "cloud",
imagePrefix: imagePrefix,
})
.then((data) => {
if (data) {
toast.success("Registry Tested Successfully");
} else {
toast.error("Registry Test Failed");
}
})
.catch(() => {
toast.error("Error to test the registry");
});
}}
>
Test Registry
</Button>
)}
<Button
isLoading={form.formState.isSubmitting}
form="hook-form"
type="submit"
>
Update
</Button>
</DialogFooter>
</Form>
</DialogContent>
</Dialog>
);
};

View File

@@ -34,16 +34,16 @@ export const ShowDestinations = () => {
<AddDestination />
</div>
) : (
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-4">
{data?.map((destination, index) => (
<div
key={destination.destinationId}
className="flex items-center justify-between"
className="flex items-center justify-between border p-3.5 rounded-lg"
>
<span className="text-sm text-muted-foreground">
{index + 1}. {destination.name}
</span>
<div className="flex flex-row gap-3">
<div className="flex flex-row gap-1">
<UpdateDestination
destinationId={destination.destinationId}
/>

View File

@@ -87,10 +87,10 @@ export const GithubSetup = () => {
{haveGithubConfigured ? (
<div className="flex flex-col gap-2">
<div className="flex items-center gap-4">
<span className="text-muted-foreground">
<span className="text-muted-foreground text-sm">
Github account configured succesfully.
</span>
<BadgeCheck className="size-5 text-green-700" />
<BadgeCheck className="size-4 text-green-700" />
</div>
<div className="flex items-end">
<RemoveGithubApp />
@@ -101,9 +101,9 @@ export const GithubSetup = () => {
{data?.githubAppName ? (
<div className="flex w-fit flex-col gap-4">
<span className="text-muted-foreground">
Youve successfully created a GitHub app named
{data.githubAppName}! The next step is to install this app in
your GitHub account.
You've successfully created a github app named{" "}
<strong>{data.githubAppName}</strong>! The next step is to
install this app in your GitHub account.
</span>
<div className="flex flex-row gap-4">
@@ -121,12 +121,12 @@ export const GithubSetup = () => {
) : (
<div>
<div className="flex items-center gap-2">
<span className="text-muted-foreground">
To integrate your GitHub account with our services, youll
<p className="text-muted-foreground text-sm">
To integrate your GitHub account with our services, you'll
need to create and install a GitHub app. This process is
straightforward and only takes a few minutes. Click the
button below to get started.
</span>
</p>
</div>
<div className="mt-4 flex flex-col gap-4">

View File

@@ -147,11 +147,11 @@ export const ProfileForm = () => {
}}
defaultValue={field.value}
value={field.value}
className="flex flex-row flex-wrap gap-2 max-xl:justify-cente"
className="flex flex-row flex-wrap gap-2 max-xl:justify-center"
>
{randomImages.map((image) => (
<FormItem key={image}>
<FormLabel className="[&:has([data-state=checked])>img]:border-primary [&:has([data-state=checked])>img]:border-1 [&:has([data-state=checked])>img]:p-px">
<FormLabel className="[&:has([data-state=checked])>img]:border-primary [&:has([data-state=checked])>img]:border-1 [&:has([data-state=checked])>img]:p-px cursor-pointer">
<FormControl>
<RadioGroupItem
value={image}
@@ -163,7 +163,7 @@ export const ProfileForm = () => {
key={image}
src={image}
alt="avatar"
className="h-12 w-12 rounded-full border transition-transform"
className="h-12 w-12 rounded-full border hover:p-px hover:border-primary transition-transform"
/>
</FormLabel>
</FormItem>

View File

@@ -23,11 +23,11 @@ import { extractServices } from "@/pages/dashboard/project/[projectId]";
import { api } from "@/utils/api";
import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
import { ListTodo } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
const addPermissions = z.object({
accesedProjects: z.array(z.string()).optional(),
@@ -107,9 +107,12 @@ export const AddUserPermissions = ({ userId }: Props) => {
return (
<Dialog>
<DialogTrigger className="" asChild>
<Button variant="ghost">
<ListTodo className="size-4 text-muted-foreground " />
</Button>
<DropdownMenuItem
className="w-full cursor-pointer"
onSelect={(e) => e.preventDefault()}
>
Add Permissions
</DropdownMenuItem>
</DialogTrigger>
<DialogContent className="max-h-[85vh] overflow-y-auto sm:max-w-4xl">
<DialogHeader>

View File

@@ -25,6 +25,7 @@ import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { PlusIcon } from "lucide-react";
const addUser = z.object({
email: z
@@ -66,7 +67,9 @@ export const AddUser = () => {
return (
<Dialog>
<DialogTrigger className="" asChild>
<Button>Add User</Button>
<Button>
<PlusIcon className="h-4 w-4" /> Add User
</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl">
<DialogHeader>

View File

@@ -14,6 +14,7 @@ import { Button } from "@/components/ui/button";
import { api } from "@/utils/api";
import { TrashIcon } from "lucide-react";
import { toast } from "sonner";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
interface Props {
authId: string;
@@ -24,9 +25,12 @@ export const DeleteUser = ({ authId }: Props) => {
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="ghost" isLoading={isLoading}>
<TrashIcon className="size-4 text-muted-foreground " />
</Button>
<DropdownMenuItem
className="w-full cursor-pointer text-red-500 hover:!text-red-600"
onSelect={(e) => e.preventDefault()}
>
Delete User
</DropdownMenuItem>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>

View File

@@ -6,7 +6,7 @@ import {
CardTitle,
} from "@/components/ui/card";
import { api } from "@/utils/api";
import { CopyIcon, Users } from "lucide-react";
import { MoreHorizontal, Users } from "lucide-react";
import { AddUser } from "./add-user";
import { DeleteUser } from "./delete-user";
import { format } from "date-fns";
@@ -14,7 +14,24 @@ import { useEffect, useState } from "react";
import { AddUserPermissions } from "./add-permissions";
import copy from "copy-to-clipboard";
import { toast } from "sonner";
import { UpdateUser } from "./update-user";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Badge } from "@/components/ui/badge";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
export const ShowUsers = () => {
const { data } = api.user.all.useQuery();
@@ -25,82 +42,109 @@ export const ShowUsers = () => {
return (
<div className="h-full col-span-2">
<Card className="bg-transparent h-full border-none">
<CardHeader>
<CardTitle className="text-xl">Users</CardTitle>
<CardDescription>Add, manage and delete users.</CardDescription>
<Card className="bg-transparent h-full ">
<CardHeader className="flex flex-row gap-2 justify-between w-full flex-wrap">
<div className="flex flex-col gap-2">
<CardTitle className="text-xl">Users</CardTitle>
<CardDescription>Add, manage and delete users.</CardDescription>
</div>
{data && data.length > 0 && (
<div className="flex flex-col gap-3 items-end">
<AddUser />
</div>
)}
</CardHeader>
<CardContent className="space-y-2 h-full">
{data?.length === 0 ? (
<div className="flex flex-col items-center gap-3">
<Users className="size-8 self-center text-muted-foreground" />
<span className="text-base text-muted-foreground">
To create a user is required to add
To create a user, you need to add:
</span>
<AddUser />
</div>
) : (
<div className="flex flex-col gap-6">
{data?.map((user) => {
return (
<div
key={user.userId}
className="flex gap-2 flex-col justify-start border p-4 rounded-lg"
>
<span className="text-sm text-foreground">
{user.auth.email}
</span>
{!user.isRegistered && (
<span className="text-sm text-muted-foreground">
Expire In{" "}
{format(new Date(user.expirationDate), "PPpp")}
</span>
)}
<span className="text-sm text-muted-foreground">
{user.isRegistered ? "Registered" : "Not Registered"}
</span>
{user.auth.is2FAEnabled && (
<span className="text-sm text-muted-foreground">
{user.auth.is2FAEnabled
? "2FA Enabled"
: "2FA Not Enabled"}
</span>
)}
<div className="flex flex-wrap flex-row gap-3">
{!user.isRegistered && (
<div className="overflow-x-auto flex flex-row gap-4 items-center">
<div className="overflow-x-auto">
<span className="text-sm text-muted-foreground ">
{`${url}/invitation?token=${user.token}`}
</span>
</div>
<button
type="button"
// className="absolute right-2 top-2"
onClick={() => {
copy(`${url}/invitation?token=${user.token}`);
toast.success("Invitation Copied to clipboard");
}}
<Table>
<TableCaption>See all users</TableCaption>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Email</TableHead>
<TableHead className="text-center">Status</TableHead>
<TableHead className="text-center">2FA</TableHead>
<TableHead className="text-center">Expiration</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data?.map((user) => {
return (
<TableRow key={user.userId}>
<TableCell className="w-[100px]">
{user.auth.email}
</TableCell>
<TableCell className="text-center">
<Badge
variant={
user.isRegistered ? "default" : "secondary"
}
>
<CopyIcon className="size-4 text-muted-foreground" />
</button>
</div>
)}
{user.isRegistered
? "Registered"
: "Not Registered"}
</Badge>
</TableCell>
<TableCell className="text-center">
{user.auth.is2FAEnabled
? "2FA Enabled"
: "2FA Not Enabled"}
</TableCell>
<TableCell className="text-right">
<span className="text-sm text-muted-foreground">
{format(new Date(user.expirationDate), "PPpp")}
</span>
</TableCell>
{user.isRegistered && (
<AddUserPermissions userId={user.userId} />
)}
{user.isRegistered && <UpdateUser authId={user.authId} />}
<DeleteUser authId={user.authId} />
</div>
</div>
);
})}
<div className="flex flex-col justify-end gap-3 w-full items-end">
<AddUser />
</div>
<TableCell className="text-right flex justify-end">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
{!user.isRegistered && (
<DropdownMenuItem
className="w-full cursor-pointer"
onSelect={(e) => {
copy(
`${origin}/invitation?token=${user.token}`,
);
toast.success(
"Invitation Copied to clipboard",
);
}}
>
Copy Invitation
</DropdownMenuItem>
)}
{user.isRegistered && (
<AddUserPermissions userId={user.userId} />
)}
<DeleteUser authId={user.authId} />
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
)}
</CardContent>

View File

@@ -27,10 +27,9 @@ import { DockerTerminalModal } from "./web-server/docker-terminal-modal";
import { ShowMainTraefikConfig } from "./web-server/show-main-traefik-config";
import { ShowServerTraefikConfig } from "./web-server/show-server-traefik-config";
import { ShowServerMiddlewareConfig } from "./web-server/show-server-middleware-config";
import { UpdateWebServer } from "./web-server/update-webserver";
import { UpdateServer } from "./web-server/update-server";
export const WebServer = () => {
const [fetchAfterFirstRender, setFetchAfterFirstRender] = useState(false);
const { data, refetch } = api.admin.one.useQuery();
const { mutateAsync: reloadServer, isLoading } =
api.settings.reloadServer.useMutation();
@@ -61,13 +60,6 @@ export const WebServer = () => {
const { mutateAsync: updateDockerCleanup } =
api.settings.updateDockerCleanup.useMutation();
const { data: query } = api.settings.checkAndUpdateImage.useQuery(void 0, {
enabled: fetchAfterFirstRender,
});
useEffect(() => {
setFetchAfterFirstRender(true);
}, []);
return (
<Card className="rounded-lg w-full bg-transparent">
@@ -279,7 +271,7 @@ export const WebServer = () => {
</DropdownMenuContent>
</DropdownMenu>
{query ? <UpdateWebServer /> : null}
<UpdateServer />
</div>
<div className="flex items-center flex-wrap justify-between gap-4">

View File

@@ -16,7 +16,6 @@ import {
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Textarea } from "@/components/ui/textarea";
import { api } from "@/utils/api";
import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -24,6 +23,7 @@ import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { CodeEditor } from "@/components/shared/code-editor";
import { validateAndFormatYAML } from "../../application/advanced/traefik/update-traefik-config";
const UpdateMainTraefikConfigSchema = z.object({
@@ -105,8 +105,8 @@ export const ShowMainTraefikConfig = ({ children }: Props) => {
<FormItem className="relative">
<FormLabel>Traefik config</FormLabel>
<FormControl>
<Textarea
className="h-[35rem] font-mono"
<CodeEditor
wrapperClassName="h-[35rem] font-mono"
placeholder={`providers:
docker:
defaultRule: 'Host('dokploy.com')'

View File

@@ -16,7 +16,6 @@ import {
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Textarea } from "@/components/ui/textarea";
import { api } from "@/utils/api";
import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -24,6 +23,7 @@ import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { CodeEditor } from "@/components/shared/code-editor";
import { validateAndFormatYAML } from "../../application/advanced/traefik/update-traefik-config";
const UpdateServerMiddlewareConfigSchema = z.object({
@@ -98,7 +98,7 @@ export const ShowServerMiddlewareConfig = ({ children }: Props) => {
<form
id="hook-form-update-server-traefik-config"
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full py-4 relative"
className="grid w-full py-4 relative overflow-auto"
>
<div className="flex flex-col">
<FormField
@@ -108,8 +108,8 @@ export const ShowServerMiddlewareConfig = ({ children }: Props) => {
<FormItem className="relative">
<FormLabel>Traefik config</FormLabel>
<FormControl>
<Textarea
className="h-[35rem] font-mono"
<CodeEditor
wrapperClassName="h-[35rem] font-mono"
placeholder={`http:
routers:
router-name:

View File

@@ -16,7 +16,6 @@ import {
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Textarea } from "@/components/ui/textarea";
import { api } from "@/utils/api";
import { AlertBlock } from "@/components/shared/alert-block";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -24,6 +23,7 @@ import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { CodeEditor } from "@/components/shared/code-editor";
import { validateAndFormatYAML } from "../../application/advanced/traefik/update-traefik-config";
const UpdateServerTraefikConfigSchema = z.object({
@@ -98,7 +98,7 @@ export const ShowServerTraefikConfig = ({ children }: Props) => {
<form
id="hook-form-update-server-traefik-config"
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full py-4 relative"
className="grid w-full py-4 relative overflow-auto"
>
<div className="flex flex-col">
<FormField
@@ -108,8 +108,8 @@ export const ShowServerTraefikConfig = ({ children }: Props) => {
<FormItem className="relative">
<FormLabel>Traefik config</FormLabel>
<FormControl>
<Textarea
className="h-[35rem] font-mono"
<CodeEditor
wrapperClassName="h-[35rem] font-mono"
placeholder={`http:
routers:
router-name:

View File

@@ -1,4 +1,4 @@
import React from "react";
import type React from "react";
import {
Dialog,
DialogContent,

View File

@@ -0,0 +1,98 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { api } from "@/utils/api";
import { AlertBlock } from "@/components/shared/alert-block";
import { RefreshCcw } from "lucide-react";
import { useState } from "react";
import { toast } from "sonner";
import Link from "next/link";
import { UpdateWebServer } from "./update-webserver";
export const UpdateServer = () => {
const [isUpdateAvailable, setIsUpdateAvailable] = useState<null | boolean>(
null,
);
const { mutateAsync: checkAndUpdateImage, isLoading } =
api.settings.checkAndUpdateImage.useMutation();
const [isOpen, setIsOpen] = useState(false);
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button variant="secondary">
<RefreshCcw className="h-4 w-4" />
Updates
</Button>
</DialogTrigger>
<DialogContent className="sm:m:max-w-lg ">
<DialogHeader>
<DialogTitle>Web Server Update</DialogTitle>
<DialogDescription>
Check new releases and update your dokploy
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-4">
<span className="text-sm text-muted-foreground">
We suggest to update your dokploy to the latest version only if you:
</span>
<ul className="list-disc list-inside text-sm text-muted-foreground">
<li>Want to try the latest features</li>
<li>Some bug that is blocking to use some features</li>
</ul>
<AlertBlock type="info">
Please we recommend to see the latest version to see if there are
any breaking changes before updating. Go to{" "}
<Link
href="https://github.com/Dokploy/dokploy/releases"
target="_blank"
className="text-foreground"
>
Dokploy Releases
</Link>{" "}
to check the latest version.
</AlertBlock>
<div className="w-full flex flex-col gap-4">
{isUpdateAvailable === false && (
<div className="flex flex-col items-center gap-3">
<RefreshCcw className="size-6 self-center text-muted-foreground" />
<span className="text-sm text-muted-foreground">
You are using the latest version
</span>
</div>
)}
{isUpdateAvailable ? (
<UpdateWebServer />
) : (
<Button
className="w-full"
onClick={async () => {
await checkAndUpdateImage()
.then(async (e) => {
setIsUpdateAvailable(e);
})
.catch(() => {
setIsUpdateAvailable(false);
toast.error("Error to check updates");
});
toast.success("Check updates");
}}
isLoading={isLoading}
>
Check updates
</Button>
)}
</div>
</div>
</DialogContent>
</Dialog>
);
};

View File

@@ -19,7 +19,11 @@ export const UpdateWebServer = () => {
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button className="relative" variant="secondary" isLoading={isLoading}>
<Button
className="relative w-full"
variant="secondary"
isLoading={isLoading}
>
<span className="absolute -right-1 -top-2 flex h-3 w-3">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" />
<span className="relative inline-flex rounded-full h-3 w-3 bg-green-500" />

View File

@@ -8,7 +8,7 @@ export const ProjectLayout = ({ children }: Props) => {
return (
<div>
<div
className="bg-radial relative flex flex-col bg-background pt-6"
className="bg-radial relative flex flex-col bg-background"
id="app-container"
>
<div className="flex items-center justify-center">

View File

@@ -59,6 +59,12 @@ export const SettingsLayout = ({ children }: Props) => {
icon: Users,
href: "/dashboard/settings/users",
},
{
title: "Cluster",
label: "",
icon: Server,
href: "/dashboard/settings/cluster",
},
]
: []),
]}
@@ -75,6 +81,7 @@ import {
Activity,
Database,
Route,
Server,
ShieldCheck,
User2,
Users,
@@ -102,7 +109,6 @@ export const Nav = ({ links }: NavProps) => {
<nav className="grid gap-1 px-2 group-[[data-collapsed=true]]:justify-center group-[[data-collapsed=true]]:px-2">
{links.map((link, index) => {
const isActive = router.pathname === link.href;
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
return (
<Link
key={index}
@@ -110,7 +116,7 @@ export const Nav = ({ links }: NavProps) => {
className={cn(
buttonVariants({ variant: "ghost", size: "sm" }),
isActive &&
"dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white",
"dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white bg-muted",
"justify-start",
)}
>

View File

@@ -0,0 +1,45 @@
import CodeMirror, { type ReactCodeMirrorProps } from "@uiw/react-codemirror";
import { yaml } from "@codemirror/lang-yaml";
import { json } from "@codemirror/lang-json";
import { githubLight, githubDark } from "@uiw/codemirror-theme-github";
import { cn } from "@/lib/utils";
import { useTheme } from "next-themes";
interface Props extends ReactCodeMirrorProps {
wrapperClassName?: string;
disabled?: boolean;
}
export const CodeEditor = ({
className,
wrapperClassName,
...props
}: Props) => {
const { resolvedTheme } = useTheme();
return (
<div className={cn("relative overflow-auto", wrapperClassName)}>
<CodeMirror
basicSetup={{
lineNumbers: true,
foldGutter: true,
highlightSelectionMatches: true,
highlightActiveLine: !props.disabled,
allowMultipleSelections: true,
}}
theme={resolvedTheme === "dark" ? githubDark : githubLight}
extensions={[yaml(), json()]}
{...props}
editable={!props.disabled}
className={cn(
"w-full h-full text-sm leading-relaxed",
`cm-theme-${resolvedTheme}`,
className,
)}
/>
{props.disabled && (
<div className="absolute top-0 left-0 w-full h-full flex items-center justify-center z-[10] [background:var(--overlay)]" />
)}
</div>
);
};

View File

@@ -4,19 +4,26 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { format, formatDistanceToNow } from "date-fns";
interface Props {
date: string;
children?: React.ReactNode;
className?: string;
}
export const DateTooltip = ({ date, children }: Props) => {
export const DateTooltip = ({ date, children, className }: Props) => {
return (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger>
<span className="flex items-center text-muted-foreground text-left">
<span
className={cn(
"flex items-center text-muted-foreground text-left",
className,
)}
>
{children}{" "}
{formatDistanceToNow(new Date(date), {
addSuffix: true,

View File

@@ -88,8 +88,7 @@ const Tree = React.forwardRef<HTMLDivElement, TreeProps>(
const { ref: refRoot, width, height } = useResizeObserver();
return (
<div ref={refRoot} className={cn("overflow-hidden", className)}>
{/* style={{ width, height }} */}
<div ref={refRoot} className={cn("overflow-y-auto", className)}>
<ScrollArea>
<div className="relative p-2">
<TreeItem

View File

@@ -147,7 +147,6 @@ const FormMessage = React.forwardRef<
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message) : children;
if (!body) {
return null;
}

View File

@@ -46,7 +46,7 @@ else
fi
docker swarm leave --force 2>/dev/null
docker swarm init --advertise-addr 127.0.0.1 --listen-addr 0.0.0.0;
docker swarm init;
echo "Swarm initialized"

View File

@@ -0,0 +1,22 @@
DO $$ BEGIN
CREATE TYPE "RegistryType" AS ENUM('selfHosted', 'cloud');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "registry" (
"registryId" text PRIMARY KEY NOT NULL,
"registryName" text NOT NULL,
"username" text NOT NULL,
"password" text NOT NULL,
"registryUrl" text NOT NULL,
"createdAt" text NOT NULL,
"selfHosted" "RegistryType" DEFAULT 'cloud' NOT NULL,
"adminId" text NOT NULL
);
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "registry" ADD CONSTRAINT "registry_adminId_admin_adminId_fk" FOREIGN KEY ("adminId") REFERENCES "admin"("adminId") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;

View File

@@ -0,0 +1,6 @@
ALTER TABLE "application" ADD COLUMN "registryId" text;--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "application" ADD CONSTRAINT "application_registryId_registry_registryId_fk" FOREIGN KEY ("registryId") REFERENCES "public"."registry"("registryId") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;

View File

@@ -0,0 +1 @@
ALTER TABLE "application" ADD COLUMN "replicas" integer DEFAULT 1;

View File

@@ -0,0 +1,7 @@
ALTER TABLE "application" DROP CONSTRAINT "application_registryId_registry_registryId_fk";
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "application" ADD CONSTRAINT "application_registryId_registry_registryId_fk" FOREIGN KEY ("registryId") REFERENCES "public"."registry"("registryId") ON DELETE set null ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;

View File

@@ -0,0 +1 @@
ALTER TABLE "application" ALTER COLUMN "replicas" SET NOT NULL;

View File

@@ -0,0 +1 @@
ALTER TABLE "registry" ADD COLUMN "imagePrefix" text NOT NULL;

View File

@@ -0,0 +1 @@
ALTER TABLE "registry" ALTER COLUMN "imagePrefix" DROP NOT NULL;

View File

@@ -0,0 +1,7 @@
ALTER TABLE "application" ADD COLUMN "healthCheckSwarm" json;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "restartPolicySwarm" json;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "placementSwarm" json;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "updateConfigSwarm" json;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "rollbackConfigSwarm" json;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "modeSwarm" json;--> statement-breakpoint
ALTER TABLE "application" ADD COLUMN "labelsSwarm" json;

View File

@@ -0,0 +1 @@
ALTER TABLE "application" ADD COLUMN "networkSwarm" json;

View File

@@ -1,10 +1,8 @@
{
"id": "c6215051-7cd1-412d-b8df-b50d58acacff",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "5",
"dialect": "pg",
"version": "6",
"dialect": "postgresql",
"tables": {
"application": {
"public.application": {
"name": "application",
"schema": "",
"columns": {
@@ -234,29 +232,29 @@
"application_projectId_project_projectId_fk": {
"name": "application_projectId_project_projectId_fk",
"tableFrom": "application",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"application_appName_unique": {
"name": "application_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"postgres": {
"public.postgres": {
"name": "postgres",
"schema": "",
"columns": {
@@ -375,29 +373,29 @@
"postgres_projectId_project_projectId_fk": {
"name": "postgres_projectId_project_projectId_fk",
"tableFrom": "postgres",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"postgres_appName_unique": {
"name": "postgres_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"user": {
"public.user": {
"name": "user",
"schema": "",
"columns": {
@@ -499,34 +497,34 @@
"user_adminId_admin_adminId_fk": {
"name": "user_adminId_admin_adminId_fk",
"tableFrom": "user",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"user_authId_auth_id_fk": {
"name": "user_authId_auth_id_fk",
"tableFrom": "user",
"tableTo": "auth",
"columnsFrom": [
"authId"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"admin": {
"public.admin": {
"name": "admin",
"schema": "",
"columns": {
@@ -628,21 +626,21 @@
"admin_authId_auth_id_fk": {
"name": "admin_authId_auth_id_fk",
"tableFrom": "admin",
"tableTo": "auth",
"columnsFrom": [
"authId"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"auth": {
"public.auth": {
"name": "auth",
"schema": "",
"columns": {
@@ -689,14 +687,14 @@
"uniqueConstraints": {
"auth_email_unique": {
"name": "auth_email_unique",
"nullsNotDistinct": false,
"columns": [
"email"
]
],
"nullsNotDistinct": false
}
}
},
"project": {
"public.project": {
"name": "project",
"schema": "",
"columns": {
@@ -736,21 +734,21 @@
"project_adminId_admin_adminId_fk": {
"name": "project_adminId_admin_adminId_fk",
"tableFrom": "project",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"domain": {
"public.domain": {
"name": "domain",
"schema": "",
"columns": {
@@ -818,21 +816,21 @@
"domain_applicationId_application_applicationId_fk": {
"name": "domain_applicationId_application_applicationId_fk",
"tableFrom": "domain",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"mariadb": {
"public.mariadb": {
"name": "mariadb",
"schema": "",
"columns": {
@@ -957,29 +955,29 @@
"mariadb_projectId_project_projectId_fk": {
"name": "mariadb_projectId_project_projectId_fk",
"tableFrom": "mariadb",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mariadb_appName_unique": {
"name": "mariadb_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"mongo": {
"public.mongo": {
"name": "mongo",
"schema": "",
"columns": {
@@ -1092,29 +1090,29 @@
"mongo_projectId_project_projectId_fk": {
"name": "mongo_projectId_project_projectId_fk",
"tableFrom": "mongo",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mongo_appName_unique": {
"name": "mongo_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"mysql": {
"public.mysql": {
"name": "mysql",
"schema": "",
"columns": {
@@ -1239,29 +1237,29 @@
"mysql_projectId_project_projectId_fk": {
"name": "mysql_projectId_project_projectId_fk",
"tableFrom": "mysql",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mysql_appName_unique": {
"name": "mysql_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"backup": {
"public.backup": {
"name": "backup",
"schema": "",
"columns": {
@@ -1337,73 +1335,73 @@
"backup_destinationId_destination_destinationId_fk": {
"name": "backup_destinationId_destination_destinationId_fk",
"tableFrom": "backup",
"tableTo": "destination",
"columnsFrom": [
"destinationId"
],
"tableTo": "destination",
"columnsTo": [
"destinationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_postgresId_postgres_postgresId_fk": {
"name": "backup_postgresId_postgres_postgresId_fk",
"tableFrom": "backup",
"tableTo": "postgres",
"columnsFrom": [
"postgresId"
],
"tableTo": "postgres",
"columnsTo": [
"postgresId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mariadbId_mariadb_mariadbId_fk": {
"name": "backup_mariadbId_mariadb_mariadbId_fk",
"tableFrom": "backup",
"tableTo": "mariadb",
"columnsFrom": [
"mariadbId"
],
"tableTo": "mariadb",
"columnsTo": [
"mariadbId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mysqlId_mysql_mysqlId_fk": {
"name": "backup_mysqlId_mysql_mysqlId_fk",
"tableFrom": "backup",
"tableTo": "mysql",
"columnsFrom": [
"mysqlId"
],
"tableTo": "mysql",
"columnsTo": [
"mysqlId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mongoId_mongo_mongoId_fk": {
"name": "backup_mongoId_mongo_mongoId_fk",
"tableFrom": "backup",
"tableTo": "mongo",
"columnsFrom": [
"mongoId"
],
"tableTo": "mongo",
"columnsTo": [
"mongoId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"destination": {
"public.destination": {
"name": "destination",
"schema": "",
"columns": {
@@ -1461,21 +1459,21 @@
"destination_adminId_admin_adminId_fk": {
"name": "destination_adminId_admin_adminId_fk",
"tableFrom": "destination",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"deployment": {
"public.deployment": {
"name": "deployment",
"schema": "",
"columns": {
@@ -1522,21 +1520,21 @@
"deployment_applicationId_application_applicationId_fk": {
"name": "deployment_applicationId_application_applicationId_fk",
"tableFrom": "deployment",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"mount": {
"public.mount": {
"name": "mount",
"schema": "",
"columns": {
@@ -1625,86 +1623,86 @@
"mount_applicationId_application_applicationId_fk": {
"name": "mount_applicationId_application_applicationId_fk",
"tableFrom": "mount",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_postgresId_postgres_postgresId_fk": {
"name": "mount_postgresId_postgres_postgresId_fk",
"tableFrom": "mount",
"tableTo": "postgres",
"columnsFrom": [
"postgresId"
],
"tableTo": "postgres",
"columnsTo": [
"postgresId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mariadbId_mariadb_mariadbId_fk": {
"name": "mount_mariadbId_mariadb_mariadbId_fk",
"tableFrom": "mount",
"tableTo": "mariadb",
"columnsFrom": [
"mariadbId"
],
"tableTo": "mariadb",
"columnsTo": [
"mariadbId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mongoId_mongo_mongoId_fk": {
"name": "mount_mongoId_mongo_mongoId_fk",
"tableFrom": "mount",
"tableTo": "mongo",
"columnsFrom": [
"mongoId"
],
"tableTo": "mongo",
"columnsTo": [
"mongoId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mysqlId_mysql_mysqlId_fk": {
"name": "mount_mysqlId_mysql_mysqlId_fk",
"tableFrom": "mount",
"tableTo": "mysql",
"columnsFrom": [
"mysqlId"
],
"tableTo": "mysql",
"columnsTo": [
"mysqlId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_redisId_redis_redisId_fk": {
"name": "mount_redisId_redis_redisId_fk",
"tableFrom": "mount",
"tableTo": "redis",
"columnsFrom": [
"redisId"
],
"tableTo": "redis",
"columnsTo": [
"redisId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"certificate": {
"public.certificate": {
"name": "certificate",
"schema": "",
"columns": {
@@ -1751,14 +1749,14 @@
"uniqueConstraints": {
"certificate_certificatePath_unique": {
"name": "certificate_certificatePath_unique",
"nullsNotDistinct": false,
"columns": [
"certificatePath"
]
],
"nullsNotDistinct": false
}
}
},
"session": {
"public.session": {
"name": "session",
"schema": "",
"columns": {
@@ -1786,21 +1784,21 @@
"session_user_id_auth_id_fk": {
"name": "session_user_id_auth_id_fk",
"tableFrom": "session",
"tableTo": "auth",
"columnsFrom": [
"user_id"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"redirect": {
"public.redirect": {
"name": "redirect",
"schema": "",
"columns": {
@@ -1853,21 +1851,21 @@
"redirect_applicationId_application_applicationId_fk": {
"name": "redirect_applicationId_application_applicationId_fk",
"tableFrom": "redirect",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"security": {
"public.security": {
"name": "security",
"schema": "",
"columns": {
@@ -1907,30 +1905,30 @@
"security_applicationId_application_applicationId_fk": {
"name": "security_applicationId_application_applicationId_fk",
"tableFrom": "security",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"security_username_applicationId_unique": {
"name": "security_username_applicationId_unique",
"nullsNotDistinct": false,
"columns": [
"username",
"applicationId"
]
],
"nullsNotDistinct": false
}
}
},
"port": {
"public.port": {
"name": "port",
"schema": "",
"columns": {
@@ -1970,21 +1968,21 @@
"port_applicationId_application_applicationId_fk": {
"name": "port_applicationId_application_applicationId_fk",
"tableFrom": "port",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"redis": {
"public.redis": {
"name": "redis",
"schema": "",
"columns": {
@@ -2091,118 +2089,130 @@
"redis_projectId_project_projectId_fk": {
"name": "redis_projectId_project_projectId_fk",
"tableFrom": "redis",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"redis_appName_unique": {
"name": "redis_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
}
},
"enums": {
"buildType": {
"public.buildType": {
"name": "buildType",
"values": {
"dockerfile": "dockerfile",
"heroku_buildpacks": "heroku_buildpacks",
"paketo_buildpacks": "paketo_buildpacks",
"nixpacks": "nixpacks"
}
"schema": "public",
"values": [
"dockerfile",
"heroku_buildpacks",
"paketo_buildpacks",
"nixpacks"
]
},
"sourceType": {
"public.sourceType": {
"name": "sourceType",
"values": {
"docker": "docker",
"git": "git",
"github": "github"
}
"schema": "public",
"values": [
"docker",
"git",
"github"
]
},
"Roles": {
"public.Roles": {
"name": "Roles",
"values": {
"admin": "admin",
"user": "user"
}
"schema": "public",
"values": [
"admin",
"user"
]
},
"databaseType": {
"public.databaseType": {
"name": "databaseType",
"values": {
"postgres": "postgres",
"mariadb": "mariadb",
"mysql": "mysql",
"mongo": "mongo"
}
"schema": "public",
"values": [
"postgres",
"mariadb",
"mysql",
"mongo"
]
},
"deploymentStatus": {
"public.deploymentStatus": {
"name": "deploymentStatus",
"values": {
"running": "running",
"done": "done",
"error": "error"
}
"schema": "public",
"values": [
"running",
"done",
"error"
]
},
"mountType": {
"public.mountType": {
"name": "mountType",
"values": {
"bind": "bind",
"volume": "volume",
"file": "file"
}
"schema": "public",
"values": [
"bind",
"volume",
"file"
]
},
"serviceType": {
"public.serviceType": {
"name": "serviceType",
"values": {
"application": "application",
"postgres": "postgres",
"mysql": "mysql",
"mariadb": "mariadb",
"mongo": "mongo",
"redis": "redis"
}
"schema": "public",
"values": [
"application",
"postgres",
"mysql",
"mariadb",
"mongo",
"redis"
]
},
"protocolType": {
"public.protocolType": {
"name": "protocolType",
"values": {
"tcp": "tcp",
"udp": "udp"
}
"schema": "public",
"values": [
"tcp",
"udp"
]
},
"applicationStatus": {
"public.applicationStatus": {
"name": "applicationStatus",
"values": {
"idle": "idle",
"running": "running",
"done": "done",
"error": "error"
}
"schema": "public",
"values": [
"idle",
"running",
"done",
"error"
]
},
"certificateType": {
"public.certificateType": {
"name": "certificateType",
"values": {
"letsencrypt": "letsencrypt",
"none": "none"
}
"schema": "public",
"values": [
"letsencrypt",
"none"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
"tables": {},
"columns": {}
},
"id": "c6215051-7cd1-412d-b8df-b50d58acacff",
"prevId": "00000000-0000-0000-0000-000000000000"
}

View File

@@ -1,10 +1,8 @@
{
"id": "3a4dfad7-ae33-4ae3-b60e-4f40f44f5652",
"prevId": "c6215051-7cd1-412d-b8df-b50d58acacff",
"version": "5",
"dialect": "pg",
"version": "6",
"dialect": "postgresql",
"tables": {
"application": {
"public.application": {
"name": "application",
"schema": "",
"columns": {
@@ -234,29 +232,29 @@
"application_projectId_project_projectId_fk": {
"name": "application_projectId_project_projectId_fk",
"tableFrom": "application",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"application_appName_unique": {
"name": "application_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"postgres": {
"public.postgres": {
"name": "postgres",
"schema": "",
"columns": {
@@ -375,29 +373,29 @@
"postgres_projectId_project_projectId_fk": {
"name": "postgres_projectId_project_projectId_fk",
"tableFrom": "postgres",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"postgres_appName_unique": {
"name": "postgres_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"user": {
"public.user": {
"name": "user",
"schema": "",
"columns": {
@@ -499,34 +497,34 @@
"user_adminId_admin_adminId_fk": {
"name": "user_adminId_admin_adminId_fk",
"tableFrom": "user",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"user_authId_auth_id_fk": {
"name": "user_authId_auth_id_fk",
"tableFrom": "user",
"tableTo": "auth",
"columnsFrom": [
"authId"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"admin": {
"public.admin": {
"name": "admin",
"schema": "",
"columns": {
@@ -628,21 +626,21 @@
"admin_authId_auth_id_fk": {
"name": "admin_authId_auth_id_fk",
"tableFrom": "admin",
"tableTo": "auth",
"columnsFrom": [
"authId"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"auth": {
"public.auth": {
"name": "auth",
"schema": "",
"columns": {
@@ -695,14 +693,14 @@
"uniqueConstraints": {
"auth_email_unique": {
"name": "auth_email_unique",
"nullsNotDistinct": false,
"columns": [
"email"
]
],
"nullsNotDistinct": false
}
}
},
"project": {
"public.project": {
"name": "project",
"schema": "",
"columns": {
@@ -742,21 +740,21 @@
"project_adminId_admin_adminId_fk": {
"name": "project_adminId_admin_adminId_fk",
"tableFrom": "project",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"domain": {
"public.domain": {
"name": "domain",
"schema": "",
"columns": {
@@ -824,21 +822,21 @@
"domain_applicationId_application_applicationId_fk": {
"name": "domain_applicationId_application_applicationId_fk",
"tableFrom": "domain",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"mariadb": {
"public.mariadb": {
"name": "mariadb",
"schema": "",
"columns": {
@@ -963,29 +961,29 @@
"mariadb_projectId_project_projectId_fk": {
"name": "mariadb_projectId_project_projectId_fk",
"tableFrom": "mariadb",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mariadb_appName_unique": {
"name": "mariadb_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"mongo": {
"public.mongo": {
"name": "mongo",
"schema": "",
"columns": {
@@ -1098,29 +1096,29 @@
"mongo_projectId_project_projectId_fk": {
"name": "mongo_projectId_project_projectId_fk",
"tableFrom": "mongo",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mongo_appName_unique": {
"name": "mongo_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"mysql": {
"public.mysql": {
"name": "mysql",
"schema": "",
"columns": {
@@ -1245,29 +1243,29 @@
"mysql_projectId_project_projectId_fk": {
"name": "mysql_projectId_project_projectId_fk",
"tableFrom": "mysql",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mysql_appName_unique": {
"name": "mysql_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"backup": {
"public.backup": {
"name": "backup",
"schema": "",
"columns": {
@@ -1343,73 +1341,73 @@
"backup_destinationId_destination_destinationId_fk": {
"name": "backup_destinationId_destination_destinationId_fk",
"tableFrom": "backup",
"tableTo": "destination",
"columnsFrom": [
"destinationId"
],
"tableTo": "destination",
"columnsTo": [
"destinationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_postgresId_postgres_postgresId_fk": {
"name": "backup_postgresId_postgres_postgresId_fk",
"tableFrom": "backup",
"tableTo": "postgres",
"columnsFrom": [
"postgresId"
],
"tableTo": "postgres",
"columnsTo": [
"postgresId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mariadbId_mariadb_mariadbId_fk": {
"name": "backup_mariadbId_mariadb_mariadbId_fk",
"tableFrom": "backup",
"tableTo": "mariadb",
"columnsFrom": [
"mariadbId"
],
"tableTo": "mariadb",
"columnsTo": [
"mariadbId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mysqlId_mysql_mysqlId_fk": {
"name": "backup_mysqlId_mysql_mysqlId_fk",
"tableFrom": "backup",
"tableTo": "mysql",
"columnsFrom": [
"mysqlId"
],
"tableTo": "mysql",
"columnsTo": [
"mysqlId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mongoId_mongo_mongoId_fk": {
"name": "backup_mongoId_mongo_mongoId_fk",
"tableFrom": "backup",
"tableTo": "mongo",
"columnsFrom": [
"mongoId"
],
"tableTo": "mongo",
"columnsTo": [
"mongoId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"destination": {
"public.destination": {
"name": "destination",
"schema": "",
"columns": {
@@ -1467,21 +1465,21 @@
"destination_adminId_admin_adminId_fk": {
"name": "destination_adminId_admin_adminId_fk",
"tableFrom": "destination",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"deployment": {
"public.deployment": {
"name": "deployment",
"schema": "",
"columns": {
@@ -1528,21 +1526,21 @@
"deployment_applicationId_application_applicationId_fk": {
"name": "deployment_applicationId_application_applicationId_fk",
"tableFrom": "deployment",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"mount": {
"public.mount": {
"name": "mount",
"schema": "",
"columns": {
@@ -1631,86 +1629,86 @@
"mount_applicationId_application_applicationId_fk": {
"name": "mount_applicationId_application_applicationId_fk",
"tableFrom": "mount",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_postgresId_postgres_postgresId_fk": {
"name": "mount_postgresId_postgres_postgresId_fk",
"tableFrom": "mount",
"tableTo": "postgres",
"columnsFrom": [
"postgresId"
],
"tableTo": "postgres",
"columnsTo": [
"postgresId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mariadbId_mariadb_mariadbId_fk": {
"name": "mount_mariadbId_mariadb_mariadbId_fk",
"tableFrom": "mount",
"tableTo": "mariadb",
"columnsFrom": [
"mariadbId"
],
"tableTo": "mariadb",
"columnsTo": [
"mariadbId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mongoId_mongo_mongoId_fk": {
"name": "mount_mongoId_mongo_mongoId_fk",
"tableFrom": "mount",
"tableTo": "mongo",
"columnsFrom": [
"mongoId"
],
"tableTo": "mongo",
"columnsTo": [
"mongoId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mysqlId_mysql_mysqlId_fk": {
"name": "mount_mysqlId_mysql_mysqlId_fk",
"tableFrom": "mount",
"tableTo": "mysql",
"columnsFrom": [
"mysqlId"
],
"tableTo": "mysql",
"columnsTo": [
"mysqlId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_redisId_redis_redisId_fk": {
"name": "mount_redisId_redis_redisId_fk",
"tableFrom": "mount",
"tableTo": "redis",
"columnsFrom": [
"redisId"
],
"tableTo": "redis",
"columnsTo": [
"redisId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"certificate": {
"public.certificate": {
"name": "certificate",
"schema": "",
"columns": {
@@ -1757,14 +1755,14 @@
"uniqueConstraints": {
"certificate_certificatePath_unique": {
"name": "certificate_certificatePath_unique",
"nullsNotDistinct": false,
"columns": [
"certificatePath"
]
],
"nullsNotDistinct": false
}
}
},
"session": {
"public.session": {
"name": "session",
"schema": "",
"columns": {
@@ -1792,21 +1790,21 @@
"session_user_id_auth_id_fk": {
"name": "session_user_id_auth_id_fk",
"tableFrom": "session",
"tableTo": "auth",
"columnsFrom": [
"user_id"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"redirect": {
"public.redirect": {
"name": "redirect",
"schema": "",
"columns": {
@@ -1859,21 +1857,21 @@
"redirect_applicationId_application_applicationId_fk": {
"name": "redirect_applicationId_application_applicationId_fk",
"tableFrom": "redirect",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"security": {
"public.security": {
"name": "security",
"schema": "",
"columns": {
@@ -1913,30 +1911,30 @@
"security_applicationId_application_applicationId_fk": {
"name": "security_applicationId_application_applicationId_fk",
"tableFrom": "security",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"security_username_applicationId_unique": {
"name": "security_username_applicationId_unique",
"nullsNotDistinct": false,
"columns": [
"username",
"applicationId"
]
],
"nullsNotDistinct": false
}
}
},
"port": {
"public.port": {
"name": "port",
"schema": "",
"columns": {
@@ -1976,21 +1974,21 @@
"port_applicationId_application_applicationId_fk": {
"name": "port_applicationId_application_applicationId_fk",
"tableFrom": "port",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"redis": {
"public.redis": {
"name": "redis",
"schema": "",
"columns": {
@@ -2097,118 +2095,130 @@
"redis_projectId_project_projectId_fk": {
"name": "redis_projectId_project_projectId_fk",
"tableFrom": "redis",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"redis_appName_unique": {
"name": "redis_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
}
},
"enums": {
"buildType": {
"public.buildType": {
"name": "buildType",
"values": {
"dockerfile": "dockerfile",
"heroku_buildpacks": "heroku_buildpacks",
"paketo_buildpacks": "paketo_buildpacks",
"nixpacks": "nixpacks"
}
"schema": "public",
"values": [
"dockerfile",
"heroku_buildpacks",
"paketo_buildpacks",
"nixpacks"
]
},
"sourceType": {
"public.sourceType": {
"name": "sourceType",
"values": {
"docker": "docker",
"git": "git",
"github": "github"
}
"schema": "public",
"values": [
"docker",
"git",
"github"
]
},
"Roles": {
"public.Roles": {
"name": "Roles",
"values": {
"admin": "admin",
"user": "user"
}
"schema": "public",
"values": [
"admin",
"user"
]
},
"databaseType": {
"public.databaseType": {
"name": "databaseType",
"values": {
"postgres": "postgres",
"mariadb": "mariadb",
"mysql": "mysql",
"mongo": "mongo"
}
"schema": "public",
"values": [
"postgres",
"mariadb",
"mysql",
"mongo"
]
},
"deploymentStatus": {
"public.deploymentStatus": {
"name": "deploymentStatus",
"values": {
"running": "running",
"done": "done",
"error": "error"
}
"schema": "public",
"values": [
"running",
"done",
"error"
]
},
"mountType": {
"public.mountType": {
"name": "mountType",
"values": {
"bind": "bind",
"volume": "volume",
"file": "file"
}
"schema": "public",
"values": [
"bind",
"volume",
"file"
]
},
"serviceType": {
"public.serviceType": {
"name": "serviceType",
"values": {
"application": "application",
"postgres": "postgres",
"mysql": "mysql",
"mariadb": "mariadb",
"mongo": "mongo",
"redis": "redis"
}
"schema": "public",
"values": [
"application",
"postgres",
"mysql",
"mariadb",
"mongo",
"redis"
]
},
"protocolType": {
"public.protocolType": {
"name": "protocolType",
"values": {
"tcp": "tcp",
"udp": "udp"
}
"schema": "public",
"values": [
"tcp",
"udp"
]
},
"applicationStatus": {
"public.applicationStatus": {
"name": "applicationStatus",
"values": {
"idle": "idle",
"running": "running",
"done": "done",
"error": "error"
}
"schema": "public",
"values": [
"idle",
"running",
"done",
"error"
]
},
"certificateType": {
"public.certificateType": {
"name": "certificateType",
"values": {
"letsencrypt": "letsencrypt",
"none": "none"
}
"schema": "public",
"values": [
"letsencrypt",
"none"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
"tables": {},
"columns": {}
},
"id": "3a4dfad7-ae33-4ae3-b60e-4f40f44f5652",
"prevId": "c6215051-7cd1-412d-b8df-b50d58acacff"
}

View File

@@ -1,10 +1,8 @@
{
"id": "665483bd-5123-4c2b-beef-bfa9b91b9356",
"prevId": "3a4dfad7-ae33-4ae3-b60e-4f40f44f5652",
"version": "5",
"dialect": "pg",
"version": "6",
"dialect": "postgresql",
"tables": {
"application": {
"public.application": {
"name": "application",
"schema": "",
"columns": {
@@ -234,29 +232,29 @@
"application_projectId_project_projectId_fk": {
"name": "application_projectId_project_projectId_fk",
"tableFrom": "application",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"application_appName_unique": {
"name": "application_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"postgres": {
"public.postgres": {
"name": "postgres",
"schema": "",
"columns": {
@@ -375,29 +373,29 @@
"postgres_projectId_project_projectId_fk": {
"name": "postgres_projectId_project_projectId_fk",
"tableFrom": "postgres",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"postgres_appName_unique": {
"name": "postgres_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"user": {
"public.user": {
"name": "user",
"schema": "",
"columns": {
@@ -499,34 +497,34 @@
"user_adminId_admin_adminId_fk": {
"name": "user_adminId_admin_adminId_fk",
"tableFrom": "user",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"user_authId_auth_id_fk": {
"name": "user_authId_auth_id_fk",
"tableFrom": "user",
"tableTo": "auth",
"columnsFrom": [
"authId"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"admin": {
"public.admin": {
"name": "admin",
"schema": "",
"columns": {
@@ -628,21 +626,21 @@
"admin_authId_auth_id_fk": {
"name": "admin_authId_auth_id_fk",
"tableFrom": "admin",
"tableTo": "auth",
"columnsFrom": [
"authId"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"auth": {
"public.auth": {
"name": "auth",
"schema": "",
"columns": {
@@ -702,14 +700,14 @@
"uniqueConstraints": {
"auth_email_unique": {
"name": "auth_email_unique",
"nullsNotDistinct": false,
"columns": [
"email"
]
],
"nullsNotDistinct": false
}
}
},
"project": {
"public.project": {
"name": "project",
"schema": "",
"columns": {
@@ -749,21 +747,21 @@
"project_adminId_admin_adminId_fk": {
"name": "project_adminId_admin_adminId_fk",
"tableFrom": "project",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"domain": {
"public.domain": {
"name": "domain",
"schema": "",
"columns": {
@@ -831,21 +829,21 @@
"domain_applicationId_application_applicationId_fk": {
"name": "domain_applicationId_application_applicationId_fk",
"tableFrom": "domain",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"mariadb": {
"public.mariadb": {
"name": "mariadb",
"schema": "",
"columns": {
@@ -970,29 +968,29 @@
"mariadb_projectId_project_projectId_fk": {
"name": "mariadb_projectId_project_projectId_fk",
"tableFrom": "mariadb",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mariadb_appName_unique": {
"name": "mariadb_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"mongo": {
"public.mongo": {
"name": "mongo",
"schema": "",
"columns": {
@@ -1105,29 +1103,29 @@
"mongo_projectId_project_projectId_fk": {
"name": "mongo_projectId_project_projectId_fk",
"tableFrom": "mongo",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mongo_appName_unique": {
"name": "mongo_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"mysql": {
"public.mysql": {
"name": "mysql",
"schema": "",
"columns": {
@@ -1252,29 +1250,29 @@
"mysql_projectId_project_projectId_fk": {
"name": "mysql_projectId_project_projectId_fk",
"tableFrom": "mysql",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mysql_appName_unique": {
"name": "mysql_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"backup": {
"public.backup": {
"name": "backup",
"schema": "",
"columns": {
@@ -1350,73 +1348,73 @@
"backup_destinationId_destination_destinationId_fk": {
"name": "backup_destinationId_destination_destinationId_fk",
"tableFrom": "backup",
"tableTo": "destination",
"columnsFrom": [
"destinationId"
],
"tableTo": "destination",
"columnsTo": [
"destinationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_postgresId_postgres_postgresId_fk": {
"name": "backup_postgresId_postgres_postgresId_fk",
"tableFrom": "backup",
"tableTo": "postgres",
"columnsFrom": [
"postgresId"
],
"tableTo": "postgres",
"columnsTo": [
"postgresId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mariadbId_mariadb_mariadbId_fk": {
"name": "backup_mariadbId_mariadb_mariadbId_fk",
"tableFrom": "backup",
"tableTo": "mariadb",
"columnsFrom": [
"mariadbId"
],
"tableTo": "mariadb",
"columnsTo": [
"mariadbId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mysqlId_mysql_mysqlId_fk": {
"name": "backup_mysqlId_mysql_mysqlId_fk",
"tableFrom": "backup",
"tableTo": "mysql",
"columnsFrom": [
"mysqlId"
],
"tableTo": "mysql",
"columnsTo": [
"mysqlId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mongoId_mongo_mongoId_fk": {
"name": "backup_mongoId_mongo_mongoId_fk",
"tableFrom": "backup",
"tableTo": "mongo",
"columnsFrom": [
"mongoId"
],
"tableTo": "mongo",
"columnsTo": [
"mongoId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"destination": {
"public.destination": {
"name": "destination",
"schema": "",
"columns": {
@@ -1474,21 +1472,21 @@
"destination_adminId_admin_adminId_fk": {
"name": "destination_adminId_admin_adminId_fk",
"tableFrom": "destination",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"deployment": {
"public.deployment": {
"name": "deployment",
"schema": "",
"columns": {
@@ -1535,21 +1533,21 @@
"deployment_applicationId_application_applicationId_fk": {
"name": "deployment_applicationId_application_applicationId_fk",
"tableFrom": "deployment",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"mount": {
"public.mount": {
"name": "mount",
"schema": "",
"columns": {
@@ -1638,86 +1636,86 @@
"mount_applicationId_application_applicationId_fk": {
"name": "mount_applicationId_application_applicationId_fk",
"tableFrom": "mount",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_postgresId_postgres_postgresId_fk": {
"name": "mount_postgresId_postgres_postgresId_fk",
"tableFrom": "mount",
"tableTo": "postgres",
"columnsFrom": [
"postgresId"
],
"tableTo": "postgres",
"columnsTo": [
"postgresId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mariadbId_mariadb_mariadbId_fk": {
"name": "mount_mariadbId_mariadb_mariadbId_fk",
"tableFrom": "mount",
"tableTo": "mariadb",
"columnsFrom": [
"mariadbId"
],
"tableTo": "mariadb",
"columnsTo": [
"mariadbId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mongoId_mongo_mongoId_fk": {
"name": "mount_mongoId_mongo_mongoId_fk",
"tableFrom": "mount",
"tableTo": "mongo",
"columnsFrom": [
"mongoId"
],
"tableTo": "mongo",
"columnsTo": [
"mongoId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mysqlId_mysql_mysqlId_fk": {
"name": "mount_mysqlId_mysql_mysqlId_fk",
"tableFrom": "mount",
"tableTo": "mysql",
"columnsFrom": [
"mysqlId"
],
"tableTo": "mysql",
"columnsTo": [
"mysqlId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_redisId_redis_redisId_fk": {
"name": "mount_redisId_redis_redisId_fk",
"tableFrom": "mount",
"tableTo": "redis",
"columnsFrom": [
"redisId"
],
"tableTo": "redis",
"columnsTo": [
"redisId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"certificate": {
"public.certificate": {
"name": "certificate",
"schema": "",
"columns": {
@@ -1764,14 +1762,14 @@
"uniqueConstraints": {
"certificate_certificatePath_unique": {
"name": "certificate_certificatePath_unique",
"nullsNotDistinct": false,
"columns": [
"certificatePath"
]
],
"nullsNotDistinct": false
}
}
},
"session": {
"public.session": {
"name": "session",
"schema": "",
"columns": {
@@ -1799,21 +1797,21 @@
"session_user_id_auth_id_fk": {
"name": "session_user_id_auth_id_fk",
"tableFrom": "session",
"tableTo": "auth",
"columnsFrom": [
"user_id"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"redirect": {
"public.redirect": {
"name": "redirect",
"schema": "",
"columns": {
@@ -1866,21 +1864,21 @@
"redirect_applicationId_application_applicationId_fk": {
"name": "redirect_applicationId_application_applicationId_fk",
"tableFrom": "redirect",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"security": {
"public.security": {
"name": "security",
"schema": "",
"columns": {
@@ -1920,30 +1918,30 @@
"security_applicationId_application_applicationId_fk": {
"name": "security_applicationId_application_applicationId_fk",
"tableFrom": "security",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"security_username_applicationId_unique": {
"name": "security_username_applicationId_unique",
"nullsNotDistinct": false,
"columns": [
"username",
"applicationId"
]
],
"nullsNotDistinct": false
}
}
},
"port": {
"public.port": {
"name": "port",
"schema": "",
"columns": {
@@ -1983,21 +1981,21 @@
"port_applicationId_application_applicationId_fk": {
"name": "port_applicationId_application_applicationId_fk",
"tableFrom": "port",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"redis": {
"public.redis": {
"name": "redis",
"schema": "",
"columns": {
@@ -2104,118 +2102,130 @@
"redis_projectId_project_projectId_fk": {
"name": "redis_projectId_project_projectId_fk",
"tableFrom": "redis",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"redis_appName_unique": {
"name": "redis_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
}
},
"enums": {
"buildType": {
"public.buildType": {
"name": "buildType",
"values": {
"dockerfile": "dockerfile",
"heroku_buildpacks": "heroku_buildpacks",
"paketo_buildpacks": "paketo_buildpacks",
"nixpacks": "nixpacks"
}
"schema": "public",
"values": [
"dockerfile",
"heroku_buildpacks",
"paketo_buildpacks",
"nixpacks"
]
},
"sourceType": {
"public.sourceType": {
"name": "sourceType",
"values": {
"docker": "docker",
"git": "git",
"github": "github"
}
"schema": "public",
"values": [
"docker",
"git",
"github"
]
},
"Roles": {
"public.Roles": {
"name": "Roles",
"values": {
"admin": "admin",
"user": "user"
}
"schema": "public",
"values": [
"admin",
"user"
]
},
"databaseType": {
"public.databaseType": {
"name": "databaseType",
"values": {
"postgres": "postgres",
"mariadb": "mariadb",
"mysql": "mysql",
"mongo": "mongo"
}
"schema": "public",
"values": [
"postgres",
"mariadb",
"mysql",
"mongo"
]
},
"deploymentStatus": {
"public.deploymentStatus": {
"name": "deploymentStatus",
"values": {
"running": "running",
"done": "done",
"error": "error"
}
"schema": "public",
"values": [
"running",
"done",
"error"
]
},
"mountType": {
"public.mountType": {
"name": "mountType",
"values": {
"bind": "bind",
"volume": "volume",
"file": "file"
}
"schema": "public",
"values": [
"bind",
"volume",
"file"
]
},
"serviceType": {
"public.serviceType": {
"name": "serviceType",
"values": {
"application": "application",
"postgres": "postgres",
"mysql": "mysql",
"mariadb": "mariadb",
"mongo": "mongo",
"redis": "redis"
}
"schema": "public",
"values": [
"application",
"postgres",
"mysql",
"mariadb",
"mongo",
"redis"
]
},
"protocolType": {
"public.protocolType": {
"name": "protocolType",
"values": {
"tcp": "tcp",
"udp": "udp"
}
"schema": "public",
"values": [
"tcp",
"udp"
]
},
"applicationStatus": {
"public.applicationStatus": {
"name": "applicationStatus",
"values": {
"idle": "idle",
"running": "running",
"done": "done",
"error": "error"
}
"schema": "public",
"values": [
"idle",
"running",
"done",
"error"
]
},
"certificateType": {
"public.certificateType": {
"name": "certificateType",
"values": {
"letsencrypt": "letsencrypt",
"none": "none"
}
"schema": "public",
"values": [
"letsencrypt",
"none"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
"tables": {},
"columns": {}
},
"id": "665483bd-5123-4c2b-beef-bfa9b91b9356",
"prevId": "3a4dfad7-ae33-4ae3-b60e-4f40f44f5652"
}

View File

@@ -1,10 +1,8 @@
{
"id": "5a1d3f2b-9c31-4125-9645-015170550b51",
"prevId": "665483bd-5123-4c2b-beef-bfa9b91b9356",
"version": "5",
"dialect": "pg",
"version": "6",
"dialect": "postgresql",
"tables": {
"application": {
"public.application": {
"name": "application",
"schema": "",
"columns": {
@@ -234,29 +232,29 @@
"application_projectId_project_projectId_fk": {
"name": "application_projectId_project_projectId_fk",
"tableFrom": "application",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"application_appName_unique": {
"name": "application_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"postgres": {
"public.postgres": {
"name": "postgres",
"schema": "",
"columns": {
@@ -375,29 +373,29 @@
"postgres_projectId_project_projectId_fk": {
"name": "postgres_projectId_project_projectId_fk",
"tableFrom": "postgres",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"postgres_appName_unique": {
"name": "postgres_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"user": {
"public.user": {
"name": "user",
"schema": "",
"columns": {
@@ -506,34 +504,34 @@
"user_adminId_admin_adminId_fk": {
"name": "user_adminId_admin_adminId_fk",
"tableFrom": "user",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"user_authId_auth_id_fk": {
"name": "user_authId_auth_id_fk",
"tableFrom": "user",
"tableTo": "auth",
"columnsFrom": [
"authId"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"admin": {
"public.admin": {
"name": "admin",
"schema": "",
"columns": {
@@ -635,21 +633,21 @@
"admin_authId_auth_id_fk": {
"name": "admin_authId_auth_id_fk",
"tableFrom": "admin",
"tableTo": "auth",
"columnsFrom": [
"authId"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"auth": {
"public.auth": {
"name": "auth",
"schema": "",
"columns": {
@@ -709,14 +707,14 @@
"uniqueConstraints": {
"auth_email_unique": {
"name": "auth_email_unique",
"nullsNotDistinct": false,
"columns": [
"email"
]
],
"nullsNotDistinct": false
}
}
},
"project": {
"public.project": {
"name": "project",
"schema": "",
"columns": {
@@ -756,21 +754,21 @@
"project_adminId_admin_adminId_fk": {
"name": "project_adminId_admin_adminId_fk",
"tableFrom": "project",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"domain": {
"public.domain": {
"name": "domain",
"schema": "",
"columns": {
@@ -838,21 +836,21 @@
"domain_applicationId_application_applicationId_fk": {
"name": "domain_applicationId_application_applicationId_fk",
"tableFrom": "domain",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"mariadb": {
"public.mariadb": {
"name": "mariadb",
"schema": "",
"columns": {
@@ -977,29 +975,29 @@
"mariadb_projectId_project_projectId_fk": {
"name": "mariadb_projectId_project_projectId_fk",
"tableFrom": "mariadb",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mariadb_appName_unique": {
"name": "mariadb_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"mongo": {
"public.mongo": {
"name": "mongo",
"schema": "",
"columns": {
@@ -1112,29 +1110,29 @@
"mongo_projectId_project_projectId_fk": {
"name": "mongo_projectId_project_projectId_fk",
"tableFrom": "mongo",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mongo_appName_unique": {
"name": "mongo_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"mysql": {
"public.mysql": {
"name": "mysql",
"schema": "",
"columns": {
@@ -1259,29 +1257,29 @@
"mysql_projectId_project_projectId_fk": {
"name": "mysql_projectId_project_projectId_fk",
"tableFrom": "mysql",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mysql_appName_unique": {
"name": "mysql_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"backup": {
"public.backup": {
"name": "backup",
"schema": "",
"columns": {
@@ -1357,73 +1355,73 @@
"backup_destinationId_destination_destinationId_fk": {
"name": "backup_destinationId_destination_destinationId_fk",
"tableFrom": "backup",
"tableTo": "destination",
"columnsFrom": [
"destinationId"
],
"tableTo": "destination",
"columnsTo": [
"destinationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_postgresId_postgres_postgresId_fk": {
"name": "backup_postgresId_postgres_postgresId_fk",
"tableFrom": "backup",
"tableTo": "postgres",
"columnsFrom": [
"postgresId"
],
"tableTo": "postgres",
"columnsTo": [
"postgresId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mariadbId_mariadb_mariadbId_fk": {
"name": "backup_mariadbId_mariadb_mariadbId_fk",
"tableFrom": "backup",
"tableTo": "mariadb",
"columnsFrom": [
"mariadbId"
],
"tableTo": "mariadb",
"columnsTo": [
"mariadbId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mysqlId_mysql_mysqlId_fk": {
"name": "backup_mysqlId_mysql_mysqlId_fk",
"tableFrom": "backup",
"tableTo": "mysql",
"columnsFrom": [
"mysqlId"
],
"tableTo": "mysql",
"columnsTo": [
"mysqlId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mongoId_mongo_mongoId_fk": {
"name": "backup_mongoId_mongo_mongoId_fk",
"tableFrom": "backup",
"tableTo": "mongo",
"columnsFrom": [
"mongoId"
],
"tableTo": "mongo",
"columnsTo": [
"mongoId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"destination": {
"public.destination": {
"name": "destination",
"schema": "",
"columns": {
@@ -1481,21 +1479,21 @@
"destination_adminId_admin_adminId_fk": {
"name": "destination_adminId_admin_adminId_fk",
"tableFrom": "destination",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"deployment": {
"public.deployment": {
"name": "deployment",
"schema": "",
"columns": {
@@ -1542,21 +1540,21 @@
"deployment_applicationId_application_applicationId_fk": {
"name": "deployment_applicationId_application_applicationId_fk",
"tableFrom": "deployment",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"mount": {
"public.mount": {
"name": "mount",
"schema": "",
"columns": {
@@ -1645,86 +1643,86 @@
"mount_applicationId_application_applicationId_fk": {
"name": "mount_applicationId_application_applicationId_fk",
"tableFrom": "mount",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_postgresId_postgres_postgresId_fk": {
"name": "mount_postgresId_postgres_postgresId_fk",
"tableFrom": "mount",
"tableTo": "postgres",
"columnsFrom": [
"postgresId"
],
"tableTo": "postgres",
"columnsTo": [
"postgresId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mariadbId_mariadb_mariadbId_fk": {
"name": "mount_mariadbId_mariadb_mariadbId_fk",
"tableFrom": "mount",
"tableTo": "mariadb",
"columnsFrom": [
"mariadbId"
],
"tableTo": "mariadb",
"columnsTo": [
"mariadbId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mongoId_mongo_mongoId_fk": {
"name": "mount_mongoId_mongo_mongoId_fk",
"tableFrom": "mount",
"tableTo": "mongo",
"columnsFrom": [
"mongoId"
],
"tableTo": "mongo",
"columnsTo": [
"mongoId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mysqlId_mysql_mysqlId_fk": {
"name": "mount_mysqlId_mysql_mysqlId_fk",
"tableFrom": "mount",
"tableTo": "mysql",
"columnsFrom": [
"mysqlId"
],
"tableTo": "mysql",
"columnsTo": [
"mysqlId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_redisId_redis_redisId_fk": {
"name": "mount_redisId_redis_redisId_fk",
"tableFrom": "mount",
"tableTo": "redis",
"columnsFrom": [
"redisId"
],
"tableTo": "redis",
"columnsTo": [
"redisId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"certificate": {
"public.certificate": {
"name": "certificate",
"schema": "",
"columns": {
@@ -1771,14 +1769,14 @@
"uniqueConstraints": {
"certificate_certificatePath_unique": {
"name": "certificate_certificatePath_unique",
"nullsNotDistinct": false,
"columns": [
"certificatePath"
]
],
"nullsNotDistinct": false
}
}
},
"session": {
"public.session": {
"name": "session",
"schema": "",
"columns": {
@@ -1806,21 +1804,21 @@
"session_user_id_auth_id_fk": {
"name": "session_user_id_auth_id_fk",
"tableFrom": "session",
"tableTo": "auth",
"columnsFrom": [
"user_id"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"redirect": {
"public.redirect": {
"name": "redirect",
"schema": "",
"columns": {
@@ -1873,21 +1871,21 @@
"redirect_applicationId_application_applicationId_fk": {
"name": "redirect_applicationId_application_applicationId_fk",
"tableFrom": "redirect",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"security": {
"public.security": {
"name": "security",
"schema": "",
"columns": {
@@ -1927,30 +1925,30 @@
"security_applicationId_application_applicationId_fk": {
"name": "security_applicationId_application_applicationId_fk",
"tableFrom": "security",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"security_username_applicationId_unique": {
"name": "security_username_applicationId_unique",
"nullsNotDistinct": false,
"columns": [
"username",
"applicationId"
]
],
"nullsNotDistinct": false
}
}
},
"port": {
"public.port": {
"name": "port",
"schema": "",
"columns": {
@@ -1990,21 +1988,21 @@
"port_applicationId_application_applicationId_fk": {
"name": "port_applicationId_application_applicationId_fk",
"tableFrom": "port",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"redis": {
"public.redis": {
"name": "redis",
"schema": "",
"columns": {
@@ -2111,118 +2109,130 @@
"redis_projectId_project_projectId_fk": {
"name": "redis_projectId_project_projectId_fk",
"tableFrom": "redis",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"redis_appName_unique": {
"name": "redis_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
}
},
"enums": {
"buildType": {
"public.buildType": {
"name": "buildType",
"values": {
"dockerfile": "dockerfile",
"heroku_buildpacks": "heroku_buildpacks",
"paketo_buildpacks": "paketo_buildpacks",
"nixpacks": "nixpacks"
}
"schema": "public",
"values": [
"dockerfile",
"heroku_buildpacks",
"paketo_buildpacks",
"nixpacks"
]
},
"sourceType": {
"public.sourceType": {
"name": "sourceType",
"values": {
"docker": "docker",
"git": "git",
"github": "github"
}
"schema": "public",
"values": [
"docker",
"git",
"github"
]
},
"Roles": {
"public.Roles": {
"name": "Roles",
"values": {
"admin": "admin",
"user": "user"
}
"schema": "public",
"values": [
"admin",
"user"
]
},
"databaseType": {
"public.databaseType": {
"name": "databaseType",
"values": {
"postgres": "postgres",
"mariadb": "mariadb",
"mysql": "mysql",
"mongo": "mongo"
}
"schema": "public",
"values": [
"postgres",
"mariadb",
"mysql",
"mongo"
]
},
"deploymentStatus": {
"public.deploymentStatus": {
"name": "deploymentStatus",
"values": {
"running": "running",
"done": "done",
"error": "error"
}
"schema": "public",
"values": [
"running",
"done",
"error"
]
},
"mountType": {
"public.mountType": {
"name": "mountType",
"values": {
"bind": "bind",
"volume": "volume",
"file": "file"
}
"schema": "public",
"values": [
"bind",
"volume",
"file"
]
},
"serviceType": {
"public.serviceType": {
"name": "serviceType",
"values": {
"application": "application",
"postgres": "postgres",
"mysql": "mysql",
"mariadb": "mariadb",
"mongo": "mongo",
"redis": "redis"
}
"schema": "public",
"values": [
"application",
"postgres",
"mysql",
"mariadb",
"mongo",
"redis"
]
},
"protocolType": {
"public.protocolType": {
"name": "protocolType",
"values": {
"tcp": "tcp",
"udp": "udp"
}
"schema": "public",
"values": [
"tcp",
"udp"
]
},
"applicationStatus": {
"public.applicationStatus": {
"name": "applicationStatus",
"values": {
"idle": "idle",
"running": "running",
"done": "done",
"error": "error"
}
"schema": "public",
"values": [
"idle",
"running",
"done",
"error"
]
},
"certificateType": {
"public.certificateType": {
"name": "certificateType",
"values": {
"letsencrypt": "letsencrypt",
"none": "none"
}
"schema": "public",
"values": [
"letsencrypt",
"none"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
"tables": {},
"columns": {}
},
"id": "5a1d3f2b-9c31-4125-9645-015170550b51",
"prevId": "665483bd-5123-4c2b-beef-bfa9b91b9356"
}

View File

@@ -1,10 +1,8 @@
{
"id": "7bb4bbcf-791c-4888-919e-f74bc0528b5f",
"prevId": "5a1d3f2b-9c31-4125-9645-015170550b51",
"version": "5",
"dialect": "pg",
"version": "6",
"dialect": "postgresql",
"tables": {
"application": {
"public.application": {
"name": "application",
"schema": "",
"columns": {
@@ -210,29 +208,29 @@
"application_projectId_project_projectId_fk": {
"name": "application_projectId_project_projectId_fk",
"tableFrom": "application",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"application_appName_unique": {
"name": "application_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"postgres": {
"public.postgres": {
"name": "postgres",
"schema": "",
"columns": {
@@ -351,29 +349,29 @@
"postgres_projectId_project_projectId_fk": {
"name": "postgres_projectId_project_projectId_fk",
"tableFrom": "postgres",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"postgres_appName_unique": {
"name": "postgres_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"user": {
"public.user": {
"name": "user",
"schema": "",
"columns": {
@@ -482,34 +480,34 @@
"user_adminId_admin_adminId_fk": {
"name": "user_adminId_admin_adminId_fk",
"tableFrom": "user",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"user_authId_auth_id_fk": {
"name": "user_authId_auth_id_fk",
"tableFrom": "user",
"tableTo": "auth",
"columnsFrom": [
"authId"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"admin": {
"public.admin": {
"name": "admin",
"schema": "",
"columns": {
@@ -611,21 +609,21 @@
"admin_authId_auth_id_fk": {
"name": "admin_authId_auth_id_fk",
"tableFrom": "admin",
"tableTo": "auth",
"columnsFrom": [
"authId"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"auth": {
"public.auth": {
"name": "auth",
"schema": "",
"columns": {
@@ -685,14 +683,14 @@
"uniqueConstraints": {
"auth_email_unique": {
"name": "auth_email_unique",
"nullsNotDistinct": false,
"columns": [
"email"
]
],
"nullsNotDistinct": false
}
}
},
"project": {
"public.project": {
"name": "project",
"schema": "",
"columns": {
@@ -732,21 +730,21 @@
"project_adminId_admin_adminId_fk": {
"name": "project_adminId_admin_adminId_fk",
"tableFrom": "project",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"domain": {
"public.domain": {
"name": "domain",
"schema": "",
"columns": {
@@ -814,21 +812,21 @@
"domain_applicationId_application_applicationId_fk": {
"name": "domain_applicationId_application_applicationId_fk",
"tableFrom": "domain",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"mariadb": {
"public.mariadb": {
"name": "mariadb",
"schema": "",
"columns": {
@@ -953,29 +951,29 @@
"mariadb_projectId_project_projectId_fk": {
"name": "mariadb_projectId_project_projectId_fk",
"tableFrom": "mariadb",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mariadb_appName_unique": {
"name": "mariadb_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"mongo": {
"public.mongo": {
"name": "mongo",
"schema": "",
"columns": {
@@ -1088,29 +1086,29 @@
"mongo_projectId_project_projectId_fk": {
"name": "mongo_projectId_project_projectId_fk",
"tableFrom": "mongo",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mongo_appName_unique": {
"name": "mongo_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"mysql": {
"public.mysql": {
"name": "mysql",
"schema": "",
"columns": {
@@ -1235,29 +1233,29 @@
"mysql_projectId_project_projectId_fk": {
"name": "mysql_projectId_project_projectId_fk",
"tableFrom": "mysql",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"mysql_appName_unique": {
"name": "mysql_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
},
"backup": {
"public.backup": {
"name": "backup",
"schema": "",
"columns": {
@@ -1333,73 +1331,73 @@
"backup_destinationId_destination_destinationId_fk": {
"name": "backup_destinationId_destination_destinationId_fk",
"tableFrom": "backup",
"tableTo": "destination",
"columnsFrom": [
"destinationId"
],
"tableTo": "destination",
"columnsTo": [
"destinationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_postgresId_postgres_postgresId_fk": {
"name": "backup_postgresId_postgres_postgresId_fk",
"tableFrom": "backup",
"tableTo": "postgres",
"columnsFrom": [
"postgresId"
],
"tableTo": "postgres",
"columnsTo": [
"postgresId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mariadbId_mariadb_mariadbId_fk": {
"name": "backup_mariadbId_mariadb_mariadbId_fk",
"tableFrom": "backup",
"tableTo": "mariadb",
"columnsFrom": [
"mariadbId"
],
"tableTo": "mariadb",
"columnsTo": [
"mariadbId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mysqlId_mysql_mysqlId_fk": {
"name": "backup_mysqlId_mysql_mysqlId_fk",
"tableFrom": "backup",
"tableTo": "mysql",
"columnsFrom": [
"mysqlId"
],
"tableTo": "mysql",
"columnsTo": [
"mysqlId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"backup_mongoId_mongo_mongoId_fk": {
"name": "backup_mongoId_mongo_mongoId_fk",
"tableFrom": "backup",
"tableTo": "mongo",
"columnsFrom": [
"mongoId"
],
"tableTo": "mongo",
"columnsTo": [
"mongoId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"destination": {
"public.destination": {
"name": "destination",
"schema": "",
"columns": {
@@ -1457,21 +1455,21 @@
"destination_adminId_admin_adminId_fk": {
"name": "destination_adminId_admin_adminId_fk",
"tableFrom": "destination",
"tableTo": "admin",
"columnsFrom": [
"adminId"
],
"tableTo": "admin",
"columnsTo": [
"adminId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"deployment": {
"public.deployment": {
"name": "deployment",
"schema": "",
"columns": {
@@ -1518,21 +1516,21 @@
"deployment_applicationId_application_applicationId_fk": {
"name": "deployment_applicationId_application_applicationId_fk",
"tableFrom": "deployment",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"mount": {
"public.mount": {
"name": "mount",
"schema": "",
"columns": {
@@ -1621,86 +1619,86 @@
"mount_applicationId_application_applicationId_fk": {
"name": "mount_applicationId_application_applicationId_fk",
"tableFrom": "mount",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_postgresId_postgres_postgresId_fk": {
"name": "mount_postgresId_postgres_postgresId_fk",
"tableFrom": "mount",
"tableTo": "postgres",
"columnsFrom": [
"postgresId"
],
"tableTo": "postgres",
"columnsTo": [
"postgresId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mariadbId_mariadb_mariadbId_fk": {
"name": "mount_mariadbId_mariadb_mariadbId_fk",
"tableFrom": "mount",
"tableTo": "mariadb",
"columnsFrom": [
"mariadbId"
],
"tableTo": "mariadb",
"columnsTo": [
"mariadbId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mongoId_mongo_mongoId_fk": {
"name": "mount_mongoId_mongo_mongoId_fk",
"tableFrom": "mount",
"tableTo": "mongo",
"columnsFrom": [
"mongoId"
],
"tableTo": "mongo",
"columnsTo": [
"mongoId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_mysqlId_mysql_mysqlId_fk": {
"name": "mount_mysqlId_mysql_mysqlId_fk",
"tableFrom": "mount",
"tableTo": "mysql",
"columnsFrom": [
"mysqlId"
],
"tableTo": "mysql",
"columnsTo": [
"mysqlId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
},
"mount_redisId_redis_redisId_fk": {
"name": "mount_redisId_redis_redisId_fk",
"tableFrom": "mount",
"tableTo": "redis",
"columnsFrom": [
"redisId"
],
"tableTo": "redis",
"columnsTo": [
"redisId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"certificate": {
"public.certificate": {
"name": "certificate",
"schema": "",
"columns": {
@@ -1747,14 +1745,14 @@
"uniqueConstraints": {
"certificate_certificatePath_unique": {
"name": "certificate_certificatePath_unique",
"nullsNotDistinct": false,
"columns": [
"certificatePath"
]
],
"nullsNotDistinct": false
}
}
},
"session": {
"public.session": {
"name": "session",
"schema": "",
"columns": {
@@ -1782,21 +1780,21 @@
"session_user_id_auth_id_fk": {
"name": "session_user_id_auth_id_fk",
"tableFrom": "session",
"tableTo": "auth",
"columnsFrom": [
"user_id"
],
"tableTo": "auth",
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"redirect": {
"public.redirect": {
"name": "redirect",
"schema": "",
"columns": {
@@ -1849,21 +1847,21 @@
"redirect_applicationId_application_applicationId_fk": {
"name": "redirect_applicationId_application_applicationId_fk",
"tableFrom": "redirect",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"security": {
"public.security": {
"name": "security",
"schema": "",
"columns": {
@@ -1903,30 +1901,30 @@
"security_applicationId_application_applicationId_fk": {
"name": "security_applicationId_application_applicationId_fk",
"tableFrom": "security",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"security_username_applicationId_unique": {
"name": "security_username_applicationId_unique",
"nullsNotDistinct": false,
"columns": [
"username",
"applicationId"
]
],
"nullsNotDistinct": false
}
}
},
"port": {
"public.port": {
"name": "port",
"schema": "",
"columns": {
@@ -1966,21 +1964,21 @@
"port_applicationId_application_applicationId_fk": {
"name": "port_applicationId_application_applicationId_fk",
"tableFrom": "port",
"tableTo": "application",
"columnsFrom": [
"applicationId"
],
"tableTo": "application",
"columnsTo": [
"applicationId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"redis": {
"public.redis": {
"name": "redis",
"schema": "",
"columns": {
@@ -2087,118 +2085,130 @@
"redis_projectId_project_projectId_fk": {
"name": "redis_projectId_project_projectId_fk",
"tableFrom": "redis",
"tableTo": "project",
"columnsFrom": [
"projectId"
],
"tableTo": "project",
"columnsTo": [
"projectId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"onUpdate": "no action",
"onDelete": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"redis_appName_unique": {
"name": "redis_appName_unique",
"nullsNotDistinct": false,
"columns": [
"appName"
]
],
"nullsNotDistinct": false
}
}
}
},
"enums": {
"buildType": {
"public.buildType": {
"name": "buildType",
"values": {
"dockerfile": "dockerfile",
"heroku_buildpacks": "heroku_buildpacks",
"paketo_buildpacks": "paketo_buildpacks",
"nixpacks": "nixpacks"
}
"schema": "public",
"values": [
"dockerfile",
"heroku_buildpacks",
"paketo_buildpacks",
"nixpacks"
]
},
"sourceType": {
"public.sourceType": {
"name": "sourceType",
"values": {
"docker": "docker",
"git": "git",
"github": "github"
}
"schema": "public",
"values": [
"docker",
"git",
"github"
]
},
"Roles": {
"public.Roles": {
"name": "Roles",
"values": {
"admin": "admin",
"user": "user"
}
"schema": "public",
"values": [
"admin",
"user"
]
},
"databaseType": {
"public.databaseType": {
"name": "databaseType",
"values": {
"postgres": "postgres",
"mariadb": "mariadb",
"mysql": "mysql",
"mongo": "mongo"
}
"schema": "public",
"values": [
"postgres",
"mariadb",
"mysql",
"mongo"
]
},
"deploymentStatus": {
"public.deploymentStatus": {
"name": "deploymentStatus",
"values": {
"running": "running",
"done": "done",
"error": "error"
}
"schema": "public",
"values": [
"running",
"done",
"error"
]
},
"mountType": {
"public.mountType": {
"name": "mountType",
"values": {
"bind": "bind",
"volume": "volume",
"file": "file"
}
"schema": "public",
"values": [
"bind",
"volume",
"file"
]
},
"serviceType": {
"public.serviceType": {
"name": "serviceType",
"values": {
"application": "application",
"postgres": "postgres",
"mysql": "mysql",
"mariadb": "mariadb",
"mongo": "mongo",
"redis": "redis"
}
"schema": "public",
"values": [
"application",
"postgres",
"mysql",
"mariadb",
"mongo",
"redis"
]
},
"protocolType": {
"public.protocolType": {
"name": "protocolType",
"values": {
"tcp": "tcp",
"udp": "udp"
}
"schema": "public",
"values": [
"tcp",
"udp"
]
},
"applicationStatus": {
"public.applicationStatus": {
"name": "applicationStatus",
"values": {
"idle": "idle",
"running": "running",
"done": "done",
"error": "error"
}
"schema": "public",
"values": [
"idle",
"running",
"done",
"error"
]
},
"certificateType": {
"public.certificateType": {
"name": "certificateType",
"values": {
"letsencrypt": "letsencrypt",
"none": "none"
}
"schema": "public",
"values": [
"letsencrypt",
"none"
]
}
},
"schemas": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
"tables": {},
"columns": {}
},
"id": "7bb4bbcf-791c-4888-919e-f74bc0528b5f",
"prevId": "5a1d3f2b-9c31-4125-9645-015170550b51"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,69 @@
"when": 1714004732716,
"tag": "0004_nice_tenebrous",
"breakpoints": true
},
{
"idx": 5,
"version": "5",
"when": 1715551130605,
"tag": "0005_cute_terror",
"breakpoints": true
},
{
"idx": 6,
"version": "6",
"when": 1715563165991,
"tag": "0006_oval_jimmy_woo",
"breakpoints": true
},
{
"idx": 7,
"version": "6",
"when": 1715563497100,
"tag": "0007_cute_guardsmen",
"breakpoints": true
},
{
"idx": 8,
"version": "6",
"when": 1715564143641,
"tag": "0008_lazy_sage",
"breakpoints": true
},
{
"idx": 9,
"version": "6",
"when": 1715564774423,
"tag": "0009_majestic_spencer_smythe",
"breakpoints": true
},
{
"idx": 10,
"version": "6",
"when": 1715574037832,
"tag": "0010_lean_black_widow",
"breakpoints": true
},
{
"idx": 11,
"version": "6",
"when": 1715574230599,
"tag": "0011_petite_calypso",
"breakpoints": true
},
{
"idx": 12,
"version": "6",
"when": 1716015716708,
"tag": "0012_chubby_umar",
"breakpoints": true
},
{
"idx": 13,
"version": "6",
"when": 1716076179443,
"tag": "0013_blushing_starjammers",
"breakpoints": true
}
]
}

View File

@@ -1,6 +1,6 @@
{
"name": "dokploy",
"version": "v0.0.4",
"version": "v0.1.0",
"private": true,
"license": "AGPL-3.0-only",
"type": "module",
@@ -12,15 +12,17 @@
"setup": "tsx -r dotenv/config setup.ts && sleep 5 && pnpm run migration:run",
"reset-password": "node dist/reset-password.mjs",
"dev": "tsx watch -r dotenv/config ./server/server.ts --project tsconfig.server.json ",
"migration:generate": "drizzle-kit generate:pg --config ./server/db/drizzle.config.ts",
"studio":"drizzle-kit studio --config ./server/db/drizzle.config.ts",
"migration:generate": "drizzle-kit generate --config ./server/db/drizzle.config.ts",
"migration:run": "tsx -r dotenv/config migration.ts",
"migration:up":"drizzle-kit up --config ./server/db/drizzle.config.ts",
"migration:drop": "drizzle-kit drop --config ./server/db/drizzle.config.ts",
"db:push": "drizzle-kit push:pg --config ./server/db/drizzle.config.ts",
"db:push": "drizzle-kit --config ./server/db/drizzle.config.ts",
"db:truncate": "tsx -r dotenv/config ./server/db/reset.ts",
"db:studio": "drizzle-kit studio",
"lint": "biome lint",
"db:seed": "dotenv tsx ./server/db/seed.ts",
"db:clean": "dotenv tsx ./server/db/reset.ts",
"db:seed": "tsx -r dotenv/config ./server/db/seed.ts",
"db:clean": "tsx -r dotenv/config ./server/db/reset.ts",
"docker:build": "./docker/build.sh",
"docker:push": "./docker/push.sh",
"docker:build:canary": "./docker/build.sh canary",
@@ -29,6 +31,8 @@
},
"dependencies": {
"@aws-sdk/client-s3": "3.515.0",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-yaml": "^6.1.1",
"@faker-js/faker": "^8.4.1",
"@hookform/resolvers": "^3.3.4",
"@lucia-auth/adapter-drizzle": "1.0.7",
@@ -57,6 +61,8 @@
"@trpc/next": "^10.43.6",
"@trpc/react-query": "^10.43.6",
"@trpc/server": "^10.43.6",
"@uiw/codemirror-theme-github": "^4.22.1",
"@uiw/react-codemirror": "^4.22.1",
"@xterm/addon-attach": "0.10.0",
"@xterm/xterm": "^5.4.0",
"bcrypt": "5.1.1",
@@ -116,7 +122,7 @@
"@types/tar-fs": "2.0.4",
"@types/ws": "8.5.10",
"autoprefixer": "^10.4.14",
"drizzle-kit": "^0.20.14",
"drizzle-kit": "^0.21.1",
"esbuild": "0.20.2",
"localtunnel": "2.0.2",
"postcss": "^8.4.31",

View File

@@ -12,8 +12,8 @@ export default function Custom404({ statusCode }: Props) {
<div className="container mx-auto h-screen items-center justify-center flex">
<div className="-mx-4 flex">
<div className="w-full px-4">
<div className="mx-auto max-w-[400px] text-center">
<h2 className="mb-2 text-[50px] font-bold leading-none text-white sm:text-[80px] md:text-[100px]">
<div className="mx-auto max-w-[700px] text-center">
<h2 className="mb-2 text-[50px] font-bold leading-none text-white sm:text-[80px]">
{statusCode
? `An error ${statusCode} occurred on server`
: "An error occurred on client"}

View File

@@ -35,7 +35,25 @@ export default async function handler(
const deploymentTitle = extractCommitMessage(req.headers, req.body);
const sourceType = application.sourceType;
if (sourceType === "github") {
if (sourceType === "docker") {
const applicationDockerTag = extractImageTag(application.dockerImage);
const webhookDockerTag = extractImageTagFromRequest(
req.headers,
req.body,
);
if (
applicationDockerTag &&
webhookDockerTag &&
webhookDockerTag !== applicationDockerTag
) {
res.status(301).json({
message: `Application Image Tag (${applicationDockerTag}) doesn't match request event payload Image Tag (${webhookDockerTag}).`,
});
return;
}
}
else if (sourceType === "github") {
const branchName = extractBranchName(req.headers, req.body);
if (!branchName || branchName !== application.branch) {
res.status(301).json({ message: "Branch Not Match" });
@@ -79,6 +97,36 @@ export default async function handler(
res.status(400).json({ message: "Error To Deploy Application", error });
}
}
/**
* Return the last part of the image name, which is the tag
* Example: "my-image" => null
* Example: "my-image:latest" => "latest"
* Example: "my-image:1.0.0" => "1.0.0"
* Example: "myregistryhost:5000/fedora/httpd:version1.0" => "version1.0"
* @link https://docs.docker.com/reference/cli/docker/image/tag/
*/
function extractImageTag(dockerImage: string | null) {
if (!dockerImage || typeof dockerImage !== "string") {
return null;
}
const tag = dockerImage.split(":").pop();
return tag === dockerImage ? "latest" : tag;
}
/**
* @link https://docs.docker.com/docker-hub/webhooks/#example-webhook-payload
*/
function extractImageTagFromRequest(headers: any, body: any): string | null {
if (headers["user-agent"]?.includes("Go-http-client")) {
if (body.push_data && body.repository) {
return body.push_data.tag;
}
}
return null;
}
function extractCommitMessage(headers: any, body: any) {
// GitHub
if (headers["x-github-event"]) {

View File

@@ -212,7 +212,7 @@ const Project = (
}}
className="group relative cursor-pointer bg-transparent transition-colors hover:bg-card h-fit"
>
<div className="absolute -right-1 -top-1">
<div className="absolute -right-1 -top-2">
<StatusTooltip status={service.status} />
</div>

View File

@@ -1,3 +1,4 @@
import { ShowClusterSettings } from "@/components/dashboard/application/advanced/cluster/show-cluster-settings";
import { AddCommand } from "@/components/dashboard/application/advanced/general/add-command";
import { ShowPorts } from "@/components/dashboard/application/advanced/ports/show-port";
import { ShowRedirects } from "@/components/dashboard/application/advanced/redirects/show-redirects";
@@ -134,7 +135,7 @@ const Service = (
<TabsTrigger value="domains">Domains</TabsTrigger>
<TabsTrigger value="advanced">Advanced</TabsTrigger>
</TabsList>
<div className="flex flex-row gap-4">
<div className="flex flex-row gap-2">
<UpdateApplication applicationId={applicationId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && (
<DeleteApplication applicationId={applicationId} />
@@ -175,6 +176,7 @@ const Service = (
<TabsContent value="advanced">
<div className="flex flex-col gap-4 pt-2.5">
<AddCommand applicationId={applicationId} />
<ShowClusterSettings applicationId={applicationId} />
<ShowApplicationResources applicationId={applicationId} />
<ShowVolumes applicationId={applicationId} />
<ShowRedirects applicationId={applicationId} />

View File

@@ -116,7 +116,7 @@ const Mariadb = (
<TabsTrigger value="logs">Logs</TabsTrigger>
<TabsTrigger value="advanced">Advanced</TabsTrigger>
</TabsList>
<div className="flex flex-row gap-4">
<div className="flex flex-row gap-2">
<UpdateMariadb mariadbId={mariadbId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && (
<DeleteMariadb mariadbId={mariadbId} />

View File

@@ -118,7 +118,7 @@ const Mongo = (
<TabsTrigger value="advanced">Advanced</TabsTrigger>
</TabsList>
<div className="flex flex-row gap-4">
<div className="flex flex-row gap-2">
<UpdateMongo mongoId={mongoId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && (
<DeleteMongo mongoId={mongoId} />

View File

@@ -117,7 +117,7 @@ const MySql = (
<TabsTrigger value="advanced">Advanced</TabsTrigger>
</TabsList>
<div className="flex flex-row gap-4">
<div className="flex flex-row gap-2">
<UpdateMysql mysqlId={mysqlId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && (
<DeleteMysql mysqlId={mysqlId} />

View File

@@ -118,7 +118,7 @@ const Postgresql = (
<TabsTrigger value="advanced">Advanced</TabsTrigger>
</TabsList>
<div className="flex flex-row gap-4">
<div className="flex flex-row gap-2">
<UpdatePostgres postgresId={postgresId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && (
<DeletePostgres postgresId={postgresId} />

View File

@@ -116,7 +116,7 @@ const Redis = (
<TabsTrigger value="advanced">Advanced</TabsTrigger>
</TabsList>
<div className="flex flex-row gap-4">
<div className="flex flex-row gap-2">
<UpdateRedis redisId={redisId} />
{(auth?.rol === "admin" || user?.canDeleteServices) && (
<DeleteRedis redisId={redisId} />

View File

@@ -0,0 +1,43 @@
import { ShowRegistry } from "@/components/dashboard/settings/cluster/registry/show-registry";
import { ShowNodes } from "@/components/dashboard/settings/cluster/nodes/show-nodes";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { SettingsLayout } from "@/components/layouts/settings-layout";
import { validateRequest } from "@/server/auth/auth";
import type { GetServerSidePropsContext } from "next";
import React, { type ReactElement } from "react";
const Page = () => {
return (
<div className="flex flex-col gap-4 w-full">
<ShowRegistry />
<ShowNodes />
</div>
);
};
export default Page;
Page.getLayout = (page: ReactElement) => {
return (
<DashboardLayout tab={"settings"}>
<SettingsLayout>{page}</SettingsLayout>
</DashboardLayout>
);
};
export async function getServerSideProps(
ctx: GetServerSidePropsContext<{ serviceId: string }>,
) {
const { user, session } = await validateRequest(ctx.req, ctx.res);
if (!user || user.rol === "user") {
return {
redirect: {
permanent: true,
destination: "/",
},
};
}
return {
props: {},
};
}

View File

@@ -193,7 +193,7 @@ export default function Home({ hasAdmin }: Props) {
<div className="mt-4 text-sm flex flex-row justify-center gap-2">
<Link
className="hover:underline text-muted-foreground"
href="https://docs.dokploy.com/reset-password"
href="https://docs.dokploy.com/get-started/reset-password"
target="_blank"
>
Lost your password?

284
pnpm-lock.yaml generated
View File

@@ -8,6 +8,12 @@ dependencies:
'@aws-sdk/client-s3':
specifier: 3.515.0
version: 3.515.0
'@codemirror/lang-json':
specifier: ^6.0.1
version: 6.0.1
'@codemirror/lang-yaml':
specifier: ^6.1.1
version: 6.1.1(@codemirror/view@6.26.3)
'@faker-js/faker':
specifier: ^8.4.1
version: 8.4.1
@@ -92,6 +98,12 @@ dependencies:
'@trpc/server':
specifier: ^10.43.6
version: 10.45.2
'@uiw/codemirror-theme-github':
specifier: ^4.22.1
version: 4.22.1(@codemirror/language@6.10.1)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3)
'@uiw/react-codemirror':
specifier: ^4.22.1
version: 4.22.1(@babel/runtime@7.24.0)(@codemirror/autocomplete@6.16.0)(@codemirror/language@6.10.1)(@codemirror/lint@6.8.0)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.26.3)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0)
'@xterm/addon-attach':
specifier: 0.10.0
version: 0.10.0(@xterm/xterm@5.4.0)
@@ -266,8 +278,8 @@ devDependencies:
specifier: ^10.4.14
version: 10.4.18(postcss@8.4.35)
drizzle-kit:
specifier: ^0.20.14
version: 0.20.14
specifier: ^0.21.1
version: 0.21.1
esbuild:
specifier: 0.20.2
version: 0.20.2
@@ -983,6 +995,7 @@ packages:
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
libc: [musl]
requiresBuild: true
dev: true
optional: true
@@ -992,6 +1005,7 @@ packages:
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
libc: [glibc]
requiresBuild: true
dev: true
optional: true
@@ -1001,6 +1015,7 @@ packages:
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
libc: [musl]
requiresBuild: true
dev: true
optional: true
@@ -1010,6 +1025,7 @@ packages:
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
libc: [glibc]
requiresBuild: true
dev: true
optional: true
@@ -1032,11 +1048,96 @@ packages:
dev: true
optional: true
/@drizzle-team/studio@0.0.39:
resolution: {integrity: sha512-c5Hkm7MmQC2n5qAsKShjQrHoqlfGslB8+qWzsGGZ+2dHMRTNG60UuzalF0h0rvBax5uzPXuGkYLGaQ+TUX3yMw==}
/@codemirror/autocomplete@6.16.0(@codemirror/language@6.10.1)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3)(@lezer/common@1.2.1):
resolution: {integrity: sha512-P/LeCTtZHRTCU4xQsa89vSKWecYv1ZqwzOd5topheGRf+qtacFgBeIMQi3eL8Kt/BUNvxUWkx+5qP2jlGoARrg==}
peerDependencies:
'@codemirror/language': ^6.0.0
'@codemirror/state': ^6.0.0
'@codemirror/view': ^6.0.0
'@lezer/common': ^1.0.0
dependencies:
superjson: 2.2.1
dev: true
'@codemirror/language': 6.10.1
'@codemirror/state': 6.4.1
'@codemirror/view': 6.26.3
'@lezer/common': 1.2.1
dev: false
/@codemirror/commands@6.5.0:
resolution: {integrity: sha512-rK+sj4fCAN/QfcY9BEzYMgp4wwL/q5aj/VfNSoH1RWPF9XS/dUwBkvlL3hpWgEjOqlpdN1uLC9UkjJ4tmyjJYg==}
dependencies:
'@codemirror/language': 6.10.1
'@codemirror/state': 6.4.1
'@codemirror/view': 6.26.3
'@lezer/common': 1.2.1
dev: false
/@codemirror/lang-json@6.0.1:
resolution: {integrity: sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==}
dependencies:
'@codemirror/language': 6.10.1
'@lezer/json': 1.0.2
dev: false
/@codemirror/lang-yaml@6.1.1(@codemirror/view@6.26.3):
resolution: {integrity: sha512-HV2NzbK9bbVnjWxwObuZh5FuPCowx51mEfoFT9y3y+M37fA3+pbxx4I7uePuygFzDsAmCTwQSc/kXh/flab4uw==}
dependencies:
'@codemirror/autocomplete': 6.16.0(@codemirror/language@6.10.1)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3)(@lezer/common@1.2.1)
'@codemirror/language': 6.10.1
'@codemirror/state': 6.4.1
'@lezer/common': 1.2.1
'@lezer/highlight': 1.2.0
'@lezer/yaml': 1.0.3
transitivePeerDependencies:
- '@codemirror/view'
dev: false
/@codemirror/language@6.10.1:
resolution: {integrity: sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==}
dependencies:
'@codemirror/state': 6.4.1
'@codemirror/view': 6.26.3
'@lezer/common': 1.2.1
'@lezer/highlight': 1.2.0
'@lezer/lr': 1.4.0
style-mod: 4.1.2
dev: false
/@codemirror/lint@6.8.0:
resolution: {integrity: sha512-lsFofvaw0lnPRJlQylNsC4IRt/1lI4OD/yYslrSGVndOJfStc58v+8p9dgGiD90ktOfL7OhBWns1ZETYgz0EJA==}
dependencies:
'@codemirror/state': 6.4.1
'@codemirror/view': 6.26.3
crelt: 1.0.6
dev: false
/@codemirror/search@6.5.6:
resolution: {integrity: sha512-rpMgcsh7o0GuCDUXKPvww+muLA1pDJaFrpq/CCHtpQJYz8xopu4D1hPcKRoDD0YlF8gZaqTNIRa4VRBWyhyy7Q==}
dependencies:
'@codemirror/state': 6.4.1
'@codemirror/view': 6.26.3
crelt: 1.0.6
dev: false
/@codemirror/state@6.4.1:
resolution: {integrity: sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==}
dev: false
/@codemirror/theme-one-dark@6.1.2:
resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==}
dependencies:
'@codemirror/language': 6.10.1
'@codemirror/state': 6.4.1
'@codemirror/view': 6.26.3
'@lezer/highlight': 1.2.0
dev: false
/@codemirror/view@6.26.3:
resolution: {integrity: sha512-gmqxkPALZjkgSxIeeweY/wGQXBfwTUaLs8h7OKtSwfbj9Ct3L11lD+u1sS7XHppxFQoMDiMDp07P9f3I2jWOHw==}
dependencies:
'@codemirror/state': 6.4.1
style-mod: 4.1.2
w3c-keyname: 2.2.8
dev: false
/@emnapi/core@0.45.0:
resolution: {integrity: sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==}
@@ -1769,6 +1870,38 @@ packages:
resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==}
dev: false
/@lezer/common@1.2.1:
resolution: {integrity: sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==}
dev: false
/@lezer/highlight@1.2.0:
resolution: {integrity: sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==}
dependencies:
'@lezer/common': 1.2.1
dev: false
/@lezer/json@1.0.2:
resolution: {integrity: sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==}
dependencies:
'@lezer/common': 1.2.1
'@lezer/highlight': 1.2.0
'@lezer/lr': 1.4.0
dev: false
/@lezer/lr@1.4.0:
resolution: {integrity: sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==}
dependencies:
'@lezer/common': 1.2.1
dev: false
/@lezer/yaml@1.0.3:
resolution: {integrity: sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==}
dependencies:
'@lezer/common': 1.2.1
'@lezer/highlight': 1.2.0
'@lezer/lr': 1.4.0
dev: false
/@lucia-auth/adapter-drizzle@1.0.7(lucia@3.1.1):
resolution: {integrity: sha512-X/V7fLBca8EC/gPXCntwbQpb0+F9oEuRoHElvsi9rCrdnGhCMNxHgwAvgiQ6pes+rIYpyvx4n3hvjqo/fPo03A==}
peerDependencies:
@@ -1880,6 +2013,7 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
requiresBuild: true
dev: false
optional: true
@@ -1889,6 +2023,7 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
requiresBuild: true
dev: false
optional: true
@@ -1898,6 +2033,7 @@ packages:
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
requiresBuild: true
dev: false
optional: true
@@ -1907,6 +2043,7 @@ packages:
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
requiresBuild: true
dev: false
optional: true
@@ -1997,6 +2134,7 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
requiresBuild: true
dev: false
optional: true
@@ -2006,6 +2144,7 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
requiresBuild: true
dev: false
optional: true
@@ -2015,6 +2154,7 @@ packages:
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
requiresBuild: true
dev: false
optional: true
@@ -2024,6 +2164,7 @@ packages:
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
requiresBuild: true
dev: false
optional: true
@@ -2144,6 +2285,7 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
requiresBuild: true
dev: false
optional: true
@@ -2153,6 +2295,7 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
requiresBuild: true
dev: false
optional: true
@@ -2162,6 +2305,7 @@ packages:
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
requiresBuild: true
dev: false
optional: true
@@ -2171,6 +2315,7 @@ packages:
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
requiresBuild: true
dev: false
optional: true
@@ -4392,6 +4537,75 @@ packages:
'@types/node': 18.19.24
dev: true
/@uiw/codemirror-extensions-basic-setup@4.22.1(@codemirror/autocomplete@6.16.0)(@codemirror/commands@6.5.0)(@codemirror/language@6.10.1)(@codemirror/lint@6.8.0)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3):
resolution: {integrity: sha512-Iz8eFaZBNrwjaAADszOxOv2byDMn4rqob/luuSPAzJjTrSn5KawRXcoNLoWGPGNO6Mils6bIly/g2LaU34otNw==}
peerDependencies:
'@codemirror/autocomplete': '>=6.0.0'
'@codemirror/commands': '>=6.0.0'
'@codemirror/language': '>=6.0.0'
'@codemirror/lint': '>=6.0.0'
'@codemirror/search': '>=6.0.0'
'@codemirror/state': '>=6.0.0'
'@codemirror/view': '>=6.0.0'
dependencies:
'@codemirror/autocomplete': 6.16.0(@codemirror/language@6.10.1)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3)(@lezer/common@1.2.1)
'@codemirror/commands': 6.5.0
'@codemirror/language': 6.10.1
'@codemirror/lint': 6.8.0
'@codemirror/search': 6.5.6
'@codemirror/state': 6.4.1
'@codemirror/view': 6.26.3
dev: false
/@uiw/codemirror-theme-github@4.22.1(@codemirror/language@6.10.1)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3):
resolution: {integrity: sha512-trotMuMd8PQjvst4hg9b5Mz5qlD5HQzPVAZQFlQKaaF8mEKMk77nFCPhfD3Rg2WEq02lx6fqmOcErQjY0GRHew==}
dependencies:
'@uiw/codemirror-themes': 4.22.1(@codemirror/language@6.10.1)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3)
transitivePeerDependencies:
- '@codemirror/language'
- '@codemirror/state'
- '@codemirror/view'
dev: false
/@uiw/codemirror-themes@4.22.1(@codemirror/language@6.10.1)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3):
resolution: {integrity: sha512-5TeB8wCc0aNd3YEhzOvgekpAFQfEm4fCTUcGmEIQqaRNgKAM83HYNpE1JF2j7x2oDFugdiO0yJynS6bo1zVOuw==}
peerDependencies:
'@codemirror/language': '>=6.0.0'
'@codemirror/state': '>=6.0.0'
'@codemirror/view': '>=6.0.0'
dependencies:
'@codemirror/language': 6.10.1
'@codemirror/state': 6.4.1
'@codemirror/view': 6.26.3
dev: false
/@uiw/react-codemirror@4.22.1(@babel/runtime@7.24.0)(@codemirror/autocomplete@6.16.0)(@codemirror/language@6.10.1)(@codemirror/lint@6.8.0)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.26.3)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-yrq9FdGZ6E4Rh+7W0xyirSEeESGyG/k54/DfFqSk40fqel/3x/3fqjIImEZUYPxxgFPmZ3RtP+O0Em46nwRvgg==}
peerDependencies:
'@babel/runtime': '>=7.11.0'
'@codemirror/state': '>=6.0.0'
'@codemirror/theme-one-dark': '>=6.0.0'
'@codemirror/view': '>=6.0.0'
codemirror: '>=6.0.0'
react: '>=16.8.0'
react-dom: '>=16.8.0'
dependencies:
'@babel/runtime': 7.24.0
'@codemirror/commands': 6.5.0
'@codemirror/state': 6.4.1
'@codemirror/theme-one-dark': 6.1.2
'@codemirror/view': 6.26.3
'@uiw/codemirror-extensions-basic-setup': 4.22.1(@codemirror/autocomplete@6.16.0)(@codemirror/commands@6.5.0)(@codemirror/language@6.10.1)(@codemirror/lint@6.8.0)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3)
codemirror: 6.0.1(@lezer/common@1.2.1)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
transitivePeerDependencies:
- '@codemirror/autocomplete'
- '@codemirror/language'
- '@codemirror/lint'
- '@codemirror/search'
dev: false
/@xterm/addon-attach@0.10.0(@xterm/xterm@5.4.0):
resolution: {integrity: sha512-ES/XO8pC1tPHSkh4j7qzM8ajFt++u8KMvfRc9vKIbjHTDOxjl9IUVo+vcQgLn3FTCM3w2czTvBss8nMWlD83Cg==}
peerDependencies:
@@ -4728,19 +4942,9 @@ packages:
engines: {node: '>=6'}
dev: false
/camelcase@7.0.1:
resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==}
engines: {node: '>=14.16'}
dev: true
/caniuse-lite@1.0.30001598:
resolution: {integrity: sha512-j8mQRDziG94uoBfeFuqsJUNECW37DXpnvhcMJMdlH2u3MRkq1sAI0LJcXP1i/Py0KbSIC4UDj8YHPrTn5YsL+Q==}
/chalk@5.3.0:
resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
dev: true
/chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
@@ -4841,6 +5045,20 @@ packages:
- '@types/react'
dev: false
/codemirror@6.0.1(@lezer/common@1.2.1):
resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==}
dependencies:
'@codemirror/autocomplete': 6.16.0(@codemirror/language@6.10.1)(@codemirror/state@6.4.1)(@codemirror/view@6.26.3)(@lezer/common@1.2.1)
'@codemirror/commands': 6.5.0
'@codemirror/language': 6.10.1
'@codemirror/lint': 6.8.0
'@codemirror/search': 6.5.6
'@codemirror/state': 6.4.1
'@codemirror/view': 6.26.3
transitivePeerDependencies:
- '@lezer/common'
dev: false
/color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@@ -4877,6 +5095,7 @@ packages:
engines: {node: '>=12.13'}
dependencies:
is-what: 4.1.16
dev: false
/copy-to-clipboard@3.3.3:
resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==}
@@ -4894,6 +5113,10 @@ packages:
dev: false
optional: true
/crelt@1.0.6:
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
dev: false
/cron-parser@4.9.0:
resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==}
engines: {node: '>=12.0.0'}
@@ -5143,14 +5366,11 @@ packages:
wordwrap: 1.0.0
dev: true
/drizzle-kit@0.20.14:
resolution: {integrity: sha512-0fHv3YIEaUcSVPSGyaaBfOi9bmpajjhbJNdPsRMIUvYdLVxBu9eGjH8mRc3Qk7HVmEidFc/lhG1YyJhoXrn5yA==}
/drizzle-kit@0.21.1:
resolution: {integrity: sha512-Sp7OnCdROiE2ebMuHsAfrnRoHVGYCvErQxUh7/0l6R1caHssZu9oZu/hW9rLU19xnTK4/y3iSe3sL0Cc530wCg==}
hasBin: true
dependencies:
'@drizzle-team/studio': 0.0.39
'@esbuild-kit/esm-loader': 2.6.5
camelcase: 7.0.1
chalk: 5.3.0
commander: 9.5.0
env-paths: 3.0.0
esbuild: 0.19.12
@@ -5158,8 +5378,6 @@ packages:
glob: 8.1.0
hanji: 0.0.5
json-diff: 0.9.0
minimatch: 7.4.6
semver: 7.6.0
zod: 3.23.4
transitivePeerDependencies:
- supports-color
@@ -5834,6 +6052,7 @@ packages:
/is-what@4.1.16:
resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
engines: {node: '>=12.13'}
dev: false
/isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
@@ -6015,6 +6234,7 @@ packages:
engines: {node: '>=10'}
dependencies:
yallist: 4.0.0
dev: false
/lru-queue@0.1.0:
resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==}
@@ -6095,13 +6315,6 @@ packages:
brace-expansion: 2.0.1
dev: true
/minimatch@7.4.6:
resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==}
engines: {node: '>=10'}
dependencies:
brace-expansion: 2.0.1
dev: true
/minimatch@9.0.3:
resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -6909,6 +7122,7 @@ packages:
hasBin: true
dependencies:
lru-cache: 6.0.0
dev: false
/set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
@@ -7038,6 +7252,10 @@ packages:
resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==}
dev: false
/style-mod@4.1.2:
resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==}
dev: false
/styled-jsx@5.1.1(react@18.2.0):
resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==}
engines: {node: '>= 12.0.0'}
@@ -7073,6 +7291,7 @@ packages:
engines: {node: '>=16'}
dependencies:
copy-anything: 3.0.5
dev: false
/supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
@@ -7351,6 +7570,10 @@ packages:
d3-timer: 3.0.1
dev: false
/w3c-keyname@2.2.8:
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
dev: false
/webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
dev: false
@@ -7456,6 +7679,7 @@ packages:
/yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
dev: false
/yaml@2.4.1:
resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==}

Some files were not shown because too many files have changed in this diff Show More