feat: initial commit

This commit is contained in:
Mauricio Siu
2024-04-28 23:57:52 -06:00
parent 8857a20344
commit be56ba046c
412 changed files with 60777 additions and 1 deletions

View File

@@ -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: "/",
},
};
}

View File

@@ -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: "/",
},
};
}

View 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: "/",
},
};
}

View 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: "/",
},
};
}

View File

@@ -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: "/",
},
};
}

View 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: "/",
},
};
}