mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat: initial commit
This commit is contained in:
72
pages/dashboard/docker.tsx
Normal file
72
pages/dashboard/docker.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { appRouter } from "@/server/api/root";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import React, { type ReactElement } from "react";
|
||||
import superjson from "superjson";
|
||||
import { ShowContainers } from "@/components/dashboard/docker/show/show-containers";
|
||||
|
||||
const Dashboard = () => {
|
||||
return <ShowContainers />;
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
|
||||
Dashboard.getLayout = (page: ReactElement) => {
|
||||
return <DashboardLayout tab={"docker"}>{page}</DashboardLayout>;
|
||||
};
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||
) {
|
||||
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
const { req, res, resolvedUrl } = ctx;
|
||||
|
||||
const helpers = createServerSideHelpers({
|
||||
router: appRouter,
|
||||
ctx: {
|
||||
req: req as any,
|
||||
res: res as any,
|
||||
db: null as any,
|
||||
session: session,
|
||||
user: user,
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
try {
|
||||
await helpers.project.all.prefetch();
|
||||
const auth = await helpers.auth.get.fetch();
|
||||
|
||||
if (auth.rol === "user") {
|
||||
const user = await helpers.user.byAuthId.fetch({
|
||||
authId: auth.id,
|
||||
});
|
||||
|
||||
if (!user.canAccessToDocker) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
}
|
||||
}
|
||||
32
pages/dashboard/monitoring.tsx
Normal file
32
pages/dashboard/monitoring.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { ShowMonitoring } from "@/components/dashboard/monitoring/web-server/show";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import React, { type ReactElement } from "react";
|
||||
|
||||
const Dashboard = () => {
|
||||
return <ShowMonitoring />;
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
|
||||
Dashboard.getLayout = (page: ReactElement) => {
|
||||
return <DashboardLayout tab={"monitoring"}>{page}</DashboardLayout>;
|
||||
};
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||
) {
|
||||
const { user } = await validateRequest(ctx.req, ctx.res);
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
}
|
||||
334
pages/dashboard/project/[projectId].tsx
Normal file
334
pages/dashboard/project/[projectId].tsx
Normal file
@@ -0,0 +1,334 @@
|
||||
import { AddApplication } from "@/components/dashboard/project/add-application";
|
||||
import { AddDatabase } from "@/components/dashboard/project/add-database";
|
||||
import {
|
||||
MariadbIcon,
|
||||
MongodbIcon,
|
||||
MysqlIcon,
|
||||
PostgresqlIcon,
|
||||
RedisIcon,
|
||||
} from "@/components/icons/data-tools-icons";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { ProjectLayout } from "@/components/layouts/project-layout";
|
||||
import { DateTooltip } from "@/components/shared/date-tooltip";
|
||||
import { StatusTooltip } from "@/components/shared/status-tooltip";
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
} from "@/components/ui/breadcrumb";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { appRouter } from "@/server/api/root";
|
||||
import type { findProjectById } from "@/server/api/services/project";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import { api } from "@/utils/api";
|
||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||
import { FolderInput, GlobeIcon, PlusIcon } from "lucide-react";
|
||||
import type {
|
||||
GetServerSidePropsContext,
|
||||
InferGetServerSidePropsType,
|
||||
} from "next";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { type ReactElement } from "react";
|
||||
import superjson from "superjson";
|
||||
|
||||
export type Services = {
|
||||
name: string;
|
||||
type: "mariadb" | "application" | "postgres" | "mysql" | "mongo" | "redis";
|
||||
description?: string | null;
|
||||
id: string;
|
||||
createdAt: string;
|
||||
status?: "idle" | "running" | "done" | "error";
|
||||
};
|
||||
|
||||
type Project = Awaited<ReturnType<typeof findProjectById>>;
|
||||
|
||||
export const extractServices = (data: Project | undefined) => {
|
||||
const applications: Services[] =
|
||||
data?.applications.map((item) => ({
|
||||
name: item.name,
|
||||
type: "application",
|
||||
id: item.applicationId,
|
||||
createdAt: item.createdAt,
|
||||
status: item.applicationStatus,
|
||||
description: item.description,
|
||||
})) || [];
|
||||
|
||||
const mariadb: Services[] =
|
||||
data?.mariadb.map((item) => ({
|
||||
name: item.name,
|
||||
type: "mariadb",
|
||||
id: item.mariadbId,
|
||||
createdAt: item.createdAt,
|
||||
status: item.applicationStatus,
|
||||
description: item.description,
|
||||
})) || [];
|
||||
|
||||
const postgres: Services[] =
|
||||
data?.postgres.map((item) => ({
|
||||
name: item.name,
|
||||
type: "postgres",
|
||||
id: item.postgresId,
|
||||
createdAt: item.createdAt,
|
||||
status: item.applicationStatus,
|
||||
description: item.description,
|
||||
})) || [];
|
||||
|
||||
const mongo: Services[] =
|
||||
data?.mongo.map((item) => ({
|
||||
name: item.name,
|
||||
type: "mongo",
|
||||
id: item.mongoId,
|
||||
createdAt: item.createdAt,
|
||||
status: item.applicationStatus,
|
||||
description: item.description,
|
||||
})) || [];
|
||||
|
||||
const redis: Services[] =
|
||||
data?.redis.map((item) => ({
|
||||
name: item.name,
|
||||
type: "redis",
|
||||
id: item.redisId,
|
||||
createdAt: item.createdAt,
|
||||
status: item.applicationStatus,
|
||||
description: item.description,
|
||||
})) || [];
|
||||
|
||||
const mysql: Services[] =
|
||||
data?.mysql.map((item) => ({
|
||||
name: item.name,
|
||||
type: "mysql",
|
||||
id: item.mysqlId,
|
||||
createdAt: item.createdAt,
|
||||
status: item.applicationStatus,
|
||||
description: item.description,
|
||||
})) || [];
|
||||
|
||||
applications.push(...mysql, ...redis, ...mongo, ...postgres, ...mariadb);
|
||||
|
||||
applications.sort((a, b) => {
|
||||
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
||||
});
|
||||
|
||||
return applications;
|
||||
};
|
||||
|
||||
const Project = (
|
||||
props: InferGetServerSidePropsType<typeof getServerSideProps>,
|
||||
) => {
|
||||
const { projectId } = props;
|
||||
const { data: auth } = api.auth.get.useQuery();
|
||||
const { data: user } = api.user.byAuthId.useQuery(
|
||||
{
|
||||
authId: auth?.id || "",
|
||||
},
|
||||
{
|
||||
enabled: !!auth?.id && auth?.rol === "user",
|
||||
},
|
||||
);
|
||||
const { data } = api.project.one.useQuery({ projectId });
|
||||
const router = useRouter();
|
||||
|
||||
const emptyServices =
|
||||
data?.mariadb?.length === 0 &&
|
||||
data?.mongo?.length === 0 &&
|
||||
data?.mysql?.length === 0 &&
|
||||
data?.postgres?.length === 0 &&
|
||||
data?.redis?.length === 0 &&
|
||||
data?.applications?.length === 0;
|
||||
|
||||
const applications = extractServices(data);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink as={Link} href="/dashboard/projects">
|
||||
Projects
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbItem isCurrentPage>
|
||||
<BreadcrumbLink>{data?.name}</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
<header className="mb-6 flex w-full items-center justify-between flex-wrap gap-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-xl font-bold lg:text-3xl">{data?.name}</h1>
|
||||
|
||||
<p className="lg:text-medium text-muted-foreground">
|
||||
{data?.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{(auth?.rol === "admin" || user?.canCreateServices) && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
Create Service
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-[200px] space-y-2" align="end">
|
||||
<DropdownMenuLabel className="text-sm font-normal ">
|
||||
Actions
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<AddApplication projectId={projectId} />
|
||||
<AddDatabase projectId={projectId} />
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full gap-8 ">
|
||||
{emptyServices ? (
|
||||
<div className="flex h-[70vh] w-full flex-col items-center justify-center">
|
||||
<FolderInput className="size-10 md:size-28 text-muted" />
|
||||
<span className="text-center font-medium text-muted-foreground">
|
||||
No services added yet. Click on Create Service.
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex w-full flex-col gap-4">
|
||||
<div className="grid gap-5 pb-10 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{applications?.map((service) => (
|
||||
<Card
|
||||
key={service.id}
|
||||
onClick={() => {
|
||||
router.push(
|
||||
`/dashboard/project/${projectId}/services/${service.type}/${service.id}`,
|
||||
);
|
||||
}}
|
||||
className="group relative cursor-pointer bg-transparent transition-colors hover:bg-card h-fit"
|
||||
>
|
||||
<div className="absolute -right-1 -top-1">
|
||||
<StatusTooltip status={service.status} />
|
||||
</div>
|
||||
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center justify-between">
|
||||
<div className="flex flex-row items-center gap-2 justify-between w-full">
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="text-base flex items-center gap-2 font-medium leading-none flex-wrap">
|
||||
{service.name}
|
||||
</span>
|
||||
{service.description && (
|
||||
<span className="text-sm font-medium text-muted-foreground">
|
||||
{service.description}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<span className="text-sm font-medium text-muted-foreground self-start">
|
||||
{service.type === "postgres" && (
|
||||
<PostgresqlIcon className="h-7 w-7" />
|
||||
)}
|
||||
{service.type === "redis" && (
|
||||
<RedisIcon className="h-7 w-7" />
|
||||
)}
|
||||
{service.type === "mariadb" && (
|
||||
<MariadbIcon className="h-7 w-7" />
|
||||
)}
|
||||
{service.type === "mongo" && (
|
||||
<MongodbIcon className="h-7 w-7" />
|
||||
)}
|
||||
{service.type === "mysql" && (
|
||||
<MysqlIcon className="h-7 w-7" />
|
||||
)}
|
||||
{service.type === "application" && (
|
||||
<GlobeIcon className="h-6 w-6" />
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardFooter className="">
|
||||
<div className="space-y-1 text-sm">
|
||||
<DateTooltip date={service.createdAt}>
|
||||
Created
|
||||
</DateTooltip>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Project;
|
||||
Project.getLayout = (page: ReactElement) => {
|
||||
return <ProjectLayout>{page}</ProjectLayout>;
|
||||
};
|
||||
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ projectId: string }>,
|
||||
) {
|
||||
const { params } = ctx;
|
||||
|
||||
const { req, res } = ctx;
|
||||
const { user, session } = await validateRequest(req, res);
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
// Fetch data from external API
|
||||
const helpers = createServerSideHelpers({
|
||||
router: appRouter,
|
||||
ctx: {
|
||||
req: req as any,
|
||||
res: res as any,
|
||||
db: null as any,
|
||||
session: session,
|
||||
user: user,
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
|
||||
// Valid project, if not return to initial homepage....
|
||||
if (typeof params?.projectId === "string") {
|
||||
try {
|
||||
await helpers.project.one.fetch({
|
||||
projectId: params?.projectId,
|
||||
});
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
projectId: params?.projectId,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
import { AddCommand } from "@/components/dashboard/application/advanced/general/add-command";
|
||||
import { ShowPorts } from "@/components/dashboard/application/advanced/ports/show-port";
|
||||
import { ShowRedirects } from "@/components/dashboard/application/advanced/redirects/show-redirects";
|
||||
import { ShowSecurity } from "@/components/dashboard/application/advanced/security/show-security";
|
||||
import { ShowApplicationResources } from "@/components/dashboard/application/advanced/show-application-advanced-settings";
|
||||
import { ShowTraefikConfig } from "@/components/dashboard/application/advanced/traefik/show-traefik-config";
|
||||
import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes";
|
||||
import { DeleteApplication } from "@/components/dashboard/application/delete-application";
|
||||
import { ShowDeployments } from "@/components/dashboard/application/deployments/show-deployments";
|
||||
import { ShowDomains } from "@/components/dashboard/application/domains/show-domains";
|
||||
import { ShowEnviroment } from "@/components/dashboard/application/enviroment/show";
|
||||
import { ShowGeneralApplication } from "@/components/dashboard/application/general/show";
|
||||
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
|
||||
import { UpdateApplication } from "@/components/dashboard/application/update-application";
|
||||
import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { ProjectLayout } from "@/components/layouts/project-layout";
|
||||
import { StatusTooltip } from "@/components/shared/status-tooltip";
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
} from "@/components/ui/breadcrumb";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { appRouter } from "@/server/api/root";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import { api } from "@/utils/api";
|
||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||
import { GlobeIcon } from "lucide-react";
|
||||
import type {
|
||||
GetServerSidePropsContext,
|
||||
InferGetServerSidePropsType,
|
||||
} from "next";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useState, type ReactElement } from "react";
|
||||
import superjson from "superjson";
|
||||
|
||||
type TabState =
|
||||
| "projects"
|
||||
| "settings"
|
||||
| "advanced"
|
||||
| "deployments"
|
||||
| "domains"
|
||||
| "monitoring";
|
||||
|
||||
const Service = (
|
||||
props: InferGetServerSidePropsType<typeof getServerSideProps>,
|
||||
) => {
|
||||
const { applicationId, activeTab } = props;
|
||||
const router = useRouter();
|
||||
const { projectId } = router.query;
|
||||
const [tab, setSab] = useState<TabState>(activeTab);
|
||||
const { data } = api.application.one.useQuery(
|
||||
{ applicationId },
|
||||
{
|
||||
refetchInterval: 5000,
|
||||
},
|
||||
);
|
||||
|
||||
const { data: auth } = api.auth.get.useQuery();
|
||||
const { data: user } = api.user.byAuthId.useQuery(
|
||||
{
|
||||
authId: auth?.id || "",
|
||||
},
|
||||
{
|
||||
enabled: !!auth?.id && auth?.rol === "user",
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="pb-10">
|
||||
<div className="flex flex-col gap-4">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink as={Link} href="/dashboard/projects">
|
||||
Projects
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink
|
||||
as={Link}
|
||||
href={`/dashboard/project/${data?.project.projectId}`}
|
||||
>
|
||||
{data?.project.name}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
|
||||
<BreadcrumbItem isCurrentPage>
|
||||
<BreadcrumbLink>{data?.name}</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
<header className="mb-6 flex w-full items-center justify-between max-sm:flex-wrap gap-4">
|
||||
<div className="flex flex-col justify-between w-fit gap-2">
|
||||
<div className="flex flex-row items-center gap-2 xl:gap-4 flex-wrap">
|
||||
<h1 className="flex items-center gap-2 text-xl font-bold lg:text-3xl">
|
||||
{data?.name}
|
||||
</h1>
|
||||
<span className="text-sm">{data?.appName}</span>
|
||||
</div>
|
||||
|
||||
{data?.description && (
|
||||
<p className="text-sm text-muted-foreground max-w-6xl">
|
||||
{data?.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="relative flex flex-row gap-4">
|
||||
<div className="absolute -right-1 -top-2">
|
||||
<StatusTooltip status={data?.applicationStatus} />
|
||||
</div>
|
||||
|
||||
<GlobeIcon className="h-6 w-6 text-muted-foreground" />
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
<Tabs
|
||||
value={tab}
|
||||
defaultValue="general"
|
||||
className="w-full"
|
||||
onValueChange={(e) => {
|
||||
setSab(e as TabState);
|
||||
const newPath = `/dashboard/project/${projectId}/services/application/${applicationId}?tab=${e}`;
|
||||
router.push(newPath, undefined, { shallow: true });
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-row items-center justify-between w-full gap-4">
|
||||
<TabsList className="md:grid md:w-fit md:grid-cols-7 max-md:overflow-y-scroll justify-start">
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
<TabsTrigger value="enviroment">Enviroment</TabsTrigger>
|
||||
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
<TabsTrigger value="deployments">Deployments</TabsTrigger>
|
||||
<TabsTrigger value="domains">Domains</TabsTrigger>
|
||||
<TabsTrigger value="advanced">Advanced</TabsTrigger>
|
||||
</TabsList>
|
||||
<div className="flex flex-row gap-4">
|
||||
<UpdateApplication applicationId={applicationId} />
|
||||
{(auth?.rol === "admin" || user?.canDeleteServices) && (
|
||||
<DeleteApplication applicationId={applicationId} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TabsContent value="general">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowGeneralApplication applicationId={applicationId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="enviroment">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowEnviroment applicationId={applicationId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="monitoring">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<DockerMonitoring appName={data?.appName || ""} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="logs">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowDockerLogs appName={data?.appName || ""} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="deployments" className="w-full">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowDeployments applicationId={applicationId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="domains" className="w-full">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowDomains applicationId={applicationId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="advanced">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<AddCommand applicationId={applicationId} />
|
||||
<ShowApplicationResources applicationId={applicationId} />
|
||||
<ShowVolumes applicationId={applicationId} />
|
||||
<ShowRedirects applicationId={applicationId} />
|
||||
<ShowSecurity applicationId={applicationId} />
|
||||
<ShowPorts applicationId={applicationId} />
|
||||
<ShowTraefikConfig applicationId={applicationId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Service;
|
||||
Service.getLayout = (page: ReactElement) => {
|
||||
return <ProjectLayout>{page}</ProjectLayout>;
|
||||
};
|
||||
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{
|
||||
applicationId: string;
|
||||
activeTab: TabState;
|
||||
}>,
|
||||
) {
|
||||
const { query, params, req, res } = ctx;
|
||||
|
||||
const activeTab = query.tab;
|
||||
const { user, session } = await validateRequest(req, res);
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
// Fetch data from external API
|
||||
const helpers = createServerSideHelpers({
|
||||
router: appRouter,
|
||||
ctx: {
|
||||
req: req as any,
|
||||
res: res as any,
|
||||
db: null as any,
|
||||
session: session,
|
||||
user: user,
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
|
||||
// Valid project, if not return to initial homepage....
|
||||
if (typeof params?.applicationId === "string") {
|
||||
try {
|
||||
await helpers.application.one.fetch({
|
||||
applicationId: params?.applicationId,
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
applicationId: params?.applicationId,
|
||||
activeTab: (activeTab || "general") as TabState,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: "/dashboard",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
|
||||
import { ShowAdvancedMariadb } from "@/components/dashboard/mariadb/advanced/show-mariadb-advanced-settings";
|
||||
import { ShowBackupMariadb } from "@/components/dashboard/mariadb/backups/show-backup-mariadb";
|
||||
import { DeleteMariadb } from "@/components/dashboard/mariadb/delete-mariadb";
|
||||
import { ShowMariadbEnviroment } from "@/components/dashboard/mariadb/enviroment/show-mariadb-enviroment";
|
||||
import { ShowExternalMariadbCredentials } from "@/components/dashboard/mariadb/general/show-external-mariadb-credentials";
|
||||
import { ShowGeneralMariadb } from "@/components/dashboard/mariadb/general/show-general-mariadb";
|
||||
import { ShowInternalMariadbCredentials } from "@/components/dashboard/mariadb/general/show-internal-mariadb-credentials";
|
||||
import { UpdateMariadb } from "@/components/dashboard/mariadb/update-mariadb";
|
||||
import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show";
|
||||
import { MariadbIcon } from "@/components/icons/data-tools-icons";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { ProjectLayout } from "@/components/layouts/project-layout";
|
||||
import { StatusTooltip } from "@/components/shared/status-tooltip";
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
} from "@/components/ui/breadcrumb";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { appRouter } from "@/server/api/root";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import { api } from "@/utils/api";
|
||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||
import type {
|
||||
GetServerSidePropsContext,
|
||||
InferGetServerSidePropsType,
|
||||
} from "next";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useState, type ReactElement } from "react";
|
||||
import superjson from "superjson";
|
||||
|
||||
type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced";
|
||||
|
||||
const Mariadb = (
|
||||
props: InferGetServerSidePropsType<typeof getServerSideProps>,
|
||||
) => {
|
||||
const { mariadbId, activeTab } = props;
|
||||
const router = useRouter();
|
||||
const { projectId } = router.query;
|
||||
const [tab, setSab] = useState<TabState>(activeTab);
|
||||
const { data } = api.mariadb.one.useQuery({ mariadbId });
|
||||
const { data: auth } = api.auth.get.useQuery();
|
||||
const { data: user } = api.user.byAuthId.useQuery(
|
||||
{
|
||||
authId: auth?.id || "",
|
||||
},
|
||||
{
|
||||
enabled: !!auth?.id && auth?.rol === "user",
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="pb-10">
|
||||
<div className="flex flex-col gap-4">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink as={Link} href="/dashboard/projects">
|
||||
Projects
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink
|
||||
as={Link}
|
||||
href={`/dashboard/project/${data?.project.projectId}`}
|
||||
>
|
||||
{data?.project.name}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
|
||||
<BreadcrumbItem isCurrentPage>
|
||||
<BreadcrumbLink>{data?.name}</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
<header className="mb-6 flex w-full items-center justify-between max-sm:flex-wrap gap-4">
|
||||
<div className="flex flex-col justify-between w-fit gap-2">
|
||||
<div className="flex flex-row items-center gap-2 xl:gap-4 flex-wrap">
|
||||
<h1 className="flex items-center gap-2 text-xl font-bold lg:text-3xl">
|
||||
{data?.name}
|
||||
</h1>
|
||||
<span className="text-sm">{data?.appName}</span>
|
||||
</div>
|
||||
|
||||
{data?.description && (
|
||||
<p className="text-sm text-muted-foreground max-w-6xl">
|
||||
{data?.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="relative flex flex-row gap-4">
|
||||
<div className="absolute -right-1 -top-2">
|
||||
<StatusTooltip status={data?.applicationStatus} />
|
||||
</div>
|
||||
<MariadbIcon className="h-8 w-8 text-muted-foreground" />
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
<Tabs
|
||||
value={tab}
|
||||
defaultValue="general"
|
||||
className="w-full"
|
||||
onValueChange={(e) => {
|
||||
setSab(e as TabState);
|
||||
const newPath = `/dashboard/project/${projectId}/services/mariadb/${mariadbId}?tab=${e}`;
|
||||
|
||||
router.push(newPath, undefined, { shallow: true });
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-row items-center justify-between w-full gap-4">
|
||||
<TabsList className="md:grid md:w-fit md:grid-cols-6 max-md:overflow-y-scroll justify-start">
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
<TabsTrigger value="enviroment">Enviroment</TabsTrigger>
|
||||
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
||||
<TabsTrigger value="backups">Backups</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
<TabsTrigger value="advanced">Advanced</TabsTrigger>
|
||||
</TabsList>
|
||||
<div className="flex flex-row gap-4">
|
||||
<UpdateMariadb mariadbId={mariadbId} />
|
||||
{(auth?.rol === "admin" || user?.canDeleteServices) && (
|
||||
<DeleteMariadb mariadbId={mariadbId} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TabsContent value="general">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowGeneralMariadb mariadbId={mariadbId} />
|
||||
<ShowInternalMariadbCredentials mariadbId={mariadbId} />
|
||||
<ShowExternalMariadbCredentials mariadbId={mariadbId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="enviroment">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowMariadbEnviroment mariadbId={mariadbId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="monitoring">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<DockerMonitoring appName={data?.appName || ""} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="logs">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowDockerLogs appName={data?.appName || ""} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="backups">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowBackupMariadb mariadbId={mariadbId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="advanced">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowAdvancedMariadb mariadbId={mariadbId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Mariadb;
|
||||
Mariadb.getLayout = (page: ReactElement) => {
|
||||
return <ProjectLayout>{page}</ProjectLayout>;
|
||||
};
|
||||
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ mariadbId: string; activeTab: TabState }>,
|
||||
) {
|
||||
const { query, params, req, res } = ctx;
|
||||
const activeTab = query.tab;
|
||||
|
||||
const { user, session } = await validateRequest(req, res);
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
// Fetch data from external API
|
||||
const helpers = createServerSideHelpers({
|
||||
router: appRouter,
|
||||
ctx: {
|
||||
req: req as any,
|
||||
res: res as any,
|
||||
db: null as any,
|
||||
session: session,
|
||||
user: user,
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
|
||||
if (typeof params?.mariadbId === "string") {
|
||||
try {
|
||||
await helpers.mariadb.one.fetch({
|
||||
mariadbId: params?.mariadbId,
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
mariadbId: params?.mariadbId,
|
||||
activeTab: (activeTab || "general") as TabState,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: "/dashboard",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
228
pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx
Normal file
228
pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx
Normal file
@@ -0,0 +1,228 @@
|
||||
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
|
||||
import { ShowAdvancedMongo } from "@/components/dashboard/mongo/advanced/show-mongo-advanced-settings";
|
||||
import { ShowBackupMongo } from "@/components/dashboard/mongo/backups/show-backup-mongo";
|
||||
import { DeleteMongo } from "@/components/dashboard/mongo/delete-mongo";
|
||||
import { ShowMongoEnviroment } from "@/components/dashboard/mongo/enviroment/show-mongo-enviroment";
|
||||
import { ShowExternalMongoCredentials } from "@/components/dashboard/mongo/general/show-external-mongo-credentials";
|
||||
import { ShowGeneralMongo } from "@/components/dashboard/mongo/general/show-general-mongo";
|
||||
import { ShowInternalMongoCredentials } from "@/components/dashboard/mongo/general/show-internal-mongo-credentials";
|
||||
import { UpdateMongo } from "@/components/dashboard/mongo/update-mongo";
|
||||
import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show";
|
||||
import { MongodbIcon } from "@/components/icons/data-tools-icons";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { ProjectLayout } from "@/components/layouts/project-layout";
|
||||
import { StatusTooltip } from "@/components/shared/status-tooltip";
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
} from "@/components/ui/breadcrumb";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { appRouter } from "@/server/api/root";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import { api } from "@/utils/api";
|
||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||
import type {
|
||||
GetServerSidePropsContext,
|
||||
InferGetServerSidePropsType,
|
||||
} from "next";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useState, type ReactElement } from "react";
|
||||
import superjson from "superjson";
|
||||
|
||||
type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced";
|
||||
|
||||
const Mongo = (
|
||||
props: InferGetServerSidePropsType<typeof getServerSideProps>,
|
||||
) => {
|
||||
const { mongoId, activeTab } = props;
|
||||
const router = useRouter();
|
||||
const { projectId } = router.query;
|
||||
const [tab, setSab] = useState<TabState>(activeTab);
|
||||
const { data } = api.mongo.one.useQuery({ mongoId });
|
||||
|
||||
const { data: auth } = api.auth.get.useQuery();
|
||||
const { data: user } = api.user.byAuthId.useQuery(
|
||||
{
|
||||
authId: auth?.id || "",
|
||||
},
|
||||
{
|
||||
enabled: !!auth?.id && auth?.rol === "user",
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="pb-10">
|
||||
<div className="flex flex-col gap-4">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink as={Link} href="/dashboard/projects">
|
||||
Projects
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink
|
||||
as={Link}
|
||||
href={`/dashboard/project/${data?.project.projectId}`}
|
||||
>
|
||||
{data?.project.name}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
|
||||
<BreadcrumbItem isCurrentPage>
|
||||
<BreadcrumbLink>{data?.name}</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
<header className="mb-6 flex w-full items-center justify-between max-sm:flex-wrap gap-4">
|
||||
<div className="flex flex-col justify-between w-fit gap-2">
|
||||
<div className="flex flex-row items-center gap-2 xl:gap-4 flex-wrap">
|
||||
<h1 className="flex items-center gap-2 text-xl font-bold lg:text-3xl">
|
||||
{data?.name}
|
||||
</h1>
|
||||
<span className="text-sm">{data?.appName}</span>
|
||||
</div>
|
||||
|
||||
{data?.description && (
|
||||
<p className="text-sm text-muted-foreground max-w-6xl">
|
||||
{data?.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="relative flex flex-row gap-4">
|
||||
<div className="absolute -right-1 -top-2">
|
||||
<StatusTooltip status={data?.applicationStatus} />
|
||||
</div>
|
||||
<MongodbIcon className="h-8 w-8 text-muted-foreground" />
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
<Tabs
|
||||
value={tab}
|
||||
defaultValue="general"
|
||||
className="w-full"
|
||||
onValueChange={(e) => {
|
||||
setSab(e as TabState);
|
||||
const newPath = `/dashboard/project/${projectId}/services/mongo/${mongoId}?tab=${e}`;
|
||||
|
||||
router.push(newPath, undefined, { shallow: true });
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-row items-center justify-between w-full gap-4">
|
||||
<TabsList className="md:grid md:w-fit md:grid-cols-6 max-md:overflow-y-scroll justify-start">
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
<TabsTrigger value="enviroment">Enviroment</TabsTrigger>
|
||||
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
||||
<TabsTrigger value="backups">Backups</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
<TabsTrigger value="advanced">Advanced</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<div className="flex flex-row gap-4">
|
||||
<UpdateMongo mongoId={mongoId} />
|
||||
{(auth?.rol === "admin" || user?.canDeleteServices) && (
|
||||
<DeleteMongo mongoId={mongoId} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TabsContent value="general">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowGeneralMongo mongoId={mongoId} />
|
||||
<ShowInternalMongoCredentials mongoId={mongoId} />
|
||||
<ShowExternalMongoCredentials mongoId={mongoId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="enviroment">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowMongoEnviroment mongoId={mongoId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="monitoring">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<DockerMonitoring appName={data?.appName || ""} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="logs">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowDockerLogs appName={data?.appName || ""} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="backups">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowBackupMongo mongoId={mongoId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="advanced">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowAdvancedMongo mongoId={mongoId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Mongo;
|
||||
Mongo.getLayout = (page: ReactElement) => {
|
||||
return <ProjectLayout>{page}</ProjectLayout>;
|
||||
};
|
||||
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ mongoId: string; activeTab: TabState }>,
|
||||
) {
|
||||
const { query, params, req, res } = ctx;
|
||||
const activeTab = query.tab;
|
||||
|
||||
const { user, session } = await validateRequest(req, res);
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
// Fetch data from external API
|
||||
const helpers = createServerSideHelpers({
|
||||
router: appRouter,
|
||||
ctx: {
|
||||
req: req as any,
|
||||
res: res as any,
|
||||
db: null as any,
|
||||
session: session,
|
||||
user: user,
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
|
||||
if (typeof params?.mongoId === "string") {
|
||||
try {
|
||||
await helpers.mongo.one.fetch({
|
||||
mongoId: params?.mongoId,
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
mongoId: params?.mongoId,
|
||||
activeTab: (activeTab || "general") as TabState,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: "/dashboard",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
227
pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx
Normal file
227
pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx
Normal file
@@ -0,0 +1,227 @@
|
||||
import { ShowAdvancedMysql } from "@/components/dashboard/mysql/advanced/show-mysql-advanced-settings";
|
||||
import { ShowBackupMySql } from "@/components/dashboard/mysql/backups/show-backup-mysql";
|
||||
import { DeleteMysql } from "@/components/dashboard/mysql/delete-mysql";
|
||||
import { ShowMysqlEnviroment } from "@/components/dashboard/mysql/enviroment/show-mysql-enviroment";
|
||||
import { ShowExternalMysqlCredentials } from "@/components/dashboard/mysql/general/show-external-mysql-credentials";
|
||||
import { ShowGeneralMysql } from "@/components/dashboard/mysql/general/show-general-mysql";
|
||||
import { ShowInternalMysqlCredentials } from "@/components/dashboard/mysql/general/show-internal-mysql-credentials";
|
||||
import { MysqlIcon } from "@/components/icons/data-tools-icons";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
} from "@/components/ui/breadcrumb";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { appRouter } from "@/server/api/root";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import { api } from "@/utils/api";
|
||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||
import type {
|
||||
GetServerSidePropsContext,
|
||||
InferGetServerSidePropsType,
|
||||
} from "next";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useState, type ReactElement } from "react";
|
||||
import superjson from "superjson";
|
||||
import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show";
|
||||
import { StatusTooltip } from "@/components/shared/status-tooltip";
|
||||
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
|
||||
import { UpdateMysql } from "@/components/dashboard/mysql/update-mysql";
|
||||
import { ProjectLayout } from "@/components/layouts/project-layout";
|
||||
|
||||
type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced";
|
||||
|
||||
const MySql = (
|
||||
props: InferGetServerSidePropsType<typeof getServerSideProps>,
|
||||
) => {
|
||||
const { mysqlId, activeTab } = props;
|
||||
const router = useRouter();
|
||||
const { projectId } = router.query;
|
||||
const [tab, setSab] = useState<TabState>(activeTab);
|
||||
const { data } = api.mysql.one.useQuery({ mysqlId });
|
||||
const { data: auth } = api.auth.get.useQuery();
|
||||
const { data: user } = api.user.byAuthId.useQuery(
|
||||
{
|
||||
authId: auth?.id || "",
|
||||
},
|
||||
{
|
||||
enabled: !!auth?.id && auth?.rol === "user",
|
||||
},
|
||||
);
|
||||
return (
|
||||
<div className="pb-10">
|
||||
<div className="flex flex-col gap-4">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink as={Link} href="/dashboard/projects">
|
||||
Projects
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink
|
||||
as={Link}
|
||||
href={`/dashboard/project/${data?.project.projectId}`}
|
||||
>
|
||||
{data?.project.name}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
|
||||
<BreadcrumbItem isCurrentPage>
|
||||
<BreadcrumbLink>{data?.name}</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
<header className="mb-6 flex w-full items-center justify-between max-sm:flex-wrap gap-4">
|
||||
<div className="flex flex-col justify-between w-fit gap-2">
|
||||
<div className="flex flex-row items-center gap-2 xl:gap-4 flex-wrap">
|
||||
<h1 className="flex items-center gap-2 text-xl font-bold lg:text-3xl">
|
||||
{data?.name}
|
||||
</h1>
|
||||
<span className="text-sm">{data?.appName}</span>
|
||||
</div>
|
||||
|
||||
{data?.description && (
|
||||
<p className="text-sm text-muted-foreground max-w-6xl">
|
||||
{data?.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="relative flex flex-row gap-4">
|
||||
<div className="absolute -right-1 -top-2">
|
||||
<StatusTooltip status={data?.applicationStatus} />
|
||||
</div>
|
||||
|
||||
<MysqlIcon className="h-8 w-8 text-muted-foreground" />
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
<Tabs
|
||||
value={tab}
|
||||
defaultValue="general"
|
||||
className="w-full"
|
||||
onValueChange={(e) => {
|
||||
setSab(e as TabState);
|
||||
const newPath = `/dashboard/project/${projectId}/services/mysql/${mysqlId}?tab=${e}`;
|
||||
|
||||
router.push(newPath, undefined, { shallow: true });
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-row items-center justify-between w-full gap-4">
|
||||
<TabsList className="md:grid md:w-fit md:grid-cols-6 max-md:overflow-y-scroll justify-start">
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
<TabsTrigger value="enviroment">Enviroment</TabsTrigger>
|
||||
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
||||
<TabsTrigger value="backups">Backups</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
<TabsTrigger value="advanced">Advanced</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<div className="flex flex-row gap-4">
|
||||
<UpdateMysql mysqlId={mysqlId} />
|
||||
{(auth?.rol === "admin" || user?.canDeleteServices) && (
|
||||
<DeleteMysql mysqlId={mysqlId} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TabsContent value="general">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowGeneralMysql mysqlId={mysqlId} />
|
||||
<ShowInternalMysqlCredentials mysqlId={mysqlId} />
|
||||
<ShowExternalMysqlCredentials mysqlId={mysqlId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="enviroment" className="w-full">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowMysqlEnviroment mysqlId={mysqlId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="monitoring">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<DockerMonitoring appName={data?.appName || ""} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="logs">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowDockerLogs appName={data?.appName || ""} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="backups">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowBackupMySql mysqlId={mysqlId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="advanced">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowAdvancedMysql mysqlId={mysqlId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MySql;
|
||||
MySql.getLayout = (page: ReactElement) => {
|
||||
return <ProjectLayout>{page}</ProjectLayout>;
|
||||
};
|
||||
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ mysqlId: string; activeTab: TabState }>,
|
||||
) {
|
||||
const { query, params, req, res } = ctx;
|
||||
const activeTab = query.tab;
|
||||
|
||||
const { user, session } = await validateRequest(req, res);
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
// Fetch data from external API
|
||||
const helpers = createServerSideHelpers({
|
||||
router: appRouter,
|
||||
ctx: {
|
||||
req: req as any,
|
||||
res: res as any,
|
||||
db: null as any,
|
||||
session: session,
|
||||
user: user,
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
|
||||
if (typeof params?.mysqlId === "string") {
|
||||
try {
|
||||
await helpers.mysql.one.fetch({
|
||||
mysqlId: params?.mysqlId,
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
mysqlId: params?.mysqlId,
|
||||
activeTab: (activeTab || "general") as TabState,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: "/dashboard",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
import { ShowAdvancedPostgres } from "@/components/dashboard/postgres/advanced/show-postgres-advanced-settings";
|
||||
import { ShowBackupPostgres } from "@/components/dashboard/postgres/backups/show-backup-postgres";
|
||||
import { DeletePostgres } from "@/components/dashboard/postgres/delete-postgres";
|
||||
import { ShowPostgresEnviroment } from "@/components/dashboard/postgres/enviroment/show-postgres-enviroment";
|
||||
import { ShowExternalPostgresCredentials } from "@/components/dashboard/postgres/general/show-external-postgres-credentials";
|
||||
import { ShowGeneralPostgres } from "@/components/dashboard/postgres/general/show-general-postgres";
|
||||
import { ShowInternalPostgresCredentials } from "@/components/dashboard/postgres/general/show-internal-postgres-credentials";
|
||||
import { PostgresqlIcon } from "@/components/icons/data-tools-icons";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
} from "@/components/ui/breadcrumb";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { appRouter } from "@/server/api/root";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import { api } from "@/utils/api";
|
||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||
import type {
|
||||
GetServerSidePropsContext,
|
||||
InferGetServerSidePropsType,
|
||||
} from "next";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useState, type ReactElement } from "react";
|
||||
import superjson from "superjson";
|
||||
import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show";
|
||||
import { StatusTooltip } from "@/components/shared/status-tooltip";
|
||||
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
|
||||
import { UpdatePostgres } from "@/components/dashboard/postgres/update-postgres";
|
||||
import { ProjectLayout } from "@/components/layouts/project-layout";
|
||||
|
||||
type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced";
|
||||
|
||||
const Postgresql = (
|
||||
props: InferGetServerSidePropsType<typeof getServerSideProps>,
|
||||
) => {
|
||||
const { postgresId, activeTab } = props;
|
||||
const router = useRouter();
|
||||
const { projectId } = router.query;
|
||||
const [tab, setSab] = useState<TabState>(activeTab);
|
||||
const { data } = api.postgres.one.useQuery({ postgresId });
|
||||
const { data: auth } = api.auth.get.useQuery();
|
||||
const { data: user } = api.user.byAuthId.useQuery(
|
||||
{
|
||||
authId: auth?.id || "",
|
||||
},
|
||||
{
|
||||
enabled: !!auth?.id && auth?.rol === "user",
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="pb-10">
|
||||
<div className="flex flex-col gap-4">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink as={Link} href="/dashboard/projects">
|
||||
Projects
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink
|
||||
as={Link}
|
||||
href={`/dashboard/project/${data?.project.projectId}`}
|
||||
>
|
||||
{data?.project.name}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
|
||||
<BreadcrumbItem isCurrentPage>
|
||||
<BreadcrumbLink>{data?.name}</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
<header className="mb-6 flex w-full items-center justify-between max-sm:flex-wrap gap-4">
|
||||
<div className="flex flex-col justify-between w-fit gap-2">
|
||||
<div className="flex flex-row items-center gap-2 xl:gap-4 flex-wrap">
|
||||
<h1 className="flex items-center gap-2 text-xl font-bold lg:text-3xl">
|
||||
{data?.name}
|
||||
</h1>
|
||||
<span className="text-sm">{data?.appName}</span>
|
||||
</div>
|
||||
|
||||
{data?.description && (
|
||||
<p className="text-sm text-muted-foreground max-w-6xl">
|
||||
{data?.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="relative flex flex-row gap-4">
|
||||
<div className="absolute -right-1 -top-2">
|
||||
<StatusTooltip status={data?.applicationStatus} />
|
||||
</div>
|
||||
|
||||
<PostgresqlIcon className="h-8 w-8 text-muted-foreground" />
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
<Tabs
|
||||
value={tab}
|
||||
defaultValue="general"
|
||||
className="w-full"
|
||||
onValueChange={(e) => {
|
||||
setSab(e as TabState);
|
||||
const newPath = `/dashboard/project/${projectId}/services/postgres/${postgresId}?tab=${e}`;
|
||||
|
||||
router.push(newPath, undefined, { shallow: true });
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-row items-center justify-between w-full gap-4">
|
||||
<TabsList className="md:grid md:w-fit md:grid-cols-6 max-md:overflow-y-scroll justify-start">
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
<TabsTrigger value="enviroment">Enviroment</TabsTrigger>
|
||||
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
||||
<TabsTrigger value="backups">Backups</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
<TabsTrigger value="advanced">Advanced</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<div className="flex flex-row gap-4">
|
||||
<UpdatePostgres postgresId={postgresId} />
|
||||
{(auth?.rol === "admin" || user?.canDeleteServices) && (
|
||||
<DeletePostgres postgresId={postgresId} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TabsContent value="general">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowGeneralPostgres postgresId={postgresId} />
|
||||
<ShowInternalPostgresCredentials postgresId={postgresId} />
|
||||
<ShowExternalPostgresCredentials postgresId={postgresId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="enviroment">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowPostgresEnviroment postgresId={postgresId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="monitoring">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<DockerMonitoring appName={data?.appName || ""} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="logs">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowDockerLogs appName={data?.appName || ""} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="backups">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowBackupPostgres postgresId={postgresId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="advanced">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowAdvancedPostgres postgresId={postgresId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Postgresql;
|
||||
Postgresql.getLayout = (page: ReactElement) => {
|
||||
return <ProjectLayout>{page}</ProjectLayout>;
|
||||
};
|
||||
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ postgresId: string; activeTab: TabState }>,
|
||||
) {
|
||||
const { query, params, req, res } = ctx;
|
||||
const activeTab = query.tab;
|
||||
const { user, session } = await validateRequest(req, res);
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
// Fetch data from external API
|
||||
const helpers = createServerSideHelpers({
|
||||
router: appRouter,
|
||||
ctx: {
|
||||
req: req as any,
|
||||
res: res as any,
|
||||
db: null as any,
|
||||
session: session,
|
||||
user: user,
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
|
||||
if (typeof params?.postgresId === "string") {
|
||||
try {
|
||||
await helpers.postgres.one.fetch({
|
||||
postgresId: params?.postgresId,
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
postgresId: params?.postgresId,
|
||||
activeTab: (activeTab || "general") as TabState,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: "/dashboard",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
220
pages/dashboard/project/[projectId]/services/redis/[redisId].tsx
Normal file
220
pages/dashboard/project/[projectId]/services/redis/[redisId].tsx
Normal file
@@ -0,0 +1,220 @@
|
||||
import { ShowAdvancedRedis } from "@/components/dashboard/redis/advanced/show-redis-advanced-settings";
|
||||
import { DeleteRedis } from "@/components/dashboard/redis/delete-redis";
|
||||
import { ShowRedisEnviroment } from "@/components/dashboard/redis/enviroment/show-redis-enviroment";
|
||||
import { ShowExternalRedisCredentials } from "@/components/dashboard/redis/general/show-external-redis-credentials";
|
||||
import { ShowGeneralRedis } from "@/components/dashboard/redis/general/show-general-redis";
|
||||
import { ShowInternalRedisCredentials } from "@/components/dashboard/redis/general/show-internal-redis-credentials";
|
||||
import { RedisIcon } from "@/components/icons/data-tools-icons";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
} from "@/components/ui/breadcrumb";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { appRouter } from "@/server/api/root";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import { api } from "@/utils/api";
|
||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||
import type {
|
||||
GetServerSidePropsContext,
|
||||
InferGetServerSidePropsType,
|
||||
} from "next";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useState, type ReactElement } from "react";
|
||||
import superjson from "superjson";
|
||||
import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show";
|
||||
import { StatusTooltip } from "@/components/shared/status-tooltip";
|
||||
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
|
||||
import { UpdateRedis } from "@/components/dashboard/redis/update-redis";
|
||||
import { ProjectLayout } from "@/components/layouts/project-layout";
|
||||
|
||||
type TabState = "projects" | "monitoring" | "settings" | "advanced";
|
||||
|
||||
const Redis = (
|
||||
props: InferGetServerSidePropsType<typeof getServerSideProps>,
|
||||
) => {
|
||||
const { redisId, activeTab } = props;
|
||||
const router = useRouter();
|
||||
const { projectId } = router.query;
|
||||
const [tab, setSab] = useState<TabState>(activeTab);
|
||||
const { data } = api.redis.one.useQuery({ redisId });
|
||||
|
||||
const { data: auth } = api.auth.get.useQuery();
|
||||
const { data: user } = api.user.byAuthId.useQuery(
|
||||
{
|
||||
authId: auth?.id || "",
|
||||
},
|
||||
{
|
||||
enabled: !!auth?.id && auth?.rol === "user",
|
||||
},
|
||||
);
|
||||
return (
|
||||
<div className="pb-10">
|
||||
<div className="flex flex-col gap-4">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink as={Link} href="/dashboard/projects">
|
||||
Projects
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink
|
||||
as={Link}
|
||||
href={`/dashboard/project/${data?.project.projectId}`}
|
||||
>
|
||||
{data?.project.name}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
|
||||
<BreadcrumbItem isCurrentPage>
|
||||
<BreadcrumbLink>{data?.name}</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
<header className="mb-6 flex w-full items-center justify-between max-sm:flex-wrap gap-4">
|
||||
<div className="flex flex-col justify-between w-fit gap-2">
|
||||
<div className="flex flex-row items-center gap-2 xl:gap-4 flex-wrap">
|
||||
<h1 className="flex items-center gap-2 text-xl font-bold lg:text-3xl">
|
||||
{data?.name}
|
||||
</h1>
|
||||
<span className="text-sm">{data?.appName}</span>
|
||||
</div>
|
||||
|
||||
{data?.description && (
|
||||
<p className="text-sm text-muted-foreground max-w-6xl">
|
||||
{data?.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="relative flex flex-row gap-4">
|
||||
<div className="absolute -right-1 -top-2">
|
||||
<StatusTooltip status={data?.applicationStatus} />
|
||||
</div>
|
||||
|
||||
<RedisIcon className="h-8 w-8 text-muted-foreground" />
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
<Tabs
|
||||
value={tab}
|
||||
defaultValue="general"
|
||||
className="w-full"
|
||||
onValueChange={(e) => {
|
||||
setSab(e as TabState);
|
||||
const newPath = `/dashboard/project/${projectId}/services/redis/${redisId}?tab=${e}`;
|
||||
|
||||
router.push(newPath, undefined, { shallow: true });
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-row items-center justify-between w-full gap-4">
|
||||
<TabsList className="md:grid md:w-fit md:grid-cols-5 max-md:overflow-y-scroll justify-start">
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
<TabsTrigger value="enviroment">Enviroment</TabsTrigger>
|
||||
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
||||
<TabsTrigger value="logs">Logs</TabsTrigger>
|
||||
<TabsTrigger value="advanced">Advanced</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<div className="flex flex-row gap-4">
|
||||
<UpdateRedis redisId={redisId} />
|
||||
{(auth?.rol === "admin" || user?.canDeleteServices) && (
|
||||
<DeleteRedis redisId={redisId} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TabsContent value="general">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowGeneralRedis redisId={redisId} />
|
||||
<ShowInternalRedisCredentials redisId={redisId} />
|
||||
<ShowExternalRedisCredentials redisId={redisId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="enviroment">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowRedisEnviroment redisId={redisId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="monitoring">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<DockerMonitoring appName={data?.appName || ""} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="logs">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowDockerLogs appName={data?.appName || ""} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="advanced">
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<ShowAdvancedRedis redisId={redisId} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Redis;
|
||||
Redis.getLayout = (page: ReactElement) => {
|
||||
return <ProjectLayout>{page}</ProjectLayout>;
|
||||
};
|
||||
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ redisId: string; activeTab: TabState }>,
|
||||
) {
|
||||
const { query, params, req, res } = ctx;
|
||||
const activeTab = query.tab;
|
||||
|
||||
const { user, session } = await validateRequest(req, res);
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
// Fetch data from external API
|
||||
const helpers = createServerSideHelpers({
|
||||
router: appRouter,
|
||||
ctx: {
|
||||
req: req as any,
|
||||
res: res as any,
|
||||
db: null as any,
|
||||
session: session,
|
||||
user: user,
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
if (typeof params?.redisId === "string") {
|
||||
try {
|
||||
await helpers.redis.one.fetch({
|
||||
redisId: params?.redisId,
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
redisId: params?.redisId,
|
||||
activeTab: (activeTab || "general") as TabState,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: "/dashboard",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
32
pages/dashboard/projects.tsx
Normal file
32
pages/dashboard/projects.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { ShowProjects } from "@/components/dashboard/projects/show";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import React, { type ReactElement } from "react";
|
||||
|
||||
const Dashboard = () => {
|
||||
return <ShowProjects />;
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
|
||||
Dashboard.getLayout = (page: ReactElement) => {
|
||||
return <DashboardLayout tab={"projects"}>{page}</DashboardLayout>;
|
||||
};
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||
) {
|
||||
const { user } = await validateRequest(ctx.req, ctx.res);
|
||||
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
}
|
||||
41
pages/dashboard/settings/appearance.tsx
Normal file
41
pages/dashboard/settings/appearance.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { AppearanceForm } from "@/components/dashboard/settings/appearance-form";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import React, { type ReactElement } from "react";
|
||||
|
||||
const Page = () => {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
<AppearanceForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
Page.getLayout = (page: ReactElement) => {
|
||||
return (
|
||||
<DashboardLayout tab={"settings"}>
|
||||
<SettingsLayout>{page}</SettingsLayout>
|
||||
</DashboardLayout>
|
||||
);
|
||||
};
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||
) {
|
||||
const { user } = await validateRequest(ctx.req, ctx.res);
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
}
|
||||
41
pages/dashboard/settings/certificates.tsx
Normal file
41
pages/dashboard/settings/certificates.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { ShowCertificates } from "@/components/dashboard/settings/certificates/show-certificates";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import React, { type ReactElement } from "react";
|
||||
|
||||
const Page = () => {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
<ShowCertificates />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
Page.getLayout = (page: ReactElement) => {
|
||||
return (
|
||||
<DashboardLayout tab={"settings"}>
|
||||
<SettingsLayout>{page}</SettingsLayout>
|
||||
</DashboardLayout>
|
||||
);
|
||||
};
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||
) {
|
||||
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
||||
if (!user || user.rol === "user") {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
}
|
||||
41
pages/dashboard/settings/destinations.tsx
Normal file
41
pages/dashboard/settings/destinations.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { ShowDestinations } from "@/components/dashboard/settings/destination/show-destinations";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import React, { type ReactElement } from "react";
|
||||
|
||||
const Page = () => {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
<ShowDestinations />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
Page.getLayout = (page: ReactElement) => {
|
||||
return (
|
||||
<DashboardLayout tab={"settings"}>
|
||||
<SettingsLayout>{page}</SettingsLayout>
|
||||
</DashboardLayout>
|
||||
);
|
||||
};
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||
) {
|
||||
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
||||
if (!user || user.rol === "user") {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
}
|
||||
41
pages/dashboard/settings/profile.tsx
Normal file
41
pages/dashboard/settings/profile.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { ProfileForm } from "@/components/dashboard/settings/profile/profile-form";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import React, { type ReactElement } from "react";
|
||||
|
||||
const Page = () => {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
<ProfileForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
Page.getLayout = (page: ReactElement) => {
|
||||
return (
|
||||
<DashboardLayout tab={"settings"}>
|
||||
<SettingsLayout>{page}</SettingsLayout>
|
||||
</DashboardLayout>
|
||||
);
|
||||
};
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||
) {
|
||||
const { user } = await validateRequest(ctx.req, ctx.res);
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
}
|
||||
54
pages/dashboard/settings/server.tsx
Normal file
54
pages/dashboard/settings/server.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { GithubSetup } from "@/components/dashboard/settings/github/github-setup";
|
||||
import { WebDomain } from "@/components/dashboard/settings/web-domain";
|
||||
import { WebServer } from "@/components/dashboard/settings/web-server";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import React, { type ReactElement } from "react";
|
||||
|
||||
const Page = () => {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
<WebDomain />
|
||||
<GithubSetup />
|
||||
<WebServer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
Page.getLayout = (page: ReactElement) => {
|
||||
return (
|
||||
<DashboardLayout tab={"settings"}>
|
||||
<SettingsLayout>{page}</SettingsLayout>
|
||||
</DashboardLayout>
|
||||
);
|
||||
};
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||
) {
|
||||
const { user } = await validateRequest(ctx.req, ctx.res);
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
console.log(user);
|
||||
if (user.rol === "user") {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/dashboard/settings/profile",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
}
|
||||
41
pages/dashboard/settings/users.tsx
Normal file
41
pages/dashboard/settings/users.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { ShowUsers } from "@/components/dashboard/settings/users/show-users";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import React, { type ReactElement } from "react";
|
||||
|
||||
const Page = () => {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 w-full">
|
||||
<ShowUsers />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
Page.getLayout = (page: ReactElement) => {
|
||||
return (
|
||||
<DashboardLayout tab={"settings"}>
|
||||
<SettingsLayout>{page}</SettingsLayout>
|
||||
</DashboardLayout>
|
||||
);
|
||||
};
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||
) {
|
||||
const { user } = await validateRequest(ctx.req, ctx.res);
|
||||
if (!user || user.rol === "user") {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
}
|
||||
72
pages/dashboard/traefik.tsx
Normal file
72
pages/dashboard/traefik.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
import { appRouter } from "@/server/api/root";
|
||||
import { validateRequest } from "@/server/auth/auth";
|
||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import React, { type ReactElement } from "react";
|
||||
import superjson from "superjson";
|
||||
import { ShowTraefikSystem } from "@/components/dashboard/file-system/show-traefik-system";
|
||||
|
||||
const Dashboard = () => {
|
||||
return <ShowTraefikSystem />;
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
|
||||
Dashboard.getLayout = (page: ReactElement) => {
|
||||
return <DashboardLayout tab={"traefik"}>{page}</DashboardLayout>;
|
||||
};
|
||||
export async function getServerSideProps(
|
||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||
) {
|
||||
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
const { req, res } = ctx;
|
||||
|
||||
const helpers = createServerSideHelpers({
|
||||
router: appRouter,
|
||||
ctx: {
|
||||
req: req as any,
|
||||
res: res as any,
|
||||
db: null as any,
|
||||
session: session,
|
||||
user: user,
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
try {
|
||||
await helpers.project.all.prefetch();
|
||||
const auth = await helpers.auth.get.fetch();
|
||||
|
||||
if (auth.rol === "user") {
|
||||
const user = await helpers.user.byAuthId.fetch({
|
||||
authId: auth.id,
|
||||
});
|
||||
|
||||
if (!user.canAccessToTraefikFiles) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
props: {
|
||||
trpcState: helpers.dehydrate(),
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user