Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64ada7020a | ||
|
|
faf24dfa25 | ||
|
|
6f4bf428c7 | ||
|
|
a43627d869 | ||
|
|
addd102d39 | ||
|
|
0d85fd0e3c | ||
|
|
86fc59d850 | ||
|
|
3cd3db6828 | ||
|
|
2c6fd0f52b | ||
|
|
5fb682b58d | ||
|
|
b77f330222 | ||
|
|
f0b8f3eaa0 | ||
|
|
36af22630b | ||
|
|
2d53a700f6 | ||
|
|
00eeffee13 | ||
|
|
7a32698031 | ||
|
|
9d834e1a79 | ||
|
|
91819c2488 | ||
|
|
f64392469d | ||
|
|
889e72d21e | ||
|
|
86165d1104 | ||
|
|
54adab16cf | ||
|
|
2e3b0ddcde | ||
|
|
ed0f3cadd6 | ||
|
|
898880634a | ||
|
|
b4e154fb28 | ||
|
|
2e6489d315 | ||
|
|
210fed30a2 | ||
|
|
60521c1025 | ||
|
|
1a496e35c0 | ||
|
|
f37f98aade | ||
|
|
0eb7b3ecb1 | ||
|
|
1a7c602861 | ||
|
|
4706adc0c0 | ||
|
|
85f025c729 | ||
|
|
06005eb333 | ||
|
|
0c01efb249 | ||
|
|
7e9e9dc865 | ||
|
|
b28bf5f9ec | ||
|
|
c071be6ad9 | ||
|
|
059a98c575 | ||
|
|
d2a07195b0 | ||
|
|
4ff1b3c19f | ||
|
|
39abd7e374 | ||
|
|
899d7565f6 | ||
|
|
3cfc2d6cd8 | ||
|
|
817fa91173 | ||
|
|
4d8a4f713d | ||
|
|
4865f4f969 | ||
|
|
0b7feb5483 | ||
|
|
1c139b9503 | ||
|
|
d47b7e62e6 | ||
|
|
33940a345a | ||
|
|
f230bda1f7 | ||
|
|
687524d154 |
@@ -26,7 +26,7 @@ Dokploy include multiples features to make your life easier.
|
||||
* **Traefik Integration**: Automatically integrates with Traefik for routing and load balancing.
|
||||
* **Real-time Monitoring**: Monitor CPU, memory, storage, and network usage, for every resource.
|
||||
* **Docker Management**: Easily deploy and manage Docker containers.
|
||||
* **CLI (Soon⌛)**: Manage your applications and databases using the command line.
|
||||
* **CLI/API**: Manage your applications and databases using the command line or trought the API.
|
||||
* **Self-Hosted**: Self-host Dokploy on your VPS.
|
||||
|
||||
|
||||
|
||||
@@ -86,6 +86,11 @@ export const ShowDeployments = ({ applicationId }: Props) => {
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{deployment.title}
|
||||
</span>
|
||||
{deployment.description && (
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{deployment.description}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col items-end gap-2">
|
||||
<div className="text-sm capitalize text-muted-foreground">
|
||||
|
||||
79
components/dashboard/application/domains/generate-domain.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { RefreshCcw } from "lucide-react";
|
||||
import { GenerateTraefikMe } from "./generate-traefikme";
|
||||
import { GenerateWildCard } from "./generate-wildcard";
|
||||
import Link from "next/link";
|
||||
import { api } from "@/utils/api";
|
||||
|
||||
interface Props {
|
||||
applicationId: string;
|
||||
}
|
||||
|
||||
export const GenerateDomain = ({ applicationId }: Props) => {
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger className="" asChild>
|
||||
<Button variant="secondary">
|
||||
Generate Domain
|
||||
<RefreshCcw className="size-4 text-muted-foreground " />
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Generate Domain</DialogTitle>
|
||||
<DialogDescription>
|
||||
Generate Domains for your applications
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
<ul className="flex flex-col gap-4">
|
||||
<li className="flex flex-row items-center gap-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-base font-bold">
|
||||
1. Generate TraefikMe Domain
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
This option generates a free domain provided by{" "}
|
||||
<Link
|
||||
href="https://traefik.me"
|
||||
className="text-primary"
|
||||
target="_blank"
|
||||
>
|
||||
TraefikMe
|
||||
</Link>
|
||||
. We recommend using this for quick domain testing or if you
|
||||
don't have a domain yet.
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{/* <li className="flex flex-row items-center gap-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-base font-bold">
|
||||
2. Use Wildcard Domain
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
To use this option, you need to set up an 'A' record in your
|
||||
domain provider. For example, create a record for
|
||||
*.yourdomain.com.
|
||||
</div>
|
||||
</div>
|
||||
</li> */}
|
||||
</ul>
|
||||
<div className="flex flex-row gap-4 w-full">
|
||||
<GenerateTraefikMe applicationId={applicationId} />
|
||||
{/* <GenerateWildCard applicationId={applicationId} /> */}
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
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 { RefreshCcw } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface Props {
|
||||
applicationId: string;
|
||||
}
|
||||
export const GenerateTraefikMe = ({ applicationId }: Props) => {
|
||||
const { mutateAsync, isLoading } = api.domain.generateDomain.useMutation();
|
||||
const utils = api.useUtils();
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="secondary" isLoading={isLoading}>
|
||||
Generate Domain
|
||||
<RefreshCcw className="size-4 text-muted-foreground " />
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
Are you sure to generate a new domain?
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This will generate a new domain and will be used to access to the
|
||||
application
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={async () => {
|
||||
await mutateAsync({
|
||||
applicationId,
|
||||
})
|
||||
.then((data) => {
|
||||
utils.domain.byApplicationId.invalidate({
|
||||
applicationId: applicationId,
|
||||
});
|
||||
utils.application.readTraefikConfig.invalidate({
|
||||
applicationId: applicationId,
|
||||
});
|
||||
toast.success("Generated Domain succesfully");
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error to generate Domain");
|
||||
});
|
||||
}}
|
||||
>
|
||||
Confirm
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
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 { SquareAsterisk } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface Props {
|
||||
applicationId: string;
|
||||
}
|
||||
export const GenerateWildCard = ({ applicationId }: Props) => {
|
||||
const { mutateAsync, isLoading } = api.domain.generateWildcard.useMutation();
|
||||
const utils = api.useUtils();
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="secondary" isLoading={isLoading}>
|
||||
Generate Wildcard Domain
|
||||
<SquareAsterisk className="size-4 text-muted-foreground " />
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
Are you sure to generate a new wildcard domain?
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This will generate a new domain and will be used to access to the
|
||||
application
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={async () => {
|
||||
await mutateAsync({
|
||||
applicationId,
|
||||
})
|
||||
.then((data) => {
|
||||
utils.domain.byApplicationId.invalidate({
|
||||
applicationId: applicationId,
|
||||
});
|
||||
utils.application.readTraefikConfig.invalidate({
|
||||
applicationId: applicationId,
|
||||
});
|
||||
toast.success("Generated Domain succesfully");
|
||||
})
|
||||
.catch((e) => {
|
||||
toast.error(`Error to generate Domain: ${e.message}`);
|
||||
});
|
||||
}}
|
||||
>
|
||||
Confirm
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
};
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { ExternalLink, GlobeIcon } from "lucide-react";
|
||||
import { ExternalLink, GlobeIcon, RefreshCcw } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { api } from "@/utils/api";
|
||||
import { Input } from "@/components/ui/input";
|
||||
@@ -14,6 +14,7 @@ import { DeleteDomain } from "./delete-domain";
|
||||
import Link from "next/link";
|
||||
import { AddDomain } from "./add-domain";
|
||||
import { UpdateDomain } from "./update-domain";
|
||||
import { GenerateDomain } from "./generate-domain";
|
||||
|
||||
interface Props {
|
||||
applicationId: string;
|
||||
@@ -31,7 +32,7 @@ export const ShowDomains = ({ applicationId }: Props) => {
|
||||
return (
|
||||
<div className="flex w-full flex-col gap-5 ">
|
||||
<Card className="bg-background">
|
||||
<CardHeader className="flex flex-row items-center justify-between">
|
||||
<CardHeader className="flex flex-row items-center flex-wrap gap-4 justify-between">
|
||||
<div className="flex flex-col gap-1">
|
||||
<CardTitle className="text-xl">Domains</CardTitle>
|
||||
<CardDescription>
|
||||
@@ -39,11 +40,16 @@ export const ShowDomains = ({ applicationId }: Props) => {
|
||||
</CardDescription>
|
||||
</div>
|
||||
|
||||
{data && data?.length > 0 && (
|
||||
<AddDomain applicationId={applicationId}>
|
||||
<GlobeIcon className="size-4" /> Add Domain
|
||||
</AddDomain>
|
||||
)}
|
||||
<div className="flex flex-row gap-4 flex-wrap">
|
||||
{data && data?.length > 0 && (
|
||||
<AddDomain applicationId={applicationId}>
|
||||
<GlobeIcon className="size-4" /> Add Domain
|
||||
</AddDomain>
|
||||
)}
|
||||
{data && data?.length > 0 && (
|
||||
<GenerateDomain applicationId={applicationId} />
|
||||
)}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="flex w-full flex-row gap-4">
|
||||
{data?.length === 0 ? (
|
||||
@@ -53,9 +59,13 @@ export const ShowDomains = ({ applicationId }: Props) => {
|
||||
To access to the application is required to set at least 1
|
||||
domain
|
||||
</span>
|
||||
<AddDomain applicationId={applicationId}>
|
||||
<GlobeIcon className="size-4" /> Add Domain
|
||||
</AddDomain>
|
||||
<div className="flex flex-row gap-4 flex-wrap">
|
||||
<AddDomain applicationId={applicationId}>
|
||||
<GlobeIcon className="size-4" /> Add Domain
|
||||
</AddDomain>
|
||||
|
||||
<GenerateDomain applicationId={applicationId} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex w-full flex-col gap-4">
|
||||
|
||||
@@ -62,11 +62,11 @@ export const StopCompose = ({ composeId }: Props) => {
|
||||
toast.success("Compose rebuild succesfully");
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error to rebuild the compose");
|
||||
toast.error("Error to stop the compose");
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error to rebuild the compose");
|
||||
toast.error("Error to stop the compose");
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -161,10 +161,6 @@ export const ShowContainers = () => {
|
||||
</Table>
|
||||
</div>
|
||||
<div className="flex items-center justify-end space-x-2 py-4">
|
||||
<div className="flex-1 text-sm text-muted-foreground">
|
||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
||||
</div>
|
||||
<div className="space-x-2 flex flex-wrap">
|
||||
<Button
|
||||
variant="outline"
|
||||
|
||||
@@ -141,7 +141,13 @@ export const DockerMonitoring = ({
|
||||
network: data.network[data.network.length - 1] ?? currentData.network,
|
||||
disk: data.disk[data.disk.length - 1] ?? currentData.disk,
|
||||
});
|
||||
setAcummulativeData(data);
|
||||
setAcummulativeData({
|
||||
block: data?.block || [],
|
||||
cpu: data?.cpu || [],
|
||||
disk: data?.disk || [],
|
||||
memory: data?.memory || [],
|
||||
network: data?.network || [],
|
||||
});
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -54,7 +54,7 @@ export const AddTemplate = ({ projectId }: Props) => {
|
||||
</DropdownMenuItem>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-7xl p-0">
|
||||
<div className="sticky top-0 z-10 flex flex-col gap-4 bg-black p-6 border-b">
|
||||
<div className="sticky top-0 z-10 flex flex-col gap-4 dark:bg-black p-6 border-b">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create Template</DialogTitle>
|
||||
<DialogDescription>
|
||||
|
||||
@@ -76,8 +76,8 @@ export const ShowProjects = () => {
|
||||
project?.compose.length;
|
||||
return (
|
||||
<div key={project.projectId} className="w-full lg:max-w-md">
|
||||
<Card className="group relative w-full bg-transparent transition-colors hover:bg-card">
|
||||
<Link href={`/dashboard/project/${project.projectId}`}>
|
||||
<Link href={`/dashboard/project/${project.projectId}`}>
|
||||
<Card className="group relative w-full bg-transparent transition-colors hover:bg-card">
|
||||
<Button
|
||||
className="absolute -right-3 -top-3 size-9 translate-y-1 rounded-full p-0 opacity-0 transition-all duration-200 group-hover:translate-y-0 group-hover:opacity-100"
|
||||
size="sm"
|
||||
@@ -85,113 +85,122 @@ export const ShowProjects = () => {
|
||||
>
|
||||
<ExternalLinkIcon className="size-3.5" />
|
||||
</Button>
|
||||
</Link>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center justify-between gap-2">
|
||||
<span className="flex flex-col gap-1.5">
|
||||
<div className="flex items-center gap-2">
|
||||
<BookIcon className="size-4 text-muted-foreground" />
|
||||
<Link
|
||||
className="text-base font-medium leading-none"
|
||||
href={`/dashboard/project/${project.projectId}`}
|
||||
>
|
||||
{project.name}
|
||||
</Link>
|
||||
</div>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center justify-between gap-2">
|
||||
<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">
|
||||
{project.name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span className="text-sm font-medium text-muted-foreground">
|
||||
{project.description}
|
||||
<span className="text-sm font-medium text-muted-foreground">
|
||||
{project.description}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<div className="flex self-start space-x-1">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="px-2">
|
||||
<MoreHorizontalIcon className="size-5" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-[200px] space-y-2">
|
||||
<DropdownMenuLabel className="font-normal">
|
||||
Actions
|
||||
</DropdownMenuLabel>
|
||||
<div className="flex self-start space-x-1">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="px-2"
|
||||
>
|
||||
<MoreHorizontalIcon className="size-5" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-[200px] space-y-2">
|
||||
<DropdownMenuLabel className="font-normal">
|
||||
Actions
|
||||
</DropdownMenuLabel>
|
||||
|
||||
<UpdateProject projectId={project.projectId} />
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<UpdateProject projectId={project.projectId} />
|
||||
</div>
|
||||
|
||||
{(auth?.rol === "admin" ||
|
||||
user?.canDeleteProjects) && (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger className="w-full">
|
||||
<DropdownMenuItem
|
||||
className="w-full cursor-pointer space-x-3"
|
||||
onSelect={(e) => e.preventDefault()}
|
||||
>
|
||||
<TrashIcon className="size-4" />
|
||||
<span>Delete</span>
|
||||
</DropdownMenuItem>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
Are you sure to delete this project?
|
||||
</AlertDialogTitle>
|
||||
{!emptyServices ? (
|
||||
<div className="flex flex-row gap-4 rounded-lg bg-yellow-50 p-2 dark:bg-yellow-950">
|
||||
<AlertTriangle className="text-yellow-600 dark:text-yellow-400" />
|
||||
<span className="text-sm text-yellow-600 dark:text-yellow-400">
|
||||
You have active services, please delete
|
||||
them first
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<AlertDialogDescription>
|
||||
This action cannot be undone
|
||||
</AlertDialogDescription>
|
||||
)}
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
disabled={!emptyServices}
|
||||
onClick={async () => {
|
||||
await mutateAsync({
|
||||
projectId: project.projectId,
|
||||
})
|
||||
.then(() => {
|
||||
toast.success(
|
||||
"Project delete succesfully",
|
||||
);
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error(
|
||||
"Error to delete this project",
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
utils.project.all.invalidate();
|
||||
});
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
{(auth?.rol === "admin" ||
|
||||
user?.canDeleteProjects) && (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger className="w-full">
|
||||
<DropdownMenuItem
|
||||
className="w-full cursor-pointer space-x-3"
|
||||
onSelect={(e) => e.preventDefault()}
|
||||
>
|
||||
<TrashIcon className="size-4" />
|
||||
<span>Delete</span>
|
||||
</DropdownMenuItem>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
Are you sure to delete this project?
|
||||
</AlertDialogTitle>
|
||||
{!emptyServices ? (
|
||||
<div className="flex flex-row gap-4 rounded-lg bg-yellow-50 p-2 dark:bg-yellow-950">
|
||||
<AlertTriangle className="text-yellow-600 dark:text-yellow-400" />
|
||||
<span className="text-sm text-yellow-600 dark:text-yellow-400">
|
||||
You have active services, please
|
||||
delete them first
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<AlertDialogDescription>
|
||||
This action cannot be undone
|
||||
</AlertDialogDescription>
|
||||
)}
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>
|
||||
Cancel
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
disabled={!emptyServices}
|
||||
onClick={async () => {
|
||||
await mutateAsync({
|
||||
projectId: project.projectId,
|
||||
})
|
||||
.then(() => {
|
||||
toast.success(
|
||||
"Project delete succesfully",
|
||||
);
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error(
|
||||
"Error to delete this project",
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
utils.project.all.invalidate();
|
||||
});
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)}
|
||||
</div>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardFooter className="pt-4">
|
||||
<div className="space-y-1 text-sm flex flex-row justify-between max-sm:flex-wrap w-full gap-2 sm:gap-4">
|
||||
<DateTooltip date={project.createdAt}>
|
||||
Created
|
||||
</DateTooltip>
|
||||
<span>
|
||||
{totalServices}{" "}
|
||||
{totalServices === 1 ? "service" : "services"}
|
||||
</span>
|
||||
</div>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardFooter className="pt-4">
|
||||
<div className="space-y-1 text-sm flex flex-row justify-between max-sm:flex-wrap w-full gap-2 sm:gap-4">
|
||||
<DateTooltip date={project.createdAt}>Created</DateTooltip>
|
||||
<span>
|
||||
{totalServices}{" "}
|
||||
{totalServices === 1 ? "service" : "services"}
|
||||
</span>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
1
drizzle/0017_minor_post.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "deployment" ADD COLUMN "description" text;
|
||||
@@ -1 +0,0 @@
|
||||
ALTER TABLE "user" ALTER COLUMN "token" DROP NOT NULL;
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"id": "7610c85e-c3e4-4a32-8ce9-7f48b298f956",
|
||||
"id": "ec852f38-886a-43b4-9295-73984ed8ef45",
|
||||
"prevId": "2d8d7670-b942-4573-9c44-6e81d2a2fa16",
|
||||
"version": "6",
|
||||
"dialect": "postgresql",
|
||||
@@ -465,7 +465,7 @@
|
||||
"name": "token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": true
|
||||
},
|
||||
"isRegistered": {
|
||||
"name": "isRegistered",
|
||||
@@ -1585,6 +1585,12 @@
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "deploymentStatus",
|
||||
|
||||
@@ -124,8 +124,8 @@
|
||||
{
|
||||
"idx": 17,
|
||||
"version": "6",
|
||||
"when": 1719109531147,
|
||||
"tag": "0017_yummy_norrin_radd",
|
||||
"when": 1719547174326,
|
||||
"tag": "0017_minor_post",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "dokploy",
|
||||
"version": "v0.2.4",
|
||||
"version": "v0.3.0",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-only",
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "npm run build-server && npm run build-next",
|
||||
@@ -19,7 +19,7 @@
|
||||
"migration:drop": "drizzle-kit drop --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",
|
||||
"db:studio": "drizzle-kit studio --config ./server/db/drizzle.config.ts",
|
||||
"lint": "biome lint",
|
||||
"db:seed": "tsx -r dotenv/config ./server/db/seed.ts",
|
||||
"db:clean": "tsx -r dotenv/config ./server/db/reset.ts",
|
||||
|
||||
29
pages/api/[...trpc].ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { createOpenApiNextHandler } from "@dokploy/trpc-openapi";
|
||||
import { appRouter } from "@/server/api/root";
|
||||
import { createTRPCContext } from "@/server/api/trpc";
|
||||
import { validateBearerToken } from "@/server/auth/token";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
let { session, user } = await validateBearerToken(req);
|
||||
|
||||
if (!session) {
|
||||
const cookieResult = await validateRequest(req, res);
|
||||
session = cookieResult.session;
|
||||
user = cookieResult.user;
|
||||
}
|
||||
|
||||
if (!user || !session) {
|
||||
res.status(401).json({ message: "Unauthorized" });
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
return createOpenApiNextHandler({
|
||||
router: appRouter,
|
||||
createContext: createTRPCContext,
|
||||
})(req, res);
|
||||
};
|
||||
|
||||
export default handler;
|
||||
@@ -33,6 +33,7 @@ export default async function handler(
|
||||
}
|
||||
|
||||
const deploymentTitle = extractCommitMessage(req.headers, req.body);
|
||||
const deploymentHash = extractHash(req.headers, req.body);
|
||||
|
||||
const sourceType = application.sourceType;
|
||||
|
||||
@@ -75,6 +76,7 @@ export default async function handler(
|
||||
const jobData: DeploymentJob = {
|
||||
applicationId: application.applicationId as string,
|
||||
titleLog: deploymentTitle,
|
||||
descriptionLog: `Hash: ${deploymentHash}`,
|
||||
type: "deploy",
|
||||
applicationType: "application",
|
||||
};
|
||||
@@ -166,6 +168,37 @@ export const extractCommitMessage = (headers: any, body: any) => {
|
||||
return "NEW CHANGES";
|
||||
};
|
||||
|
||||
export const extractHash = (headers: any, body: any) => {
|
||||
// GitHub
|
||||
if (headers["x-github-event"]) {
|
||||
return body.head_commit ? body.head_commit.id : "";
|
||||
}
|
||||
|
||||
// GitLab
|
||||
if (headers["x-gitlab-event"]) {
|
||||
return (
|
||||
body.checkout_sha ||
|
||||
(body.commits && body.commits.length > 0
|
||||
? body.commits[0].id
|
||||
: "NEW COMMIT")
|
||||
);
|
||||
}
|
||||
|
||||
// Bitbucket
|
||||
if (headers["x-event-key"]?.includes("repo:push")) {
|
||||
return body.push.changes && body.push.changes.length > 0
|
||||
? body.push.changes[0].new.target.hash
|
||||
: "NEW COMMIT";
|
||||
}
|
||||
|
||||
// Gitea
|
||||
if (headers["x-gitea-event"]) {
|
||||
return body.after || "NEW COMMIT";
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
export const extractBranchName = (headers: any, body: any) => {
|
||||
if (headers["x-github-event"] || headers["x-gitea-event"]) {
|
||||
return body?.ref?.replace("refs/heads/", "");
|
||||
|
||||
@@ -4,7 +4,11 @@ import type { DeploymentJob } from "@/server/queues/deployments-queue";
|
||||
import { myQueue } from "@/server/queues/queueSetup";
|
||||
import { eq } from "drizzle-orm";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { extractBranchName, extractCommitMessage } from "../[refreshToken]";
|
||||
import {
|
||||
extractBranchName,
|
||||
extractCommitMessage,
|
||||
extractHash,
|
||||
} from "../[refreshToken]";
|
||||
import { updateCompose } from "@/server/api/services/compose";
|
||||
|
||||
export default async function handler(
|
||||
@@ -34,7 +38,7 @@ export default async function handler(
|
||||
}
|
||||
|
||||
const deploymentTitle = extractCommitMessage(req.headers, req.body);
|
||||
|
||||
const deploymentHash = extractHash(req.headers, req.body);
|
||||
const sourceType = composeResult.sourceType;
|
||||
|
||||
if (sourceType === "github") {
|
||||
@@ -61,6 +65,7 @@ export default async function handler(
|
||||
titleLog: deploymentTitle,
|
||||
type: "deploy",
|
||||
applicationType: "compose",
|
||||
descriptionLog: `Hash: ${deploymentHash}`,
|
||||
};
|
||||
await myQueue.add(
|
||||
"deployments",
|
||||
|
||||
@@ -6,15 +6,32 @@ import type { GetServerSidePropsContext, NextPage } from "next";
|
||||
import dynamic from "next/dynamic";
|
||||
import "swagger-ui-react/swagger-ui.css";
|
||||
import superjson from "superjson";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const SwaggerUI = dynamic(() => import("swagger-ui-react"), { ssr: false });
|
||||
|
||||
const Home: NextPage = () => {
|
||||
const { data } = api.settings.getOpenApiDocument.useQuery();
|
||||
const [spec, setSpec] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
// Esto solo se ejecutará en el cliente
|
||||
if (data) {
|
||||
const protocolAndHost = `${window.location.protocol}//${window.location.host}/api`;
|
||||
const newSpec = {
|
||||
...data,
|
||||
servers: [{ url: protocolAndHost }],
|
||||
externalDocs: {
|
||||
url: `${protocolAndHost}/settings.getOpenApiDocument`,
|
||||
},
|
||||
};
|
||||
setSpec(newSpec);
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-white">
|
||||
<SwaggerUI spec={data || {}} />
|
||||
<SwaggerUI spec={spec} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
85
pnpm-lock.yaml
generated
@@ -100,7 +100,7 @@ dependencies:
|
||||
version: 10.45.2(@trpc/server@10.45.2)
|
||||
'@trpc/next':
|
||||
specifier: ^10.43.6
|
||||
version: 10.45.2(@tanstack/react-query@4.36.1)(@trpc/client@10.45.2)(@trpc/react-query@10.45.2)(@trpc/server@10.45.2)(next@14.2.3)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 10.45.2(@tanstack/react-query@4.36.1)(@trpc/client@10.45.2)(@trpc/react-query@10.45.2)(@trpc/server@10.45.2)(next@14.2.4)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@trpc/react-query':
|
||||
specifier: ^10.43.6
|
||||
version: 10.45.2(@tanstack/react-query@4.36.1)(@trpc/client@10.45.2)(@trpc/server@10.45.2)(react-dom@18.2.0)(react@18.2.0)
|
||||
@@ -193,10 +193,10 @@ dependencies:
|
||||
version: 3.3.7
|
||||
next:
|
||||
specifier: ^14.1.3
|
||||
version: 14.2.3(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 14.2.4(react-dom@18.2.0)(react@18.2.0)
|
||||
next-themes:
|
||||
specifier: ^0.2.1
|
||||
version: 0.2.1(next@14.2.3)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 0.2.1(next@14.2.4)(react-dom@18.2.0)(react@18.2.0)
|
||||
node-os-utils:
|
||||
specifier: 1.3.7
|
||||
version: 1.3.7
|
||||
@@ -2059,12 +2059,12 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/env@14.2.3:
|
||||
resolution: {integrity: sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==}
|
||||
/@next/env@14.2.4:
|
||||
resolution: {integrity: sha512-3EtkY5VDkuV2+lNmKlbkibIJxcO4oIHEhBWne6PaAp+76J9KoSsGvNikp6ivzAT8dhhBMYrm6op2pS1ApG0Hzg==}
|
||||
dev: false
|
||||
|
||||
/@next/swc-darwin-arm64@14.2.3:
|
||||
resolution: {integrity: sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==}
|
||||
/@next/swc-darwin-arm64@14.2.4:
|
||||
resolution: {integrity: sha512-AH3mO4JlFUqsYcwFUHb1wAKlebHU/Hv2u2kb1pAuRanDZ7pD/A/KPD98RHZmwsJpdHQwfEc/06mgpSzwrJYnNg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
@@ -2072,8 +2072,8 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-darwin-x64@14.2.3:
|
||||
resolution: {integrity: sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==}
|
||||
/@next/swc-darwin-x64@14.2.4:
|
||||
resolution: {integrity: sha512-QVadW73sWIO6E2VroyUjuAxhWLZWEpiFqHdZdoQ/AMpN9YWGuHV8t2rChr0ahy+irKX5mlDU7OY68k3n4tAZTg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
@@ -2081,48 +2081,44 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-arm64-gnu@14.2.3:
|
||||
resolution: {integrity: sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==}
|
||||
/@next/swc-linux-arm64-gnu@14.2.4:
|
||||
resolution: {integrity: sha512-KT6GUrb3oyCfcfJ+WliXuJnD6pCpZiosx2X3k66HLR+DMoilRb76LpWPGb4tZprawTtcnyrv75ElD6VncVamUQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-arm64-musl@14.2.3:
|
||||
resolution: {integrity: sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==}
|
||||
/@next/swc-linux-arm64-musl@14.2.4:
|
||||
resolution: {integrity: sha512-Alv8/XGSs/ytwQcbCHwze1HmiIkIVhDHYLjczSVrf0Wi2MvKn/blt7+S6FJitj3yTlMwMxII1gIJ9WepI4aZ/A==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-x64-gnu@14.2.3:
|
||||
resolution: {integrity: sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==}
|
||||
/@next/swc-linux-x64-gnu@14.2.4:
|
||||
resolution: {integrity: sha512-ze0ShQDBPCqxLImzw4sCdfnB3lRmN3qGMB2GWDRlq5Wqy4G36pxtNOo2usu/Nm9+V2Rh/QQnrRc2l94kYFXO6Q==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-linux-x64-musl@14.2.3:
|
||||
resolution: {integrity: sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==}
|
||||
/@next/swc-linux-x64-musl@14.2.4:
|
||||
resolution: {integrity: sha512-8dwC0UJoc6fC7PX70csdaznVMNr16hQrTDAMPvLPloazlcaWfdPogq+UpZX6Drqb1OBlwowz8iG7WR0Tzk/diQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-arm64-msvc@14.2.3:
|
||||
resolution: {integrity: sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==}
|
||||
/@next/swc-win32-arm64-msvc@14.2.4:
|
||||
resolution: {integrity: sha512-jxyg67NbEWkDyvM+O8UDbPAyYRZqGLQDTPwvrBBeOSyVWW/jFQkQKQ70JDqDSYg1ZDdl+E3nkbFbq8xM8E9x8A==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
@@ -2130,8 +2126,8 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-ia32-msvc@14.2.3:
|
||||
resolution: {integrity: sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==}
|
||||
/@next/swc-win32-ia32-msvc@14.2.4:
|
||||
resolution: {integrity: sha512-twrmN753hjXRdcrZmZttb/m5xaCBFa48Dt3FbeEItpJArxriYDunWxJn+QFXdJ3hPkm4u7CKxncVvnmgQMY1ag==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
@@ -2139,8 +2135,8 @@ packages:
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@next/swc-win32-x64-msvc@14.2.3:
|
||||
resolution: {integrity: sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==}
|
||||
/@next/swc-win32-x64-msvc@14.2.4:
|
||||
resolution: {integrity: sha512-tkLrjBzqFTP8DVrAAQmZelEahfR9OxWpFR++vAI9FBhCiIxtwHwBHC23SBHCTURBtwB4kc/x44imVOnkKGNVGg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@@ -4952,7 +4948,7 @@ packages:
|
||||
'@trpc/server': 10.45.2
|
||||
dev: false
|
||||
|
||||
/@trpc/next@10.45.2(@tanstack/react-query@4.36.1)(@trpc/client@10.45.2)(@trpc/react-query@10.45.2)(@trpc/server@10.45.2)(next@14.2.3)(react-dom@18.2.0)(react@18.2.0):
|
||||
/@trpc/next@10.45.2(@tanstack/react-query@4.36.1)(@trpc/client@10.45.2)(@trpc/react-query@10.45.2)(@trpc/server@10.45.2)(next@14.2.4)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-RSORmfC+/nXdmRY1pQ0AalsVgSzwNAFbZLYHiTvPM5QQ8wmMEHilseCYMXpu0se/TbPt9zVR6Ka2d7O6zxKkXg==}
|
||||
peerDependencies:
|
||||
'@tanstack/react-query': ^4.18.0
|
||||
@@ -4967,7 +4963,7 @@ packages:
|
||||
'@trpc/client': 10.45.2(@trpc/server@10.45.2)
|
||||
'@trpc/react-query': 10.45.2(@tanstack/react-query@4.36.1)(@trpc/client@10.45.2)(@trpc/server@10.45.2)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@trpc/server': 10.45.2
|
||||
next: 14.2.3(react-dom@18.2.0)(react@18.2.0)
|
||||
next: 14.2.4(react-dom@18.2.0)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -8104,14 +8100,14 @@ packages:
|
||||
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
|
||||
dev: false
|
||||
|
||||
/next-themes@0.2.1(next@14.2.3)(react-dom@18.2.0)(react@18.2.0):
|
||||
/next-themes@0.2.1(next@14.2.4)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
|
||||
peerDependencies:
|
||||
next: '*'
|
||||
react: '*'
|
||||
react-dom: '*'
|
||||
dependencies:
|
||||
next: 14.2.3(react-dom@18.2.0)(react@18.2.0)
|
||||
next: 14.2.4(react-dom@18.2.0)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
@@ -8120,8 +8116,8 @@ packages:
|
||||
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
|
||||
dev: true
|
||||
|
||||
/next@14.2.3(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==}
|
||||
/next@14.2.4(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-R8/V7vugY+822rsQGQCjoLhMuC9oFj9SOi4Cl4b2wjDrseD0LRZ10W7R6Czo4w9ZznVSshKjuIomsRjvm9EKJQ==}
|
||||
engines: {node: '>=18.17.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -8138,7 +8134,7 @@ packages:
|
||||
sass:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@next/env': 14.2.3
|
||||
'@next/env': 14.2.4
|
||||
'@swc/helpers': 0.5.5
|
||||
busboy: 1.6.0
|
||||
caniuse-lite: 1.0.30001598
|
||||
@@ -8148,15 +8144,15 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
styled-jsx: 5.1.1(react@18.2.0)
|
||||
optionalDependencies:
|
||||
'@next/swc-darwin-arm64': 14.2.3
|
||||
'@next/swc-darwin-x64': 14.2.3
|
||||
'@next/swc-linux-arm64-gnu': 14.2.3
|
||||
'@next/swc-linux-arm64-musl': 14.2.3
|
||||
'@next/swc-linux-x64-gnu': 14.2.3
|
||||
'@next/swc-linux-x64-musl': 14.2.3
|
||||
'@next/swc-win32-arm64-msvc': 14.2.3
|
||||
'@next/swc-win32-ia32-msvc': 14.2.3
|
||||
'@next/swc-win32-x64-msvc': 14.2.3
|
||||
'@next/swc-darwin-arm64': 14.2.4
|
||||
'@next/swc-darwin-x64': 14.2.4
|
||||
'@next/swc-linux-arm64-gnu': 14.2.4
|
||||
'@next/swc-linux-arm64-musl': 14.2.4
|
||||
'@next/swc-linux-x64-gnu': 14.2.4
|
||||
'@next/swc-linux-x64-musl': 14.2.4
|
||||
'@next/swc-win32-arm64-msvc': 14.2.4
|
||||
'@next/swc-win32-ia32-msvc': 14.2.4
|
||||
'@next/swc-win32-x64-msvc': 14.2.4
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- babel-plugin-macros
|
||||
@@ -8551,7 +8547,7 @@ packages:
|
||||
dependencies:
|
||||
nanoid: 3.3.7
|
||||
picocolors: 1.0.0
|
||||
source-map-js: 1.0.2
|
||||
source-map-js: 1.2.0
|
||||
dev: false
|
||||
|
||||
/postcss@8.4.35:
|
||||
@@ -9404,7 +9400,6 @@ packages:
|
||||
/source-map-js@1.2.0:
|
||||
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/source-map-support@0.5.21:
|
||||
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
|
||||
|
||||
BIN
public/templates/appsmith.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
public/templates/baserow.webp
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
public/templates/directus.jpg
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
public/templates/documenso.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
public/templates/excalidraw.jpg
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
public/templates/ghost.jpeg
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
public/templates/glitchtip.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
70
public/templates/grafana.svg
Normal file
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 142.5 145.6" style="enable-background:new 0 0 142.5 145.6;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#565656;}
|
||||
.st1{fill:url(#SVGID_1_);}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M28.7,131.5c-0.3,7.9-6.6,14.1-14.4,14.1C6.1,145.6,0,139,0,130.9s6.6-14.7,14.7-14.7c3.6,0,7.2,1.6,10.2,4.4
|
||||
l-2.3,2.9c-2.3-2-5.1-3.4-7.9-3.4c-5.9,0-10.8,4.8-10.8,10.8c0,6.1,4.6,10.8,10.4,10.8c5.2,0,9.3-3.8,10.2-8.8H12.6v-3.5h16.1
|
||||
V131.5z"/>
|
||||
<path class="st0" d="M42.3,129.5h-2.2c-2.4,0-4.4,2-4.4,4.4v11.4h-3.9v-19.6H35v1.6c1.1-1.1,2.7-1.6,4.6-1.6h4.2L42.3,129.5z"/>
|
||||
<path class="st0" d="M63.7,145.3h-3.4v-2.5c-2.6,2.5-6.6,3.7-10.7,1.9c-3-1.3-5.3-4.1-5.9-7.4c-1.2-6.3,3.7-11.9,9.9-11.9
|
||||
c2.6,0,5,1.1,6.7,2.8v-2.5h3.4V145.3z M59.7,137c0.9-4-2.1-7.6-6-7.6c-3.4,0-6.1,2.8-6.1,6.1c0,3.8,3.3,6.7,7.2,6.1
|
||||
C57.1,141.2,59.1,139.3,59.7,137z"/>
|
||||
<path class="st0" d="M71.5,124.7v1.1h6.2v3.4h-6.2v16.1h-3.8v-20.5c0-4.3,3.1-6.8,7-6.8h4.7l-1.6,3.7h-3.1
|
||||
C72.9,121.6,71.5,123,71.5,124.7z"/>
|
||||
<path class="st0" d="M98.5,145.3h-3.3v-2.5c-2.6,2.5-6.6,3.7-10.7,1.9c-3-1.3-5.3-4.1-5.9-7.4c-1.2-6.3,3.7-11.9,9.9-11.9
|
||||
c2.6,0,5,1.1,6.7,2.8v-2.5h3.4v19.6H98.5z M94.5,137c0.9-4-2.1-7.6-6-7.6c-3.4,0-6.1,2.8-6.1,6.1c0,3.8,3.3,6.7,7.2,6.1
|
||||
C92,141.2,93.9,139.3,94.5,137z"/>
|
||||
<path class="st0" d="M119.4,133.8v11.5h-3.9v-11.6c0-2.4-2-4.4-4.4-4.4c-2.5,0-4.4,2-4.4,4.4v11.6h-3.9v-19.6h3.2v1.7
|
||||
c1.4-1.3,3.3-2,5.2-2C115.8,125.5,119.4,129.2,119.4,133.8z"/>
|
||||
<path class="st0" d="M142.4,145.3h-3.3v-2.5c-2.6,2.5-6.6,3.7-10.7,1.9c-3-1.3-5.3-4.1-5.9-7.4c-1.2-6.3,3.7-11.9,9.9-11.9
|
||||
c2.6,0,5,1.1,6.7,2.8v-2.5h3.4v19.6H142.4z M138.4,137c0.9-4-2.1-7.6-6-7.6c-3.4,0-6.1,2.8-6.1,6.1c0,3.8,3.3,6.7,7.2,6.1
|
||||
C135.9,141.2,137.8,139.3,138.4,137z"/>
|
||||
</g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="71.25" y1="10.4893" x2="71.25" y2="113.3415" gradientTransform="matrix(1 0 0 -1 0 148.6)">
|
||||
<stop offset="0" style="stop-color:#FCEE1F"/>
|
||||
<stop offset="1" style="stop-color:#F15B2A"/>
|
||||
</linearGradient>
|
||||
<path class="st1" d="M122.9,49.9c-0.2-1.9-0.5-4.1-1.1-6.5c-0.6-2.4-1.6-5-2.9-7.8c-1.4-2.7-3.1-5.6-5.4-8.3
|
||||
c-0.9-1.1-1.9-2.1-2.9-3.2c1.6-6.3-1.9-11.8-1.9-11.8c-6.1-0.4-9.9,1.9-11.3,2.9c-0.2-0.1-0.5-0.2-0.7-0.3c-1-0.4-2.1-0.8-3.2-1.2
|
||||
c-1.1-0.3-2.2-0.7-3.3-0.9c-1.1-0.3-2.3-0.5-3.5-0.7c-0.2,0-0.4-0.1-0.6-0.1C83.5,3.6,75.9,0,75.9,0c-8.7,5.6-10.4,13.1-10.4,13.1
|
||||
s0,0.2-0.1,0.4c-0.5,0.1-0.9,0.3-1.4,0.4c-0.6,0.2-1.3,0.4-1.9,0.7c-0.6,0.3-1.3,0.5-1.9,0.8c-1.3,0.6-2.5,1.2-3.8,1.9
|
||||
c-1.2,0.7-2.4,1.4-3.5,2.2c-0.2-0.1-0.3-0.2-0.3-0.2c-11.7-4.5-22.1,0.9-22.1,0.9c-0.9,12.5,4.7,20.3,5.8,21.7
|
||||
c-0.3,0.8-0.5,1.5-0.8,2.3c-0.9,2.8-1.5,5.7-1.9,8.7c-0.1,0.4-0.1,0.9-0.2,1.3c-10.8,5.3-14,16.3-14,16.3c9,10.4,19.6,11,19.6,11
|
||||
l0,0c1.3,2.4,2.9,4.7,4.6,6.8c0.7,0.9,1.5,1.7,2.3,2.6c-3.3,9.4,0.5,17.3,0.5,17.3c10.1,0.4,16.7-4.4,18.1-5.5c1,0.3,2,0.6,3,0.9
|
||||
c3.1,0.8,6.3,1.3,9.4,1.4c0.8,0,1.6,0,2.4,0h0.4H80h0.5H81l0,0c4.7,6.8,13.1,7.7,13.1,7.7c5.9-6.3,6.3-12.4,6.3-13.8l0,0
|
||||
c0,0,0,0,0-0.1s0-0.2,0-0.2l0,0c0-0.1,0-0.2,0-0.3c1.2-0.9,2.4-1.8,3.6-2.8c2.4-2.1,4.4-4.6,6.2-7.2c0.2-0.2,0.3-0.5,0.5-0.7
|
||||
c6.7,0.4,11.4-4.2,11.4-4.2c-1.1-7-5.1-10.4-5.9-11l0,0c0,0,0,0-0.1-0.1l-0.1-0.1l0,0l-0.1-0.1c0-0.4,0.1-0.8,0.1-1.3
|
||||
c0.1-0.8,0.1-1.5,0.1-2.3v-0.6v-0.3v-0.1c0-0.2,0-0.1,0-0.2v-0.5v-0.6c0-0.2,0-0.4,0-0.6s0-0.4-0.1-0.6l-0.1-0.6l-0.1-0.6
|
||||
c-0.1-0.8-0.3-1.5-0.4-2.3c-0.7-3-1.9-5.9-3.4-8.4c-1.6-2.6-3.5-4.8-5.7-6.8c-2.2-1.9-4.6-3.5-7.2-4.6c-2.6-1.2-5.2-1.9-7.9-2.2
|
||||
c-1.3-0.2-2.7-0.2-4-0.2h-0.5h-0.1h-0.2h-0.2h-0.5c-0.2,0-0.4,0-0.5,0c-0.7,0.1-1.4,0.2-2,0.3c-2.7,0.5-5.2,1.5-7.4,2.8
|
||||
c-2.2,1.3-4.1,3-5.7,4.9s-2.8,3.9-3.6,6.1c-0.8,2.1-1.3,4.4-1.4,6.5c0,0.5,0,1.1,0,1.6c0,0.1,0,0.3,0,0.4v0.4c0,0.3,0,0.5,0.1,0.8
|
||||
c0.1,1.1,0.3,2.1,0.6,3.1c0.6,2,1.5,3.8,2.7,5.4s2.5,2.8,4,3.8s3,1.7,4.6,2.2c1.6,0.5,3.1,0.7,4.5,0.6c0.2,0,0.4,0,0.5,0
|
||||
c0.1,0,0.2,0,0.3,0s0.2,0,0.3,0c0.2,0,0.3,0,0.5,0h0.1h0.1c0.1,0,0.2,0,0.3,0c0.2,0,0.4-0.1,0.5-0.1c0.2,0,0.3-0.1,0.5-0.1
|
||||
c0.3-0.1,0.7-0.2,1-0.3c0.6-0.2,1.2-0.5,1.8-0.7c0.6-0.3,1.1-0.6,1.5-0.9c0.1-0.1,0.3-0.2,0.4-0.3c0.5-0.4,0.6-1.1,0.2-1.6
|
||||
c-0.4-0.4-1-0.5-1.5-0.3C88,74,87.9,74,87.7,74.1c-0.4,0.2-0.9,0.4-1.3,0.5c-0.5,0.1-1,0.3-1.5,0.4c-0.3,0-0.5,0.1-0.8,0.1
|
||||
c-0.1,0-0.3,0-0.4,0c-0.1,0-0.3,0-0.4,0s-0.3,0-0.4,0c-0.2,0-0.3,0-0.5,0c0,0-0.1,0,0,0h-0.1h-0.1c-0.1,0-0.1,0-0.2,0
|
||||
s-0.3,0-0.4-0.1c-1.1-0.2-2.3-0.5-3.4-1c-1.1-0.5-2.2-1.2-3.1-2.1c-1-0.9-1.8-1.9-2.5-3.1c-0.7-1.2-1.1-2.5-1.3-3.8
|
||||
c-0.1-0.7-0.2-1.4-0.1-2.1c0-0.2,0-0.4,0-0.6c0,0.1,0,0,0,0v-0.1v-0.1c0-0.1,0-0.2,0-0.3c0-0.4,0.1-0.7,0.2-1.1c0.5-3,2-5.9,4.3-8.1
|
||||
c0.6-0.6,1.2-1.1,1.9-1.5c0.7-0.5,1.4-0.9,2.1-1.2c0.7-0.3,1.5-0.6,2.3-0.8s1.6-0.4,2.4-0.4c0.4,0,0.8-0.1,1.2-0.1
|
||||
c0.1,0,0.2,0,0.3,0h0.3h0.2c0.1,0,0,0,0,0h0.1h0.3c0.9,0.1,1.8,0.2,2.6,0.4c1.7,0.4,3.4,1,5,1.9c3.2,1.8,5.9,4.5,7.5,7.8
|
||||
c0.8,1.6,1.4,3.4,1.7,5.3c0.1,0.5,0.1,0.9,0.2,1.4v0.3V66c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2,0,0.3v0.3v0.3c0,0.2,0,0.6,0,0.8
|
||||
c0,0.5-0.1,1-0.1,1.5c-0.1,0.5-0.1,1-0.2,1.5s-0.2,1-0.3,1.5c-0.2,1-0.6,1.9-0.9,2.9c-0.7,1.9-1.7,3.7-2.9,5.3
|
||||
c-2.4,3.3-5.7,6-9.4,7.7c-1.9,0.8-3.8,1.5-5.8,1.8c-1,0.2-2,0.3-3,0.3H81h-0.2h-0.3H80h-0.3c0.1,0,0,0,0,0h-0.1
|
||||
c-0.5,0-1.1,0-1.6-0.1c-2.2-0.2-4.3-0.6-6.4-1.2c-2.1-0.6-4.1-1.4-6-2.4c-3.8-2-7.2-4.9-9.9-8.2c-1.3-1.7-2.5-3.5-3.5-5.4
|
||||
s-1.7-3.9-2.3-5.9c-0.6-2-0.9-4.1-1-6.2v-0.4v-0.1v-0.1v-0.2V60v-0.1v-0.1v-0.2v-0.5V59l0,0v-0.2c0-0.3,0-0.5,0-0.8
|
||||
c0-1,0.1-2.1,0.3-3.2c0.1-1.1,0.3-2.1,0.5-3.2c0.2-1.1,0.5-2.1,0.8-3.2c0.6-2.1,1.3-4.1,2.2-6c1.8-3.8,4.1-7.2,6.8-9.9
|
||||
c0.7-0.7,1.4-1.3,2.2-1.9c0.3-0.3,1-0.9,1.8-1.4c0.8-0.5,1.6-1,2.5-1.4c0.4-0.2,0.8-0.4,1.3-0.6c0.2-0.1,0.4-0.2,0.7-0.3
|
||||
c0.2-0.1,0.4-0.2,0.7-0.3c0.9-0.4,1.8-0.7,2.7-1c0.2-0.1,0.5-0.1,0.7-0.2c0.2-0.1,0.5-0.1,0.7-0.2c0.5-0.1,0.9-0.2,1.4-0.4
|
||||
c0.2-0.1,0.5-0.1,0.7-0.2c0.2,0,0.5-0.1,0.7-0.1c0.2,0,0.5-0.1,0.7-0.1l0.4-0.1l0.4-0.1c0.2,0,0.5-0.1,0.7-0.1
|
||||
c0.3,0,0.5-0.1,0.8-0.1c0.2,0,0.6-0.1,0.8-0.1c0.2,0,0.3,0,0.5-0.1h0.3h0.2h0.2c0.3,0,0.5,0,0.8-0.1h0.4c0,0,0.1,0,0,0h0.1h0.2
|
||||
c0.2,0,0.5,0,0.7,0c0.9,0,1.8,0,2.7,0c1.8,0.1,3.6,0.3,5.3,0.6c3.4,0.6,6.7,1.7,9.6,3.2c2.9,1.4,5.6,3.2,7.8,5.1
|
||||
c0.1,0.1,0.3,0.2,0.4,0.4c0.1,0.1,0.3,0.2,0.4,0.4c0.3,0.2,0.5,0.5,0.8,0.7c0.3,0.2,0.5,0.5,0.8,0.7c0.2,0.3,0.5,0.5,0.7,0.8
|
||||
c1,1,1.9,2.1,2.7,3.1c1.6,2.1,2.9,4.2,3.9,6.2c0.1,0.1,0.1,0.2,0.2,0.4c0.1,0.1,0.1,0.2,0.2,0.4s0.2,0.5,0.4,0.7
|
||||
c0.1,0.2,0.2,0.5,0.3,0.7c0.1,0.2,0.2,0.5,0.3,0.7c0.4,0.9,0.7,1.8,1,2.7c0.5,1.4,0.8,2.6,1.1,3.6c0.1,0.4,0.5,0.7,0.9,0.7
|
||||
c0.5,0,0.8-0.4,0.8-0.9C123,52.7,123,51.4,122.9,49.9z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.6 KiB |
BIN
public/templates/meilisearch.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
public/templates/metabase.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
public/templates/minio.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/templates/n8n.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
public/templates/nocodb.png
Normal file
|
After Width: | Height: | Size: 131 KiB |
BIN
public/templates/odoo.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
public/templates/phpmyadmin.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
public/templates/rocketchat.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/templates/uptime-kuma.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
public/templates/wordpress.png
Normal file
|
After Width: | Height: | Size: 192 KiB |
@@ -162,6 +162,7 @@ export const applicationRouter = createTRPCRouter({
|
||||
const jobData: DeploymentJob = {
|
||||
applicationId: input.applicationId,
|
||||
titleLog: "Rebuild deployment",
|
||||
descriptionLog: "",
|
||||
type: "redeploy",
|
||||
applicationType: "application",
|
||||
};
|
||||
@@ -294,6 +295,7 @@ export const applicationRouter = createTRPCRouter({
|
||||
const jobData: DeploymentJob = {
|
||||
applicationId: input.applicationId,
|
||||
titleLog: "Manual deployment",
|
||||
descriptionLog: "",
|
||||
type: "deploy",
|
||||
applicationType: "application",
|
||||
};
|
||||
|
||||
@@ -138,6 +138,7 @@ export const composeRouter = createTRPCRouter({
|
||||
titleLog: "Manual deployment",
|
||||
type: "deploy",
|
||||
applicationType: "compose",
|
||||
descriptionLog: "",
|
||||
};
|
||||
await myQueue.add(
|
||||
"deployments",
|
||||
@@ -156,6 +157,7 @@ export const composeRouter = createTRPCRouter({
|
||||
titleLog: "Rebuild deployment",
|
||||
type: "redeploy",
|
||||
applicationType: "compose",
|
||||
descriptionLog: "",
|
||||
};
|
||||
await myQueue.add(
|
||||
"deployments",
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
createDomain,
|
||||
findDomainById,
|
||||
findDomainsByApplicationId,
|
||||
generateDomain,
|
||||
generateWildcard,
|
||||
removeDomainById,
|
||||
updateDomainById,
|
||||
} from "../services/domain";
|
||||
@@ -35,6 +37,16 @@ export const domainRouter = createTRPCRouter({
|
||||
.query(async ({ input }) => {
|
||||
return await findDomainsByApplicationId(input.applicationId);
|
||||
}),
|
||||
generateDomain: protectedProcedure
|
||||
.input(apiFindDomainByApplication)
|
||||
.mutation(async ({ input }) => {
|
||||
return generateDomain(input);
|
||||
}),
|
||||
generateWildcard: protectedProcedure
|
||||
.input(apiFindDomainByApplication)
|
||||
.mutation(async ({ input }) => {
|
||||
return generateWildcard(input);
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.input(apiUpdateDomain)
|
||||
.mutation(async ({ input }) => {
|
||||
|
||||
@@ -248,7 +248,7 @@ export const settingsRouter = createTRPCRouter({
|
||||
getOpenApiDocument: protectedProcedure.query(
|
||||
async ({ ctx }): Promise<unknown> => {
|
||||
const protocol = ctx.req.headers["x-forwarded-proto"];
|
||||
const url = `${protocol}://${ctx.req.headers.host}/api/trpc`;
|
||||
const url = `${protocol}://${ctx.req.headers.host}/api`;
|
||||
const openApiDocument = generateOpenApiDocument(appRouter, {
|
||||
title: "tRPC OpenAPI",
|
||||
version: "1.0.0",
|
||||
|
||||
@@ -130,15 +130,18 @@ export const updateApplicationStatus = async (
|
||||
export const deployApplication = async ({
|
||||
applicationId,
|
||||
titleLog = "Manual deployment",
|
||||
descriptionLog = "",
|
||||
}: {
|
||||
applicationId: string;
|
||||
titleLog: string;
|
||||
descriptionLog: string;
|
||||
}) => {
|
||||
const application = await findApplicationById(applicationId);
|
||||
const admin = await findAdmin();
|
||||
const deployment = await createDeployment({
|
||||
applicationId: applicationId,
|
||||
title: titleLog,
|
||||
description: descriptionLog,
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -173,14 +176,17 @@ export const deployApplication = async ({
|
||||
export const rebuildApplication = async ({
|
||||
applicationId,
|
||||
titleLog = "Rebuild deployment",
|
||||
descriptionLog = "",
|
||||
}: {
|
||||
applicationId: string;
|
||||
titleLog: string;
|
||||
descriptionLog: string;
|
||||
}) => {
|
||||
const application = await findApplicationById(applicationId);
|
||||
const deployment = await createDeployment({
|
||||
applicationId: applicationId,
|
||||
title: titleLog,
|
||||
description: descriptionLog,
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
@@ -134,15 +134,18 @@ export const updateCompose = async (
|
||||
export const deployCompose = async ({
|
||||
composeId,
|
||||
titleLog = "Manual deployment",
|
||||
descriptionLog = "",
|
||||
}: {
|
||||
composeId: string;
|
||||
titleLog: string;
|
||||
descriptionLog: string;
|
||||
}) => {
|
||||
const compose = await findComposeById(composeId);
|
||||
const admin = await findAdmin();
|
||||
const deployment = await createDeploymentCompose({
|
||||
composeId: composeId,
|
||||
title: titleLog,
|
||||
description: descriptionLog,
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -170,14 +173,17 @@ export const deployCompose = async ({
|
||||
export const rebuildCompose = async ({
|
||||
composeId,
|
||||
titleLog = "Rebuild deployment",
|
||||
descriptionLog = "",
|
||||
}: {
|
||||
composeId: string;
|
||||
titleLog: string;
|
||||
descriptionLog: string;
|
||||
}) => {
|
||||
const compose = await findComposeById(composeId);
|
||||
const deployment = await createDeploymentCompose({
|
||||
composeId: composeId,
|
||||
title: titleLog,
|
||||
description: descriptionLog,
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
@@ -60,6 +60,7 @@ export const createDeployment = async (
|
||||
title: deployment.title || "Deployment",
|
||||
status: "running",
|
||||
logPath: logFilePath,
|
||||
description: deployment.description || "",
|
||||
})
|
||||
.returning();
|
||||
if (deploymentCreate.length === 0 || !deploymentCreate[0]) {
|
||||
@@ -100,6 +101,7 @@ export const createDeploymentCompose = async (
|
||||
.values({
|
||||
composeId: deployment.composeId,
|
||||
title: deployment.title || "Deployment",
|
||||
description: deployment.description || "",
|
||||
status: "running",
|
||||
logPath: logFilePath,
|
||||
})
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import { db } from "@/server/db";
|
||||
import { type apiCreateDomain, domains } from "@/server/db/schema";
|
||||
import {
|
||||
type apiCreateDomain,
|
||||
type apiFindDomainByApplication,
|
||||
domains,
|
||||
} from "@/server/db/schema";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { findApplicationById } from "./application";
|
||||
import { manageDomain } from "@/server/utils/traefik/domain";
|
||||
import { findAdmin } from "./admin";
|
||||
import { generateRandomDomain } from "@/templates/utils";
|
||||
|
||||
export type Domain = typeof domains.$inferSelect;
|
||||
|
||||
@@ -29,6 +35,58 @@ export const createDomain = async (input: typeof apiCreateDomain._type) => {
|
||||
await manageDomain(application, domain);
|
||||
});
|
||||
};
|
||||
|
||||
export const generateDomain = async (
|
||||
input: typeof apiFindDomainByApplication._type,
|
||||
) => {
|
||||
const application = await findApplicationById(input.applicationId);
|
||||
const admin = await findAdmin();
|
||||
const domain = await createDomain({
|
||||
applicationId: application.applicationId,
|
||||
host: generateRandomDomain({
|
||||
serverIp: admin.serverIp || "",
|
||||
projectName: application.appName,
|
||||
}),
|
||||
port: 3000,
|
||||
certificateType: "none",
|
||||
https: false,
|
||||
path: "/",
|
||||
});
|
||||
|
||||
return domain;
|
||||
};
|
||||
|
||||
export const generateWildcard = async (
|
||||
input: typeof apiFindDomainByApplication._type,
|
||||
) => {
|
||||
const application = await findApplicationById(input.applicationId);
|
||||
const admin = await findAdmin();
|
||||
|
||||
if (!admin.host) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "We need a host to generate a wildcard domain",
|
||||
});
|
||||
}
|
||||
const domain = await createDomain({
|
||||
applicationId: application.applicationId,
|
||||
host: generateWildcardDomain(application.appName, admin.host || ""),
|
||||
port: 3000,
|
||||
certificateType: "none",
|
||||
https: false,
|
||||
path: "/",
|
||||
});
|
||||
|
||||
return domain;
|
||||
};
|
||||
|
||||
export const generateWildcardDomain = (
|
||||
appName: string,
|
||||
serverDomain: string,
|
||||
) => {
|
||||
return `${appName}-${serverDomain}`;
|
||||
};
|
||||
|
||||
export const findDomainById = async (domainId: string) => {
|
||||
const domain = await db.query.domains.findFirst({
|
||||
where: eq(domains.domainId, domainId),
|
||||
|
||||
@@ -18,6 +18,7 @@ export const deployments = pgTable("deployment", {
|
||||
.primaryKey()
|
||||
.$defaultFn(() => nanoid()),
|
||||
title: text("title").notNull(),
|
||||
description: text("description"),
|
||||
status: deploymentStatus("status").default("running"),
|
||||
logPath: text("logPath").notNull(),
|
||||
applicationId: text("applicationId").references(
|
||||
@@ -49,6 +50,7 @@ const schema = createInsertSchema(deployments, {
|
||||
logPath: z.string().min(1),
|
||||
applicationId: z.string(),
|
||||
composeId: z.string(),
|
||||
description: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiCreateDeployment = schema
|
||||
@@ -57,6 +59,7 @@ export const apiCreateDeployment = schema
|
||||
status: true,
|
||||
logPath: true,
|
||||
applicationId: true,
|
||||
description: true,
|
||||
})
|
||||
.extend({
|
||||
applicationId: z.string().min(1),
|
||||
@@ -68,6 +71,7 @@ export const apiCreateDeploymentCompose = schema
|
||||
status: true,
|
||||
logPath: true,
|
||||
composeId: true,
|
||||
description: true,
|
||||
})
|
||||
.extend({
|
||||
composeId: z.string().min(1),
|
||||
|
||||
@@ -10,12 +10,14 @@ type DeployJob =
|
||||
| {
|
||||
applicationId: string;
|
||||
titleLog: string;
|
||||
descriptionLog: string;
|
||||
type: "deploy" | "redeploy";
|
||||
applicationType: "application";
|
||||
}
|
||||
| {
|
||||
composeId: string;
|
||||
titleLog: string;
|
||||
descriptionLog: string;
|
||||
type: "deploy" | "redeploy";
|
||||
applicationType: "compose";
|
||||
};
|
||||
@@ -31,11 +33,13 @@ export const deploymentWorker = new Worker(
|
||||
await rebuildApplication({
|
||||
applicationId: job.data.applicationId,
|
||||
titleLog: job.data.titleLog,
|
||||
descriptionLog: job.data.descriptionLog,
|
||||
});
|
||||
} else if (job.data.type === "deploy") {
|
||||
await deployApplication({
|
||||
applicationId: job.data.applicationId,
|
||||
titleLog: job.data.titleLog,
|
||||
descriptionLog: job.data.descriptionLog,
|
||||
});
|
||||
}
|
||||
} else if (job.data.applicationType === "compose") {
|
||||
@@ -43,11 +47,13 @@ export const deploymentWorker = new Worker(
|
||||
await deployCompose({
|
||||
composeId: job.data.composeId,
|
||||
titleLog: job.data.titleLog,
|
||||
descriptionLog: job.data.descriptionLog,
|
||||
});
|
||||
} else if (job.data.type === "redeploy") {
|
||||
await rebuildCompose({
|
||||
composeId: job.data.composeId,
|
||||
titleLog: job.data.titleLog,
|
||||
descriptionLog: job.data.descriptionLog,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,9 @@ export const buildMariadb = async (mariadb: MariadbWithMounts) => {
|
||||
Resources: {
|
||||
...resources,
|
||||
},
|
||||
Placement: {
|
||||
Constraints: ["node.role==manager"],
|
||||
},
|
||||
},
|
||||
Mode: {
|
||||
Replicated: {
|
||||
|
||||
@@ -63,6 +63,9 @@ export const buildMongo = async (mongo: MongoWithMounts) => {
|
||||
Resources: {
|
||||
...resources,
|
||||
},
|
||||
Placement: {
|
||||
Constraints: ["node.role==manager"],
|
||||
},
|
||||
},
|
||||
Mode: {
|
||||
Replicated: {
|
||||
|
||||
@@ -69,6 +69,9 @@ export const buildMysql = async (mysql: MysqlWithMounts) => {
|
||||
Resources: {
|
||||
...resources,
|
||||
},
|
||||
Placement: {
|
||||
Constraints: ["node.role==manager"],
|
||||
},
|
||||
},
|
||||
Mode: {
|
||||
Replicated: {
|
||||
|
||||
@@ -63,6 +63,9 @@ export const buildPostgres = async (postgres: PostgresWithMounts) => {
|
||||
Resources: {
|
||||
...resources,
|
||||
},
|
||||
Placement: {
|
||||
Constraints: ["node.role==manager"],
|
||||
},
|
||||
},
|
||||
Mode: {
|
||||
Replicated: {
|
||||
|
||||
@@ -50,17 +50,19 @@ export const buildRedis = async (redis: RedisWithMounts) => {
|
||||
Image: dockerImage,
|
||||
Env: envVariables,
|
||||
Mounts: [...volumesMount, ...bindsMount, ...filesMount],
|
||||
...(command
|
||||
? {
|
||||
Command: ["/bin/sh"],
|
||||
Args: ["-c", command],
|
||||
}
|
||||
: {}),
|
||||
Command: ["/bin/sh"],
|
||||
Args: [
|
||||
"-c",
|
||||
command ? command : `redis-server --requirepass ${databasePassword}`,
|
||||
],
|
||||
},
|
||||
Networks: [{ Target: "dokploy-network" }],
|
||||
Resources: {
|
||||
...resources,
|
||||
},
|
||||
Placement: {
|
||||
Constraints: ["node.role==manager"],
|
||||
},
|
||||
},
|
||||
Mode: {
|
||||
Replicated: {
|
||||
|
||||
18
templates/appsmith/docker-compose.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
appsmith:
|
||||
image: index.docker.io/appsmith/appsmith-ee:v1.29
|
||||
networks:
|
||||
- dokploy-network
|
||||
ports:
|
||||
- ${APP_SMITH_PORT}
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.${HASH}.rule=Host(`${APP_SMITH_HOST}`)"
|
||||
- "traefik.http.services.${HASH}.loadbalancer.server.port=${APP_SMITH_PORT}"
|
||||
volumes:
|
||||
- ./stacks:/appsmith-stacks
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
20
templates/appsmith/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const envs = [
|
||||
`APP_SMITH_HOST=${randomDomain}`,
|
||||
"APP_SMITH_PORT=80",
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
22
templates/baserow/docker-compose.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
baserow:
|
||||
image: baserow/baserow:1.25.2
|
||||
networks:
|
||||
- dokploy-network
|
||||
environment:
|
||||
BASEROW_PUBLIC_URL: "http://${BASEROW_HOST}"
|
||||
ports:
|
||||
- ${BASEROW_PORT}
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.${HASH}.rule=Host(`${BASEROW_HOST}`)
|
||||
- traefik.http.services.${HASH}.loadbalancer.server.port=${BASEROW_PORT}
|
||||
volumes:
|
||||
- baserow_data:/baserow/data
|
||||
volumes:
|
||||
baserow_data:
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
20
templates/baserow/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const envs = [
|
||||
`BASEROW_HOST=${randomDomain}`,
|
||||
"BASEROW_PORT=80",
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
@@ -17,8 +16,8 @@ services:
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
- NEXTAUTH_SECRET=asklmdaklsmdklasmdklasd
|
||||
- CALENDSO_ENCRYPTION_KEY=asklmdaklsmdklasmdklasd
|
||||
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
|
||||
- CALENDSO_ENCRYPTION_KEY=${CALENDSO_ENCRYPTION_KEY}
|
||||
- DATABASE_URL=postgres://postgres:password@postgres:5432/db
|
||||
- NEXT_PUBLIC_WEBAPP_URL=http://${CALCOM_HOST}
|
||||
- NEXTAUTH_URL=http://${CALCOM_HOST}/api/auth
|
||||
|
||||
@@ -3,16 +3,22 @@ import {
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
generateBase64,
|
||||
} from "../utils";
|
||||
|
||||
// https://cal.com/
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const calcomEncryptionKey = generateBase64(32);
|
||||
const nextAuthSecret = generateBase64(32);
|
||||
|
||||
const envs = [
|
||||
`CALCOM_HOST=${randomDomain}`,
|
||||
"CALCOM_PORT=3000",
|
||||
`HASH=${mainServiceHash}`,
|
||||
`NEXTAUTH_SECRET=${nextAuthSecret}`,
|
||||
`CALENDSO_ENCRYPTION_KEY=${calcomEncryptionKey}`,
|
||||
];
|
||||
|
||||
return {
|
||||
|
||||
56
templates/directus/docker-compose.yml
Normal file
@@ -0,0 +1,56 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
database:
|
||||
image: postgis/postgis:13-master
|
||||
volumes:
|
||||
- directus:/var/lib/postgresql/data
|
||||
networks:
|
||||
- dokploy-network
|
||||
environment:
|
||||
POSTGRES_USER: "directus"
|
||||
POSTGRES_PASSWORD: "directus"
|
||||
POSTGRES_DB: "directus"
|
||||
|
||||
cache:
|
||||
image: redis:6
|
||||
networks:
|
||||
- dokploy-network
|
||||
|
||||
directus:
|
||||
image: directus/directus:10.12.1
|
||||
networks:
|
||||
- dokploy-network
|
||||
ports:
|
||||
- 8055
|
||||
volumes:
|
||||
- ./uploads:/directus/uploads
|
||||
- ./extensions:/directus/extensions
|
||||
depends_on:
|
||||
- cache
|
||||
- database
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.${HASH}.rule=Host(`${DIRECTUS_HOST}`)
|
||||
- traefik.http.services.${HASH}.loadbalancer.server.port=${DIRECTUS_PORT}
|
||||
environment:
|
||||
SECRET: "replace-with-secure-random-value"
|
||||
|
||||
DB_CLIENT: "pg"
|
||||
DB_HOST: "database"
|
||||
DB_PORT: "5432"
|
||||
DB_DATABASE: "directus"
|
||||
DB_USER: "directus"
|
||||
DB_PASSWORD: "directus"
|
||||
|
||||
CACHE_ENABLED: "true"
|
||||
CACHE_AUTO_PURGE: "true"
|
||||
CACHE_STORE: "redis"
|
||||
REDIS: "redis://cache:6379"
|
||||
|
||||
ADMIN_EMAIL: "admin@example.com"
|
||||
ADMIN_PASSWORD: "d1r3ctu5"
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
volumes:
|
||||
directus:
|
||||
20
templates/directus/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const envs = [
|
||||
`DIRECTUS_HOST=${randomDomain}`,
|
||||
"DIRECTUS_PORT=8055",
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
53
templates/documenso/docker-compose.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
networks:
|
||||
- dokploy-network
|
||||
volumes:
|
||||
- documenso-data:/var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_USER=documenso
|
||||
- POSTGRES_PASSWORD=password
|
||||
- POSTGRES_DB=documenso
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U documenso"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
|
||||
documenso:
|
||||
image: documenso/documenso:1.5.6-rc.2
|
||||
networks:
|
||||
- dokploy-network
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- PORT=${DOCUMENSO_PORT}
|
||||
- NEXTAUTH_URL=http://${DOCUMENSO_HOST}
|
||||
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
|
||||
- NEXT_PRIVATE_ENCRYPTION_KEY=${NEXT_PRIVATE_ENCRYPTION_KEY}
|
||||
- NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY=${NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY}
|
||||
- NEXT_PUBLIC_WEBAPP_URL=http://${DOCUMENSO_HOST}
|
||||
- NEXT_PRIVATE_DATABASE_URL=postgres://documenso:password@postgres:5432/documenso
|
||||
- NEXT_PRIVATE_DIRECT_DATABASE_URL=postgres://documenso:password@postgres:5432/documenso
|
||||
- NEXT_PUBLIC_UPLOAD_TRANSPORT=database
|
||||
- NEXT_PRIVATE_SMTP_TRANSPORT=smtp-auth
|
||||
- NEXT_PRIVATE_SIGNING_LOCAL_FILE_PATH=/opt/documenso/cert.p12
|
||||
ports:
|
||||
- ${DOCUMENSO_PORT}
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.${HASH}.rule=Host(`${DOCUMENSO_HOST}`)"
|
||||
- "traefik.http.services.${HASH}.loadbalancer.server.port=${DOCUMENSO_PORT}"
|
||||
volumes:
|
||||
- /opt/documenso/cert.p12:/opt/documenso/cert.p12
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
documenso-data:
|
||||
30
templates/documenso/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
generateBase64,
|
||||
generatePassword,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
|
||||
const nextAuthSecret = generateBase64(32);
|
||||
const documensoEncryptionKey = generatePassword(32);
|
||||
const documensoSecondaryEncryptionKey = generatePassword(64);
|
||||
|
||||
const envs = [
|
||||
`DOCUMENSO_HOST=${randomDomain}`,
|
||||
"DOCUMENSO_PORT=3000",
|
||||
`HASH=${mainServiceHash}`,
|
||||
`NEXTAUTH_SECRET=${nextAuthSecret}`,
|
||||
`NEXT_PRIVATE_ENCRYPTION_KEY=${documensoEncryptionKey}`,
|
||||
`NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY=${documensoSecondaryEncryptionKey}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
17
templates/excalidraw/docker-compose.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
excalidraw:
|
||||
networks:
|
||||
- dokploy-network
|
||||
image: excalidraw/excalidraw:latest
|
||||
ports:
|
||||
- ${EXCALIDRAW_PORT}
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.${HASH}.rule=Host(`${EXCALIDRAW_HOST}`)
|
||||
- traefik.http.services.${HASH}.loadbalancer.server.port=${EXCALIDRAW_PORT}
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
20
templates/excalidraw/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const envs = [
|
||||
`EXCALIDRAW_HOST=${randomDomain}`,
|
||||
"EXCALIDRAW_PORT=80",
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
41
templates/ghost/docker-compose.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
|
||||
ghost:
|
||||
image: ghost:5-alpine
|
||||
restart: always
|
||||
networks:
|
||||
- dokploy-network
|
||||
ports:
|
||||
- ${GHOST_PORT}
|
||||
environment:
|
||||
database__client: mysql
|
||||
database__connection__host: db
|
||||
database__connection__user: root
|
||||
database__connection__password: example
|
||||
database__connection__database: ghost
|
||||
url: http://${GHOST_HOST}
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.${HASH}.rule=Host(`${GHOST_HOST}`)
|
||||
- traefik.http.services.${HASH}.loadbalancer.server.port=${GHOST_PORT}
|
||||
volumes:
|
||||
- ghost:/var/lib/ghost/content
|
||||
|
||||
db:
|
||||
image: mysql:8.0
|
||||
restart: always
|
||||
networks:
|
||||
- dokploy-network
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: example
|
||||
volumes:
|
||||
- db:/var/lib/mysql
|
||||
|
||||
volumes:
|
||||
ghost:
|
||||
db:
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
20
templates/ghost/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const envs = [
|
||||
`GHOST_HOST=${randomDomain}`,
|
||||
"GHOST_PORT=2368",
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
71
templates/glitchtip/docker-compose.yml
Normal file
@@ -0,0 +1,71 @@
|
||||
x-environment:
|
||||
&default-environment
|
||||
DATABASE_URL: postgres://postgres:postgres@postgres:5432/postgres
|
||||
SECRET_KEY: ${SECRET_KEY}
|
||||
PORT: ${GLITCHTIP_PORT}
|
||||
EMAIL_URL: consolemail://
|
||||
GLITCHTIP_DOMAIN: http://${GLITCHTIP_HOST}
|
||||
DEFAULT_FROM_EMAIL: email@glitchtip.com
|
||||
CELERY_WORKER_AUTOSCALE: "1,3"
|
||||
CELERY_WORKER_MAX_TASKS_PER_CHILD: "10000"
|
||||
|
||||
x-depends_on:
|
||||
&default-depends_on
|
||||
- postgres
|
||||
- redis
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
environment:
|
||||
POSTGRES_HOST_AUTH_METHOD: "trust"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- pg-data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- dokploy-network
|
||||
redis:
|
||||
image: redis
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- dokploy-network
|
||||
web:
|
||||
image: glitchtip/glitchtip:v4.0
|
||||
depends_on: *default-depends_on
|
||||
ports:
|
||||
- ${GLITCHTIP_PORT}
|
||||
environment: *default-environment
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- uploads:/code/uploads
|
||||
networks:
|
||||
- dokploy-network
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.${HASH}.rule=Host(`${GLITCHTIP_HOST}`)
|
||||
- traefik.http.services.${HASH}.loadbalancer.server.port=${GLITCHTIP_PORT}
|
||||
worker:
|
||||
image: glitchtip/glitchtip:v4.0
|
||||
command: ./bin/run-celery-with-beat.sh
|
||||
depends_on: *default-depends_on
|
||||
environment: *default-environment
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- uploads:/code/uploads
|
||||
networks:
|
||||
- dokploy-network
|
||||
migrate:
|
||||
image: glitchtip/glitchtip:v4.0
|
||||
depends_on: *default-depends_on
|
||||
command: "./manage.py migrate"
|
||||
environment: *default-environment
|
||||
networks:
|
||||
- dokploy-network
|
||||
|
||||
volumes:
|
||||
pg-data:
|
||||
uploads:
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
23
templates/glitchtip/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
generateBase64,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const secretKey = generateBase64(32);
|
||||
const envs = [
|
||||
`GLITCHTIP_HOST=${randomDomain}`,
|
||||
"GLITCHTIP_PORT=8000",
|
||||
`SECRET_KEY=${secretKey}`,
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
20
templates/grafana/docker-compose.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
grafana:
|
||||
networks:
|
||||
- dokploy-network
|
||||
image: grafana/grafana-enterprise:9.5.20
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- ${GRAFANA_PORT}
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.${HASH}.rule=Host(`${GRAFANA_HOST}`)
|
||||
- traefik.http.services.${HASH}.loadbalancer.server.port=${GRAFANA_PORT}
|
||||
volumes:
|
||||
- grafana-storage:/var/lib/grafana
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
volumes:
|
||||
grafana-storage: {}
|
||||
20
templates/grafana/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const envs = [
|
||||
`GRAFANA_HOST=${randomDomain}`,
|
||||
"GRAFANA_PORT=3000",
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
25
templates/meilisearch/docker-compose.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
meilisearch:
|
||||
networks:
|
||||
- dokploy-network
|
||||
image: getmeili/meilisearch:v1.8.3
|
||||
ports:
|
||||
- ${MEILISEARCH_PORT}
|
||||
volumes:
|
||||
- meili_data:/meili_data
|
||||
environment:
|
||||
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY}
|
||||
MEILI_ENV: ${MEILI_ENV}
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.${HASH}.rule=Host(`${MEILISEARCH_HOST}`)
|
||||
- traefik.http.services.${HASH}.loadbalancer.server.port=${MEILISEARCH_PORT}
|
||||
|
||||
volumes:
|
||||
meili_data:
|
||||
driver: local
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
24
templates/meilisearch/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
generateBase64,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const masterKey = generateBase64(32);
|
||||
const envs = [
|
||||
`MEILISEARCH_HOST=${randomDomain}`,
|
||||
"MEILISEARCH_PORT=7700",
|
||||
"MEILI_ENV=development",
|
||||
`MEILI_MASTER_KEY=${masterKey}`,
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
38
templates/metabase/docker-compose.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
metabase:
|
||||
image: metabase/metabase:v0.50.8
|
||||
volumes:
|
||||
- /dev/urandom:/dev/random:ro
|
||||
ports:
|
||||
- ${METABASE_PORT}
|
||||
environment:
|
||||
MB_DB_TYPE: postgres
|
||||
MB_DB_DBNAME: metabaseappdb
|
||||
MB_DB_PORT: 5432
|
||||
MB_DB_USER: metabase
|
||||
MB_DB_PASS: mysecretpassword
|
||||
MB_DB_HOST: postgres
|
||||
networks:
|
||||
- dokploy-network
|
||||
healthcheck:
|
||||
test: curl --fail -I http://localhost:3000/api/health || exit 1
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.${HASH}.rule=Host(`${METABASE_HOST}`)
|
||||
- traefik.http.services.${HASH}.loadbalancer.server.port=${METABASE_PORT}
|
||||
postgres:
|
||||
image: postgres:14
|
||||
environment:
|
||||
POSTGRES_USER: metabase
|
||||
POSTGRES_DB: metabaseappdb
|
||||
POSTGRES_PASSWORD: mysecretpassword
|
||||
networks:
|
||||
- dokploy-network
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
20
templates/metabase/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const envs = [
|
||||
`METABASE_HOST=${randomDomain}`,
|
||||
"METABASE_PORT=3000",
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
31
templates/minio/docker-compose.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
minio:
|
||||
image: minio/minio
|
||||
ports:
|
||||
- ${MINIO_API_PORT}
|
||||
- ${MINIO_DASHBOARD_PORT}
|
||||
volumes:
|
||||
- minio-data:/data
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin123
|
||||
command: server /data --console-address ":9001"
|
||||
networks:
|
||||
- dokploy-network
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.${HASH}.service=${HASH}
|
||||
- traefik.http.routers.${HASH}.rule=Host(`${MINIO_DASHBOARD_HOST}`)
|
||||
- traefik.http.services.${HASH}.loadbalancer.server.port=${MINIO_DASHBOARD_PORT}
|
||||
# API router and service
|
||||
- traefik.http.routers.${HASH}-api.service=${HASH}-api
|
||||
- traefik.http.routers.${HASH}-api.rule=Host(`${MINIO_API_HOST}`)
|
||||
- traefik.http.services.${HASH}-api.loadbalancer.server.port=${MINIO_API_PORT}
|
||||
|
||||
volumes:
|
||||
minio-data:
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
23
templates/minio/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const apiDomain = generateRandomDomain(schema);
|
||||
const envs = [
|
||||
`MINIO_DASHBOARD_HOST=${randomDomain}`,
|
||||
"MINIO_DASHBOARD_PORT=9001",
|
||||
`MINIO_API_HOST=${apiDomain}`,
|
||||
"MINIO_API_PORT=9000",
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
29
templates/n8n/docker-compose.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
n8n:
|
||||
image: docker.n8n.io/n8nio/n8n:1.48.1
|
||||
restart: always
|
||||
networks:
|
||||
- dokploy-network
|
||||
ports:
|
||||
- ${N8N_PORT}
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.${HASH}.rule=Host(`${N8N_HOST}`)
|
||||
- traefik.http.services.${HASH}.loadbalancer.server.port=${N8N_PORT}
|
||||
environment:
|
||||
- N8N_HOST=${N8N_HOST}
|
||||
- N8N_PORT=5678
|
||||
- N8N_PROTOCOL=http
|
||||
- NODE_ENV=production
|
||||
- WEBHOOK_URL=https://${N8N_HOST}/
|
||||
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
|
||||
- N8N_SECURE_COOKIE=false
|
||||
volumes:
|
||||
- n8n_data:/home/node/.n8n
|
||||
|
||||
volumes:
|
||||
n8n_data:
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
21
templates/n8n/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const envs = [
|
||||
`N8N_HOST=${randomDomain}`,
|
||||
"N8N_PORT=5678",
|
||||
`HASH=${mainServiceHash}`,
|
||||
"GENERIC_TIMEZONE=Europe/Berlin",
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
44
templates/nocodb/docker-compose.yml
Normal file
@@ -0,0 +1,44 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
nocodb:
|
||||
image: nocodb/nocodb:0.251.0
|
||||
restart: always
|
||||
networks:
|
||||
- dokploy-network
|
||||
ports:
|
||||
- ${NOCODB_PORT}
|
||||
environment:
|
||||
NC_DB : "pg://root_db?u=postgres&p=password&d=root_db"
|
||||
PORT : ${NOCODB_PORT}
|
||||
NC_REDIS_URL: ${NC_REDIS_URL}
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.${HASH}.rule=Host(`${NOCODB_HOST}`)
|
||||
- traefik.http.services.${HASH}.loadbalancer.server.port=${NOCODB_PORT}
|
||||
volumes:
|
||||
- nc_data:/usr/app/data
|
||||
|
||||
root_db:
|
||||
image: postgres:14.7
|
||||
restart: always
|
||||
networks:
|
||||
- dokploy-network
|
||||
environment:
|
||||
POSTGRES_DB: root_db
|
||||
POSTGRES_PASSWORD: password
|
||||
POSTGRES_USER: postgres
|
||||
healthcheck:
|
||||
interval: 10s
|
||||
retries: 10
|
||||
test: "pg_isready -U \"$$POSTGRES_USER\" -d \"$$POSTGRES_DB\""
|
||||
timeout: 2s
|
||||
volumes:
|
||||
- "db_data:/var/lib/postgresql/data"
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
db_data: {}
|
||||
nc_data: {}
|
||||
28
templates/nocodb/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
// EXAMPLE
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
generateBase64,
|
||||
type Template,
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const secretBase = generateBase64(64);
|
||||
const toptKeyBase = generateBase64(32);
|
||||
|
||||
const envs = [
|
||||
`NOCODB_HOST=${randomDomain}`,
|
||||
"NOCODB_PORT=8000",
|
||||
`NC_AUTH_JWT_SECRET=${secretBase}`,
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
42
templates/odoo/docker-compose.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
web:
|
||||
image: odoo:16.0
|
||||
networks:
|
||||
- dokploy-network
|
||||
depends_on:
|
||||
- db
|
||||
ports:
|
||||
- ${ODOO_PORT}
|
||||
environment:
|
||||
- HOST=db
|
||||
- USER=odoo
|
||||
- PASSWORD=odoo
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.${HASH}.rule=Host(`${ODOO_HOST}`)"
|
||||
- "traefik.http.services.${HASH}.loadbalancer.server.port=${ODOO_PORT}"
|
||||
volumes:
|
||||
- odoo-web-data:/var/lib/odoo
|
||||
- ./config:/etc/odoo
|
||||
- ./addons:/mnt/extra-addons
|
||||
|
||||
db:
|
||||
image: postgres:13
|
||||
networks:
|
||||
- dokploy-network
|
||||
environment:
|
||||
- POSTGRES_DB=postgres
|
||||
- POSTGRES_USER=odoo
|
||||
- POSTGRES_PASSWORD=odoo
|
||||
volumes:
|
||||
- odoo-db-data:/var/lib/postgresql/data
|
||||
|
||||
volumes:
|
||||
odoo-web-data:
|
||||
odoo-db-data:
|
||||
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
20
templates/odoo/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const envs = [
|
||||
`ODOO_HOST=${randomDomain}`,
|
||||
"ODOO_PORT=8069",
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
40
templates/phpmyadmin/docker-compose.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
db:
|
||||
image: mysql:5.7
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
|
||||
MYSQL_DATABASE: tu_base_de_datos
|
||||
MYSQL_USER: ${MYSQL_USER}
|
||||
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
networks:
|
||||
- dokploy-network
|
||||
|
||||
phpmyadmin:
|
||||
image: phpmyadmin/phpmyadmin:5.2.1
|
||||
environment:
|
||||
PMA_HOST: db
|
||||
PMA_USER: ${MYSQL_USER}
|
||||
PMA_PASSWORD: ${MYSQL_PASSWORD}
|
||||
PMA_ARBITRARY: 1
|
||||
ports:
|
||||
- ${PHPMYADMIN_PORT}
|
||||
depends_on:
|
||||
- db
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.${HASH}.rule=Host(`${PHPMYADMIN_HOST}`)
|
||||
- traefik.http.services.${HASH}.loadbalancer.server.port=${PHPMYADMIN_PORT}
|
||||
networks:
|
||||
- dokploy-network
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
27
templates/phpmyadmin/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
generatePassword,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const rootPassword = generatePassword(32);
|
||||
const password = generatePassword(32);
|
||||
const envs = [
|
||||
`PHPMYADMIN_HOST=${randomDomain}`,
|
||||
"PHPMYADMIN_PORT=80",
|
||||
`HASH=${mainServiceHash}`,
|
||||
`MYSQL_ROOT_PASSWORD=${rootPassword}`,
|
||||
"MYSQL_DATABASE=mysql",
|
||||
"MYSQL_USER=phpmyadmin",
|
||||
`MYSQL_PASSWORD=${password}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
plausible_db:
|
||||
# Plausible v2.1.0 was tested against PostgreSQL versions 15 and 16
|
||||
# https://github.com/plausible/analytics/blob/v2.1.0/.github/workflows/elixir.yml#L21-L32
|
||||
image: postgres:16-alpine
|
||||
restart: always
|
||||
networks:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: "3.7"
|
||||
version: "3.8"
|
||||
services:
|
||||
pocketbase:
|
||||
image: spectado/pocketbase:0.22.12
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
// https://pocketbase.io/docs/
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
|
||||
48
templates/rocketchat/docker-compose.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
rocketchat:
|
||||
image: registry.rocket.chat/rocketchat/rocket.chat:6.9.2
|
||||
restart: always
|
||||
environment:
|
||||
MONGO_URL: "mongodb://mongodb:27017/rocketchat?replicaSet=rs0"
|
||||
MONGO_OPLOG_URL: "mongodb://mongodb:27017/local?replicaSet=rs0"
|
||||
ROOT_URL: ${ROOT_URL:-http://${ROCKETCHAT_HOST}:${ROCKETCHAT_PORT}}
|
||||
PORT: ${ROCKETCHAT_PORT}
|
||||
DEPLOY_METHOD: docker
|
||||
DEPLOY_PLATFORM:
|
||||
REG_TOKEN:
|
||||
depends_on:
|
||||
- mongodb
|
||||
ports:
|
||||
- ${ROCKETCHAT_PORT}
|
||||
networks:
|
||||
- dokploy-network
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.${HASH}.rule=Host(`${ROCKETCHAT_HOST}`)
|
||||
- traefik.http.services.${HASH}.loadbalancer.server.port=${ROCKETCHAT_PORT}
|
||||
|
||||
mongodb:
|
||||
image: docker.io/bitnami/mongodb:5.0
|
||||
restart: always
|
||||
volumes:
|
||||
- mongodb_data:/bitnami/mongodb
|
||||
environment:
|
||||
MONGODB_REPLICA_SET_MODE: primary
|
||||
MONGODB_REPLICA_SET_NAME: rs0
|
||||
MONGODB_PORT_NUMBER: 27017
|
||||
MONGODB_INITIAL_PRIMARY_HOST: mongodb
|
||||
MONGODB_INITIAL_PRIMARY_PORT_NUMBER: 27017
|
||||
MONGODB_ADVERTISED_HOSTNAME: mongodb
|
||||
MONGODB_ENABLE_JOURNAL: true
|
||||
ALLOW_EMPTY_PASSWORD: yes
|
||||
networks:
|
||||
- dokploy-network
|
||||
|
||||
volumes:
|
||||
mongodb_data: { driver: local }
|
||||
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
21
templates/rocketchat/index.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
|
||||
const envs = [
|
||||
`ROCKETCHAT_HOST=${randomDomain}`,
|
||||
"ROCKETCHAT_PORT=3000",
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
@@ -34,7 +34,7 @@ export const templates: TemplateData[] = [
|
||||
{
|
||||
id: "calcom",
|
||||
name: "Calcom",
|
||||
version: "2.7.6",
|
||||
version: "v2.7.6",
|
||||
description:
|
||||
"Calcom is a open source alternative to Calendly that allows to create scheduling and booking services.",
|
||||
|
||||
@@ -47,4 +47,275 @@ export const templates: TemplateData[] = [
|
||||
tags: ["scheduling", "booking"],
|
||||
load: () => import("./calcom/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "grafana",
|
||||
name: "Grafana",
|
||||
version: "9.5.20",
|
||||
description:
|
||||
"Grafana is an open source platform for data visualization and monitoring.",
|
||||
logo: "grafana.svg",
|
||||
links: {
|
||||
github: "https://github.com/grafana/grafana",
|
||||
website: "https://grafana.com/",
|
||||
docs: "https://grafana.com/docs/",
|
||||
},
|
||||
tags: ["monitoring"],
|
||||
load: () => import("./grafana/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "directus",
|
||||
name: "Directus",
|
||||
version: "10.12.1",
|
||||
description:
|
||||
"Directus is an open source headless CMS that provides an API-first solution for building custom backends.",
|
||||
logo: "directus.jpg",
|
||||
links: {
|
||||
github: "https://github.com/directus/directus",
|
||||
website: "https://directus.io/",
|
||||
docs: "https://docs.directus.io/",
|
||||
},
|
||||
tags: ["cms"],
|
||||
load: () => import("./directus/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "baserow",
|
||||
name: "Baserow",
|
||||
version: "1.25.2",
|
||||
description:
|
||||
"Baserow is an open source database management tool that allows you to create and manage databases.",
|
||||
logo: "baserow.webp",
|
||||
links: {
|
||||
github: "https://github.com/Baserow/baserow",
|
||||
website: "https://baserow.io/",
|
||||
docs: "https://baserow.io/docs/index",
|
||||
},
|
||||
tags: ["database"],
|
||||
load: () => import("./baserow/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "ghost",
|
||||
name: "Ghost",
|
||||
version: "5.0.0",
|
||||
description:
|
||||
"Ghost is a free and open source, professional publishing platform built on a modern Node.js technology stack.",
|
||||
logo: "ghost.jpeg",
|
||||
links: {
|
||||
github: "https://github.com/TryGhost/Ghost",
|
||||
website: "https://ghost.org/",
|
||||
docs: "https://ghost.org/docs/",
|
||||
},
|
||||
tags: ["cms"],
|
||||
load: () => import("./ghost/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "uptime-kuma",
|
||||
name: "Uptime Kuma",
|
||||
version: "1.21.4",
|
||||
description:
|
||||
"Uptime Kuma is a free and open source monitoring tool that allows you to monitor your websites and applications.",
|
||||
logo: "uptime-kuma.png",
|
||||
links: {
|
||||
github: "https://github.com/louislam/uptime-kuma",
|
||||
website: "https://uptime.kuma.pet/",
|
||||
docs: "https://github.com/louislam/uptime-kuma/wiki",
|
||||
},
|
||||
tags: ["monitoring"],
|
||||
load: () => import("./uptime-kuma/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "n8n",
|
||||
name: "n8n",
|
||||
version: "1.48.1",
|
||||
description:
|
||||
"n8n is an open source low-code platform for automating workflows and integrations.",
|
||||
logo: "n8n.png",
|
||||
links: {
|
||||
github: "https://github.com/n8n-io/n8n",
|
||||
website: "https://n8n.io/",
|
||||
docs: "https://docs.n8n.io/",
|
||||
},
|
||||
tags: ["automation"],
|
||||
load: () => import("./n8n/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "wordpress",
|
||||
name: "Wordpress",
|
||||
version: "5.8.3",
|
||||
description:
|
||||
"Wordpress is a free and open source content management system (CMS) for publishing and managing websites.",
|
||||
logo: "wordpress.png",
|
||||
links: {
|
||||
github: "https://github.com/WordPress/WordPress",
|
||||
website: "https://wordpress.org/",
|
||||
docs: "https://wordpress.org/documentation/",
|
||||
},
|
||||
tags: ["cms"],
|
||||
load: () => import("./wordpress/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "odoo",
|
||||
name: "Odoo",
|
||||
version: "16.0",
|
||||
description:
|
||||
"Odoo is a free and open source business management software that helps you manage your company's operations.",
|
||||
logo: "odoo.png",
|
||||
links: {
|
||||
github: "https://github.com/odoo/odoo",
|
||||
website: "https://odoo.com/",
|
||||
docs: "https://www.odoo.com/documentation/",
|
||||
},
|
||||
tags: ["cms"],
|
||||
load: () => import("./odoo/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "appsmith",
|
||||
name: "Appsmith",
|
||||
version: "v1.29",
|
||||
description:
|
||||
"Appsmith is a free and open source platform for building internal tools and applications.",
|
||||
logo: "appsmith.png",
|
||||
links: {
|
||||
github: "https://github.com/appsmithorg/appsmith",
|
||||
website: "https://appsmith.com/",
|
||||
docs: "https://docs.appsmith.com/",
|
||||
},
|
||||
tags: ["cms"],
|
||||
load: () => import("./appsmith/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "excalidraw",
|
||||
name: "Excalidraw",
|
||||
version: "latest",
|
||||
description:
|
||||
"Excalidraw is a free and open source online diagramming tool that lets you easily create and share beautiful diagrams.",
|
||||
logo: "excalidraw.jpg",
|
||||
links: {
|
||||
github: "https://github.com/excalidraw/excalidraw",
|
||||
website: "https://excalidraw.com/",
|
||||
docs: "https://docs.excalidraw.com/",
|
||||
},
|
||||
tags: ["drawing"],
|
||||
load: () => import("./excalidraw/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "documenso",
|
||||
name: "Documenso",
|
||||
version: "v1.5.6",
|
||||
description:
|
||||
"Documenso is the open source alternative to DocuSign for signing documents digitally",
|
||||
|
||||
links: {
|
||||
github: "https://github.com/documenso/documenso",
|
||||
website: "https://documenso.com/",
|
||||
docs: "https://documenso.com/docs",
|
||||
},
|
||||
logo: "documenso.png",
|
||||
tags: ["document-signing"],
|
||||
load: () => import("./documenso/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "nocodb",
|
||||
name: "NocoDB",
|
||||
version: "0.251.0",
|
||||
description:
|
||||
"NocoDB is an opensource Airtable alternative that turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart spreadsheet.",
|
||||
|
||||
links: {
|
||||
github: "https://github.com/nocodb/nocodb",
|
||||
website: "https://nocodb.com/",
|
||||
docs: "https://docs.nocodb.com/",
|
||||
},
|
||||
logo: "nocodb.png",
|
||||
tags: ["database", "spreadsheet", "low-code", "nocode"],
|
||||
load: () => import("./nocodb/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "meilisearch",
|
||||
name: "Meilisearch",
|
||||
version: "v1.8.3",
|
||||
description:
|
||||
"Meilisearch is a free and open-source search engine that allows you to easily add search functionality to your web applications.",
|
||||
logo: "meilisearch.png",
|
||||
links: {
|
||||
github: "https://github.com/meilisearch/meilisearch",
|
||||
website: "https://www.meilisearch.com/",
|
||||
docs: "https://docs.meilisearch.com/",
|
||||
},
|
||||
tags: ["search"],
|
||||
load: () => import("./meilisearch/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "phpmyadmin",
|
||||
name: "Phpmyadmin",
|
||||
version: "5.2.1",
|
||||
description:
|
||||
"Phpmyadmin is a free and open-source web interface for MySQL and MariaDB that allows you to manage your databases.",
|
||||
logo: "phpmyadmin.png",
|
||||
links: {
|
||||
github: "https://github.com/phpmyadmin/phpmyadmin",
|
||||
website: "https://www.phpmyadmin.net/",
|
||||
docs: "https://www.phpmyadmin.net/docs/",
|
||||
},
|
||||
tags: ["database"],
|
||||
load: () => import("./phpmyadmin/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "rocketchat",
|
||||
name: "Rocketchat",
|
||||
version: "6.9.2",
|
||||
description:
|
||||
"Rocket.Chat is a free and open-source web chat platform that allows you to build and manage your own chat applications.",
|
||||
logo: "rocketchat.png",
|
||||
links: {
|
||||
github: "https://github.com/RocketChat/Rocket.Chat",
|
||||
website: "https://rocket.chat/",
|
||||
docs: "https://rocket.chat/docs/",
|
||||
},
|
||||
tags: ["chat"],
|
||||
load: () => import("./rocketchat/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "minio",
|
||||
name: "Minio",
|
||||
description:
|
||||
"Minio is an open source object storage server compatible with Amazon S3 cloud storage service.",
|
||||
logo: "minio.png",
|
||||
version: "latest",
|
||||
links: {
|
||||
github: "https://github.com/minio/minio",
|
||||
website: "https://minio.io/",
|
||||
docs: "https://docs.minio.io/",
|
||||
},
|
||||
tags: ["storage"],
|
||||
load: () => import("./minio/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "metabase",
|
||||
name: "Metabase",
|
||||
version: "v0.50.8",
|
||||
description:
|
||||
"Metabase is an open source business intelligence tool that allows you to ask questions and visualize data.",
|
||||
logo: "metabase.png",
|
||||
links: {
|
||||
github: "https://github.com/metabase/metabase",
|
||||
website: "https://www.metabase.com/",
|
||||
docs: "https://www.metabase.com/docs/",
|
||||
},
|
||||
tags: ["database", "dashboard"],
|
||||
load: () => import("./metabase/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "glitchtip",
|
||||
name: "Glitchtip",
|
||||
version: "v4.0",
|
||||
description: "Glitchtip is simple, open source error tracking",
|
||||
logo: "glitchtip.png",
|
||||
links: {
|
||||
github: "https://github.com/glitchtip/glitchtip",
|
||||
website: "https://glitchtip.com/",
|
||||
docs: "https://glitchtip.com/documentation",
|
||||
},
|
||||
tags: ["hosting"],
|
||||
load: () => import("./glitchtip/index").then((m) => m.generate),
|
||||
},
|
||||
];
|
||||
|
||||
21
templates/uptime-kuma/docker-compose.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
uptime-kuma:
|
||||
networks:
|
||||
- dokploy-network
|
||||
image: louislam/uptime-kuma:1
|
||||
restart: always
|
||||
ports:
|
||||
- ${UPTIME_KUMA_PORT}
|
||||
volumes:
|
||||
- uptime-kuma-data:/app/data
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.${HASH}.rule=Host(`${UPTIME_KUMA_HOST}`)
|
||||
- traefik.http.services.${HASH}.loadbalancer.server.port=${UPTIME_KUMA_PORT}
|
||||
|
||||
volumes:
|
||||
uptime-kuma-data:
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
20
templates/uptime-kuma/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const envs = [
|
||||
`UPTIME_KUMA_HOST=${randomDomain}`,
|
||||
"UPTIME_KUMA_PORT=3001",
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||
39
templates/wordpress/docker-compose.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
wordpress:
|
||||
image: wordpress:5.8.3
|
||||
networks:
|
||||
- dokploy-network
|
||||
ports:
|
||||
- ${WORDPRESS_PORT}
|
||||
environment:
|
||||
WORDPRESS_DB_HOST: db
|
||||
WORDPRESS_DB_USER: exampleuser
|
||||
WORDPRESS_DB_PASSWORD: examplepass
|
||||
WORDPRESS_DB_NAME: exampledb
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.${HASH}.rule=Host(`${WORDPRESS_HOST}`)"
|
||||
- "traefik.http.services.${HASH}.loadbalancer.server.port=${WORDPRESS_PORT}"
|
||||
volumes:
|
||||
- wordpress_data:/var/www/html
|
||||
|
||||
db:
|
||||
image: mysql:5.7.34
|
||||
networks:
|
||||
- dokploy-network
|
||||
environment:
|
||||
MYSQL_DATABASE: exampledb
|
||||
MYSQL_USER: exampleuser
|
||||
MYSQL_PASSWORD: examplepass
|
||||
MYSQL_ROOT_PASSWORD: rootpass
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
|
||||
volumes:
|
||||
wordpress_data:
|
||||
db_data:
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
20
templates/wordpress/index.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
generateHash,
|
||||
generateRandomDomain,
|
||||
type Template,
|
||||
type Schema,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const mainServiceHash = generateHash(schema.projectName);
|
||||
const randomDomain = generateRandomDomain(schema);
|
||||
const envs = [
|
||||
`WORDPRESS_HOST=${randomDomain}`,
|
||||
"WORDPRESS_PORT=80",
|
||||
`HASH=${mainServiceHash}`,
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
};
|
||||
}
|
||||