mirror of
https://github.com/wassname/Open-Assistant.git
synced 2026-06-27 16:10:30 +08:00
System config parameter view
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { ReactNode, useEffect } from "react";
|
||||
|
||||
export const AdminArea = ({ children }: { children: ReactNode }) => {
|
||||
const router = useRouter();
|
||||
const { data: session, status } = useSession();
|
||||
|
||||
useEffect(() => {
|
||||
if (status === "loading") {
|
||||
return;
|
||||
}
|
||||
if (session?.user.role === "admin") {
|
||||
return;
|
||||
}
|
||||
router.push("/");
|
||||
}, [router, session, status]);
|
||||
return <main>{status === "loading" ? "loading..." : children}</main>;
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
// https://nextjs.org/docs/basic-features/layouts
|
||||
|
||||
import { Box, Grid } from "@chakra-ui/react";
|
||||
import { Activity, BarChart2, Layout, MessageSquare, Users } from "lucide-react";
|
||||
import { Activity, BarChart2, Layout, MessageSquare, Settings, Users } from "lucide-react";
|
||||
import type { NextPage } from "next";
|
||||
import { Header } from "src/components/Header";
|
||||
|
||||
@@ -37,19 +37,16 @@ export const getDashboardLayout = (page: React.ReactElement) => (
|
||||
{
|
||||
label: "Dashboard",
|
||||
pathname: "/dashboard",
|
||||
desc: "Dashboard Home",
|
||||
icon: Layout,
|
||||
},
|
||||
{
|
||||
label: "Messages",
|
||||
pathname: "/messages",
|
||||
desc: "Messages Dashboard",
|
||||
icon: MessageSquare,
|
||||
},
|
||||
{
|
||||
label: "Leaderboard",
|
||||
pathname: "/leaderboard",
|
||||
desc: "User Leaderboard",
|
||||
icon: BarChart2,
|
||||
},
|
||||
]}
|
||||
@@ -72,15 +69,18 @@ export const getAdminLayout = (page: React.ReactElement) => (
|
||||
{
|
||||
label: "Users",
|
||||
pathname: "/admin",
|
||||
desc: "Users Dashboard",
|
||||
icon: Users,
|
||||
},
|
||||
{
|
||||
label: "Status",
|
||||
pathname: "/admin/status",
|
||||
desc: "Status Dashboard",
|
||||
icon: Activity,
|
||||
},
|
||||
{
|
||||
label: "Parameters",
|
||||
pathname: "/admin/parameters",
|
||||
icon: Settings,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{page}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { useRouter } from "next/router";
|
||||
export interface MenuButtonOption {
|
||||
label: string;
|
||||
pathname: string;
|
||||
desc: string;
|
||||
icon: LucideIcon;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,15 @@ export class OasstApiClient {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fetch_full_settings() {
|
||||
return this.get<Record<string, any>>("/api/v1/admin/backend_settings/full");
|
||||
}
|
||||
|
||||
fetch_public_settings() {
|
||||
return this.get<Record<string, any>>("/api/v1/admin/backend_settings/public");
|
||||
}
|
||||
|
||||
// TODO return a strongly typed Task?
|
||||
// This method is used to store a task in RegisteredTask.task.
|
||||
// This is a raw Json type, so we can't use it to strongly type the task.
|
||||
|
||||
@@ -2,8 +2,9 @@ import "../styles/globals.css";
|
||||
import "focus-visible";
|
||||
|
||||
import type { AppProps } from "next/app";
|
||||
import Head from "next/head";
|
||||
import { SessionProvider } from "next-auth/react";
|
||||
import { appWithTranslation } from "next-i18next";
|
||||
import { appWithTranslation, useTranslation } from "next-i18next";
|
||||
import { FlagsProvider } from "react-feature-flags";
|
||||
import { getDefaultLayout, NextPageWithLayout } from "src/components/Layout";
|
||||
import flags from "src/flags";
|
||||
@@ -24,15 +25,21 @@ const swrConfig: SWRConfiguration = {
|
||||
function MyApp({ Component, pageProps: { session, cookies, ...pageProps } }: AppPropsWithLayout) {
|
||||
const getLayout = Component.getLayout ?? getDefaultLayout;
|
||||
const page = getLayout(<Component {...pageProps} />);
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<FlagsProvider value={flags}>
|
||||
<Chakra cookies={cookies}>
|
||||
<SWRConfig value={swrConfig}>
|
||||
<SessionProvider session={session}>{page}</SessionProvider>
|
||||
</SWRConfig>
|
||||
</Chakra>
|
||||
</FlagsProvider>
|
||||
<>
|
||||
<Head>
|
||||
<meta name="description" key="description" content={t("index:description")} />
|
||||
</Head>
|
||||
<FlagsProvider value={flags}>
|
||||
<Chakra cookies={cookies}>
|
||||
<SWRConfig value={swrConfig}>
|
||||
<SessionProvider session={session}>{page}</SessionProvider>
|
||||
</SWRConfig>
|
||||
</Chakra>
|
||||
</FlagsProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
export { getServerSideProps };
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useEffect } from "react";
|
||||
import { AdminArea } from "src/components/AdminArea";
|
||||
import { getAdminLayout } from "src/components/Layout";
|
||||
import { UserTable } from "src/components/UserTable";
|
||||
export { getDefaultStaticProps as getStaticProps } from "src/lib/default_static_props";
|
||||
@@ -11,34 +8,10 @@ export { getDefaultStaticProps as getStaticProps } from "src/lib/default_static_
|
||||
* admins the ability to manage their access rights.
|
||||
*/
|
||||
const AdminIndex = () => {
|
||||
const router = useRouter();
|
||||
const { data: session, status } = useSession();
|
||||
|
||||
// Check when the user session is loaded and re-route if the user is not an
|
||||
// admin. This follows the suggestion by NextJS for handling private pages:
|
||||
// https://nextjs.org/docs/api-reference/next/router#usage
|
||||
//
|
||||
// All admin pages should use the same check and routing steps.
|
||||
useEffect(() => {
|
||||
if (status === "loading") {
|
||||
return;
|
||||
}
|
||||
if (session?.user?.role === "admin") {
|
||||
return;
|
||||
}
|
||||
router.push("/");
|
||||
}, [router, session, status]);
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Open Assistant</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Conversational AI for everyone. An open source project to create a chat enabled GPT LLM run by LAION and contributors around the world."
|
||||
/>
|
||||
</Head>
|
||||
<main>{status === "loading" ? "loading..." : <UserTable />}</main>
|
||||
</>
|
||||
<AdminArea title="Open Assistant">
|
||||
<UserTable />
|
||||
</AdminArea>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Card, CardBody, CircularProgress } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import { AdminArea } from "src/components/AdminArea";
|
||||
import { getAdminLayout } from "src/components/Layout";
|
||||
import { get } from "src/lib/api";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
export { getDefaultStaticProps as getStaticProps } from "src/lib/default_static_props";
|
||||
|
||||
export default function Parameters() {
|
||||
const { data, isLoading, error } = useSWRImmutable("/api/admin/parameters", get);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Parameters - Open Assistant</title>
|
||||
</Head>
|
||||
<AdminArea>
|
||||
<Card>
|
||||
<CardBody>
|
||||
{isLoading && <CircularProgress isIndeterminate></CircularProgress>}
|
||||
{error && "Unable to load data"}
|
||||
{data && (
|
||||
<Card variant="json" overflowX="auto">
|
||||
<CardBody>
|
||||
<pre>{JSON.stringify(data, null, 2)}</pre>
|
||||
</CardBody>
|
||||
</Card>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</AdminArea>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Parameters.getLayout = getAdminLayout;
|
||||
@@ -13,12 +13,9 @@ import {
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
useColorMode,
|
||||
} from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useEffect } from "react";
|
||||
import { AdminArea } from "src/components/AdminArea";
|
||||
import { getAdminLayout } from "src/components/Layout";
|
||||
import { get } from "src/lib/api";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
@@ -30,31 +27,7 @@ export { getDefaultStaticProps as getStaticProps } from "src/lib/default_static_
|
||||
*/
|
||||
|
||||
const StatusIndex = () => {
|
||||
const router = useRouter();
|
||||
const { data: session, status } = useSession();
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const dataBackgroundColor = colorMode === "light" ? "gray.100" : "gray.800";
|
||||
// Check when the user session is loaded and re-route if the user is not an
|
||||
// admin. This follows the suggestion by NextJS for handling private pages:
|
||||
// https://nextjs.org/docs/api-reference/next/router#usage
|
||||
//
|
||||
// All admin pages should use the same check and routing steps.
|
||||
useEffect(() => {
|
||||
if (status === "loading") {
|
||||
return;
|
||||
}
|
||||
if (session?.user?.role === "admin") {
|
||||
return;
|
||||
}
|
||||
router.push("/");
|
||||
}, [router, session, status]);
|
||||
|
||||
const {
|
||||
data: dataStatus,
|
||||
error: errorStatus,
|
||||
isLoading: isLoadingStatus,
|
||||
} = useSWRImmutable("/api/admin/status", get);
|
||||
const { data: dataStatus, error: errorStatus } = useSWRImmutable("/api/admin/status", get);
|
||||
|
||||
const { tasksAvailability, stats, treeManager } = dataStatus || {};
|
||||
|
||||
@@ -62,110 +35,114 @@ const StatusIndex = () => {
|
||||
<>
|
||||
<Head>
|
||||
<title>Status - Open Assistant</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Conversational AI for everyone. An open source project to create a chat enabled GPT LLM run by LAION and contributors around the world."
|
||||
/>
|
||||
</Head>
|
||||
|
||||
<SimpleGrid columns={[1, 1, 1, 1, 1, 2]} gap={4}>
|
||||
<Card>
|
||||
<CardBody>
|
||||
<Text as="h1" fontSize="3xl" textAlign="center">
|
||||
/api/v1/tasks/availability
|
||||
</Text>
|
||||
<Box bg={dataBackgroundColor} borderRadius="xl" p="6" pt="4" pr="12">
|
||||
{tasksAvailability?.status === "fulfilled" ? (
|
||||
<pre>{JSON.stringify(tasksAvailability.value, null, 2)}</pre>
|
||||
) : tasksAvailability?.status === "rejected" ? (
|
||||
<pre>{JSON.stringify(tasksAvailability.reason, null, 2)}</pre>
|
||||
) : errorStatus ? (
|
||||
<pre>{JSON.stringify(errorStatus, null, 2)}</pre>
|
||||
) : (
|
||||
<CircularProgress isIndeterminate />
|
||||
)}
|
||||
</Box>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardBody>
|
||||
<Text as="h1" fontSize="3xl" textAlign="center">
|
||||
/api/v1/stats/
|
||||
</Text>
|
||||
<Box bg={dataBackgroundColor} borderRadius="xl" p="6" pt="4" pr="12">
|
||||
{stats?.status === "fulfilled" ? (
|
||||
<pre>{JSON.stringify(stats.value, null, 2)}</pre>
|
||||
) : stats?.status === "rejected" ? (
|
||||
<pre>{JSON.stringify(stats.reason, null, 2)}</pre>
|
||||
) : errorStatus ? (
|
||||
<pre>{JSON.stringify(errorStatus, null, 2)}</pre>
|
||||
) : (
|
||||
<CircularProgress isIndeterminate />
|
||||
)}
|
||||
</Box>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</SimpleGrid>
|
||||
<br />
|
||||
<Card>
|
||||
<CardBody>
|
||||
<Text as="h1" fontSize="3xl" textAlign="center">
|
||||
/api/v1/stats/tree_manager
|
||||
</Text>
|
||||
{treeManager?.status === "fulfilled" ? (
|
||||
<Box>
|
||||
<Text as="h2" fontSize="2xl">
|
||||
state_counts
|
||||
<AdminArea>
|
||||
<SimpleGrid columns={[1, 1, 1, 1, 1, 2]} gap={4}>
|
||||
<Card>
|
||||
<CardBody>
|
||||
<Text as="h1" fontSize="3xl" textAlign="center">
|
||||
/api/v1/tasks/availability
|
||||
</Text>
|
||||
<Box bg={dataBackgroundColor} borderRadius="xl" p="6" pt="4" pr="12">
|
||||
<pre>{JSON.stringify(treeManager.value.state_counts, null, 2)}</pre>
|
||||
</Box>
|
||||
<TableContainer>
|
||||
<br />
|
||||
<Card variant="json">
|
||||
<CardBody>
|
||||
{tasksAvailability?.status === "fulfilled" ? (
|
||||
<pre>{JSON.stringify(tasksAvailability.value, null, 2)}</pre>
|
||||
) : tasksAvailability?.status === "rejected" ? (
|
||||
<pre>{JSON.stringify(tasksAvailability.reason, null, 2)}</pre>
|
||||
) : errorStatus ? (
|
||||
<pre>{JSON.stringify(errorStatus, null, 2)}</pre>
|
||||
) : (
|
||||
<CircularProgress isIndeterminate />
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardBody>
|
||||
<Text as="h1" fontSize="3xl" textAlign="center">
|
||||
/api/v1/stats/
|
||||
</Text>
|
||||
<Card variant="json">
|
||||
<CardBody>
|
||||
{stats?.status === "fulfilled" ? (
|
||||
<pre>{JSON.stringify(stats.value, null, 2)}</pre>
|
||||
) : stats?.status === "rejected" ? (
|
||||
<pre>{JSON.stringify(stats.reason, null, 2)}</pre>
|
||||
) : errorStatus ? (
|
||||
<pre>{JSON.stringify(errorStatus, null, 2)}</pre>
|
||||
) : (
|
||||
<CircularProgress isIndeterminate />
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</SimpleGrid>
|
||||
<br />
|
||||
<Card>
|
||||
<CardBody>
|
||||
<Text as="h1" fontSize="3xl" textAlign="center">
|
||||
/api/v1/stats/tree_manager
|
||||
</Text>
|
||||
{treeManager?.status === "fulfilled" ? (
|
||||
<Box>
|
||||
<Text as="h2" fontSize="2xl">
|
||||
message_counts
|
||||
state_counts
|
||||
</Text>
|
||||
<Table variant="simple">
|
||||
<TableCaption>Tree Manager</TableCaption>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Message Tree ID</Th>
|
||||
<Th>State</Th>
|
||||
<Th>Depth</Th>
|
||||
<Th>Oldest</Th>
|
||||
<Th>Youngest</Th>
|
||||
<Th>Count</Th>
|
||||
<Th>Goal Tree Size</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{treeManager.value.message_counts.map(
|
||||
({ message_tree_id, state, depth, oldest, youngest, count, goal_tree_size }) => (
|
||||
<Tr key={message_tree_id}>
|
||||
<Td>{message_tree_id}</Td>
|
||||
<Td>{state}</Td>
|
||||
<Td>{depth}</Td>
|
||||
<Td>{oldest}</Td>
|
||||
<Td>{youngest}</Td>
|
||||
<Td>{count}</Td>
|
||||
<Td>{goal_tree_size}</Td>
|
||||
</Tr>
|
||||
)
|
||||
)}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
) : treeManager?.status === "rejected" ? (
|
||||
<pre>{JSON.stringify(treeManager.reason, null, 2)}</pre>
|
||||
) : errorStatus ? (
|
||||
<pre>{JSON.stringify(errorStatus, null, 2)}</pre>
|
||||
) : (
|
||||
<CircularProgress isIndeterminate />
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
<Card variant="json">
|
||||
<CardBody>
|
||||
<pre>{JSON.stringify(treeManager.value.state_counts, null, 2)}</pre>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<TableContainer>
|
||||
<br />
|
||||
<Text as="h2" fontSize="2xl">
|
||||
message_counts
|
||||
</Text>
|
||||
<Table variant="simple">
|
||||
<TableCaption>Tree Manager</TableCaption>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Message Tree ID</Th>
|
||||
<Th>State</Th>
|
||||
<Th>Depth</Th>
|
||||
<Th>Oldest</Th>
|
||||
<Th>Youngest</Th>
|
||||
<Th>Count</Th>
|
||||
<Th>Goal Tree Size</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{treeManager.value.message_counts.map(
|
||||
({ message_tree_id, state, depth, oldest, youngest, count, goal_tree_size }) => (
|
||||
<Tr key={message_tree_id}>
|
||||
<Td>{message_tree_id}</Td>
|
||||
<Td>{state}</Td>
|
||||
<Td>{depth}</Td>
|
||||
<Td>{oldest}</Td>
|
||||
<Td>{youngest}</Td>
|
||||
<Td>{count}</Td>
|
||||
<Td>{goal_tree_size}</Td>
|
||||
</Tr>
|
||||
)
|
||||
)}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
) : treeManager?.status === "rejected" ? (
|
||||
<pre>{JSON.stringify(treeManager.reason, null, 2)}</pre>
|
||||
) : errorStatus ? (
|
||||
<pre>{JSON.stringify(errorStatus, null, 2)}</pre>
|
||||
) : (
|
||||
<CircularProgress isIndeterminate />
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</AdminArea>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { withRole } from "src/lib/auth";
|
||||
import { createApiClient } from "src/lib/oasst_client_factory";
|
||||
|
||||
export default withRole("admin", async (_, res, token) => {
|
||||
const client = await createApiClient(token);
|
||||
|
||||
try {
|
||||
const fullSettings = await client.fetch_full_settings();
|
||||
|
||||
return res.json(fullSettings);
|
||||
} catch {
|
||||
const publicSettings = await client.fetch_public_settings();
|
||||
|
||||
return res.json(publicSettings);
|
||||
}
|
||||
});
|
||||
@@ -41,7 +41,7 @@ const Dashboard = () => {
|
||||
<>
|
||||
<Head>
|
||||
<title>{`${t("dashboard")} - ${t("common:title")}`}</title>
|
||||
<meta name="description" content="Chat with Open Assistant and provide feedback." />
|
||||
<meta name="description" content="Chat with Open Assistant and provide feedback." key="description" />
|
||||
</Head>
|
||||
<Flex direction="column" gap="10">
|
||||
<WelcomeCard />
|
||||
|
||||
@@ -24,7 +24,6 @@ const Home = () => {
|
||||
<>
|
||||
<Head>
|
||||
<title>{t("title")}</title>
|
||||
<meta name="description" content={t("index:description")} />
|
||||
</Head>
|
||||
<Box as="main" className="oa-basic-theme">
|
||||
<Hero />
|
||||
|
||||
@@ -17,11 +17,21 @@ export const cardTheme = defineMultiStyleConfig({
|
||||
footer: {},
|
||||
};
|
||||
}),
|
||||
variants: {
|
||||
elevated: definePartsStyle({
|
||||
sizes: {
|
||||
md: definePartsStyle({
|
||||
container: {
|
||||
borderRadius: "xl",
|
||||
},
|
||||
}),
|
||||
},
|
||||
variants: {
|
||||
json: definePartsStyle(({ colorMode }) => {
|
||||
const isLightMode = colorMode === "light";
|
||||
return {
|
||||
container: {
|
||||
backgroundColor: isLightMode ? "gray.100" : "gray.800",
|
||||
},
|
||||
};
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user