mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
refactor: improve sidebar
This commit is contained in:
@@ -1,98 +1,98 @@
|
|||||||
import { fs, vol } from "memfs";
|
import { fs, vol } from "memfs";
|
||||||
|
|
||||||
vi.mock("node:fs", () => ({
|
vi.mock("node:fs", () => ({
|
||||||
...fs,
|
...fs,
|
||||||
default: fs,
|
default: fs,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import type { Admin, FileConfig } from "@dokploy/server";
|
import type { Admin, FileConfig } from "@dokploy/server";
|
||||||
import {
|
import {
|
||||||
createDefaultServerTraefikConfig,
|
createDefaultServerTraefikConfig,
|
||||||
loadOrCreateConfig,
|
loadOrCreateConfig,
|
||||||
updateServerTraefik,
|
updateServerTraefik,
|
||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { beforeEach, expect, test, vi } from "vitest";
|
import { beforeEach, expect, test, vi } from "vitest";
|
||||||
|
|
||||||
const baseAdmin: Admin = {
|
const baseAdmin: Admin = {
|
||||||
cleanupCacheApplications: false,
|
cleanupCacheApplications: false,
|
||||||
cleanupCacheOnCompose: false,
|
cleanupCacheOnCompose: false,
|
||||||
cleanupCacheOnPreviews: false,
|
cleanupCacheOnPreviews: false,
|
||||||
createdAt: "",
|
createdAt: "",
|
||||||
authId: "",
|
authId: "",
|
||||||
adminId: "string",
|
adminId: "string",
|
||||||
serverIp: null,
|
serverIp: null,
|
||||||
certificateType: "none",
|
certificateType: "none",
|
||||||
host: null,
|
host: null,
|
||||||
letsEncryptEmail: null,
|
letsEncryptEmail: null,
|
||||||
sshPrivateKey: null,
|
sshPrivateKey: null,
|
||||||
enableDockerCleanup: false,
|
enableDockerCleanup: false,
|
||||||
enableLogRotation: false,
|
enableLogRotation: false,
|
||||||
serversQuantity: 0,
|
serversQuantity: 0,
|
||||||
stripeCustomerId: "",
|
stripeCustomerId: "",
|
||||||
stripeSubscriptionId: "",
|
stripeSubscriptionId: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vol.reset();
|
vol.reset();
|
||||||
createDefaultServerTraefikConfig();
|
createDefaultServerTraefikConfig();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Should read the configuration file", () => {
|
test("Should read the configuration file", () => {
|
||||||
const config: FileConfig = loadOrCreateConfig("dokploy");
|
const config: FileConfig = loadOrCreateConfig("dokploy");
|
||||||
|
|
||||||
expect(config.http?.routers?.["dokploy-router-app"]?.service).toBe(
|
expect(config.http?.routers?.["dokploy-router-app"]?.service).toBe(
|
||||||
"dokploy-service-app"
|
"dokploy-service-app",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Should apply redirect-to-https", () => {
|
test("Should apply redirect-to-https", () => {
|
||||||
updateServerTraefik(
|
updateServerTraefik(
|
||||||
{
|
{
|
||||||
...baseAdmin,
|
...baseAdmin,
|
||||||
certificateType: "letsencrypt",
|
certificateType: "letsencrypt",
|
||||||
},
|
},
|
||||||
"example.com"
|
"example.com",
|
||||||
);
|
);
|
||||||
|
|
||||||
const config: FileConfig = loadOrCreateConfig("dokploy");
|
const config: FileConfig = loadOrCreateConfig("dokploy");
|
||||||
|
|
||||||
expect(config.http?.routers?.["dokploy-router-app"]?.middlewares).toContain(
|
expect(config.http?.routers?.["dokploy-router-app"]?.middlewares).toContain(
|
||||||
"redirect-to-https"
|
"redirect-to-https",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Should change only host when no certificate", () => {
|
test("Should change only host when no certificate", () => {
|
||||||
updateServerTraefik(baseAdmin, "example.com");
|
updateServerTraefik(baseAdmin, "example.com");
|
||||||
|
|
||||||
const config: FileConfig = loadOrCreateConfig("dokploy");
|
const config: FileConfig = loadOrCreateConfig("dokploy");
|
||||||
|
|
||||||
expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined();
|
expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Should not touch config without host", () => {
|
test("Should not touch config without host", () => {
|
||||||
const originalConfig: FileConfig = loadOrCreateConfig("dokploy");
|
const originalConfig: FileConfig = loadOrCreateConfig("dokploy");
|
||||||
|
|
||||||
updateServerTraefik(baseAdmin, null);
|
updateServerTraefik(baseAdmin, null);
|
||||||
|
|
||||||
const config: FileConfig = loadOrCreateConfig("dokploy");
|
const config: FileConfig = loadOrCreateConfig("dokploy");
|
||||||
|
|
||||||
expect(originalConfig).toEqual(config);
|
expect(originalConfig).toEqual(config);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Should remove websecure if https rollback to http", () => {
|
test("Should remove websecure if https rollback to http", () => {
|
||||||
const originalConfig: FileConfig = loadOrCreateConfig("dokploy");
|
const originalConfig: FileConfig = loadOrCreateConfig("dokploy");
|
||||||
|
|
||||||
updateServerTraefik(
|
updateServerTraefik(
|
||||||
{ ...baseAdmin, certificateType: "letsencrypt" },
|
{ ...baseAdmin, certificateType: "letsencrypt" },
|
||||||
"example.com"
|
"example.com",
|
||||||
);
|
);
|
||||||
|
|
||||||
updateServerTraefik({ ...baseAdmin, certificateType: "none" }, "example.com");
|
updateServerTraefik({ ...baseAdmin, certificateType: "none" }, "example.com");
|
||||||
|
|
||||||
const config: FileConfig = loadOrCreateConfig("dokploy");
|
const config: FileConfig = loadOrCreateConfig("dokploy");
|
||||||
|
|
||||||
expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined();
|
expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined();
|
||||||
expect(
|
expect(
|
||||||
config.http?.routers?.["dokploy-router-app"]?.middlewares
|
config.http?.routers?.["dokploy-router-app"]?.middlewares,
|
||||||
).not.toContain("redirect-to-https");
|
).not.toContain("redirect-to-https");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export const ShowBackups = ({ id, type }: Props) => {
|
|||||||
{data?.length === 0 ? (
|
{data?.length === 0 ? (
|
||||||
<div className="flex flex-col items-center gap-3">
|
<div className="flex flex-col items-center gap-3">
|
||||||
<DatabaseBackup className="size-8 text-muted-foreground" />
|
<DatabaseBackup className="size-8 text-muted-foreground" />
|
||||||
<span className="text-base text-muted-foreground">
|
<span className="text-base text-muted-foreground text-center">
|
||||||
To create a backup it is required to set at least 1 provider.
|
To create a backup it is required to set at least 1 provider.
|
||||||
Please, go to{" "}
|
Please, go to{" "}
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ export const DockerMonitoring = ({
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
<div className="grid gap-6 lg:grid-cols-2">
|
||||||
<Card className="bg-background">
|
<Card className="bg-background">
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
<CardTitle className="text-sm font-medium">CPU Usage</CardTitle>
|
<CardTitle className="text-sm font-medium">CPU Usage</CardTitle>
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ export const ShowProjects = () => {
|
|||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Card className="h-full bg-sidebar p-2.5 rounded-xl ">
|
<Card className="h-full bg-sidebar p-2.5 rounded-xl ">
|
||||||
<div className="rounded-xl bg-background shadow-md ">
|
<div className="rounded-xl bg-background shadow-md ">
|
||||||
<div className="flex justify-between gap-4 w-full items-center">
|
<div className="flex justify-between gap-4 w-full items-center flex-wrap p-6">
|
||||||
<CardHeader className="">
|
<CardHeader className="p-0">
|
||||||
<CardTitle className="text-xl flex flex-row gap-2">
|
<CardTitle className="text-xl flex flex-row gap-2">
|
||||||
<FolderInput className="size-6 text-muted-foreground self-center" />
|
<FolderInput className="size-6 text-muted-foreground self-center" />
|
||||||
Projects
|
Projects
|
||||||
@@ -87,7 +87,7 @@ export const ShowProjects = () => {
|
|||||||
Create and manage your projects
|
Create and manage your projects
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<div className=" px-4 ">
|
<div className="">
|
||||||
<HandleProject />
|
<HandleProject />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export const ShowCertificates = () => {
|
|||||||
{data?.length === 0 ? (
|
{data?.length === 0 ? (
|
||||||
<div className="flex flex-col items-center gap-3 min-h-[25vh] justify-center">
|
<div className="flex flex-col items-center gap-3 min-h-[25vh] justify-center">
|
||||||
<ShieldCheck className="size-8 self-center text-muted-foreground" />
|
<ShieldCheck className="size-8 self-center text-muted-foreground" />
|
||||||
<span className="text-base text-muted-foreground">
|
<span className="text-base text-muted-foreground text-center">
|
||||||
You don't have any certificates created
|
You don't have any certificates created
|
||||||
</span>
|
</span>
|
||||||
<AddCertificate />
|
<AddCertificate />
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export const ShowNotifications = () => {
|
|||||||
{data?.length === 0 ? (
|
{data?.length === 0 ? (
|
||||||
<div className="flex flex-col items-center gap-3 min-h-[25vh] justify-center">
|
<div className="flex flex-col items-center gap-3 min-h-[25vh] justify-center">
|
||||||
<Bell />
|
<Bell />
|
||||||
<span className="text-base text-muted-foreground">
|
<span className="text-base text-muted-foreground text-center">
|
||||||
To send notifications it is required to set at least 1
|
To send notifications it is required to set at least 1
|
||||||
provider.
|
provider.
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export function NodeCard({ node, serverId }: Props) {
|
|||||||
<div className="font-medium">{node.Hostname}</div>
|
<div className="font-medium">{node.Hostname}</div>
|
||||||
<Badge variant="green">{node.ManagerStatus || "Worker"}</Badge>
|
<Badge variant="green">{node.ManagerStatus || "Worker"}</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap items-center space-x-4">
|
<div className="flex flex-wrap items-center gap-4">
|
||||||
<Badge variant="green">TLS Status: {node.TLSStatus}</Badge>
|
<Badge variant="green">TLS Status: {node.TLSStatus}</Badge>
|
||||||
<Badge variant="blue">Availability: {node.Availability}</Badge>
|
<Badge variant="blue">Availability: {node.Availability}</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export default function SwarmMonitorCard({ serverId }: Props) {
|
|||||||
return (
|
return (
|
||||||
<Card className="h-full bg-sidebar p-2.5 rounded-xl mx-auto w-full">
|
<Card className="h-full bg-sidebar p-2.5 rounded-xl mx-auto w-full">
|
||||||
<div className="rounded-xl bg-background shadow-md p-6 flex flex-col gap-4">
|
<div className="rounded-xl bg-background shadow-md p-6 flex flex-col gap-4">
|
||||||
<header className="flex items-center justify-between">
|
<header className="flex items-center flex-wrap gap-4 justify-between">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<CardTitle className="text-xl flex flex-row gap-2">
|
<CardTitle className="text-xl flex flex-row gap-2">
|
||||||
<WorkflowIcon className="size-6 text-muted-foreground self-center" />
|
<WorkflowIcon className="size-6 text-muted-foreground self-center" />
|
||||||
|
|||||||
@@ -5,9 +5,5 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DashboardLayout = ({ children }: Props) => {
|
export const DashboardLayout = ({ children }: Props) => {
|
||||||
return (
|
return <Page>{children}</Page>;
|
||||||
<Page>
|
|
||||||
<div>{children}</div>
|
|
||||||
</Page>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ interface Props {
|
|||||||
export const OnboardingLayout = ({ children }: Props) => {
|
export const OnboardingLayout = ({ children }: Props) => {
|
||||||
return (
|
return (
|
||||||
<div className="container relative min-h-svh flex-col items-center justify-center flex lg:max-w-none lg:grid lg:grid-cols-2 lg:px-0 w-full">
|
<div className="container relative min-h-svh flex-col items-center justify-center flex lg:max-w-none lg:grid lg:grid-cols-2 lg:px-0 w-full">
|
||||||
<div className="relative hidden h-full flex-col p-10 text-white dark:border-r lg:flex">
|
<div className="relative hidden h-full flex-col p-10 text-primary dark:border-r lg:flex">
|
||||||
<div className="absolute inset-0 bg-muted" />
|
<div className="absolute inset-0 bg-muted" />
|
||||||
<Link
|
<Link
|
||||||
href="https://dokploy.com"
|
href="https://dokploy.com"
|
||||||
@@ -22,35 +22,16 @@ export const OnboardingLayout = ({ children }: Props) => {
|
|||||||
</Link>
|
</Link>
|
||||||
<div className="relative z-20 mt-auto">
|
<div className="relative z-20 mt-auto">
|
||||||
<blockquote className="space-y-2">
|
<blockquote className="space-y-2">
|
||||||
<p className="text-lg">
|
<p className="text-lg text-primary">
|
||||||
“The Open Source alternative to Netlify, Vercel,
|
“The Open Source alternative to Netlify, Vercel,
|
||||||
Heroku.”
|
Heroku.”
|
||||||
</p>
|
</p>
|
||||||
{/* <footer className="text-sm">Sofia Davis</footer> */}
|
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="flex w-full flex-col justify-center space-y-6 max-w-lg mx-auto">
|
<div className="flex w-full flex-col justify-center space-y-6 max-w-lg mx-auto">
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
{/* <p className="px-8 text-center text-sm text-muted-foreground">
|
|
||||||
By clicking continue, you agree to our{" "}
|
|
||||||
<Link
|
|
||||||
href="/terms"
|
|
||||||
className="underline underline-offset-4 hover:text-primary"
|
|
||||||
>
|
|
||||||
Terms of Service
|
|
||||||
</Link>{" "}
|
|
||||||
and{" "}
|
|
||||||
<Link
|
|
||||||
href="/privacy"
|
|
||||||
className="underline underline-offset-4 hover:text-primary"
|
|
||||||
>
|
|
||||||
Privacy Policy
|
|
||||||
</Link>
|
|
||||||
.
|
|
||||||
</p> */}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4 justify-center absolute bottom-4 right-4 text-muted-foreground">
|
<div className="flex items-center gap-4 justify-center absolute bottom-4 right-4 text-muted-foreground">
|
||||||
<Button variant="ghost" size="icon">
|
<Button variant="ghost" size="icon">
|
||||||
|
|||||||
@@ -5,9 +5,5 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ProjectLayout = ({ children }: Props) => {
|
export const ProjectLayout = ({ children }: Props) => {
|
||||||
return (
|
return <Page>{children}</Page>;
|
||||||
<div>
|
|
||||||
<Page>{children}</Page>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -676,7 +676,7 @@ export default function Page({ children }: Props) {
|
|||||||
<Separator orientation="vertical" className="mr-2 h-4" />
|
<Separator orientation="vertical" className="mr-2 h-4" />
|
||||||
<Breadcrumb>
|
<Breadcrumb>
|
||||||
<BreadcrumbList>
|
<BreadcrumbList>
|
||||||
<BreadcrumbItem className="hidden md:block">
|
<BreadcrumbItem className="block">
|
||||||
<BreadcrumbLink asChild>
|
<BreadcrumbLink asChild>
|
||||||
<Link
|
<Link
|
||||||
href={activeItem?.url || "/"}
|
href={activeItem?.url || "/"}
|
||||||
@@ -686,7 +686,7 @@ export default function Page({ children }: Props) {
|
|||||||
</Link>
|
</Link>
|
||||||
</BreadcrumbLink>
|
</BreadcrumbLink>
|
||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
<BreadcrumbSeparator className="hidden md:block" />
|
<BreadcrumbSeparator className="block" />
|
||||||
<BreadcrumbItem>
|
<BreadcrumbItem>
|
||||||
<BreadcrumbPage>{activeItem?.title}</BreadcrumbPage>
|
<BreadcrumbPage>{activeItem?.title}</BreadcrumbPage>
|
||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const BreadcrumbSidebar = ({ list }: Props) => {
|
|||||||
<BreadcrumbList>
|
<BreadcrumbList>
|
||||||
{list.map((item, index) => (
|
{list.map((item, index) => (
|
||||||
<Fragment key={item.name}>
|
<Fragment key={item.name}>
|
||||||
<BreadcrumbItem className="hidden md:block">
|
<BreadcrumbItem className="block">
|
||||||
<BreadcrumbLink href={item.href} asChild={!!item.href}>
|
<BreadcrumbLink href={item.href} asChild={!!item.href}>
|
||||||
{item.href ? (
|
{item.href ? (
|
||||||
<Link href={item.href}>{item.name}</Link>
|
<Link href={item.href}>{item.name}</Link>
|
||||||
@@ -37,7 +37,7 @@ export const BreadcrumbSidebar = ({ list }: Props) => {
|
|||||||
)}
|
)}
|
||||||
</BreadcrumbLink>
|
</BreadcrumbLink>
|
||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
<BreadcrumbSeparator className="hidden md:block" />
|
<BreadcrumbSeparator className="block" />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
</BreadcrumbList>
|
</BreadcrumbList>
|
||||||
|
|||||||
@@ -329,7 +329,7 @@ const SidebarInset = React.forwardRef<
|
|||||||
<main
|
<main
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex min-h-svh flex-1 flex-col bg-background",
|
"relative flex min-h-svh overflow-auto w-full flex-col bg-background",
|
||||||
"peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow",
|
"peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -239,8 +239,8 @@ const Project = (
|
|||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Card className="h-full bg-sidebar p-2.5 rounded-xl ">
|
<Card className="h-full bg-sidebar p-2.5 rounded-xl ">
|
||||||
<div className="rounded-xl bg-background shadow-md ">
|
<div className="rounded-xl bg-background shadow-md ">
|
||||||
<div className="flex justify-between gap-4 w-full items-center">
|
<div className="flex justify-between gap-4 w-full items-center flex-wrap p-6">
|
||||||
<CardHeader className="">
|
<CardHeader className="p-0">
|
||||||
<CardTitle className="text-xl flex flex-row gap-2">
|
<CardTitle className="text-xl flex flex-row gap-2">
|
||||||
<FolderInput className="size-6 text-muted-foreground self-center" />
|
<FolderInput className="size-6 text-muted-foreground self-center" />
|
||||||
{data?.name}
|
{data?.name}
|
||||||
@@ -248,7 +248,7 @@ const Project = (
|
|||||||
<CardDescription>{data?.description}</CardDescription>
|
<CardDescription>{data?.description}</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
{(auth?.rol === "admin" || user?.canCreateServices) && (
|
{(auth?.rol === "admin" || user?.canCreateServices) && (
|
||||||
<div className="flex flex-row gap-4 flex-wrap px-4">
|
<div className="flex flex-row gap-4 flex-wrap">
|
||||||
<ProjectEnvironment projectId={projectId}>
|
<ProjectEnvironment projectId={projectId}>
|
||||||
<Button variant="outline">Project Environment</Button>
|
<Button variant="outline">Project Environment</Button>
|
||||||
</ProjectEnvironment>
|
</ProjectEnvironment>
|
||||||
|
|||||||
@@ -8,11 +8,7 @@ import type { ReactElement } from "react";
|
|||||||
import superjson from "superjson";
|
import superjson from "superjson";
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
return (
|
return <SwarmMonitorCard />;
|
||||||
<>
|
|
||||||
<SwarmMonitorCard />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Dashboard;
|
export default Dashboard;
|
||||||
|
|||||||
Reference in New Issue
Block a user