mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
refactor(tabs): hide when is cloud version
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
import { AddProject } from "@/components/dashboard/projects/add";
|
import { AddProject } from "@/components/dashboard/projects/add";
|
||||||
import type { Auth, User } from "@dokploy/builders";
|
import type { Auth, User, IS_CLOUD } from "@dokploy/builders";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
|
||||||
|
import { is } from "drizzle-orm";
|
||||||
|
|
||||||
interface TabInfo {
|
interface TabInfo {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -21,47 +22,56 @@ export type TabState =
|
|||||||
| "requests"
|
| "requests"
|
||||||
| "docker";
|
| "docker";
|
||||||
|
|
||||||
const tabMap: Record<TabState, TabInfo> = {
|
const getTabMaps = (isCloud: boolean) => {
|
||||||
projects: {
|
const tabMap: Record<TabState | undefined, TabInfo> = {
|
||||||
label: "Projects",
|
projects: {
|
||||||
description: "Manage your projects",
|
label: "Projects",
|
||||||
index: "/dashboard/projects",
|
description: "Manage your projects",
|
||||||
},
|
index: "/dashboard/projects",
|
||||||
monitoring: {
|
|
||||||
label: "Monitoring",
|
|
||||||
description: "Monitor your projects",
|
|
||||||
index: "/dashboard/monitoring",
|
|
||||||
},
|
|
||||||
traefik: {
|
|
||||||
label: "Traefik",
|
|
||||||
tabLabel: "Traefik File System",
|
|
||||||
description: "Manage your traefik",
|
|
||||||
index: "/dashboard/traefik",
|
|
||||||
isShow: ({ rol, user }) => {
|
|
||||||
return Boolean(rol === "admin" || user?.canAccessToTraefikFiles);
|
|
||||||
},
|
},
|
||||||
},
|
...(!isCloud && {
|
||||||
docker: {
|
monitoring: {
|
||||||
label: "Docker",
|
label: "Monitoring",
|
||||||
description: "Manage your docker",
|
description: "Monitor your projects",
|
||||||
index: "/dashboard/docker",
|
index: "/dashboard/monitoring",
|
||||||
isShow: ({ rol, user }) => {
|
},
|
||||||
return Boolean(rol === "admin" || user?.canAccessToDocker);
|
traefik: {
|
||||||
|
label: "Traefik",
|
||||||
|
tabLabel: "Traefik File System",
|
||||||
|
description: "Manage your traefik",
|
||||||
|
index: "/dashboard/traefik",
|
||||||
|
isShow: ({ rol, user }) => {
|
||||||
|
return Boolean(rol === "admin" || user?.canAccessToTraefikFiles);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
docker: {
|
||||||
|
label: "Docker",
|
||||||
|
description: "Manage your docker",
|
||||||
|
index: "/dashboard/docker",
|
||||||
|
isShow: ({ rol, user }) => {
|
||||||
|
return Boolean(rol === "admin" || user?.canAccessToDocker);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
requests: {
|
||||||
|
label: "Requests",
|
||||||
|
description: "Manage your requests",
|
||||||
|
index: "/dashboard/requests",
|
||||||
|
isShow: ({ rol, user }) => {
|
||||||
|
return Boolean(rol === "admin" || user?.canAccessToDocker);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
settings: {
|
||||||
|
label: "Settings",
|
||||||
|
description: "Manage your settings",
|
||||||
|
index: isCloud
|
||||||
|
? "/dashboard/settings/profile"
|
||||||
|
: "/dashboard/settings/server",
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
requests: {
|
|
||||||
label: "Requests",
|
return tabMap;
|
||||||
description: "Manage your requests",
|
|
||||||
index: "/dashboard/requests",
|
|
||||||
isShow: ({ rol, user }) => {
|
|
||||||
return Boolean(rol === "admin" || user?.canAccessToDocker);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
label: "Settings",
|
|
||||||
description: "Manage your settings",
|
|
||||||
index: "/dashboard/settings/server",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -71,9 +81,10 @@ interface Props {
|
|||||||
|
|
||||||
export const NavigationTabs = ({ tab, children }: Props) => {
|
export const NavigationTabs = ({ tab, children }: Props) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { data } = api.auth.get.useQuery();
|
const { data } = api.auth.get.useQuery();
|
||||||
const [activeTab, setActiveTab] = useState<TabState>(tab);
|
const [activeTab, setActiveTab] = useState<TabState>(tab);
|
||||||
|
const { data: isCloud } = api.settings.isCloud.useQuery();
|
||||||
|
const tabMap = useMemo(() => getTabMaps(isCloud ?? false), [isCloud]);
|
||||||
const { data: user } = api.user.byAuthId.useQuery(
|
const { data: user } = api.user.byAuthId.useQuery(
|
||||||
{
|
{
|
||||||
authId: data?.id || "",
|
authId: data?.id || "",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ interface Props {
|
|||||||
|
|
||||||
export const SettingsLayout = ({ children }: Props) => {
|
export const SettingsLayout = ({ children }: Props) => {
|
||||||
const { data } = api.auth.get.useQuery();
|
const { data } = api.auth.get.useQuery();
|
||||||
|
const { data: isCloud } = api.settings.isCloud.useQuery();
|
||||||
const { data: user } = api.user.byAuthId.useQuery(
|
const { data: user } = api.user.byAuthId.useQuery(
|
||||||
{
|
{
|
||||||
authId: data?.id || "",
|
authId: data?.id || "",
|
||||||
@@ -17,7 +18,7 @@ export const SettingsLayout = ({ children }: Props) => {
|
|||||||
<div className="md:max-w-[18rem] w-full">
|
<div className="md:max-w-[18rem] w-full">
|
||||||
<Nav
|
<Nav
|
||||||
links={[
|
links={[
|
||||||
...(data?.rol === "admin"
|
...(data?.rol === "admin" && !isCloud
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
title: "Server",
|
title: "Server",
|
||||||
@@ -60,7 +61,7 @@ export const SettingsLayout = ({ children }: Props) => {
|
|||||||
href: "/dashboard/settings/ssh-keys",
|
href: "/dashboard/settings/ssh-keys",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Git ",
|
title: "Git",
|
||||||
label: "",
|
label: "",
|
||||||
icon: GitBranch,
|
icon: GitBranch,
|
||||||
href: "/dashboard/settings/git-providers",
|
href: "/dashboard/settings/git-providers",
|
||||||
@@ -71,12 +72,23 @@ export const SettingsLayout = ({ children }: Props) => {
|
|||||||
icon: Users,
|
icon: Users,
|
||||||
href: "/dashboard/settings/users",
|
href: "/dashboard/settings/users",
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: "Cluster",
|
title: "Registry",
|
||||||
label: "",
|
label: "",
|
||||||
icon: BoxesIcon,
|
icon: ListMusic,
|
||||||
href: "/dashboard/settings/cluster",
|
href: "/dashboard/settings/registry",
|
||||||
},
|
},
|
||||||
|
...(!isCloud
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
title: "Cluster",
|
||||||
|
label: "",
|
||||||
|
icon: BoxesIcon,
|
||||||
|
href: "/dashboard/settings/cluster",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
title: "Notifications",
|
title: "Notifications",
|
||||||
label: "",
|
label: "",
|
||||||
@@ -128,6 +140,7 @@ import {
|
|||||||
GitBranch,
|
GitBranch,
|
||||||
KeyIcon,
|
KeyIcon,
|
||||||
KeyRound,
|
KeyRound,
|
||||||
|
ListMusic,
|
||||||
type LucideIcon,
|
type LucideIcon,
|
||||||
Route,
|
Route,
|
||||||
Server,
|
Server,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ShowContainers } from "@/components/dashboard/docker/show/show-containers";
|
import { ShowContainers } from "@/components/dashboard/docker/show/show-containers";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { validateRequest } from "@dokploy/builders";
|
import { IS_CLOUD, validateRequest } from "@dokploy/builders";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
@@ -19,6 +19,14 @@ Dashboard.getLayout = (page: ReactElement) => {
|
|||||||
export async function getServerSideProps(
|
export async function getServerSideProps(
|
||||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||||
) {
|
) {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
@@ -28,7 +36,7 @@ export async function getServerSideProps(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const { req, res, resolvedUrl } = ctx;
|
const { req, res } = ctx;
|
||||||
|
|
||||||
const helpers = createServerSideHelpers({
|
const helpers = createServerSideHelpers({
|
||||||
router: appRouter,
|
router: appRouter,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ShowMonitoring } from "@/components/dashboard/monitoring/web-server/show";
|
import { ShowMonitoring } from "@/components/dashboard/monitoring/web-server/show";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { validateRequest } from "@dokploy/builders";
|
import { IS_CLOUD, validateRequest } from "@dokploy/builders";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
@@ -16,6 +16,14 @@ Dashboard.getLayout = (page: ReactElement) => {
|
|||||||
export async function getServerSideProps(
|
export async function getServerSideProps(
|
||||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||||
) {
|
) {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
const { user } = await validateRequest(ctx.req, ctx.res);
|
const { user } = await validateRequest(ctx.req, ctx.res);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ShowRequests } from "@/components/dashboard/requests/show-requests";
|
import { ShowRequests } from "@/components/dashboard/requests/show-requests";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { validateRequest } from "@dokploy/builders";
|
import { IS_CLOUD, validateRequest } from "@dokploy/builders";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import type { ReactElement } from "react";
|
import type { ReactElement } from "react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
@@ -14,6 +14,14 @@ Requests.getLayout = (page: ReactElement) => {
|
|||||||
export async function getServerSideProps(
|
export async function getServerSideProps(
|
||||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||||
) {
|
) {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
const { user } = await validateRequest(ctx.req, ctx.res);
|
const { user } = await validateRequest(ctx.req, ctx.res);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ import { ShowNodes } from "@/components/dashboard/settings/cluster/nodes/show-no
|
|||||||
import { ShowRegistry } from "@/components/dashboard/settings/cluster/registry/show-registry";
|
import { ShowRegistry } from "@/components/dashboard/settings/cluster/registry/show-registry";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
import { validateRequest } from "@dokploy/builders";
|
import { IS_CLOUD, validateRequest } from "@dokploy/builders";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
const Page = () => {
|
const Page = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4 w-full">
|
<div className="flex flex-col gap-4 w-full">
|
||||||
<ShowRegistry />
|
|
||||||
<ShowNodes />
|
<ShowNodes />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -27,6 +26,14 @@ Page.getLayout = (page: ReactElement) => {
|
|||||||
export async function getServerSideProps(
|
export async function getServerSideProps(
|
||||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||||
) {
|
) {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
||||||
if (!user || user.rol === "user") {
|
if (!user || user.rol === "user") {
|
||||||
return {
|
return {
|
||||||
|
|||||||
41
apps/dokploy/pages/dashboard/settings/registry.tsx
Normal file
41
apps/dokploy/pages/dashboard/settings/registry.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { ShowRegistry } from "@/components/dashboard/settings/cluster/registry/show-registry";
|
||||||
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
|
import { validateRequest } from "@dokploy/builders";
|
||||||
|
import type { GetServerSidePropsContext } from "next";
|
||||||
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
|
const Page = () => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-4 w-full">
|
||||||
|
<ShowRegistry />
|
||||||
|
</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: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ import { WebDomain } from "@/components/dashboard/settings/web-domain";
|
|||||||
import { WebServer } from "@/components/dashboard/settings/web-server";
|
import { WebServer } from "@/components/dashboard/settings/web-server";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
import { validateRequest } from "@dokploy/builders";
|
import { IS_CLOUD, validateRequest } from "@dokploy/builders";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
@@ -27,6 +27,14 @@ Page.getLayout = (page: ReactElement) => {
|
|||||||
export async function getServerSideProps(
|
export async function getServerSideProps(
|
||||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||||
) {
|
) {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
const { user } = await validateRequest(ctx.req, ctx.res);
|
const { user } = await validateRequest(ctx.req, ctx.res);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ShowTraefikSystem } from "@/components/dashboard/file-system/show-traefik-system";
|
import { ShowTraefikSystem } from "@/components/dashboard/file-system/show-traefik-system";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { validateRequest } from "@dokploy/builders";
|
import { IS_CLOUD, validateRequest } from "@dokploy/builders";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
@@ -19,6 +19,14 @@ Dashboard.getLayout = (page: ReactElement) => {
|
|||||||
export async function getServerSideProps(
|
export async function getServerSideProps(
|
||||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||||
) {
|
) {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import {
|
|||||||
getDokployImage,
|
getDokployImage,
|
||||||
pullLatestRelease,
|
pullLatestRelease,
|
||||||
readDirectory,
|
readDirectory,
|
||||||
|
IS_CLOUD,
|
||||||
} from "@dokploy/builders";
|
} from "@dokploy/builders";
|
||||||
import packageInfo from "../../../package.json";
|
import packageInfo from "../../../package.json";
|
||||||
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
|
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
|
||||||
@@ -521,4 +522,7 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
|
isCloud: adminProcedure.query(async () => {
|
||||||
|
return IS_CLOUD;
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
1
packages/builders/src/constants/cloud.ts
Normal file
1
packages/builders/src/constants/cloud.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const IS_CLOUD = process.env.IS_CLOUD === "true";
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import Docker from "dockerode";
|
import Docker from "dockerode";
|
||||||
|
|
||||||
export const IS_CLOUD = process.env.IS_CLOUD === "true";
|
|
||||||
export const docker = new Docker();
|
export const docker = new Docker();
|
||||||
|
|
||||||
export const paths = (isServer = false) => {
|
export const paths = (isServer = false) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user