This commit is contained in:
bipproduction
2025-11-30 08:08:48 +08:00
parent 4583897684
commit b15fd3acaa
12 changed files with 152 additions and 5 deletions

View File

@@ -29,3 +29,8 @@ model ApiKey {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Configs {
id String @id @default("1")
allowRegister Boolean @default(false)
}

View File

@@ -8,6 +8,10 @@ const user = [
}
];
const configs = {
allowRegister: false
}
; (async () => {
for (const u of user) {
await prisma.user.upsert({
@@ -19,7 +23,13 @@ const user = [
console.log(`✅ User ${u.email} seeded successfully`)
}
await prisma.configs.upsert({
where: { id: "1" },
create: configs,
update: configs,
})
console.log(`✅ Configs seeded successfully`)
})().catch((e) => {
console.error(e)
process.exit(1)

View File

@@ -64,6 +64,16 @@ const Register = {
preload: () => import("./pages/Register"),
};
const ConfigLayout = {
Component: React.lazy(() => import("./pages/dashboard/config/config_layout")),
preload: () => import("./pages/dashboard/config/config_layout"),
};
const ConfigPage = {
Component: React.lazy(() => import("./pages/dashboard/config/config_page")),
preload: () => import("./pages/dashboard/config/config_page"),
};
const ApikeyPage = {
Component: React.lazy(() => import("./pages/dashboard/apikey/apikey_page")),
preload: () => import("./pages/dashboard/apikey/apikey_page"),
@@ -118,6 +128,19 @@ export default function AppRoutes() {
<Route path="/dashboard" element={<DashboardLayout.Component />}>
<Route index element={<DashboardPage.Component />} />
<Route path="/dashboard/config" element={<ConfigLayout.Component />}>
<Route index element={<ConfigPage.Component />} />
<Route
path="/dashboard/config/config"
element={
<React.Suspense fallback={<SkeletonLoading />}>
<ConfigPage.Component />
</React.Suspense>
}
/>
</Route>
<Route
path="/dashboard/apikey/apikey"
element={

View File

@@ -4,6 +4,8 @@ const clientRoutes = {
"/": "/",
"/register": "/register",
"/dashboard": "/dashboard",
"/dashboard/config": "/dashboard/config",
"/dashboard/config/config": "/dashboard/config/config",
"/dashboard/apikey/apikey": "/dashboard/apikey/apikey",
"/dashboard/dashboard": "/dashboard/dashboard",
"/*": "/*"

View File

@@ -9,6 +9,8 @@ import { LandingPage } from "./Landing";
import { renderToReadableStream } from "react-dom/server";
import { cors } from "@elysiajs/cors";
import packageJson from "./../package.json";
import Configs from "./server/routes/configs_route";
import { prisma } from "./server/lib/prisma";
const PORT = process.env.PORT || 3000;
const Docs = new Elysia().use(
@@ -68,6 +70,7 @@ const ApiUser = new Elysia({
const Api = new Elysia({
prefix: "/api",
})
.use(Configs)
.use(apiAuth)
.use(ApiKeyRoute)
.use(ApiUser);
@@ -77,6 +80,15 @@ const app = new Elysia()
.use(Api)
.use(Docs)
.use(Auth)
.get("/get-allow-register", async () => {
const configs = await prisma.configs.findUnique({ where: { id: "1" } })
return { allowRegister: configs?.allowRegister }
}, {
detail: {
description: "Get allow register",
summary: "get allow register",
},
})
.get(
"/assets/:name",
(ctx) => {

View File

@@ -13,6 +13,8 @@ import {
import { useEffect, useState } from "react";
import { Navigate } from "react-router-dom";
import apiFetch from "../lib/apiFetch";
import useSWR from "swr";
import { useNavigate } from "react-router-dom";
export default function Register() {
const [name, setName] = useState("");
@@ -20,6 +22,10 @@ export default function Register() {
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);
const navigate = useNavigate();
const { data, error, isLoading } = useSWR("/", apiFetch["get-allow-register"].get);
const allowRegister = data?.data?.allowRegister ?? false;
const handleSubmit = async () => {
setLoading(true);
@@ -57,10 +63,22 @@ export default function Register() {
checkSession();
}, []);
if (isLoading) return <Text>Loading...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
if (isAuthenticated === null) return null;
if (isAuthenticated)
return <Navigate to={clientRoutes["/dashboard"]} replace />;
if (!allowRegister) return <Container size={"md"} w={"100%"}>
<Group justify="center">
<Stack>
<Text>Allow register is disabled</Text>
<Button onClick={() => navigate(clientRoutes["/login"])}>Back to login</Button>
</Stack>
</Group>
</Container>;
return (
<Container size={420} py={80}>
<Card shadow="sm" radius="md" padding="xl">

View File

@@ -0,0 +1,5 @@
import { Outlet } from "react-router-dom";
export default function ConfigLayout() {
return <Outlet />;
}

View File

@@ -0,0 +1,37 @@
import apiFetch from "@/lib/apiFetch";
import { Container, Stack, Switch, Text } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import { useState } from "react";
import useSWR from "swr";
export default function ConfigPage() {
const { data, error, isLoading } = useSWR("/", apiFetch["get-allow-register"].get);
const [allowRegister, setAllowRegister] = useState(false);
useShallowEffect(() => {
if (data) {
setAllowRegister(data.data?.allowRegister ?? false)
}
}, [data])
if (isLoading) return <Container size="lg" w={"100%"}><Text>Loading...</Text></Container>
if (error) return <Container size="lg" w={"100%"}><Text>Error: {error.message}</Text></Container>
async function updateAllowRegister({ allowRegister }: { allowRegister: boolean }) {
const res = await apiFetch.api.configs["update-allow-register"].post({ allowRegister: allowRegister })
console.log(res.data)
setAllowRegister(res.data?.allowRegister ?? false)
}
return <Container size="lg" w={"100%"}>
<Stack>
<Text>Config Page</Text>
<Switch label="Allow Register" checked={allowRegister} onChange={(e) => {
updateAllowRegister({ allowRegister: !allowRegister })
}} />
</Stack>
</Container>
}

View File

@@ -22,6 +22,8 @@ import {
IconChevronLeft,
IconChevronRight,
IconDashboard,
IconKey,
IconSettings,
} from "@tabler/icons-react";
import type { User } from "generated/prisma";
import { useLocation, useNavigate } from "react-router-dom";
@@ -43,7 +45,6 @@ function Logout() {
color="red"
size="xs"
onClick={async () => {
modals.openConfirmModal({
title: "Confirm Logout",
children: "Are you sure you want to logout?",
@@ -205,11 +206,19 @@ function NavigationDashboard() {
<NavLink
active={isActive("/dashboard/apikey/apikey")}
leftSection={<IconDashboard size={18} />}
leftSection={<IconKey size={18} />}
label="API Keys"
description="Manage your API credentials"
onClick={() => navigate(clientRoutes["/dashboard/apikey/apikey"])}
/>
<NavLink
active={isActive("/dashboard/config/config")}
leftSection={<IconSettings size={18} />}
label="Config"
description="Manage your app config"
onClick={() => navigate(clientRoutes["/dashboard/config/config"])}
/>
</Stack>
);
}

View File

@@ -8,7 +8,7 @@ import {
Stack,
Table,
Text,
Title
Title,
} from "@mantine/core";
export default function Dashboard() {

View File

@@ -1,5 +1,5 @@
export type AppRoute = "/login" | "/" | "/register" | "/dashboard" | "/dashboard/apikey/apikey" | "/dashboard/dashboard";
export type AppRoute = "/login" | "/" | "/register" | "/dashboard" | "/dashboard/config" | "/dashboard/config/config" | "/dashboard/apikey/apikey" | "/dashboard/dashboard";
export function route(path: AppRoute, params?: Record<string,string|number>) {
if (!params) return path;

View File

@@ -0,0 +1,26 @@
import Elysia, { t } from "elysia";
import { prisma } from "@/server/lib/prisma";
const Configs = new Elysia({
prefix: "/configs",
detail: { description: "Configs API", summary: "Configs API", tags: ["configs"] },
})
.post("/update-allow-register", async ({ body }) => {
const { allowRegister } = body
await prisma.configs.update({
where: { id: "1" },
data: { allowRegister },
})
return { success: true, message: "Configs updated successfully", allowRegister }
}, {
body: t.Object({
allowRegister: t.Boolean(),
}),
detail: {
description: "Update configs",
summary: "update configs",
},
})
export default Configs