Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be934065d9 | ||
|
|
82fc9897d2 | ||
|
|
8162dcfb71 | ||
|
|
b6ab653ef3 | ||
|
|
17e9a1a497 | ||
|
|
46f7d43595 | ||
|
|
59bb59ee24 | ||
|
|
d081d477ef | ||
|
|
cf73f1f764 | ||
|
|
0e433a3d36 | ||
|
|
b08a2f54f0 | ||
|
|
29ffdf2c71 | ||
|
|
58b185f6dd | ||
|
|
819de5a32e | ||
|
|
e0fe4e4995 | ||
|
|
461d30fc26 | ||
|
|
3fee18d06e | ||
|
|
046923aeb3 | ||
|
|
4646b7d0ec | ||
|
|
a02d51e504 | ||
|
|
9728c49edd | ||
|
|
ed5b01c78d | ||
|
|
000091cfb9 | ||
|
|
2774701895 | ||
|
|
e238dd8510 | ||
|
|
56b565b512 | ||
|
|
237106428b | ||
|
|
06e1e1ba76 | ||
|
|
9eb4c3e77d | ||
|
|
f3d8351208 | ||
|
|
1e1409e651 | ||
|
|
ceaa32fd00 | ||
|
|
f466e697dd | ||
|
|
476057663b | ||
|
|
1b1d0597fe | ||
|
|
dfa73a3d7c | ||
|
|
2a24e1d7e8 | ||
|
|
5cd624c7ea | ||
|
|
34b12a0315 | ||
|
|
e9e6064eb6 | ||
|
|
6b7712e35f | ||
|
|
84aac40410 | ||
|
|
e40a0fdc50 | ||
|
|
e3677995b9 | ||
|
|
0468c25bf8 | ||
|
|
89c7e96df0 | ||
|
|
8af5afbb6c | ||
|
|
ef9f16a3c3 | ||
|
|
d15b6387a3 | ||
|
|
2e9e39dcf5 | ||
|
|
d901b02e92 | ||
|
|
766a25ccad | ||
|
|
fae97b1817 | ||
|
|
b2cc5e58a3 | ||
|
|
2f8b89c8a6 | ||
|
|
2da650610c | ||
|
|
980024c9d2 | ||
|
|
1b56a6b400 | ||
|
|
996d449f0f | ||
|
|
3b197f3624 | ||
|
|
bba7d0c828 | ||
|
|
a04b69d0fd | ||
|
|
de02a00ce9 | ||
|
|
25803f371c | ||
|
|
a4eb5c07e6 | ||
|
|
bad11f13f5 | ||
|
|
02d52d63b9 | ||
|
|
2821e43cdd | ||
|
|
adea440931 | ||
|
|
bbef99c3c2 | ||
|
|
527c01e7dc | ||
|
|
2a5a67e63c | ||
|
|
15051a1bc2 | ||
|
|
b7d45341bc | ||
|
|
1695c7cc81 | ||
|
|
fbec26fc31 | ||
|
|
4431f5601a | ||
|
|
bf48aa03a2 | ||
|
|
059c21c990 | ||
|
|
d833623ebf | ||
|
|
8b855d7ee4 | ||
|
|
706cde4ffd | ||
|
|
a39a7a276d | ||
|
|
0327334fcd | ||
|
|
3e0d4ebbd6 |
BIN
.github/sponsors/mandarin.png
vendored
Normal file
|
After Width: | Height: | Size: 22 KiB |
@@ -39,6 +39,8 @@ Dokploy includes multiple features to make your life easier.
|
||||
|
||||
To get started, run the following command on a VPS:
|
||||
|
||||
Want to skip the installation process? [Try the Dokploy Cloud](https://app.dokploy.com).
|
||||
|
||||
```bash
|
||||
curl -sSL https://dokploy.com/install.sh | sh
|
||||
```
|
||||
@@ -60,12 +62,15 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
|
||||
### Hero Sponsors 🎖
|
||||
|
||||
<div style="display: flex; align-items: center; gap: 20px;">
|
||||
<a href="https://www.hostinger.com/vps-hosting?ref=dokploy" target="_blank" style="display: inline-block;">
|
||||
<a href="https://www.hostinger.com/vps-hosting?ref=dokploy" target="_blank" style="display: inline-block; margin-right: 10px;">
|
||||
<img src=".github/sponsors/hostinger.jpg" alt="Hostinger" height="50"/>
|
||||
</a>
|
||||
<a href="https://www.lxaer.com/?ref=dokploy" target="_blank" style="display: inline-block;">
|
||||
<a href="https://www.lxaer.com/?ref=dokploy" target="_blank" style="display: inline-block; margin-right: 10px;">
|
||||
<img src=".github/sponsors/lxaer.png" alt="LX Aer" height="50"/>
|
||||
</a>
|
||||
<a href="https://mandarin3d.com/?ref=dokploy" target="_blank" style="display: inline-block;">
|
||||
<img src=".github/sponsors/mandarin.png" alt="Mandarin" height="50"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
### Premium Supporters 🥇
|
||||
|
||||
@@ -39,6 +39,24 @@ describe("createDomainLabels", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("should add the path prefix if is different than / empty", async () => {
|
||||
const labels = await createDomainLabels(
|
||||
appName,
|
||||
{
|
||||
...baseDomain,
|
||||
path: "/hello",
|
||||
},
|
||||
"websecure",
|
||||
);
|
||||
|
||||
expect(labels).toEqual([
|
||||
"traefik.http.routers.test-app-1-websecure.rule=Host(`example.com`) && PathPrefix(`/hello`)",
|
||||
"traefik.http.routers.test-app-1-websecure.entrypoints=websecure",
|
||||
"traefik.http.services.test-app-1-websecure.loadbalancer.server.port=8080",
|
||||
"traefik.http.routers.test-app-1-websecure.service=test-app-1-websecure",
|
||||
]);
|
||||
});
|
||||
|
||||
it("should add redirect middleware for https on web entrypoint", async () => {
|
||||
const httpsBaseDomain = { ...baseDomain, https: true };
|
||||
const labels = await createDomainLabels(appName, httpsBaseDomain, "web");
|
||||
|
||||
@@ -140,7 +140,7 @@ export const UpdatePort = ({ portId }: Props) => {
|
||||
<FormItem>
|
||||
<FormLabel>Target Port</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="1-65535" {...field} />
|
||||
<NumberInput placeholder="1-65535" {...field} />
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
|
||||
@@ -119,7 +119,7 @@ export const UpdateVolume = ({
|
||||
} else if (typeForm === "file") {
|
||||
form.reset({
|
||||
content: data.content || "",
|
||||
mountPath: data.mountPath,
|
||||
mountPath: "/",
|
||||
filePath: data.filePath || "",
|
||||
type: "file",
|
||||
});
|
||||
@@ -296,15 +296,13 @@ export const UpdateVolume = ({
|
||||
)}
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<DialogClose>
|
||||
<Button
|
||||
isLoading={isLoading}
|
||||
form="hook-form-update-volume"
|
||||
type="submit"
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<Button
|
||||
isLoading={isLoading}
|
||||
// form="hook-form-update-volume"
|
||||
type="submit"
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -53,7 +53,7 @@ export const AddGithubProvider = () => {
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="secondary" className="flex items-center space-x-1">
|
||||
<GithubIcon />
|
||||
<GithubIcon className="text-current fill-current" />
|
||||
<span>Github</span>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
@@ -23,29 +23,27 @@ export const ToggleDockerCleanup = ({ serverId }: Props) => {
|
||||
const enabled = data?.enableDockerCleanup || server?.enableDockerCleanup;
|
||||
|
||||
const { mutateAsync } = api.settings.updateDockerCleanup.useMutation();
|
||||
|
||||
const handleToggle = async (checked: boolean) => {
|
||||
try {
|
||||
await mutateAsync({
|
||||
enableDockerCleanup: checked,
|
||||
serverId: serverId,
|
||||
});
|
||||
if (serverId) {
|
||||
await refetchServer();
|
||||
} else {
|
||||
await refetch();
|
||||
}
|
||||
toast.success("Docker Cleanup updated");
|
||||
} catch (error) {
|
||||
toast.error("Docker Cleanup Error");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-4">
|
||||
<Switch
|
||||
checked={enabled}
|
||||
onCheckedChange={async (e) => {
|
||||
await mutateAsync({
|
||||
enableDockerCleanup: e,
|
||||
serverId: serverId,
|
||||
})
|
||||
.then(async () => {
|
||||
toast.success("Docker Cleanup Enabled");
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Docker Cleanup Error");
|
||||
});
|
||||
|
||||
if (serverId) {
|
||||
refetchServer();
|
||||
} else {
|
||||
refetch();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Switch checked={!!enabled} onCheckedChange={handleToggle} />
|
||||
<Label className="text-primary">Daily Docker Cleanup</Label>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -161,29 +161,27 @@ export const GitlabIcon = ({ className }: Props) => {
|
||||
return (
|
||||
<svg
|
||||
aria-label="gitlab"
|
||||
height="14"
|
||||
viewBox="0 0 24 22"
|
||||
width="14"
|
||||
className={cn("fill-white text-white", className)}
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={cn("text-white", className)}
|
||||
>
|
||||
<path
|
||||
d="M1.279 8.29L.044 12.294c-.117.367 0 .78.325 1.014l11.323 8.23-.009-.012-.03-.039L1.279 8.29zM22.992 13.308a.905.905 0 00.325-1.014L22.085 8.29 11.693 21.52l11.299-8.212z"
|
||||
fill="currentColor"
|
||||
d="m13.767 5.854-.02-.05L11.842.83a.5.5 0 0 0-.493-.312.5.5 0 0 0-.287.107.5.5 0 0 0-.169.257L9.607 4.819h-5.21L3.11.883A.5.5 0 0 0 2.162.83L.252 5.801l-.018.05a3.54 3.54 0 0 0 1.173 4.09l.007.005.017.012 2.903 2.174L5.77 13.22l.875.66a.59.59 0 0 0 .711 0l.875-.66 1.436-1.087 2.92-2.187.008-.006a3.54 3.54 0 0 0 1.172-4.085"
|
||||
fill="#E24329"
|
||||
/>
|
||||
<path
|
||||
d="M1.279 8.29l10.374 13.197.03.039.01-.006L22.085 8.29H1.28z"
|
||||
fill="currentColor"
|
||||
opacity="0.4"
|
||||
d="m13.767 5.854-.02-.05a6.4 6.4 0 0 0-2.562 1.152L7 10.12l2.666 2.015 2.92-2.187.007-.006a3.54 3.54 0 0 0 1.174-4.088"
|
||||
fill="#FC6D26"
|
||||
/>
|
||||
<path
|
||||
d="M15.982 8.29l-4.299 13.236-.004.011.014-.017L22.085 8.29h-6.103zM7.376 8.29H1.279l10.374 13.197L7.376 8.29z"
|
||||
fill="currentColor"
|
||||
opacity="0.6"
|
||||
d="m4.334 12.135 1.436 1.087.875.66a.59.59 0 0 0 .711 0l.875-.66 1.436-1.087S8.425 11.195 7 10.12c-1.425 1.075-2.666 2.015-2.666 2.015"
|
||||
fill="#FCA326"
|
||||
/>
|
||||
<path
|
||||
d="M18.582.308l-2.6 7.982h6.103L19.48.308c-.133-.41-.764-.41-.897 0zM1.279 8.29L3.88.308c.133-.41.764-.41.897 0l2.6 7.982H1.279z"
|
||||
fill="currentColor"
|
||||
opacity="0.4"
|
||||
d="M2.814 6.956A6.4 6.4 0 0 0 .253 5.8l-.02.05a3.54 3.54 0 0 0 1.174 4.09l.007.005.017.012 2.903 2.174L7 10.117z"
|
||||
fill="#FC6D26"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -200,7 +198,7 @@ export const GithubIcon = ({ className }: Props) => {
|
||||
>
|
||||
<path
|
||||
d="M7 .175c-3.872 0-7 3.128-7 7 0 3.084 2.013 5.71 4.79 6.65.35.066.482-.153.482-.328v-1.181c-1.947.415-2.363-.941-2.363-.941-.328-.81-.787-1.028-.787-1.028-.634-.438.044-.416.044-.416.7.044 1.071.722 1.071.722.635 1.072 1.641.766 2.035.59.066-.459.24-.765.437-.94-1.553-.175-3.193-.787-3.193-3.456 0-.766.262-1.378.721-1.881-.065-.175-.306-.897.066-1.86 0 0 .59-.197 1.925.722a6.754 6.754 0 0 1 1.75-.24c.59 0 1.203.087 1.75.24 1.335-.897 1.925-.722 1.925-.722.372.963.131 1.685.066 1.86.46.48.722 1.115.722 1.88 0 2.691-1.641 3.282-3.194 3.457.24.219.481.634.481 1.29v1.926c0 .197.131.415.481.328C11.988 12.884 14 10.259 14 7.175c0-3.872-3.128-7-7-7z"
|
||||
fill="#fff"
|
||||
fill="currentColor"
|
||||
fillRule="nonzero"
|
||||
/>
|
||||
</svg>
|
||||
@@ -209,30 +207,34 @@ export const GithubIcon = ({ className }: Props) => {
|
||||
|
||||
export const BitbucketIcon = ({ className }: Props) => {
|
||||
return (
|
||||
<svg height="14" viewBox="-2 -2 65 59" width="14" className={className}>
|
||||
<svg
|
||||
width="14"
|
||||
height="13"
|
||||
viewBox="0 0 14 13"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
d="M.454 0a.448.448 0 0 0-.448.52l1.903 11.556a.61.61 0 0 0 .597.509h9.132a.45.45 0 0 0 .448-.377L13.994.525a.448.448 0 0 0-.448-.52zM8.47 8.352H5.555l-.79-4.121h4.411z"
|
||||
fill="#2684FF"
|
||||
/>
|
||||
<path
|
||||
d="M13.384 4.23H9.176L8.47 8.353H5.555L2.113 12.44c.11.095.248.147.393.148h9.134a.45.45 0 0 0 .448-.377z"
|
||||
fill="url(#a)"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="bitbucket-:R7aq37rqjt7rrrmpjtuj7l9qjtsr:"
|
||||
x1="104.953%"
|
||||
x2="46.569%"
|
||||
y1="21.921%"
|
||||
y2="75.234%"
|
||||
id="a"
|
||||
x1="14.357"
|
||||
y1="5.383"
|
||||
x2="7.402"
|
||||
y2="10.814"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="7%" stopColor="currentColor" stopOpacity=".4" />
|
||||
<stop offset="100%" stopColor="currentColor" />
|
||||
<stop offset=".18" stop-color="#0052CC" />
|
||||
<stop offset="1" stop-color="#2684FF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path
|
||||
d="M59.696 18.86h-18.77l-3.15 18.39h-13L9.426 55.47a2.71 2.71 0 001.75.66h40.74a2 2 0 002-1.68l5.78-35.59z"
|
||||
fill="url(#bitbucket-:R7aq37rqjt7rrrmpjtuj7l9qjtsr:)"
|
||||
fillRule="nonzero"
|
||||
transform="translate(-.026 .82)"
|
||||
/>
|
||||
<path
|
||||
d="M2 .82a2 2 0 00-2 2.32l8.49 51.54a2.7 2.7 0 00.91 1.61 2.71 2.71 0 001.75.66l15.76-18.88H24.7l-3.47-18.39h38.44l2.7-16.53a2 2 0 00-2-2.32L2 .82z"
|
||||
fill="currentColor"
|
||||
fillRule="nonzero"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dokploy",
|
||||
"version": "v0.10.7",
|
||||
"version": "v0.11.2",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
@@ -11,7 +11,7 @@
|
||||
"build-next": "next build",
|
||||
"setup": "tsx -r dotenv/config setup.ts && sleep 5 && pnpm run migration:run",
|
||||
"reset-password": "node -r dotenv/config dist/reset-password.mjs",
|
||||
"dev": "TURBOPACK=1 tsx -r dotenv/config ./server/server.ts --project tsconfig.server.json ",
|
||||
"dev": "tsx -r dotenv/config ./server/server.ts --project tsconfig.server.json ",
|
||||
"studio": "drizzle-kit studio --config ./server/db/drizzle.config.ts",
|
||||
"migration:generate": "drizzle-kit generate --config ./server/db/drizzle.config.ts",
|
||||
"migration:run": "tsx -r dotenv/config migration.ts",
|
||||
|
||||
@@ -88,7 +88,6 @@ export default async function handler(
|
||||
.update(admins)
|
||||
.set({
|
||||
stripeSubscriptionId: newSubscription.id,
|
||||
serversQuantity: 0,
|
||||
stripeCustomerId: newSubscription.customer as string,
|
||||
})
|
||||
.where(eq(admins.stripeCustomerId, newSubscription.customer as string))
|
||||
@@ -121,12 +120,6 @@ export default async function handler(
|
||||
}
|
||||
case "customer.subscription.updated": {
|
||||
const newSubscription = event.data.object as Stripe.Subscription;
|
||||
await db
|
||||
.update(admins)
|
||||
.set({
|
||||
serversQuantity: newSubscription?.items?.data?.[0]?.quantity ?? 0,
|
||||
})
|
||||
.where(eq(admins.stripeCustomerId, newSubscription.customer as string));
|
||||
|
||||
const admin = await findAdminByStripeCustomerId(
|
||||
newSubscription.customer as string,
|
||||
@@ -136,8 +129,27 @@ export default async function handler(
|
||||
return res.status(400).send("Webhook Error: Admin not found");
|
||||
}
|
||||
|
||||
const newServersQuantity = admin.serversQuantity;
|
||||
await updateServersBasedOnQuantity(admin.adminId, newServersQuantity);
|
||||
if (newSubscription.status === "active") {
|
||||
await db
|
||||
.update(admins)
|
||||
.set({
|
||||
serversQuantity: newSubscription?.items?.data?.[0]?.quantity ?? 0,
|
||||
})
|
||||
.where(
|
||||
eq(admins.stripeCustomerId, newSubscription.customer as string),
|
||||
);
|
||||
|
||||
const newServersQuantity = admin.serversQuantity;
|
||||
await updateServersBasedOnQuantity(admin.adminId, newServersQuantity);
|
||||
} else {
|
||||
await disableServers(admin.adminId);
|
||||
await db
|
||||
.update(admins)
|
||||
.set({ serversQuantity: 0 })
|
||||
.where(
|
||||
eq(admins.stripeCustomerId, newSubscription.customer as string),
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -148,6 +160,13 @@ export default async function handler(
|
||||
newInvoice.subscription as string,
|
||||
);
|
||||
|
||||
if (suscription.status !== "active") {
|
||||
console.log(
|
||||
`Skipping invoice.payment_succeeded for subscription ${suscription.id} with status ${suscription.status}`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
await db
|
||||
.update(admins)
|
||||
.set({
|
||||
@@ -168,22 +187,29 @@ export default async function handler(
|
||||
}
|
||||
case "invoice.payment_failed": {
|
||||
const newInvoice = event.data.object as Stripe.Invoice;
|
||||
await db
|
||||
.update(admins)
|
||||
.set({
|
||||
serversQuantity: 0,
|
||||
})
|
||||
.where(eq(admins.stripeCustomerId, newInvoice.customer as string));
|
||||
|
||||
const admin = await findAdminByStripeCustomerId(
|
||||
newInvoice.customer as string,
|
||||
const subscription = await stripe.subscriptions.retrieve(
|
||||
newInvoice.subscription as string,
|
||||
);
|
||||
|
||||
if (!admin) {
|
||||
return res.status(400).send("Webhook Error: Admin not found");
|
||||
if (subscription.status !== "active") {
|
||||
const admin = await findAdminByStripeCustomerId(
|
||||
newInvoice.customer as string,
|
||||
);
|
||||
|
||||
if (!admin) {
|
||||
return res.status(400).send("Webhook Error: Admin not found");
|
||||
}
|
||||
await db
|
||||
.update(admins)
|
||||
.set({
|
||||
serversQuantity: 0,
|
||||
})
|
||||
.where(eq(admins.stripeCustomerId, newInvoice.customer as string));
|
||||
|
||||
await disableServers(admin.adminId);
|
||||
}
|
||||
|
||||
await disableServers(admin.adminId);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ export default function Home({ IS_CLOUD }: Props) {
|
||||
) : (
|
||||
<Link
|
||||
className="hover:underline text-muted-foreground"
|
||||
href="https://docs.dokploy.com/docs/core/get-started/reset-password"
|
||||
href="https://docs.dokploy.com/docs/core/reset-password"
|
||||
target="_blank"
|
||||
>
|
||||
Lost your password?
|
||||
|
||||
1
apps/dokploy/public/templates/coder.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 341 75" fill="#fff" xmlns="http://www.w3.org/2000/svg"><path d="M176.753 32.758h-11.199q-.306-2.173-1.253-3.86a9.8 9.8 0 0 0-2.429-2.915q-1.482-1.202-3.426-1.841-1.917-.64-4.167-.64-4.066 0-7.083 2.02-3.017 1.995-4.679 5.83-1.662 3.81-1.662 9.256 0 5.6 1.662 9.409 1.687 3.81 4.705 5.753t6.98 1.943q2.224 0 4.116-.588 1.918-.588 3.401-1.713a9.9 9.9 0 0 0 2.454-2.787q.997-1.636 1.381-3.733l11.199.051q-.435 3.605-2.173 6.955-1.713 3.323-4.628 5.957-2.889 2.609-6.904 4.142-3.988 1.508-9.025 1.508-7.006 0-12.529-3.17-5.496-3.17-8.693-9.179-3.17-6.009-3.17-14.548 0-8.565 3.221-14.574t8.745-9.153q5.523-3.17 12.426-3.17 4.55 0 8.437 1.277 3.912 1.28 6.929 3.733 3.018 2.43 4.909 5.958 1.918 3.528 2.455 8.08m25.279 34.798q-5.958 0-10.304-2.532-4.321-2.556-6.674-7.108-2.352-4.576-2.352-10.61 0-6.086 2.352-10.637 2.353-4.575 6.674-7.108 4.346-2.556 10.304-2.556 5.956 0 10.278 2.556 4.347 2.532 6.699 7.108 2.352 4.551 2.352 10.637 0 6.034-2.352 10.61-2.352 4.552-6.699 7.108-4.322 2.532-10.278 2.532m.051-8.438q2.71 0 4.525-1.534 1.817-1.56 2.736-4.244.946-2.685.946-6.111t-.946-6.11q-.92-2.685-2.736-4.245-1.815-1.56-4.525-1.56-2.736 0-4.602 1.56-1.842 1.56-2.787 4.244-.921 2.685-.921 6.111t.921 6.11q.945 2.685 2.787 4.245 1.866 1.534 4.602 1.534m40.632 8.31q-4.474 0-8.105-2.301-3.605-2.327-5.727-6.827-2.097-4.526-2.097-11.097 0-6.75 2.174-11.224 2.173-4.5 5.778-6.724 3.63-2.25 7.952-2.25 3.298 0 5.497 1.125 2.224 1.099 3.579 2.76 1.381 1.638 2.097 3.223h.332V14.426h10.867V66.79h-10.739V60.5h-.46q-.767 1.636-2.173 3.247-1.381 1.586-3.605 2.633-2.199 1.05-5.37 1.049m3.452-8.668q2.634 0 4.449-1.432 1.841-1.456 2.812-4.065.997-2.609.997-6.11 0-3.503-.971-6.086-.972-2.583-2.813-3.989-1.84-1.406-4.474-1.406-2.684 0-4.526 1.457-1.84 1.458-2.787 4.04-.946 2.583-.946 5.983 0 3.426.946 6.06.972 2.608 2.787 4.09 1.841 1.458 4.526 1.458m45.548 8.796q-6.06 0-10.432-2.455-4.347-2.48-6.699-7.006-2.352-4.551-2.352-10.764 0-6.06 2.352-10.636 2.352-4.577 6.622-7.134 4.296-2.556 10.074-2.556 3.887 0 7.236 1.252 3.375 1.228 5.881 3.708 2.531 2.48 3.937 6.238 1.406 3.733 1.406 8.745v2.991h-33.162v-6.75h22.91q0-2.352-1.023-4.167a7.33 7.33 0 0 0-2.838-2.839q-1.79-1.047-4.168-1.048-2.48 0-4.398 1.15a8.07 8.07 0 0 0-2.966 3.043q-1.073 1.893-1.099 4.22v6.417q0 2.914 1.074 5.037 1.1 2.121 3.094 3.272t4.73 1.151q1.815 0 3.324-.511 1.508-.512 2.582-1.535t1.636-2.505l10.074.665q-.766 3.63-3.145 6.34-2.352 2.685-6.085 4.194-3.707 1.483-8.565 1.483m24.933-.767V27.517h10.56v6.852h.409q1.074-3.655 3.605-5.523 2.532-1.891 5.83-1.892.817 0 1.764.103.946.102 1.662.28v9.666q-.767-.23-2.122-.41-1.356-.178-2.48-.178-2.404 0-4.296 1.048a7.7 7.7 0 0 0-2.966 2.864q-1.074 1.84-1.074 4.244V66.79zM102.464 32.547c-2.093 0-3.487-1.227-3.487-3.745V14.336C98.977 5.102 95.172 0 85.344 0H80.78v9.751h1.395c3.868 0 5.706 2.131 5.706 5.941V28.48c0 5.553 1.649 7.814 5.263 8.976-3.614 1.098-5.263 3.423-5.263 8.976v9.493c0 2.648 0 5.231-.697 7.879-.697 2.454-1.839 4.779-3.424 6.78-.888 1.163-1.902 2.132-3.043 3.036v1.291h4.565c9.828 0 13.632-5.102 13.632-14.336V46.108c0-2.583 1.332-3.745 3.488-3.745H105v-9.751h-2.536zM71.395 14.726H57.319a.574.574 0 0 1-.571-.582v-1.097c0-.323.254-.582.57-.582h14.14c.317 0 .57.259.57.582v1.098c0 .322-.316.58-.633.58m2.409 13.949H63.533a.574.574 0 0 1-.571-.581v-1.098c0-.323.254-.582.57-.582h10.272c.317 0 .571.259.571.582v1.098c0 .258-.254.58-.57.58m4.057-6.973H57.319a.574.574 0 0 1-.571-.581V20.02c0-.323.254-.581.57-.581h20.48c.318 0 .571.258.571.58v1.099c0 .258-.19.58-.507.58m-36.838-3.81c1.394 0 2.79.13 4.12.452v-2.648c0-3.745 1.903-5.94 5.707-5.94h1.395V0h-4.565c-9.828 0-13.632 5.102-13.632 14.336v4.779c2.219-.775 4.565-1.227 6.974-1.227"/><path d="M82.174 53.017c-1.015-8.202-7.228-15.047-15.218-16.597-2.219-.452-4.438-.516-6.594-.129-.063 0-.063-.065-.127-.065C56.749 28.8 49.267 23.892 41.15 23.892s-15.534 4.779-19.085 12.205c-.063 0-.063.065-.127.065a18.9 18.9 0 0 0-6.847.452C7.228 38.551 1.268 45.267.19 53.404A17 17 0 0 0 0 55.858c0 2.454 1.649 4.714 4.058 5.037a4.78 4.78 0 0 0 5.516-4.843c0-.452 0-.969.064-1.42.507-4.134 3.614-7.621 7.672-8.59a9.2 9.2 0 0 1 3.74-.193c3.869.516 7.673-1.486 9.321-4.973 1.205-2.583 3.107-4.843 5.643-6.07 2.79-1.356 5.96-1.55 8.877-.517 3.044 1.098 5.326 3.423 6.721 6.329 1.459 2.841 2.156 4.843 5.263 5.23 1.268.194 4.819.13 6.15.065 2.6 0 5.2.904 7.038 2.777a9.9 9.9 0 0 1 2.473 4.714c.57 2.906-.127 5.812-1.839 8.008-1.204 1.55-2.853 2.712-4.692 3.229-.887.258-1.775.322-2.663.322H49.33c-2.79 0-5.01-2.26-5.01-5.101V40.94c0-.774-.633-1.42-1.394-1.42H40.96c-3.868.064-6.974 4.456-6.974 9.105V65.61c0 5.037 3.994 9.106 8.94 9.106l22.319-.065c5.072-.517 9.764-3.164 12.934-7.233 3.17-3.939 4.629-9.105 3.995-14.4"/></svg>
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
BIN
apps/dokploy/public/templates/docmost.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
39
apps/dokploy/public/templates/hi-events.svg
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
apps/dokploy/public/templates/infisical.jpg
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
apps/dokploy/public/templates/influxdb.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
apps/dokploy/public/templates/macos.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
19
apps/dokploy/public/templates/vaultwarden.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg width="276" height="280" viewBox="0 0 276 280" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#a)">
|
||||
<mask id="b" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-21" width="322" height="322">
|
||||
<path d="M-18.7-18.7h317.883v317.883H-18.699z" fill="#fff" stroke="#fff" stroke-width="2.671"/>
|
||||
<path d="M146.92 33.39a6.678 6.678 0 1 1-13.357 0 6.678 6.678 0 0 1 13.357 0Zm97.007 80.184a6.678 6.678 0 1 1-4.127-12.702 6.678 6.678 0 1 1 4.127 12.702Zm-46.282 117.038a6.678 6.678 0 1 1 10.805-7.851 6.678 6.678 0 1 1-10.805 7.851Zm-125.612-7.851a6.678 6.678 0 1 1 10.806 7.851 6.678 6.678 0 0 1-10.806-7.851ZM40.684 100.872a6.678 6.678 0 1 1-4.127 12.702 6.678 6.678 0 1 1 4.127-12.702Z" fill="#000" stroke="#fff" stroke-width="2.671"/>
|
||||
</mask>
|
||||
<g mask="url(#b)" stroke="#fff">
|
||||
<path d="m56.974 49.917 70.778 198.18h24.981l70.778-198.18H197.42l-44.687 127.124q-4.441 12.213-7.495 23.038-3.052 10.546-4.996 20.262-1.942-9.715-4.996-20.54-3.052-10.825-7.494-23.315L83.342 49.917z" fill="#fff" stroke-width="12.052"/>
|
||||
<path d="M140.242 255.107c-63.438 0-114.865-51.427-114.865-114.865S76.804 25.377 140.242 25.377c63.439 0 114.865 51.427 114.865 114.865s-51.426 114.865-114.865 114.865Z" stroke-width="24.042"/>
|
||||
<path d="M17.363 132.228v16.028l-13.356-8.014zm.798 24.127 3.127 15.719-14.663-5.254zm5.489 23.507 6.133 14.808-15.407-2.293zm9.97 21.985 8.905 13.326-15.558.757zm14.067 19.617 11.333 11.333-15.111 3.778zM65.31 237.96l13.327 8.904-14.084 6.654zm20.505 12.741 14.807 6.133-12.515 9.273zm22.595 8.496 15.72 3.126-10.466 11.537zm23.819 3.924h16.027l-8.014 13.357zm24.125-.798 15.72-3.126-5.254 14.663zm23.508-5.489 14.808-6.133-2.293 15.406zm21.985-9.969 13.326-8.905.757 15.558zm19.617-14.068 11.333-11.333 3.778 15.111zm16.496-17.624 8.904-13.326 6.654 14.083zm12.741-20.503 6.134-14.808 9.273 12.515zm8.495-22.596 3.127-15.719 11.536 10.465zm3.925-23.818v-16.028l13.357 8.014zm-.798-24.127-3.127-15.719 14.664 5.254zm-5.489-23.507L250.7 85.814l15.407 2.293zm-9.97-21.985-8.904-13.326 15.557-.757zM232.797 59.02l-11.333-11.333 15.111-3.778zm-17.624-16.496-13.327-8.904 14.084-6.654zM194.67 29.783l-14.808-6.133 12.515-9.273zm-22.596-8.495-15.72-3.127L166.82 6.624zm-23.818-3.925h-16.028l8.014-13.356zm-24.126.797-15.72 3.128 5.254-14.664zm-23.508 5.49-14.808 6.133 2.293-15.406zm-21.984 9.97L65.31 42.524l-.757-15.558zM59.02 47.687 47.688 59.02 43.91 43.91zM42.524 65.31 33.62 78.638l-6.653-14.083zM29.783 85.814l-6.133 14.808-9.273-12.515zm-8.496 22.596-3.127 15.719-11.536-10.465z" fill="#fff" stroke-width="8.014" stroke-linejoin="round"/>
|
||||
<path d="M121.543 28.048h37.398l-18.699 18.7zm-93.781 95.308 11.556-35.568 12.006 23.562zM89.425 242l-30.256-21.982 26.119-4.137zm131.891-21.982L191.061 242l4.136-26.119zm19.851-132.23 11.557 35.568-23.562-12.006z" fill="#fff" stroke-width="16.028" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path fill="#fff" d="M0 0h276v280H0z"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
BIN
apps/dokploy/public/templates/windows.png
Normal file
|
After Width: | Height: | Size: 247 KiB |
@@ -132,16 +132,20 @@ export const projectRouter = createTRPCRouter({
|
||||
if (accesedProjects.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const query = await db.query.projects.findMany({
|
||||
where: sql`${projects.projectId} IN (${sql.join(
|
||||
accesedProjects.map((projectId) => sql`${projectId}`),
|
||||
sql`, `,
|
||||
)})`,
|
||||
where: and(
|
||||
sql`${projects.projectId} IN (${sql.join(
|
||||
accesedProjects.map((projectId) => sql`${projectId}`),
|
||||
sql`, `,
|
||||
)})`,
|
||||
eq(projects.adminId, ctx.user.adminId),
|
||||
),
|
||||
with: {
|
||||
applications: {
|
||||
where: and(
|
||||
buildServiceFilter(applications.applicationId, accesedServices),
|
||||
eq(projects.adminId, ctx.user.adminId),
|
||||
where: buildServiceFilter(
|
||||
applications.applicationId,
|
||||
accesedServices,
|
||||
),
|
||||
with: { domains: true },
|
||||
},
|
||||
|
||||
@@ -242,7 +242,7 @@ export const settingsRouter = createTRPCRouter({
|
||||
await cleanUpUnusedImages(server.serverId);
|
||||
await cleanUpDockerBuilder(server.serverId);
|
||||
await cleanUpSystemPrune(server.serverId);
|
||||
await sendDockerCleanupNotifications();
|
||||
await sendDockerCleanupNotifications(server.adminId);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -278,7 +278,7 @@ export const settingsRouter = createTRPCRouter({
|
||||
await cleanUpUnusedImages();
|
||||
await cleanUpDockerBuilder();
|
||||
await cleanUpSystemPrune();
|
||||
await sendDockerCleanupNotifications();
|
||||
await sendDockerCleanupNotifications(admin.adminId);
|
||||
});
|
||||
} else {
|
||||
const currentJob = scheduledJobs["docker-cleanup"];
|
||||
|
||||
@@ -24,7 +24,7 @@ import { setupTerminalWebSocketServer } from "./wss/terminal";
|
||||
config({ path: ".env" });
|
||||
const PORT = Number.parseInt(process.env.PORT || "3000", 10);
|
||||
const dev = process.env.NODE_ENV !== "production";
|
||||
const app = next({ dev, turbopack: dev });
|
||||
const app = next({ dev });
|
||||
const handle = app.getRequestHandler();
|
||||
void app.prepare().then(async () => {
|
||||
try {
|
||||
|
||||
@@ -63,7 +63,6 @@ export const setupDockerContainerLogsWebSocketServer = (
|
||||
}
|
||||
stream
|
||||
.on("close", () => {
|
||||
console.log("Connection closed ✅ Container Logs");
|
||||
client.end();
|
||||
ws.close();
|
||||
})
|
||||
@@ -88,7 +87,6 @@ export const setupDockerContainerLogsWebSocketServer = (
|
||||
privateKey: server.sshKey?.privateKey,
|
||||
});
|
||||
ws.on("close", () => {
|
||||
console.log("Connection closed ✅, From Container Logs WS");
|
||||
client.end();
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -113,7 +113,6 @@ export const setupTerminalWebSocketServer = (
|
||||
});
|
||||
|
||||
ws.on("close", () => {
|
||||
console.log("Connection closed ✅");
|
||||
stream.end();
|
||||
});
|
||||
},
|
||||
|
||||
39
apps/dokploy/templates/coder/docker-compose.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
services:
|
||||
coder:
|
||||
image: ghcr.io/coder/coder:v2.15.3
|
||||
networks:
|
||||
- dokploy-network
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
group_add:
|
||||
- "998"
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- CODER_ACCESS_URL
|
||||
- CODER_HTTP_ADDRESS
|
||||
- CODER_PG_CONNECTION_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db/${POSTGRES_DB}?sslmode=disable
|
||||
|
||||
db:
|
||||
image: postgres:17
|
||||
networks:
|
||||
- dokploy-network
|
||||
environment:
|
||||
- POSTGRES_PASSWORD
|
||||
- POSTGRES_USER
|
||||
- POSTGRES_DB
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
"pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}",
|
||||
]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
volumes:
|
||||
- db_coder_data:/var/lib/postgresql/data
|
||||
|
||||
volumes:
|
||||
db_coder_data:
|
||||
30
apps/dokploy/templates/coder/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
type DomainSchema,
|
||||
type Schema,
|
||||
type Template,
|
||||
generateRandomDomain,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const domains: DomainSchema[] = [
|
||||
{
|
||||
host: generateRandomDomain(schema),
|
||||
port: 7080,
|
||||
serviceName: "coder",
|
||||
},
|
||||
];
|
||||
|
||||
const envs = [
|
||||
"CODER_ACCESS_URL=",
|
||||
"CODER_HTTP_ADDRESS=0.0.0.0:7080",
|
||||
"",
|
||||
"POSTGRES_DB=coder",
|
||||
"POSTGRES_USER=coder",
|
||||
"POSTGRES_PASSWORD=VERY_STRONG_PASSWORD",
|
||||
];
|
||||
|
||||
return {
|
||||
domains,
|
||||
envs,
|
||||
};
|
||||
}
|
||||
47
apps/dokploy/templates/docmost/docker-compose.yml
Normal file
@@ -0,0 +1,47 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
docmost:
|
||||
image: docmost/docmost:0.4.1
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
environment:
|
||||
- APP_URL
|
||||
- APP_SECRET
|
||||
- DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?schema=public
|
||||
- REDIS_URL=redis://redis:6379
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- dokploy-network
|
||||
volumes:
|
||||
- docmost:/app/data/storage
|
||||
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
- POSTGRES_DB
|
||||
- POSTGRES_USER
|
||||
- POSTGRES_PASSWORD
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- dokploy-network
|
||||
volumes:
|
||||
- db_docmost_data:/var/lib/postgresql/data
|
||||
|
||||
redis:
|
||||
image: redis:7.2-alpine
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- dokploy-network
|
||||
volumes:
|
||||
- redis_docmost_data:/data
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
docmost:
|
||||
db_docmost_data:
|
||||
redis_docmost_data:
|
||||
29
apps/dokploy/templates/docmost/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import {
|
||||
type DomainSchema,
|
||||
type Schema,
|
||||
type Template,
|
||||
generateRandomDomain,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const domains: DomainSchema[] = [
|
||||
{
|
||||
host: generateRandomDomain(schema),
|
||||
port: 3000,
|
||||
serviceName: "docmost",
|
||||
},
|
||||
];
|
||||
|
||||
const envs = [
|
||||
"POSTGRES_DB=docmost",
|
||||
"POSTGRES_USER=docmost",
|
||||
"POSTGRES_PASSWORD=STRONG_DB_PASSWORD",
|
||||
"APP_URL=http://localhost:3000",
|
||||
"APP_SECRET=VERY_STRONG_SECRET",
|
||||
];
|
||||
|
||||
return {
|
||||
domains,
|
||||
envs,
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
gitea:
|
||||
image: gitea/gitea:1.22.2
|
||||
image: gitea/gitea:1.22.3
|
||||
environment:
|
||||
- USER_UID=${USER_UID}
|
||||
- USER_GID=${USER_GID}
|
||||
@@ -21,7 +21,7 @@ services:
|
||||
- db
|
||||
|
||||
db:
|
||||
image: postgres:16
|
||||
image: postgres:17
|
||||
restart: always
|
||||
environment:
|
||||
- POSTGRES_USER=gitea
|
||||
|
||||
45
apps/dokploy/templates/hi-events/docker-compose.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
services:
|
||||
all-in-one:
|
||||
image: daveearley/hi.events-all-in-one:v0.8.0-beta.1
|
||||
restart: always
|
||||
environment:
|
||||
- VITE_FRONTEND_URL=https://${DOMAIN}
|
||||
- APP_FRONTEND_URL=https://${DOMAIN}
|
||||
- VITE_API_URL_CLIENT=https://${DOMAIN}/api
|
||||
- VITE_API_URL_SERVER=http://localhost:80/api
|
||||
- VITE_STRIPE_PUBLISHABLE_KEY
|
||||
- LOG_CHANNEL=stderr
|
||||
- QUEUE_CONNECTION=sync
|
||||
- MAIL_MAILER=array
|
||||
- APP_KEY
|
||||
- JWT_SECRET
|
||||
- FILESYSTEM_PUBLIC_DISK=public
|
||||
- FILESYSTEM_PRIVATE_DISK=local
|
||||
- APP_CDN_URL=https://${DOMAIN}/storage
|
||||
- DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
|
||||
- MAIL_MAILER
|
||||
- MAIL_HOST
|
||||
- MAIL_PORT
|
||||
- MAIL_FROM_ADDRESS
|
||||
- MAIL_FROM_NAME
|
||||
depends_on:
|
||||
- postgres
|
||||
|
||||
postgres:
|
||||
image: elestio/postgres:16
|
||||
restart: always
|
||||
networks:
|
||||
- dokploy-network
|
||||
environment:
|
||||
- POSTGRES_DB
|
||||
- POSTGRES_USER
|
||||
- POSTGRES_PASSWORD
|
||||
volumes:
|
||||
- pg_hi-events_data:/var/lib/postgresql/data
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
pg_hi-events_data:
|
||||
41
apps/dokploy/templates/hi-events/index.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import {
|
||||
type DomainSchema,
|
||||
type Schema,
|
||||
type Template,
|
||||
generateRandomDomain,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const domains: DomainSchema[] = [
|
||||
{
|
||||
host: generateRandomDomain(schema),
|
||||
port: 80,
|
||||
serviceName: "all-in-one",
|
||||
},
|
||||
];
|
||||
|
||||
const envs = [
|
||||
"# change domain here",
|
||||
"DOMAIN=my-events.com",
|
||||
"",
|
||||
"POSTGRES_DB=hievents",
|
||||
"POSTGRES_USER=hievents",
|
||||
"POSTGRES_PASSWORD=VERY_STRONG_PASSWORD",
|
||||
"",
|
||||
"VITE_STRIPE_PUBLISHABLE_KEY=",
|
||||
"",
|
||||
"APP_KEY=my-app-key",
|
||||
"JWT_SECRET=STRONG_JWT_SECRET",
|
||||
"",
|
||||
"MAIL_MAILER=",
|
||||
"MAIL_HOST=",
|
||||
"MAIL_PORT=",
|
||||
"MAIL_FROM_ADDRESS=",
|
||||
"MAIL_FROM_NAME=",
|
||||
];
|
||||
|
||||
return {
|
||||
domains,
|
||||
envs,
|
||||
};
|
||||
}
|
||||
87
apps/dokploy/templates/infisical/docker-compose.yml
Normal file
@@ -0,0 +1,87 @@
|
||||
services:
|
||||
db-migration:
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
image: infisical/infisical:v0.90.1-postgres
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- ENCRYPTION_KEY
|
||||
- AUTH_SECRET
|
||||
- SITE_URL
|
||||
- DB_CONNECTION_URI=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- SMTP_HOST
|
||||
- SMTP_PORT
|
||||
- SMTP_FROM_NAME
|
||||
- SMTP_USERNAME
|
||||
- SMTP_PASSWORD
|
||||
- SMTP_SECURE=true
|
||||
command: npm run migration:latest
|
||||
pull_policy: always
|
||||
networks:
|
||||
- dokploy-network
|
||||
|
||||
backend:
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
db-migration:
|
||||
condition: service_completed_successfully
|
||||
image: infisical/infisical:v0.90.1-postgres
|
||||
pull_policy: always
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- ENCRYPTION_KEY
|
||||
- AUTH_SECRET
|
||||
- SITE_URL
|
||||
- DB_CONNECTION_URI=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- SMTP_HOST
|
||||
- SMTP_PORT
|
||||
- SMTP_FROM_NAME
|
||||
- SMTP_USERNAME
|
||||
- SMTP_PASSWORD
|
||||
- SMTP_SECURE=true
|
||||
networks:
|
||||
- dokploy-network
|
||||
|
||||
redis:
|
||||
image: redis:7.4.1
|
||||
env_file: .env
|
||||
restart: always
|
||||
environment:
|
||||
- ALLOW_EMPTY_PASSWORD=yes
|
||||
networks:
|
||||
- dokploy-network
|
||||
volumes:
|
||||
- redis_infisical_data:/data
|
||||
|
||||
db:
|
||||
image: postgres:14-alpine
|
||||
restart: always
|
||||
environment:
|
||||
- POSTGRES_PASSWORD
|
||||
- POSTGRES_USER
|
||||
- POSTGRES_DB
|
||||
volumes:
|
||||
- pg_infisical_data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- dokploy-network
|
||||
healthcheck:
|
||||
test: "pg_isready --username=${POSTGRES_USER} && psql --username=${POSTGRES_USER} --list"
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
|
||||
volumes:
|
||||
pg_infisical_data:
|
||||
redis_infisical_data:
|
||||
|
||||
networks:
|
||||
dokploy-network:
|
||||
external: true
|
||||
|
||||
93
apps/dokploy/templates/infisical/index.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import {
|
||||
type DomainSchema,
|
||||
type Schema,
|
||||
type Template,
|
||||
generateRandomDomain,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const domains: DomainSchema[] = [
|
||||
{
|
||||
host: generateRandomDomain(schema),
|
||||
port: 8080,
|
||||
serviceName: "backend",
|
||||
},
|
||||
];
|
||||
|
||||
const envs = [
|
||||
"# THIS IS A SAMPLE ENCRYPTION KEY AND SHOULD NEVER BE USED FOR PRODUCTION",
|
||||
"ENCRYPTION_KEY=6c1fe4e407b8911c104518103505b218",
|
||||
"",
|
||||
"# THIS IS A SAMPLE AUTH_SECRET KEY AND SHOULD NEVER BE USED FOR PRODUCTION",
|
||||
"AUTH_SECRET=5lrMXKKWCVocS/uerPsl7V+TX/aaUaI7iDkgl3tSmLE=",
|
||||
"# Postgres creds",
|
||||
"POSTGRES_PASSWORD=infisical",
|
||||
"POSTGRES_USER=infisical",
|
||||
"POSTGRES_DB=infisical",
|
||||
"",
|
||||
"# Website URL",
|
||||
"# Required",
|
||||
"SITE_URL=http://localhost:8080",
|
||||
"",
|
||||
"# Mail/SMTP",
|
||||
"SMTP_HOST=",
|
||||
"SMTP_PORT=",
|
||||
"SMTP_NAME=",
|
||||
"SMTP_USERNAME=",
|
||||
"SMTP_PASSWORD=",
|
||||
"",
|
||||
"# Integration",
|
||||
"# Optional only if integration is used",
|
||||
"CLIENT_ID_HEROKU=",
|
||||
"CLIENT_ID_VERCEL=",
|
||||
"CLIENT_ID_NETLIFY=",
|
||||
"CLIENT_ID_GITHUB=",
|
||||
"CLIENT_ID_GITHUB_APP=",
|
||||
"CLIENT_SLUG_GITHUB_APP=",
|
||||
"CLIENT_ID_GITLAB=",
|
||||
"CLIENT_ID_BITBUCKET=",
|
||||
"CLIENT_SECRET_HEROKU=",
|
||||
"CLIENT_SECRET_VERCEL=",
|
||||
"CLIENT_SECRET_NETLIFY=",
|
||||
"CLIENT_SECRET_GITHUB=",
|
||||
"CLIENT_SECRET_GITHUB_APP=",
|
||||
"CLIENT_SECRET_GITLAB=",
|
||||
"CLIENT_SECRET_BITBUCKET=",
|
||||
"CLIENT_SLUG_VERCEL=",
|
||||
"",
|
||||
"CLIENT_PRIVATE_KEY_GITHUB_APP=",
|
||||
"CLIENT_APP_ID_GITHUB_APP=",
|
||||
"",
|
||||
"# Sentry (optional) for monitoring errors",
|
||||
"SENTRY_DSN=",
|
||||
"",
|
||||
"# Infisical Cloud-specific configs",
|
||||
"# Ignore - Not applicable for self-hosted version",
|
||||
"POSTHOG_HOST=",
|
||||
"POSTHOG_PROJECT_API_KEY=",
|
||||
"",
|
||||
"# SSO-specific variables",
|
||||
"CLIENT_ID_GOOGLE_LOGIN=",
|
||||
"CLIENT_SECRET_GOOGLE_LOGIN=",
|
||||
"",
|
||||
"CLIENT_ID_GITHUB_LOGIN=",
|
||||
"CLIENT_SECRET_GITHUB_LOGIN=",
|
||||
"",
|
||||
"CLIENT_ID_GITLAB_LOGIN=",
|
||||
"CLIENT_SECRET_GITLAB_LOGIN=",
|
||||
"",
|
||||
"CAPTCHA_SECRET=",
|
||||
"",
|
||||
"NEXT_PUBLIC_CAPTCHA_SITE_KEY=",
|
||||
"",
|
||||
"PLAIN_API_KEY=",
|
||||
"PLAIN_WISH_LABEL_IDS=",
|
||||
"",
|
||||
"SSL_CLIENT_CERTIFICATE_HEADER_KEY=",
|
||||
];
|
||||
|
||||
return {
|
||||
domains,
|
||||
envs,
|
||||
};
|
||||
}
|
||||
11
apps/dokploy/templates/influxdb/docker-compose.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
services:
|
||||
influxdb:
|
||||
image: influxdb:2.7.10
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- influxdb2-data:/var/lib/influxdb2
|
||||
- influxdb2-config:/etc/influxdb2
|
||||
|
||||
volumes:
|
||||
influxdb2-data:
|
||||
influxdb2-config:
|
||||
19
apps/dokploy/templates/influxdb/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import {
|
||||
type DomainSchema,
|
||||
type Schema,
|
||||
type Template,
|
||||
generateRandomDomain,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const domains: DomainSchema[] = [
|
||||
{
|
||||
host: generateRandomDomain(schema),
|
||||
port: 8086,
|
||||
serviceName: "influxdb",
|
||||
},
|
||||
];
|
||||
return {
|
||||
domains,
|
||||
};
|
||||
}
|
||||
16
apps/dokploy/templates/macos/docker-compose.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
services:
|
||||
macos:
|
||||
image: dockurr/macos:1.14
|
||||
volumes:
|
||||
- macos-storage:/storage
|
||||
environment:
|
||||
- VERSION
|
||||
devices:
|
||||
# If in .env string 'KVM=N' is not commented, you need to comment line below
|
||||
- /dev/kvm
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
stop_grace_period: 2m
|
||||
|
||||
volumes:
|
||||
macos-storage:
|
||||
33
apps/dokploy/templates/macos/index.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import {
|
||||
type DomainSchema,
|
||||
type Schema,
|
||||
type Template,
|
||||
generateRandomDomain,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const domains: DomainSchema[] = [
|
||||
{
|
||||
host: generateRandomDomain(schema),
|
||||
port: 8006,
|
||||
serviceName: "macos",
|
||||
},
|
||||
];
|
||||
|
||||
const envs = [
|
||||
"# https://github.com/dockur/macos?tab=readme-ov-file#how-do-i-select-the-macos-version",
|
||||
"VERSION=15",
|
||||
"",
|
||||
"# Uncomment this if your PC/VM or etc does not support virtualization technology",
|
||||
"# KVM=N",
|
||||
"",
|
||||
"DISK_SIZE=64G",
|
||||
"RAM_SIZE=4G",
|
||||
"CPU_CORES=2",
|
||||
];
|
||||
|
||||
return {
|
||||
domains,
|
||||
envs,
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
nocodb:
|
||||
image: nocodb/nocodb:0.251.1
|
||||
image: nocodb/nocodb:0.257.2
|
||||
restart: always
|
||||
environment:
|
||||
NC_DB: "pg://root_db?u=postgres&p=password&d=root_db"
|
||||
@@ -11,7 +11,7 @@ services:
|
||||
- nc_data:/usr/app/data
|
||||
|
||||
root_db:
|
||||
image: postgres:14.7
|
||||
image: postgres:17
|
||||
restart: always
|
||||
networks:
|
||||
- dokploy-network
|
||||
|
||||
@@ -26,7 +26,7 @@ services:
|
||||
hard: 262144
|
||||
|
||||
plausible:
|
||||
image: ghcr.io/plausible/community-edition:v2.1.0
|
||||
image: ghcr.io/plausible/community-edition:v2.1.4
|
||||
restart: always
|
||||
command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
|
||||
depends_on:
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
version: '3.3'
|
||||
services:
|
||||
stirling-pdf:
|
||||
image: frooodle/s-pdf:0.30.1
|
||||
ports:
|
||||
- '8080'
|
||||
volumes:
|
||||
- training-data:/usr/share/tessdata #Required for extra OCR languages
|
||||
- extra-configs:/configs
|
||||
# - /location/of/customFiles:/customFiles/
|
||||
# - /location/of/logs:/logs/
|
||||
environment:
|
||||
- DOCKER_ENABLE_SECURITY=false
|
||||
- INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false
|
||||
- LANGS=en_GB
|
||||
version: "3"
|
||||
|
||||
volumes:
|
||||
training-data: {}
|
||||
extra-configs: {}
|
||||
services:
|
||||
soketi:
|
||||
image: quay.io/soketi/soketi:1.6.1-16-debian
|
||||
environment:
|
||||
SOKETI_DEBUG: "1"
|
||||
SOKETI_HOST: "0.0.0.0"
|
||||
SOKETI_PORT: "6001"
|
||||
SOKETI_METRICS_SERVER_PORT: "9601"
|
||||
restart: unless-stopped
|
||||
|
||||
@@ -34,7 +34,7 @@ export const templates: TemplateData[] = [
|
||||
{
|
||||
id: "plausible",
|
||||
name: "Plausible",
|
||||
version: "v2.1.0",
|
||||
version: "v2.1.4",
|
||||
description:
|
||||
"Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.",
|
||||
logo: "plausible.svg",
|
||||
@@ -125,7 +125,7 @@ export const templates: TemplateData[] = [
|
||||
{
|
||||
id: "uptime-kuma",
|
||||
name: "Uptime Kuma",
|
||||
version: "1.21.4",
|
||||
version: "1.23.15",
|
||||
description:
|
||||
"Uptime Kuma is a free and open source monitoring tool that allows you to monitor your websites and applications.",
|
||||
logo: "uptime-kuma.png",
|
||||
@@ -218,7 +218,6 @@ export const templates: TemplateData[] = [
|
||||
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/",
|
||||
@@ -231,7 +230,7 @@ export const templates: TemplateData[] = [
|
||||
{
|
||||
id: "nocodb",
|
||||
name: "NocoDB",
|
||||
version: "0.251.1",
|
||||
version: "0.257.2",
|
||||
description:
|
||||
"NocoDB is an opensource Airtable alternative that turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart spreadsheet.",
|
||||
|
||||
@@ -441,7 +440,7 @@ export const templates: TemplateData[] = [
|
||||
{
|
||||
id: "soketi",
|
||||
name: "Soketi",
|
||||
version: "v1.4-16",
|
||||
version: "v1.6.1-16",
|
||||
description:
|
||||
"Soketi is your simple, fast, and resilient open-source WebSockets server.",
|
||||
logo: "soketi.png",
|
||||
@@ -485,7 +484,7 @@ export const templates: TemplateData[] = [
|
||||
{
|
||||
id: "gitea",
|
||||
name: "Gitea",
|
||||
version: "1.22.2",
|
||||
version: "1.22.3",
|
||||
description:
|
||||
"Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD.",
|
||||
logo: "gitea.png",
|
||||
@@ -557,6 +556,124 @@ export const templates: TemplateData[] = [
|
||||
tags: ["cloud", "monitoring"],
|
||||
load: () => import("./portainer/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "influxdb",
|
||||
name: "InfluxDB",
|
||||
version: "2.7.10",
|
||||
description:
|
||||
"InfluxDB 2.7 is the platform purpose-built to collect, store, process and visualize time series data.",
|
||||
logo: "influxdb.png",
|
||||
links: {
|
||||
github: "https://github.com/influxdata/influxdb",
|
||||
website: "https://www.influxdata.com/",
|
||||
docs: "https://docs.influxdata.com/influxdb/v2/",
|
||||
},
|
||||
tags: ["self-hosted", "open-source", "storage", "database"],
|
||||
load: () => import("./influxdb/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "infisical",
|
||||
name: "Infisical",
|
||||
version: "0.90.1",
|
||||
description:
|
||||
"All-in-one platform to securely manage application configuration and secrets across your team and infrastructure.",
|
||||
logo: "infisical.jpg",
|
||||
links: {
|
||||
github: "https://github.com/Infisical/infisical",
|
||||
website: "https://infisical.com/",
|
||||
docs: "https://infisical.com/docs/documentation/getting-started/introduction",
|
||||
},
|
||||
tags: ["self-hosted", "open-source"],
|
||||
load: () => import("./infisical/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "docmost",
|
||||
name: "Docmost",
|
||||
version: "0.4.1",
|
||||
description:
|
||||
"Docmost, is an open-source collaborative wiki and documentation software.",
|
||||
logo: "docmost.png",
|
||||
links: {
|
||||
github: "https://github.com/docmost/docmost",
|
||||
website: "https://docmost.com/",
|
||||
docs: "https://docmost.com/docs/",
|
||||
},
|
||||
tags: ["self-hosted", "open-source", "manager"],
|
||||
load: () => import("./docmost/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "vaultwarden",
|
||||
name: "Vaultwarden",
|
||||
version: "1.32.3",
|
||||
description:
|
||||
"Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs",
|
||||
logo: "vaultwarden.svg",
|
||||
links: {
|
||||
github: "https://github.com/dani-garcia/vaultwarden",
|
||||
website: "",
|
||||
docs: "https://github.com/dani-garcia/vaultwarden/wiki",
|
||||
},
|
||||
tags: ["open-source"],
|
||||
load: () => import("./vaultwarden/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "hi-events",
|
||||
name: "Hi.events",
|
||||
version: "0.8.0-beta.1",
|
||||
description:
|
||||
"Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.",
|
||||
logo: "hi-events.svg",
|
||||
links: {
|
||||
github: "https://github.com/HiEventsDev/hi.events",
|
||||
website: "https://hi.events/",
|
||||
docs: "https://hi.events/docs",
|
||||
},
|
||||
tags: ["self-hosted", "open-source", "manager"],
|
||||
load: () => import("./hi-events/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "windows",
|
||||
name: "Windows (dockerized)",
|
||||
version: "4.00",
|
||||
description: "Windows inside a Docker container.",
|
||||
logo: "windows.png",
|
||||
links: {
|
||||
github: "https://github.com/dockur/windows",
|
||||
website: "",
|
||||
docs: "https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-use-it",
|
||||
},
|
||||
tags: ["self-hosted", "open-source", "os"],
|
||||
load: () => import("./windows/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "macos",
|
||||
name: "MacOS (dockerized)",
|
||||
version: "1.14",
|
||||
description: "MacOS inside a Docker container.",
|
||||
logo: "macos.png",
|
||||
links: {
|
||||
github: "https://github.com/dockur/macos",
|
||||
website: "",
|
||||
docs: "https://github.com/dockur/macos?tab=readme-ov-file#how-do-i-use-it",
|
||||
},
|
||||
tags: ["self-hosted", "open-source", "os"],
|
||||
load: () => import("./macos/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "coder",
|
||||
name: "Coder",
|
||||
version: "2.15.3",
|
||||
description:
|
||||
"Coder is an open-source cloud development environment (CDE) that you host in your cloud or on-premises.",
|
||||
logo: "coder.svg",
|
||||
links: {
|
||||
github: "https://github.com/coder/coder",
|
||||
website: "https://coder.com/",
|
||||
docs: "https://coder.com/docs",
|
||||
},
|
||||
tags: ["self-hosted", "open-source", "builder"],
|
||||
load: () => import("./coder/index").then((m) => m.generate),
|
||||
},
|
||||
{
|
||||
id: "stirling",
|
||||
name: "Stirling PDF",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
uptime-kuma:
|
||||
image: louislam/uptime-kuma:1
|
||||
image: louislam/uptime-kuma:1.23.15
|
||||
restart: always
|
||||
volumes:
|
||||
- uptime-kuma-data:/app/data
|
||||
|
||||
14
apps/dokploy/templates/vaultwarden/docker-compose.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
services:
|
||||
vaultwarden:
|
||||
image: vaultwarden/server:1.32.3
|
||||
restart: always
|
||||
environment:
|
||||
DOMAIN: ${DOMAIN}
|
||||
SIGNUPS_ALLOWED: ${SIGNUPS_ALLOWED}
|
||||
volumes:
|
||||
- vaultwarden:/data
|
||||
ports:
|
||||
- 80
|
||||
|
||||
volumes:
|
||||
vaultwarden:
|
||||
28
apps/dokploy/templates/vaultwarden/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import {
|
||||
type DomainSchema,
|
||||
type Schema,
|
||||
type Template,
|
||||
generateRandomDomain,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const domains: DomainSchema[] = [
|
||||
{
|
||||
host: generateRandomDomain(schema),
|
||||
port: 80,
|
||||
serviceName: "vaultwarden",
|
||||
},
|
||||
];
|
||||
|
||||
const envs = [
|
||||
"# Deactivate this with 'false' after you have created your account so that no strangers can register",
|
||||
"SIGNUPS_ALLOWED=true",
|
||||
"# required when using a reverse proxy; your domain; vaultwarden needs to know it's https to work properly with attachments",
|
||||
"DOMAIN=https://vaultwarden.example.com",
|
||||
];
|
||||
|
||||
return {
|
||||
domains,
|
||||
envs,
|
||||
};
|
||||
}
|
||||
17
apps/dokploy/templates/windows/docker-compose.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
services:
|
||||
windows:
|
||||
image: dockurr/windows:4.00
|
||||
volumes:
|
||||
- win-storage:/storage
|
||||
environment:
|
||||
- VERSION
|
||||
- KVM
|
||||
devices:
|
||||
# If in .env string 'KVM=N' is not commented, you need to comment line below
|
||||
- /dev/kvm
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
stop_grace_period: 2m
|
||||
|
||||
volumes:
|
||||
win-storage:
|
||||
39
apps/dokploy/templates/windows/index.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
type DomainSchema,
|
||||
type Schema,
|
||||
type Template,
|
||||
generateRandomDomain,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const domains: DomainSchema[] = [
|
||||
{
|
||||
host: generateRandomDomain(schema),
|
||||
port: 8006,
|
||||
serviceName: "windows",
|
||||
},
|
||||
];
|
||||
|
||||
const envs = [
|
||||
"# https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-select-the-windows-version",
|
||||
"VERSION=win11",
|
||||
"",
|
||||
"# Uncomment this if your PC/VM or etc does not support virtualization technology",
|
||||
"# KVM=N",
|
||||
"",
|
||||
"DISK_SIZE=64G",
|
||||
"RAM_SIZE=4G",
|
||||
"CPU_CORES=2",
|
||||
"",
|
||||
"USERNAME=Dokploy",
|
||||
"PASSWORD=",
|
||||
"",
|
||||
"# https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-select-the-windows-language",
|
||||
"LANGUAGE=English",
|
||||
];
|
||||
|
||||
return {
|
||||
domains,
|
||||
envs,
|
||||
};
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
import { relations, sql } from "drizzle-orm";
|
||||
import { boolean, pgEnum, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||
import { relations } from "drizzle-orm";
|
||||
import { pgEnum, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
import { admins } from "./admin";
|
||||
import { applications } from "./application";
|
||||
import { auth } from "./auth";
|
||||
/**
|
||||
* This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
|
||||
* database instance for multiple projects.
|
||||
@@ -48,7 +47,7 @@ const createSchema = createInsertSchema(registry, {
|
||||
registryUrl: z.string(),
|
||||
adminId: z.string().min(1),
|
||||
registryId: z.string().min(1),
|
||||
registryType: z.enum(["selfHosted", "cloud"]),
|
||||
registryType: z.enum(["cloud"]),
|
||||
imagePrefix: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
@@ -59,7 +58,7 @@ export const apiCreateRegistry = createSchema
|
||||
username: z.string().min(1),
|
||||
password: z.string().min(1),
|
||||
registryUrl: z.string(),
|
||||
registryType: z.enum(["selfHosted", "cloud"]),
|
||||
registryType: z.enum(["cloud"]),
|
||||
imagePrefix: z.string().nullable().optional(),
|
||||
})
|
||||
.required()
|
||||
@@ -72,7 +71,7 @@ export const apiTestRegistry = createSchema.pick({}).extend({
|
||||
username: z.string().min(1),
|
||||
password: z.string().min(1),
|
||||
registryUrl: z.string(),
|
||||
registryType: z.enum(["selfHosted", "cloud"]),
|
||||
registryType: z.enum(["cloud"]),
|
||||
imagePrefix: z.string().nullable().optional(),
|
||||
serverId: z.string().optional(),
|
||||
});
|
||||
|
||||
@@ -193,6 +193,7 @@ export const deployApplication = async ({
|
||||
applicationName: application.name,
|
||||
applicationType: "application",
|
||||
buildLink,
|
||||
adminId: application.project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
await updateDeploymentStatus(deployment.deploymentId, "error");
|
||||
@@ -204,6 +205,7 @@ export const deployApplication = async ({
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error to build",
|
||||
buildLink,
|
||||
adminId: application.project.adminId,
|
||||
});
|
||||
|
||||
console.log(
|
||||
@@ -314,6 +316,7 @@ export const deployRemoteApplication = async ({
|
||||
applicationName: application.name,
|
||||
applicationType: "application",
|
||||
buildLink,
|
||||
adminId: application.project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
// @ts-ignore
|
||||
@@ -336,6 +339,7 @@ export const deployRemoteApplication = async ({
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error to build",
|
||||
buildLink,
|
||||
adminId: application.project.adminId,
|
||||
});
|
||||
|
||||
console.log(
|
||||
|
||||
@@ -235,6 +235,7 @@ export const deployCompose = async ({
|
||||
applicationName: compose.name,
|
||||
applicationType: "compose",
|
||||
buildLink,
|
||||
adminId: compose.project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
await updateDeploymentStatus(deployment.deploymentId, "error");
|
||||
@@ -248,6 +249,7 @@ export const deployCompose = async ({
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error to build",
|
||||
buildLink,
|
||||
adminId: compose.project.adminId,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
@@ -353,6 +355,7 @@ export const deployRemoteCompose = async ({
|
||||
applicationName: compose.name,
|
||||
applicationType: "compose",
|
||||
buildLink,
|
||||
adminId: compose.project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
// @ts-ignore
|
||||
@@ -376,6 +379,7 @@ export const deployRemoteCompose = async ({
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error to build",
|
||||
buildLink,
|
||||
adminId: compose.project.adminId,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export type Mongo = typeof mongo.$inferSelect;
|
||||
|
||||
export const createMongo = async (input: typeof apiCreateMongo._type) => {
|
||||
input.appName =
|
||||
`${input.appName}-${generatePassword(6)}` || generateAppName("postgres");
|
||||
`${input.appName}-${generatePassword(6)}` || generateAppName("mongo");
|
||||
if (input.appName) {
|
||||
const valid = await validUniqueServerAppName(input.appName);
|
||||
|
||||
@@ -72,12 +72,12 @@ export const findMongoById = async (mongoId: string) => {
|
||||
|
||||
export const updateMongoById = async (
|
||||
mongoId: string,
|
||||
postgresData: Partial<Mongo>,
|
||||
mongoData: Partial<Mongo>,
|
||||
) => {
|
||||
const result = await db
|
||||
.update(mongo)
|
||||
.set({
|
||||
...postgresData,
|
||||
...mongoData,
|
||||
})
|
||||
.where(eq(mongo.mongoId, mongoId))
|
||||
.returning();
|
||||
|
||||
@@ -101,7 +101,6 @@ const installRequirements = async (serverId: string, logPath: string) => {
|
||||
}
|
||||
stream
|
||||
.on("close", () => {
|
||||
writeStream.write("Connection closed ✅");
|
||||
client.end();
|
||||
resolve();
|
||||
})
|
||||
|
||||
@@ -49,6 +49,7 @@ export const runMariadbBackup = async (
|
||||
projectName: project.name,
|
||||
databaseType: "mariadb",
|
||||
type: "success",
|
||||
adminId: project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
@@ -59,6 +60,7 @@ export const runMariadbBackup = async (
|
||||
type: "error",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error message not provided",
|
||||
adminId: project.adminId,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
|
||||
projectName: project.name,
|
||||
databaseType: "mongodb",
|
||||
type: "success",
|
||||
adminId: project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
@@ -56,6 +57,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
|
||||
type: "error",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error message not provided",
|
||||
adminId: project.adminId,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
|
||||
projectName: project.name,
|
||||
databaseType: "mysql",
|
||||
type: "success",
|
||||
adminId: project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
@@ -56,6 +57,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
|
||||
type: "error",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error message not provided",
|
||||
adminId: project.adminId,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ export const runPostgresBackup = async (
|
||||
projectName: project.name,
|
||||
databaseType: "postgres",
|
||||
type: "success",
|
||||
adminId: project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
await sendDatabaseBackupNotifications({
|
||||
@@ -58,6 +59,7 @@ export const runPostgresBackup = async (
|
||||
type: "error",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error message not provided",
|
||||
adminId: project.adminId,
|
||||
});
|
||||
|
||||
throw error;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { createWriteStream } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import type { InferResultType } from "@dokploy/server/types/with";
|
||||
import type { CreateServiceOptions } from "dockerode";
|
||||
import { uploadImage } from "../cluster/upload";
|
||||
import { uploadImage, uploadImageRemoteCommand } from "../cluster/upload";
|
||||
import {
|
||||
calculateResources,
|
||||
generateBindMounts,
|
||||
@@ -69,19 +70,30 @@ export const getBuildCommand = (
|
||||
application: ApplicationNested,
|
||||
logPath: string,
|
||||
) => {
|
||||
const { buildType } = application;
|
||||
let command = "";
|
||||
const { buildType, registry } = application;
|
||||
switch (buildType) {
|
||||
case "nixpacks":
|
||||
return getNixpacksCommand(application, logPath);
|
||||
command = getNixpacksCommand(application, logPath);
|
||||
break;
|
||||
case "heroku_buildpacks":
|
||||
return getHerokuCommand(application, logPath);
|
||||
command = getHerokuCommand(application, logPath);
|
||||
break;
|
||||
case "paketo_buildpacks":
|
||||
return getPaketoCommand(application, logPath);
|
||||
command = getPaketoCommand(application, logPath);
|
||||
break;
|
||||
case "static":
|
||||
return getStaticCommand(application, logPath);
|
||||
command = getStaticCommand(application, logPath);
|
||||
break;
|
||||
case "dockerfile":
|
||||
return getDockerCommand(application, logPath);
|
||||
command = getDockerCommand(application, logPath);
|
||||
break;
|
||||
}
|
||||
if (registry) {
|
||||
command += uploadImageRemoteCommand(application, logPath);
|
||||
}
|
||||
|
||||
return command;
|
||||
};
|
||||
|
||||
export const mechanizeDockerContainer = async (
|
||||
@@ -186,11 +198,11 @@ const getImageName = (application: ApplicationNested) => {
|
||||
return dockerImage || "ERROR-NO-IMAGE-PROVIDED";
|
||||
}
|
||||
|
||||
const registryUrl = registry?.registryUrl || "";
|
||||
const imagePrefix = registry?.imagePrefix ? `${registry.imagePrefix}/` : "";
|
||||
return registry
|
||||
? `${registryUrl}/${imagePrefix}${appName}`
|
||||
: `${appName}:latest`;
|
||||
if (registry) {
|
||||
return join(registry.imagePrefix || "", appName);
|
||||
}
|
||||
|
||||
return `${appName}:latest`;
|
||||
};
|
||||
|
||||
const getAuthConfig = (application: ApplicationNested) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { WriteStream } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import type { ApplicationNested } from "../builders";
|
||||
import { spawnAsync } from "../process/spawnAsync";
|
||||
|
||||
@@ -16,23 +17,14 @@ export const uploadImage = async (
|
||||
const { appName } = application;
|
||||
const imageName = `${appName}:latest`;
|
||||
|
||||
const finalURL =
|
||||
registryType === "selfHosted"
|
||||
? process.env.NODE_ENV === "development"
|
||||
? "localhost:5000"
|
||||
: registryUrl
|
||||
: registryUrl;
|
||||
const finalURL = registryUrl;
|
||||
|
||||
const registryTag = imagePrefix
|
||||
? `${finalURL}/${imagePrefix}/${imageName}`
|
||||
: `${finalURL}/${imageName}`;
|
||||
const registryTag = join(imagePrefix || "", imageName);
|
||||
|
||||
try {
|
||||
console.log(finalURL, registryTag);
|
||||
writeStream.write(
|
||||
`📦 [Enabled Registry] Uploading image to ${registry.registryType} | ${registryTag} | ${finalURL}\n`,
|
||||
);
|
||||
|
||||
await spawnAsync(
|
||||
"docker",
|
||||
["login", finalURL, "-u", registry.username, "-p", registry.password],
|
||||
@@ -59,7 +51,48 @@ export const uploadImage = async (
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
// docker:
|
||||
// endpoint: "unix:///var/run/docker.sock"
|
||||
// exposedByDefault: false
|
||||
// swarmMode: true
|
||||
|
||||
export const uploadImageRemoteCommand = (
|
||||
application: ApplicationNested,
|
||||
logPath: string,
|
||||
) => {
|
||||
const registry = application.registry;
|
||||
|
||||
if (!registry) {
|
||||
throw new Error("Registry not found");
|
||||
}
|
||||
|
||||
const { registryUrl, imagePrefix } = registry;
|
||||
const { appName } = application;
|
||||
const imageName = `${appName}:latest`;
|
||||
|
||||
const finalURL = registryUrl;
|
||||
|
||||
const registryTag = join(imagePrefix || "", imageName);
|
||||
|
||||
try {
|
||||
const command = `
|
||||
echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" >> ${logPath};
|
||||
docker login ${finalURL} -u ${registry.username} -p ${registry.password} >> ${logPath} 2>> ${logPath} || {
|
||||
echo "❌ DockerHub Failed" >> ${logPath};
|
||||
exit 1;
|
||||
}
|
||||
echo "✅ DockerHub Login Success" >> ${logPath};
|
||||
docker tag ${imageName} ${registryTag} >> ${logPath} 2>> ${logPath} || {
|
||||
echo "❌ Error tagging image" >> ${logPath};
|
||||
exit 1;
|
||||
}
|
||||
echo "✅ Image Tagged" >> ${logPath};
|
||||
|
||||
docker push ${registryTag} 2>> ${logPath} || {
|
||||
echo "❌ Error pushing image" >> ${logPath};
|
||||
exit 1;
|
||||
}
|
||||
echo "✅ Image Pushed" >> ${logPath};
|
||||
`;
|
||||
return command;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -259,10 +259,10 @@ export const createDomainLabels = async (
|
||||
domain: Domain,
|
||||
entrypoint: "web" | "websecure",
|
||||
) => {
|
||||
const { host, port, https, uniqueConfigKey, certificateType } = domain;
|
||||
const { host, port, https, uniqueConfigKey, certificateType, path } = domain;
|
||||
const routerName = `${appName}-${uniqueConfigKey}-${entrypoint}`;
|
||||
const labels = [
|
||||
`traefik.http.routers.${routerName}.rule=Host(\`${host}\`)`,
|
||||
`traefik.http.routers.${routerName}.rule=Host(\`${host}\`)${path && path !== "/" ? ` && PathPrefix(\`${path}\`)` : ""}`,
|
||||
`traefik.http.routers.${routerName}.entrypoints=${entrypoint}`,
|
||||
`traefik.http.services.${routerName}.loadbalancer.server.port=${port}`,
|
||||
`traefik.http.routers.${routerName}.service=${routerName}`,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { db } from "@dokploy/server/db";
|
||||
import { notifications } from "@dokploy/server/db/schema";
|
||||
import BuildFailedEmail from "@dokploy/server/emails/emails/build-failed";
|
||||
import { renderAsync } from "@react-email/components";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
@@ -16,6 +16,7 @@ interface Props {
|
||||
applicationType: string;
|
||||
errorMessage: string;
|
||||
buildLink: string;
|
||||
adminId: string;
|
||||
}
|
||||
|
||||
export const sendBuildErrorNotifications = async ({
|
||||
@@ -24,10 +25,14 @@ export const sendBuildErrorNotifications = async ({
|
||||
applicationType,
|
||||
errorMessage,
|
||||
buildLink,
|
||||
adminId,
|
||||
}: Props) => {
|
||||
const date = new Date();
|
||||
const notificationList = await db.query.notifications.findMany({
|
||||
where: eq(notifications.appBuildError, true),
|
||||
where: and(
|
||||
eq(notifications.appBuildError, true),
|
||||
eq(notifications.adminId, adminId),
|
||||
),
|
||||
with: {
|
||||
email: true,
|
||||
discord: true,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { db } from "@dokploy/server/db";
|
||||
import { notifications } from "@dokploy/server/db/schema";
|
||||
import BuildSuccessEmail from "@dokploy/server/emails/emails/build-success";
|
||||
import { renderAsync } from "@react-email/components";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
@@ -15,6 +15,7 @@ interface Props {
|
||||
applicationName: string;
|
||||
applicationType: string;
|
||||
buildLink: string;
|
||||
adminId: string;
|
||||
}
|
||||
|
||||
export const sendBuildSuccessNotifications = async ({
|
||||
@@ -22,10 +23,14 @@ export const sendBuildSuccessNotifications = async ({
|
||||
applicationName,
|
||||
applicationType,
|
||||
buildLink,
|
||||
adminId,
|
||||
}: Props) => {
|
||||
const date = new Date();
|
||||
const notificationList = await db.query.notifications.findMany({
|
||||
where: eq(notifications.appDeploy, true),
|
||||
where: and(
|
||||
eq(notifications.appDeploy, true),
|
||||
eq(notifications.adminId, adminId),
|
||||
),
|
||||
with: {
|
||||
email: true,
|
||||
discord: true,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { db } from "@dokploy/server/db";
|
||||
import { notifications } from "@dokploy/server/db/schema";
|
||||
import DatabaseBackupEmail from "@dokploy/server/emails/emails/database-backup";
|
||||
import { renderAsync } from "@react-email/components";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
@@ -16,16 +16,21 @@ export const sendDatabaseBackupNotifications = async ({
|
||||
databaseType,
|
||||
type,
|
||||
errorMessage,
|
||||
adminId,
|
||||
}: {
|
||||
projectName: string;
|
||||
applicationName: string;
|
||||
databaseType: "postgres" | "mysql" | "mongodb" | "mariadb";
|
||||
type: "error" | "success";
|
||||
adminId: string;
|
||||
errorMessage?: string;
|
||||
}) => {
|
||||
const date = new Date();
|
||||
const notificationList = await db.query.notifications.findMany({
|
||||
where: eq(notifications.databaseBackup, true),
|
||||
where: and(
|
||||
eq(notifications.databaseBackup, true),
|
||||
eq(notifications.adminId, adminId),
|
||||
),
|
||||
with: {
|
||||
email: true,
|
||||
discord: true,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { db } from "@dokploy/server/db";
|
||||
import { notifications } from "@dokploy/server/db/schema";
|
||||
import DockerCleanupEmail from "@dokploy/server/emails/emails/docker-cleanup";
|
||||
import { renderAsync } from "@react-email/components";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
@@ -11,11 +11,15 @@ import {
|
||||
} from "./utils";
|
||||
|
||||
export const sendDockerCleanupNotifications = async (
|
||||
adminId: string,
|
||||
message = "Docker cleanup for dokploy",
|
||||
) => {
|
||||
const date = new Date();
|
||||
const notificationList = await db.query.notifications.findMany({
|
||||
where: eq(notifications.dockerCleanup, true),
|
||||
where: and(
|
||||
eq(notifications.dockerCleanup, true),
|
||||
eq(notifications.adminId, adminId),
|
||||
),
|
||||
with: {
|
||||
email: true,
|
||||
discord: true,
|
||||
|
||||