Merge branch 'canary' into 394-ability-to-backup-named-volume-to-s3

This commit is contained in:
Mauricio Siu
2025-07-05 00:05:47 -06:00
58 changed files with 6017 additions and 118 deletions
+12 -11
View File
@@ -16,28 +16,29 @@ Here's how to install docker on different operating systems:
### Ubuntu ### Ubuntu
```bash ```bash
# Uninstall old versions
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done
# Update package index # Update package index
sudo apt-get update sudo apt-get update
# Install prerequisites # Install prerequisites
sudo apt-get install \ sudo apt-get install ca-certificates curl
apt-transport-https \ sudo install -m 0755 -d /etc/apt/keyrings
ca-certificates \
curl \
gnupg \
lsb-release
# Add Docker's official GPG key # Add Docker's official GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Set up stable repository # Add the repository to Apt sources
echo \ echo \
"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine # Install Docker Engine
sudo apt-get update sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
``` ```
## Windows ## Windows
+8 -28
View File
@@ -62,46 +62,26 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
### Hero Sponsors 🎖 ### Hero Sponsors 🎖
<div style="display: flex; align-items: center; gap: 20px;"> <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; margin-right: 10px;"> <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>
<img src=".github/sponsors/hostinger.jpg" alt="Hostinger" height="50"/> <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> <a href="https://mandarin3d.com/?ref=dokploy" target="_blank" style="display: inline-block;"><img src=".github/sponsors/mandarin.png" alt="Mandarin" height="50"/></a>
<a href="https://www.lxaer.com/?ref=dokploy" target="_blank" style="display: inline-block; margin-right: 10px;"> <a href="https://lightnode.com/?ref=dokploy" target="_blank" style="display: inline-block;"><img src=".github/sponsors/light-node.webp" alt="Lightnode" height="70"/></a>
<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>
<a href="https://lightnode.com/?ref=dokploy" target="_blank" style="display: inline-block;">
<img src=".github/sponsors/light-node.webp" alt="Lightnode" height="70"/>
</a>
</div> </div>
### Premium Supporters 🥇 ### Premium Supporters 🥇
<div style="display: flex; align-items: center; gap: 20px;"> <div style="display: flex; align-items: center; gap: 20px;">
<a href="https://supafort.com/?ref=dokploy" target="_blank" style="display: inline-block; margin-right: 20px;"> <a href="https://supafort.com/?ref=dokploy" target="_blank" style="display: inline-block; margin-right: 20px;"><img src="https://supafort.com/build/q-4Ht4rBZR.webp" alt="Supafort.com" height="50"/></a>
<img src="https://supafort.com/build/q-4Ht4rBZR.webp" alt="Supafort.com" height="50"/> <a href="https://agentdock.ai/?ref=dokploy" target="_blank" style="display: inline-block; margin-right: 50px;"><img src=".github/sponsors/agentdock.png" alt="agentdock.ai" height="70"/></a>
</a>
<a href="https://agentdock.ai/?ref=dokploy" target="_blank" style="display: inline-block; margin-right: 50px;">
<img src=".github/sponsors/agentdock.png" alt="agentdock.ai" height="70"/>
</a>
</div> </div>
### Elite Contributors 🥈 ### Elite Contributors 🥈
<div style="display: flex; align-items: center; gap: 20px;"> <div style="display: flex; align-items: center; gap: 20px;">
<a href="https://americancloud.com/?ref=dokploy" target="_blank" style="display: inline-block; padding: 10px; border-radius: 10px;"><img src=".github/sponsors/american-cloud.png" alt="AmericanCloud" height="70"/></a>
<a href="https://americancloud.com/?ref=dokploy" target="_blank" style="display: inline-block; padding: 10px; border-radius: 10px;"> <a href="https://tolgee.io/?utm_source=github_dokploy&utm_medium=banner&utm_campaign=dokploy" target="_blank" style="display: inline-block; margin-right: 10px;"><img src="https://dokploy.com/tolgee-logo.png" alt="Tolgee" height="80"/></a>
<img src=".github/sponsors/american-cloud.png" alt="AmericanCloud" height="70"/>
</a>
<a href="https://tolgee.io/?utm_source=github_dokploy&utm_medium=banner&utm_campaign=dokploy" target="_blank" style="display: inline-block; margin-right: 10px;">
<img src="https://dokploy.com/tolgee-logo.png" alt="Tolgee" height="80"/>
</a>
</div> </div>
<!-- Elite Contributors 🥈 --> <!-- Elite Contributors 🥈 -->
@@ -130,7 +130,7 @@ const createStringToJSONSchema = (schema: z.ZodTypeAny) => {
} }
try { try {
return JSON.parse(str); return JSON.parse(str);
} catch (_e) { } catch {
ctx.addIssue({ code: "custom", message: "Invalid JSON format" }); ctx.addIssue({ code: "custom", message: "Invalid JSON format" });
return z.NEVER; return z.NEVER;
} }
@@ -107,7 +107,7 @@ export const ShowImport = ({ composeId }: Props) => {
composeId, composeId,
}); });
setShowModal(false); setShowModal(false);
} catch (_error) { } catch {
toast.error("Error importing template"); toast.error("Error importing template");
} }
}; };
@@ -126,7 +126,7 @@ export const ShowImport = ({ composeId }: Props) => {
}); });
setTemplateInfo(result); setTemplateInfo(result);
setShowModal(true); setShowModal(true);
} catch (_error) { } catch {
toast.error("Error processing template"); toast.error("Error processing template");
} }
}; };
@@ -35,6 +35,9 @@ import { z } from "zod";
const AddPortSchema = z.object({ const AddPortSchema = z.object({
publishedPort: z.number().int().min(1).max(65535), publishedPort: z.number().int().min(1).max(65535),
publishMode: z.enum(["ingress", "host"], {
required_error: "Publish mode is required",
}),
targetPort: z.number().int().min(1).max(65535), targetPort: z.number().int().min(1).max(65535),
protocol: z.enum(["tcp", "udp"], { protocol: z.enum(["tcp", "udp"], {
required_error: "Protocol is required", required_error: "Protocol is required",
@@ -80,6 +83,7 @@ export const HandlePorts = ({
useEffect(() => { useEffect(() => {
form.reset({ form.reset({
publishedPort: data?.publishedPort ?? 0, publishedPort: data?.publishedPort ?? 0,
publishMode: data?.publishMode ?? "ingress",
targetPort: data?.targetPort ?? 0, targetPort: data?.targetPort ?? 0,
protocol: data?.protocol ?? "tcp", protocol: data?.protocol ?? "tcp",
}); });
@@ -165,6 +169,32 @@ export const HandlePorts = ({
</FormItem> </FormItem>
)} )}
/> />
<FormField
control={form.control}
name="publishMode"
render={({ field }) => {
return (
<FormItem className="md:col-span-2">
<FormLabel>Published Port Mode</FormLabel>
<Select
onValueChange={field.onChange}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a publish mode for the port" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value={"ingress"}>Ingress</SelectItem>
<SelectItem value={"host"}>Host</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
);
}}
/>
<FormField <FormField
control={form.control} control={form.control}
name="targetPort" name="targetPort"
@@ -60,7 +60,7 @@ export const ShowPorts = ({ applicationId }: Props) => {
{data?.ports.map((port) => ( {data?.ports.map((port) => (
<div key={port.portId}> <div key={port.portId}>
<div className="flex w-full flex-col sm:flex-row sm:items-center justify-between gap-4 sm:gap-10 border rounded-lg p-4"> <div className="flex w-full flex-col sm:flex-row sm:items-center justify-between gap-4 sm:gap-10 border rounded-lg p-4">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 flex-col gap-4 sm:gap-8"> <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 flex-col gap-4 sm:gap-8">
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<span className="font-medium">Published Port</span> <span className="font-medium">Published Port</span>
<span className="text-sm text-muted-foreground"> <span className="text-sm text-muted-foreground">
@@ -68,7 +68,13 @@ export const ShowPorts = ({ applicationId }: Props) => {
</span> </span>
</div> </div>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<span className="font-medium"> Target Port</span> <span className="font-medium">Published Port Mode</span>
<span className="text-sm text-muted-foreground">
{port?.publishMode?.toUpperCase()}
</span>
</div>
<div className="flex flex-col gap-1">
<span className="font-medium">Target Port</span>
<span className="text-sm text-muted-foreground"> <span className="text-sm text-muted-foreground">
{port.targetPort} {port.targetPort}
</span> </span>
@@ -77,7 +77,7 @@ export const ComposeFileEditor = ({ composeId }: Props) => {
composeId, composeId,
}); });
}) })
.catch((_e) => { .catch(() => {
toast.error("Error updating the Compose config"); toast.error("Error updating the Compose config");
}); });
}; };
@@ -40,7 +40,7 @@ export const ShowConvertedCompose = ({ composeId }: Props) => {
.then(() => { .then(() => {
refetch(); refetch();
}) })
.catch((_err) => {}); .catch(() => {});
} }
}, [isOpen]); }, [isOpen]);
@@ -103,7 +103,7 @@ export const AddApplication = ({ projectId, projectName }: Props) => {
projectId, projectId,
}); });
}) })
.catch((_e) => { .catch(() => {
toast.error("Error creating the service"); toast.error("Error creating the service");
}); });
}; };
@@ -1063,7 +1063,7 @@ export const HandleNotifications = ({ notificationId }: Props) => {
}); });
} }
toast.success("Connection Success"); toast.success("Connection Success");
} catch (_err) { } catch {
toast.error("Error testing the provider"); toast.error("Error testing the provider");
} }
}} }}
@@ -63,7 +63,7 @@ export const Disable2FA = () => {
toast.success("2FA disabled successfully"); toast.success("2FA disabled successfully");
utils.user.get.invalidate(); utils.user.get.invalidate();
setIsOpen(false); setIsOpen(false);
} catch (_error) { } catch {
form.setError("password", { form.setError("password", {
message: "Connection error. Please try again.", message: "Connection error. Please try again.",
}); });
@@ -36,7 +36,7 @@ export const ToggleDockerCleanup = ({ serverId }: Props) => {
await refetch(); await refetch();
} }
toast.success("Docker Cleanup updated"); toast.success("Docker Cleanup updated");
} catch (_error) { } catch {
toast.error("Docker Cleanup Error"); toast.error("Docker Cleanup Error");
} }
}; };
@@ -56,7 +56,7 @@ export function GPUSupport({ serverId }: GPUSupportProps) {
try { try {
await utils.settings.checkGPUStatus.invalidate({ serverId }); await utils.settings.checkGPUStatus.invalidate({ serverId });
await refetch(); await refetch();
} catch (_error) { } catch {
toast.error("Failed to refresh GPU status"); toast.error("Failed to refresh GPU status");
} finally { } finally {
setIsRefreshing(false); setIsRefreshing(false);
@@ -74,7 +74,7 @@ export function GPUSupport({ serverId }: GPUSupportProps) {
try { try {
await setupGPU.mutateAsync({ serverId }); await setupGPU.mutateAsync({ serverId });
} catch (_error) { } catch {
// Error handling is done in mutation's onError // Error handling is done in mutation's onError
} }
}; };
@@ -146,7 +146,7 @@ export const ShowInvitations = () => {
{invitation.status === "pending" && ( {invitation.status === "pending" && (
<DropdownMenuItem <DropdownMenuItem
className="w-full cursor-pointer" className="w-full cursor-pointer"
onSelect={(_e) => { onSelect={() => {
copy( copy(
`${origin}/invitation?token=${invitation.id}`, `${origin}/invitation?token=${invitation.id}`,
); );
@@ -162,7 +162,7 @@ export const ShowInvitations = () => {
{invitation.status === "pending" && ( {invitation.status === "pending" && (
<DropdownMenuItem <DropdownMenuItem
className="w-full cursor-pointer" className="w-full cursor-pointer"
onSelect={async (_e) => { onSelect={async () => {
const result = const result =
await authClient.organization.cancelInvitation( await authClient.organization.cancelInvitation(
{ {
@@ -189,7 +189,7 @@ export const ShowInvitations = () => {
)} )}
<DropdownMenuItem <DropdownMenuItem
className="w-full cursor-pointer" className="w-full cursor-pointer"
onSelect={async (_e) => { onSelect={async () => {
await removeInvitation({ await removeInvitation({
invitationId: invitation.id, invitationId: invitation.id,
}).then(() => { }).then(() => {
@@ -91,7 +91,7 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => {
}); });
toast.success(t("settings.server.webServer.traefik.portsUpdated")); toast.success(t("settings.server.webServer.traefik.portsUpdated"));
setOpen(false); setOpen(false);
} catch (_error) {} } catch {}
}; };
return ( return (
+1 -1
View File
@@ -17,7 +17,7 @@ const PopoverContent = React.forwardRef<
align={align} align={align}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", "z-50 w-full rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className, className,
)} )}
{...props} {...props}
@@ -0,0 +1,2 @@
CREATE TYPE "public"."publishModeType" AS ENUM('ingress', 'host');--> statement-breakpoint
ALTER TABLE "port" ADD COLUMN "publishMode" "publishModeType" DEFAULT 'host' NOT NULL;
File diff suppressed because it is too large Load Diff
+7
View File
@@ -694,6 +694,13 @@
"when": 1751233265357, "when": 1751233265357,
"tag": "0098_conscious_chat", "tag": "0098_conscious_chat",
"breakpoints": true "breakpoints": true
},
{
"idx": 99,
"version": "7",
"when": 1751693569786,
"tag": "0099_wise_golden_guardian",
"breakpoints": true
} }
] ]
} }
+1 -1
View File
@@ -72,7 +72,7 @@ export async function getServerSideProps(
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
}, },
}; };
} catch (_error) { } catch {
return { return {
props: {}, props: {},
}; };
@@ -390,7 +390,7 @@ const Project = (
break; break;
} }
success++; success++;
} catch (_error) { } catch {
toast.error(`Error starting service ${serviceId}`); toast.error(`Error starting service ${serviceId}`);
} }
} }
@@ -437,7 +437,7 @@ const Project = (
break; break;
} }
success++; success++;
} catch (_error) { } catch {
toast.error(`Error stopping service ${serviceId}`); toast.error(`Error stopping service ${serviceId}`);
} }
} }
@@ -1107,7 +1107,7 @@ export async function getServerSideProps(
projectId: params?.projectId, projectId: params?.projectId,
}, },
}; };
} catch (_error) { } catch {
return { return {
redirect: { redirect: {
permanent: false, permanent: false,
@@ -427,7 +427,7 @@ export async function getServerSideProps(
activeTab: (activeTab || "general") as TabState, activeTab: (activeTab || "general") as TabState,
}, },
}; };
} catch (_error) { } catch {
return { return {
redirect: { redirect: {
permanent: false, permanent: false,
@@ -423,7 +423,7 @@ export async function getServerSideProps(
activeTab: (activeTab || "general") as TabState, activeTab: (activeTab || "general") as TabState,
}, },
}; };
} catch (_error) { } catch {
return { return {
redirect: { redirect: {
permanent: false, permanent: false,
@@ -338,7 +338,7 @@ export async function getServerSideProps(
activeTab: (activeTab || "general") as TabState, activeTab: (activeTab || "general") as TabState,
}, },
}; };
} catch (_error) { } catch {
return { return {
redirect: { redirect: {
permanent: false, permanent: false,
@@ -340,7 +340,7 @@ export async function getServerSideProps(
activeTab: (activeTab || "general") as TabState, activeTab: (activeTab || "general") as TabState,
}, },
}; };
} catch (_error) { } catch {
return { return {
redirect: { redirect: {
permanent: false, permanent: false,
@@ -324,7 +324,7 @@ export async function getServerSideProps(
activeTab: (activeTab || "general") as TabState, activeTab: (activeTab || "general") as TabState,
}, },
}; };
} catch (_error) { } catch {
return { return {
redirect: { redirect: {
permanent: false, permanent: false,
@@ -321,7 +321,7 @@ export async function getServerSideProps(
activeTab: (activeTab || "general") as TabState, activeTab: (activeTab || "general") as TabState,
}, },
}; };
} catch (_error) { } catch {
return { return {
redirect: { redirect: {
permanent: false, permanent: false,
@@ -328,7 +328,7 @@ export async function getServerSideProps(
activeTab: (activeTab || "general") as TabState, activeTab: (activeTab || "general") as TabState,
}, },
}; };
} catch (_error) { } catch {
return { return {
redirect: { redirect: {
permanent: false, permanent: false,
@@ -68,7 +68,7 @@ export async function getServerSideProps(
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
}, },
}; };
} catch (_error) { } catch {
return { return {
props: {}, props: {},
}; };
@@ -69,7 +69,7 @@ export async function getServerSideProps(
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
}, },
}; };
} catch (_error) { } catch {
return { return {
props: {}, props: {},
}; };
+1 -1
View File
@@ -72,7 +72,7 @@ export async function getServerSideProps(
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
}, },
}; };
} catch (_error) { } catch {
return { return {
props: {}, props: {},
}; };
+1 -1
View File
@@ -72,7 +72,7 @@ export async function getServerSideProps(
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
}, },
}; };
} catch (_error) { } catch {
return { return {
props: {}, props: {},
}; };
+4 -4
View File
@@ -96,7 +96,7 @@ export default function Home({ IS_CLOUD }: Props) {
toast.success("Logged in successfully"); toast.success("Logged in successfully");
router.push("/dashboard/projects"); router.push("/dashboard/projects");
} catch (_error) { } catch {
toast.error("An error occurred while logging in"); toast.error("An error occurred while logging in");
} finally { } finally {
setIsLoginLoading(false); setIsLoginLoading(false);
@@ -124,7 +124,7 @@ export default function Home({ IS_CLOUD }: Props) {
toast.success("Logged in successfully"); toast.success("Logged in successfully");
router.push("/dashboard/projects"); router.push("/dashboard/projects");
} catch (_error) { } catch {
toast.error("An error occurred while verifying 2FA code"); toast.error("An error occurred while verifying 2FA code");
} finally { } finally {
setIsTwoFactorLoading(false); setIsTwoFactorLoading(false);
@@ -154,7 +154,7 @@ export default function Home({ IS_CLOUD }: Props) {
toast.success("Logged in successfully"); toast.success("Logged in successfully");
router.push("/dashboard/projects"); router.push("/dashboard/projects");
} catch (_error) { } catch {
toast.error("An error occurred while verifying backup code"); toast.error("An error occurred while verifying backup code");
} finally { } finally {
setIsBackupCodeLoading(false); setIsBackupCodeLoading(false);
@@ -478,7 +478,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
}, },
}; };
} }
} catch (_error) {} } catch {}
return { return {
props: { props: {
+1 -1
View File
@@ -133,7 +133,7 @@ const Invitation = ({
toast.success("Account created successfully"); toast.success("Account created successfully");
router.push("/dashboard/projects"); router.push("/dashboard/projects");
} catch (_error) { } catch {
toast.error("An error occurred while creating your account"); toast.error("An error occurred while creating your account");
} }
}; };
+1 -1
View File
@@ -6,7 +6,7 @@ export const isWSL = async () => {
const { stdout } = await execAsync("uname -r"); const { stdout } = await execAsync("uname -r");
const isWSL = stdout.includes("microsoft"); const isWSL = stdout.includes("microsoft");
return isWSL; return isWSL;
} catch (_error) { } catch {
return false; return false;
} }
}; };
+1 -1
View File
@@ -101,7 +101,7 @@ export const setupDeploymentLogsWebSocketServer = (
ws.close(); ws.close();
}); });
} }
} catch (_error) { } catch {
// @ts-ignore // @ts-ignore
// const errorMessage = error?.message as unknown as string; // const errorMessage = error?.message as unknown as string;
// ws.send(errorMessage); // ws.send(errorMessage);
+2
View File
@@ -12,6 +12,7 @@ import {
initializeNetwork, initializeNetwork,
initializeSwarm, initializeSwarm,
} from "@dokploy/server/setup/setup"; } from "@dokploy/server/setup/setup";
import { execAsync } from "@dokploy/server";
(async () => { (async () => {
try { try {
setupDirectories(); setupDirectories();
@@ -20,6 +21,7 @@ import {
await initializeNetwork(); await initializeNetwork();
createDefaultTraefikConfig(); createDefaultTraefikConfig();
createDefaultServerTraefikConfig(); createDefaultServerTraefikConfig();
await execAsync("docker pull traefik:v3.1.2");
await initializeTraefik(); await initializeTraefik();
await initializeRedis(); await initializeRedis();
await initializePostgres(); await initializePostgres();
+5
View File
@@ -242,3 +242,8 @@
background-color: var(--terminal-paste) !important; background-color: var(--terminal-paste) !important;
color: currentColor !important; color: currentColor !important;
} }
.cm-content,
.cm-lineWrapping {
@apply font-mono;
}
+5
View File
@@ -6,6 +6,7 @@ import { z } from "zod";
import { applications } from "./application"; import { applications } from "./application";
export const protocolType = pgEnum("protocolType", ["tcp", "udp"]); export const protocolType = pgEnum("protocolType", ["tcp", "udp"]);
export const publishModeType = pgEnum("publishModeType", ["ingress", "host"]);
export const ports = pgTable("port", { export const ports = pgTable("port", {
portId: text("portId") portId: text("portId")
@@ -13,6 +14,7 @@ export const ports = pgTable("port", {
.primaryKey() .primaryKey()
.$defaultFn(() => nanoid()), .$defaultFn(() => nanoid()),
publishedPort: integer("publishedPort").notNull(), publishedPort: integer("publishedPort").notNull(),
publishMode: publishModeType("publishMode").notNull().default("host"),
targetPort: integer("targetPort").notNull(), targetPort: integer("targetPort").notNull(),
protocol: protocolType("protocol").notNull(), protocol: protocolType("protocol").notNull(),
@@ -32,6 +34,7 @@ const createSchema = createInsertSchema(ports, {
portId: z.string().min(1), portId: z.string().min(1),
applicationId: z.string().min(1), applicationId: z.string().min(1),
publishedPort: z.number(), publishedPort: z.number(),
publishMode: z.enum(["ingress", "host"]).default("ingress"),
targetPort: z.number(), targetPort: z.number(),
protocol: z.enum(["tcp", "udp"]).default("tcp"), protocol: z.enum(["tcp", "udp"]).default("tcp"),
}); });
@@ -39,6 +42,7 @@ const createSchema = createInsertSchema(ports, {
export const apiCreatePort = createSchema export const apiCreatePort = createSchema
.pick({ .pick({
publishedPort: true, publishedPort: true,
publishMode: true,
targetPort: true, targetPort: true,
protocol: true, protocol: true,
applicationId: true, applicationId: true,
@@ -55,6 +59,7 @@ export const apiUpdatePort = createSchema
.pick({ .pick({
portId: true, portId: true,
publishedPort: true, publishedPort: true,
publishMode: true,
targetPort: true, targetPort: true,
protocol: true, protocol: true,
}) })
+2 -2
View File
@@ -73,7 +73,7 @@ export const readStatsFile = async (
const filePath = `${MONITORING_PATH}/${appName}/${statType}.json`; const filePath = `${MONITORING_PATH}/${appName}/${statType}.json`;
const data = await promises.readFile(filePath, "utf-8"); const data = await promises.readFile(filePath, "utf-8");
return JSON.parse(data); return JSON.parse(data);
} catch (_error) { } catch {
return []; return [];
} }
}; };
@@ -108,7 +108,7 @@ export const readLastValueStatsFile = async (
const data = await promises.readFile(filePath, "utf-8"); const data = await promises.readFile(filePath, "utf-8");
const stats = JSON.parse(data); const stats = JSON.parse(data);
return stats[stats.length - 1] || null; return stats[stats.length - 1] || null;
} catch (_error) { } catch {
return null; return null;
} }
}; };
+10 -10
View File
@@ -98,7 +98,7 @@ export const getConfig = async (
const config = JSON.parse(stdout); const config = JSON.parse(stdout);
return config; return config;
} catch (_error) {} } catch {}
}; };
export const getContainersByAppNameMatch = async ( export const getContainersByAppNameMatch = async (
@@ -156,7 +156,7 @@ export const getContainersByAppNameMatch = async (
}); });
return containers || []; return containers || [];
} catch (_error) {} } catch {}
return []; return [];
}; };
@@ -214,7 +214,7 @@ export const getStackContainersByAppName = async (
}); });
return containers || []; return containers || [];
} catch (_error) {} } catch {}
return []; return [];
}; };
@@ -274,7 +274,7 @@ export const getServiceContainersByAppName = async (
}); });
return containers || []; return containers || [];
} catch (_error) {} } catch {}
return []; return [];
}; };
@@ -331,7 +331,7 @@ export const getContainersByAppLabel = async (
}); });
return containers || []; return containers || [];
} catch (_error) {} } catch {}
return []; return [];
}; };
@@ -350,7 +350,7 @@ export const containerRestart = async (containerId: string) => {
const config = JSON.parse(stdout); const config = JSON.parse(stdout);
return config; return config;
} catch (_error) {} } catch {}
}; };
export const getSwarmNodes = async (serverId?: string) => { export const getSwarmNodes = async (serverId?: string) => {
@@ -379,7 +379,7 @@ export const getSwarmNodes = async (serverId?: string) => {
.split("\n") .split("\n")
.map((line) => JSON.parse(line)); .map((line) => JSON.parse(line));
return nodesArray; return nodesArray;
} catch (_error) {} } catch {}
}; };
export const getNodeInfo = async (nodeId: string, serverId?: string) => { export const getNodeInfo = async (nodeId: string, serverId?: string) => {
@@ -405,7 +405,7 @@ export const getNodeInfo = async (nodeId: string, serverId?: string) => {
const nodeInfo = JSON.parse(stdout); const nodeInfo = JSON.parse(stdout);
return nodeInfo; return nodeInfo;
} catch (_error) {} } catch {}
}; };
export const getNodeApplications = async (serverId?: string) => { export const getNodeApplications = async (serverId?: string) => {
@@ -437,7 +437,7 @@ export const getNodeApplications = async (serverId?: string) => {
.filter((service) => !service.Name.startsWith("dokploy-")); .filter((service) => !service.Name.startsWith("dokploy-"));
return appArray; return appArray;
} catch (_error) {} } catch {}
}; };
export const getApplicationInfo = async ( export const getApplicationInfo = async (
@@ -470,5 +470,5 @@ export const getApplicationInfo = async (
.map((line) => JSON.parse(line)); .map((line) => JSON.parse(line));
return appArray; return appArray;
} catch (_error) {} } catch {}
}; };
+1 -1
View File
@@ -121,7 +121,7 @@ export const issueCommentExists = async ({
comment_id: comment_id, comment_id: comment_id,
}); });
return true; return true;
} catch (_error) { } catch {
return false; return false;
} }
}; };
+1 -1
View File
@@ -212,7 +212,7 @@ export const deleteFileMount = async (mountId: string) => {
} else { } else {
await removeFileOrDirectory(fullPath); await removeFileOrDirectory(fullPath);
} }
} catch (_error) {} } catch {}
}; };
export const getBaseFilesPath = async (mountId: string) => { export const getBaseFilesPath = async (mountId: string) => {
@@ -104,7 +104,7 @@ export const removePreviewDeployment = async (previewDeploymentId: string) => {
for (const operation of cleanupOperations) { for (const operation of cleanupOperations) {
try { try {
await operation(); await operation();
} catch (_error) {} } catch {}
} }
return deployment[0]; return deployment[0];
} catch (error) { } catch (error) {
+1 -1
View File
@@ -195,7 +195,7 @@ const rollbackApplication = async (
ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1, ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1,
}, },
}); });
} catch (_error: unknown) { } catch {
await docker.createService(settings); await docker.createService(settings);
} }
}; };
@@ -66,7 +66,7 @@ export const setupMonitoring = async (serverId: string) => {
await container.inspect(); await container.inspect();
await container.remove({ force: true }); await container.remove({ force: true });
console.log("Removed existing container"); console.log("Removed existing container");
} catch (_error) { } catch {
// Container doesn't exist, continue // Container doesn't exist, continue
} }
@@ -135,7 +135,7 @@ export const setupWebMonitoring = async (userId: string) => {
await container.inspect(); await container.inspect();
await container.remove({ force: true }); await container.remove({ force: true });
console.log("Removed existing container"); console.log("Removed existing container");
} catch (_error) {} } catch {}
await docker.createContainer(settings); await docker.createContainer(settings);
const newContainer = docker.getContainer(containerName); const newContainer = docker.getContainer(containerName);
+15 -3
View File
@@ -419,14 +419,26 @@ if ! [ -x "$(command -v docker)" ]; then
systemctl enable docker >/dev/null 2>&1 systemctl enable docker >/dev/null 2>&1
;; ;;
"opencloudos") "opencloudos")
dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo >/dev/null 2>&1 # Special handling for OpenCloud OS
dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin >/dev/null 2>&1 echo " - Installing Docker for OpenCloud OS..."
dnf install -y docker >/dev/null 2>&1
if ! [ -x "$(command -v docker)" ]; then if ! [ -x "$(command -v docker)" ]; then
echo " - Docker could not be installed automatically. Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue." echo " - Docker could not be installed automatically. Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue."
exit 1 exit 1
fi fi
systemctl start docker >/dev/null 2>&1
# Remove --live-restore parameter from Docker configuration if it exists
if [ -f "/etc/sysconfig/docker" ]; then
echo " - Removing --live-restore parameter from Docker configuration..."
sed -i 's/--live-restore[^[:space:]]*//' /etc/sysconfig/docker >/dev/null 2>&1
sed -i 's/--live-restore//' /etc/sysconfig/docker >/dev/null 2>&1
# Clean up any double spaces that might be left
sed -i 's/ */ /g' /etc/sysconfig/docker >/dev/null 2>&1
fi
systemctl enable docker >/dev/null 2>&1 systemctl enable docker >/dev/null 2>&1
systemctl start docker >/dev/null 2>&1
echo " - Docker configured for OpenCloud OS"
;; ;;
"alpine") "alpine")
apk add docker docker-cli-compose >/dev/null 2>&1 apk add docker docker-cli-compose >/dev/null 2>&1
+2 -2
View File
@@ -18,7 +18,7 @@ export const dockerSwarmInitialized = async () => {
await docker.swarmInspect(); await docker.swarmInspect();
return true; return true;
} catch (_e) { } catch {
return false; return false;
} }
}; };
@@ -41,7 +41,7 @@ export const dockerNetworkInitialized = async () => {
try { try {
await docker.getNetwork("dokploy-network").inspect(); await docker.getNetwork("dokploy-network").inspect();
return true; return true;
} catch (_e) { } catch {
return false; return false;
} }
}; };
+3 -3
View File
@@ -101,11 +101,11 @@ export const initializeTraefik = async ({
console.log("Waiting for service cleanup..."); console.log("Waiting for service cleanup...");
await new Promise((resolve) => setTimeout(resolve, 5000)); await new Promise((resolve) => setTimeout(resolve, 5000));
attempts++; attempts++;
} catch (_e) { } catch {
break; break;
} }
} }
} catch (_err) { } catch {
console.log("No existing service to remove"); console.log("No existing service to remove");
} }
@@ -120,7 +120,7 @@ export const initializeTraefik = async ({
await container.remove({ force: true }); await container.remove({ force: true });
await new Promise((resolve) => setTimeout(resolve, 5000)); await new Promise((resolve) => setTimeout(resolve, 5000));
} catch (_err) { } catch {
console.log("No existing container to remove"); console.log("No existing container to remove");
} }
+2 -1
View File
@@ -183,6 +183,7 @@ export const mechanizeDockerContainer = async (
RollbackConfig, RollbackConfig,
EndpointSpec: { EndpointSpec: {
Ports: ports.map((port) => ({ Ports: ports.map((port) => ({
PublishMode: port.publishMode,
Protocol: port.protocol, Protocol: port.protocol,
TargetPort: port.targetPort, TargetPort: port.targetPort,
PublishedPort: port.publishedPort, PublishedPort: port.publishedPort,
@@ -203,7 +204,7 @@ export const mechanizeDockerContainer = async (
ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1, ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1,
}, },
}); });
} catch (_error: unknown) { } catch {
await docker.createService(settings); await docker.createService(settings);
} }
}; };
@@ -98,7 +98,7 @@ export const buildMariadb = async (mariadb: MariadbNested) => {
version: Number.parseInt(inspect.Version.Index), version: Number.parseInt(inspect.Version.Index),
...settings, ...settings,
}); });
} catch (_error) { } catch {
await docker.createService(settings); await docker.createService(settings);
} }
}; };
+1 -1
View File
@@ -152,7 +152,7 @@ ${command ?? "wait $MONGOD_PID"}`;
version: Number.parseInt(inspect.Version.Index), version: Number.parseInt(inspect.Version.Index),
...settings, ...settings,
}); });
} catch (_error) { } catch {
await docker.createService(settings); await docker.createService(settings);
} }
}; };
+1 -1
View File
@@ -104,7 +104,7 @@ export const buildMysql = async (mysql: MysqlNested) => {
version: Number.parseInt(inspect.Version.Index), version: Number.parseInt(inspect.Version.Index),
...settings, ...settings,
}); });
} catch (_error) { } catch {
await docker.createService(settings); await docker.createService(settings);
} }
}; };
+1 -1
View File
@@ -95,7 +95,7 @@ export const buildRedis = async (redis: RedisNested) => {
version: Number.parseInt(inspect.Version.Index), version: Number.parseInt(inspect.Version.Index),
...settings, ...settings,
}); });
} catch (_error) { } catch {
await docker.createService(settings); await docker.createService(settings);
} }
}; };
+1 -1
View File
@@ -117,7 +117,7 @@ export const loadDockerComposeRemote = async (
if (!stdout) return null; if (!stdout) return null;
const parsedConfig = load(stdout) as ComposeSpecification; const parsedConfig = load(stdout) as ComposeSpecification;
return parsedConfig; return parsedConfig;
} catch (_err) { } catch {
return null; return null;
} }
}; };
+1 -1
View File
@@ -101,7 +101,7 @@ export const containerExists = async (containerName: string) => {
try { try {
await container.inspect(); await container.inspect();
return true; return true;
} catch (_error) { } catch {
return false; return false;
} }
}; };
+2 -2
View File
@@ -34,7 +34,7 @@ export async function checkGPUStatus(serverId?: string): Promise<GPUInfo> {
...gpuInfo, ...gpuInfo,
...cudaInfo, ...cudaInfo,
}; };
} catch (_error) { } catch {
return { return {
driverInstalled: false, driverInstalled: false,
driverVersion: undefined, driverVersion: undefined,
@@ -315,7 +315,7 @@ const setupLocalServer = async (daemonConfig: any) => {
try { try {
await execAsync(setupCommands); await execAsync(setupCommands);
} catch (_error) { } catch {
throw new Error( throw new Error(
"Failed to configure GPU support. Please ensure you have sudo privileges and try again.", "Failed to configure GPU support. Please ensure you have sudo privileges and try again.",
); );
@@ -67,7 +67,7 @@ export const removeTraefikConfig = async (
if (fs.existsSync(configPath)) { if (fs.existsSync(configPath)) {
await fs.promises.unlink(configPath); await fs.promises.unlink(configPath);
} }
} catch (_error) {} } catch {}
}; };
export const removeTraefikConfigRemote = async ( export const removeTraefikConfigRemote = async (
@@ -78,7 +78,7 @@ export const removeTraefikConfigRemote = async (
const { DYNAMIC_TRAEFIK_PATH } = paths(true); const { DYNAMIC_TRAEFIK_PATH } = paths(true);
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`); const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
await execAsyncRemote(serverId, `rm ${configPath}`); await execAsyncRemote(serverId, `rm ${configPath}`);
} catch (_error) {} } catch {}
}; };
export const loadOrCreateConfig = (appName: string): FileConfig => { export const loadOrCreateConfig = (appName: string): FileConfig => {
@@ -110,7 +110,7 @@ export const loadOrCreateConfigRemote = async (
http: { routers: {}, services: {} }, http: { routers: {}, services: {} },
}; };
return parsedConfig; return parsedConfig;
} catch (_err) { } catch {
return fileConfig; return fileConfig;
} }
}; };
@@ -132,7 +132,7 @@ export const readRemoteConfig = async (serverId: string, appName: string) => {
const { stdout } = await execAsyncRemote(serverId, `cat ${configPath}`); const { stdout } = await execAsyncRemote(serverId, `cat ${configPath}`);
if (!stdout) return null; if (!stdout) return null;
return stdout; return stdout;
} catch (_err) { } catch {
return null; return null;
} }
}; };