mirror of
https://github.com/wassname/Open-Assistant.git
synced 2026-08-01 12:10:19 +08:00
Merge branch 'main' into 766_admin_enhancement
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
import { Box, Button, Center, Link, Text, useColorModeValue } from "@chakra-ui/react";
|
||||
import { Box, Button, Center, Link, Text } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import { FiAlertTriangle } from "react-icons/fi";
|
||||
import { PageEmptyState } from "src/components/EmptyState";
|
||||
import { EmptyState } from "src/components/EmptyState";
|
||||
import { getTransparentHeaderLayout } from "src/components/Layout";
|
||||
|
||||
function Error() {
|
||||
@@ -13,7 +12,7 @@ function Error() {
|
||||
<meta name="404" content="Sorry, this page doesn't exist." />
|
||||
</Head>
|
||||
<Center flexDirection="column" gap="4" fontSize="lg" className="subpixel-antialiased">
|
||||
<PageEmptyState />
|
||||
<EmptyState text="Sorry, the page you are looking for does not exist." icon={FiAlertTriangle} />
|
||||
<Box display="flex" flexDirection="column" alignItems="center" gap="2" mt="6">
|
||||
<Text fontSize="sm">If you were trying to contribute data but ended up here, please file a bug.</Text>
|
||||
<Button
|
||||
|
||||
+25
-11
@@ -1,32 +1,46 @@
|
||||
import { Button, Link, Stack } from "@chakra-ui/react";
|
||||
import { Box, Button, Center, Link, Text } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import NextLink from "next/link";
|
||||
import { FiAlertTriangle } from "react-icons/fi";
|
||||
import { EmptyState } from "src/components/EmptyState";
|
||||
import { getTransparentHeaderLayout } from "src/components/Layout";
|
||||
|
||||
export default function Error() {
|
||||
function ServerError() {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>500 - Open Assistant</title>
|
||||
<meta name="404" content="Sorry, this page doesn't exist." />
|
||||
</Head>
|
||||
<main className="flex h-3/4 items-center justify-center overflow-hidden subpixel-antialiased text-xl">
|
||||
<Stack>
|
||||
<p>Sorry, We encountered a server error. We're not sure what went wrong</p>
|
||||
<p>Please file a but below and describe what you were trying to accomplish</p>
|
||||
<Button leftIcon={<FiAlertTriangle className="text-blue-500" aria-hidden="true" />} variant="solid">
|
||||
<Center flexDirection="column" gap="4" fontSize="lg" className="subpixel-antialiased">
|
||||
<EmptyState
|
||||
text="Sorry, we encountered a server error. We're not sure what went wrong."
|
||||
icon={FiAlertTriangle}
|
||||
/>
|
||||
<Box display="flex" flexDirection="column" alignItems="center" gap="2" mt="6">
|
||||
<Text fontSize="sm">If you were trying to contribute data but ended up here, please file a bug.</Text>
|
||||
<Button
|
||||
width="fit-content"
|
||||
leftIcon={<FiAlertTriangle className="text-blue-500" aria-hidden="true" />}
|
||||
variant="solid"
|
||||
size="xs"
|
||||
>
|
||||
<Link
|
||||
as={NextLink}
|
||||
key="Report a Bug"
|
||||
href="https://github.com/LAION-AI/Open-Assistant/issues/new/choose"
|
||||
aria-label="Report a Bug"
|
||||
className="flex items-center"
|
||||
_hover={{ textDecoration: "none" }}
|
||||
isExternal
|
||||
>
|
||||
Report a Bug
|
||||
</Link>
|
||||
</Button>
|
||||
</Stack>
|
||||
</main>
|
||||
</Box>
|
||||
</Center>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
ServerError.getLayout = getTransparentHeaderLayout;
|
||||
|
||||
export default ServerError;
|
||||
|
||||
@@ -3,11 +3,13 @@ import "focus-visible";
|
||||
|
||||
import type { AppProps } from "next/app";
|
||||
import { SessionProvider } from "next-auth/react";
|
||||
import { appWithTranslation } from "next-i18next";
|
||||
import { FlagsProvider } from "react-feature-flags";
|
||||
import { getDefaultLayout, NextPageWithLayout } from "src/components/Layout";
|
||||
import flags from "src/flags";
|
||||
import { SWRConfig, SWRConfiguration } from "swr";
|
||||
|
||||
import nextI18NextConfig from "../../next-i18next.config.js";
|
||||
import { Chakra, getServerSideProps } from "../styles/Chakra";
|
||||
|
||||
type AppPropsWithLayout = AppProps & {
|
||||
@@ -34,4 +36,4 @@ function MyApp({ Component, pageProps: { session, cookies, ...pageProps } }: App
|
||||
);
|
||||
}
|
||||
export { getServerSideProps };
|
||||
export default MyApp;
|
||||
export default appWithTranslation(MyApp, nextI18NextConfig);
|
||||
|
||||
@@ -2,27 +2,11 @@ import { Button, Input, InputGroup } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import Router from "next/router";
|
||||
import { useSession } from "next-auth/react";
|
||||
import React, { useState } from "react";
|
||||
import React from "react";
|
||||
import { Control, useForm, useWatch } from "react-hook-form";
|
||||
|
||||
export default function Account() {
|
||||
const { data: session } = useSession();
|
||||
const [username, setUsername] = useState("");
|
||||
|
||||
const updateUser = async (e: React.SyntheticEvent) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const body = { username };
|
||||
await fetch("/api/username", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
session.user.name = username;
|
||||
await Router.push("/account");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
if (!session) {
|
||||
return;
|
||||
@@ -39,21 +23,52 @@ export default function Account() {
|
||||
<div className="oa-basic-theme">
|
||||
<main className="h-3/4 z-0 flex flex-col items-center justify-center">
|
||||
<p>{session.user.name || "No username"}</p>
|
||||
<form onSubmit={updateUser}>
|
||||
<InputGroup>
|
||||
<Input
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
placeholder="Edit Username"
|
||||
type="text"
|
||||
value={username}
|
||||
></Input>
|
||||
<Button disabled={!username} type="submit" value="Change">
|
||||
Submit
|
||||
</Button>
|
||||
</InputGroup>
|
||||
</form>
|
||||
<EditForm></EditForm>
|
||||
</main>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const EditForm = () => {
|
||||
const { data: session } = useSession();
|
||||
|
||||
const updateUser = async ({ username }: { username: string }) => {
|
||||
try {
|
||||
const body = { username };
|
||||
await fetch("/api/username", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
session.user.name = username;
|
||||
await Router.push("/account");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const { register, handleSubmit, control } = useForm<{ username: string }>({
|
||||
defaultValues: {
|
||||
username: session?.user.name,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(updateUser)}>
|
||||
<InputGroup>
|
||||
<Input placeholder="Edit Username" type="text" {...register("username")}></Input>
|
||||
<SubmitButton control={control}></SubmitButton>
|
||||
</InputGroup>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
const SubmitButton = ({ control }: { control: Control<{ username: string }> }) => {
|
||||
const username = useWatch({ control, name: "username" });
|
||||
return (
|
||||
<Button disabled={!username} type="submit" value="Change">
|
||||
Submit
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import CredentialsProvider from "next-auth/providers/credentials";
|
||||
import DiscordProvider from "next-auth/providers/discord";
|
||||
import EmailProvider from "next-auth/providers/email";
|
||||
import prisma from "src/lib/prismadb";
|
||||
import { generateUsername } from "unique-username-generator";
|
||||
|
||||
const providers: Provider[] = [];
|
||||
|
||||
@@ -90,6 +91,7 @@ export const authOptions: AuthOptions = {
|
||||
async session({ session, token }) {
|
||||
session.user.role = token.role;
|
||||
session.user.isNew = token.isNew;
|
||||
session.user.name = token.name;
|
||||
return session;
|
||||
},
|
||||
/**
|
||||
@@ -97,10 +99,11 @@ export const authOptions: AuthOptions = {
|
||||
* This let's use forward the role to the session object.
|
||||
*/
|
||||
async jwt({ token }) {
|
||||
const { isNew, role } = await prisma.user.findUnique({
|
||||
const { isNew, name, role } = await prisma.user.findUnique({
|
||||
where: { id: token.sub },
|
||||
select: { role: true, isNew: true },
|
||||
select: { name: true, role: true, isNew: true },
|
||||
});
|
||||
token.name = name;
|
||||
token.role = role;
|
||||
token.isNew = isNew;
|
||||
return token;
|
||||
@@ -110,7 +113,18 @@ export const authOptions: AuthOptions = {
|
||||
/**
|
||||
* Update the user's role after they have successfully signed in
|
||||
*/
|
||||
async signIn({ user, account }) {
|
||||
async signIn({ user, account, isNewUser }) {
|
||||
if (isNewUser && account.provider === "email") {
|
||||
await prisma.user.update({
|
||||
data: {
|
||||
name: generateUsername(),
|
||||
},
|
||||
where: {
|
||||
id: user.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Get the admin list for the user's auth type.
|
||||
const adminForAccountType = adminUserMap.get(account.provider);
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { withoutRole } from "src/lib/auth";
|
||||
import prisma from "src/lib/prismadb";
|
||||
|
||||
/**
|
||||
* Updates the user's `name` field in the `User` table.
|
||||
*/
|
||||
const handler = withoutRole("banned", async (req, res, token) => {
|
||||
const { username } = req.body;
|
||||
const { name } = await prisma.user.update({
|
||||
where: {
|
||||
id: token.sub,
|
||||
},
|
||||
data: {
|
||||
name: username,
|
||||
},
|
||||
});
|
||||
res.json({ name });
|
||||
});
|
||||
|
||||
export default handler;
|
||||
@@ -1,20 +0,0 @@
|
||||
import { getSession } from "next-auth/react";
|
||||
import prisma from "src/lib/prismadb";
|
||||
|
||||
// POST /api/post
|
||||
// Required fields in body: title
|
||||
// Optional fields in body: content
|
||||
export default async function handle(req, res) {
|
||||
const { username } = req.body;
|
||||
|
||||
const session = await getSession({ req });
|
||||
const result = await prisma.user.update({
|
||||
where: {
|
||||
email: session.user.email,
|
||||
},
|
||||
data: {
|
||||
name: username,
|
||||
},
|
||||
});
|
||||
res.json({ name: result.name });
|
||||
}
|
||||
@@ -6,11 +6,12 @@ import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { ClientSafeProvider, getProviders, signIn } from "next-auth/react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { FaBug, FaDiscord, FaEnvelope, FaGithub } from "react-icons/fa";
|
||||
import { AuthLayout } from "src/components/AuthLayout";
|
||||
import { Footer } from "src/components/Footer";
|
||||
import { Header } from "src/components/Header";
|
||||
import { RoleSelect } from "src/components/RoleSelect";
|
||||
import { Role, RoleSelect } from "src/components/RoleSelect";
|
||||
|
||||
export type SignInErrorTypes =
|
||||
| "Signin"
|
||||
@@ -60,15 +61,14 @@ function Signin({ providers }: SigninProps) {
|
||||
}
|
||||
}, [router]);
|
||||
|
||||
const signinWithEmail = (ev: React.FormEvent) => {
|
||||
ev.preventDefault();
|
||||
signIn(email.id, { callbackUrl: "/dashboard", email: emailEl.current.value });
|
||||
const signinWithEmail = (data: { email: string }) => {
|
||||
signIn(email.id, { callbackUrl: "/dashboard", email: data.email });
|
||||
};
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const bgColorClass = colorMode === "light" ? "bg-gray-50" : "bg-chakra-gray-900";
|
||||
const buttonBgColor = colorMode === "light" ? "#2563eb" : "#2563eb";
|
||||
|
||||
const { register, handleSubmit } = useForm<{ email: string }>();
|
||||
return (
|
||||
<div className={bgColorClass}>
|
||||
<Head>
|
||||
@@ -79,7 +79,7 @@ function Signin({ providers }: SigninProps) {
|
||||
<Stack spacing="2">
|
||||
{credentials && <DebugSigninForm credentials={credentials} bgColorClass={bgColorClass} />}
|
||||
{email && (
|
||||
<form onSubmit={signinWithEmail}>
|
||||
<form onSubmit={handleSubmit(signinWithEmail)}>
|
||||
<Stack>
|
||||
<Input
|
||||
type="email"
|
||||
@@ -87,7 +87,7 @@ function Signin({ providers }: SigninProps) {
|
||||
variant="outline"
|
||||
size="lg"
|
||||
placeholder="Email Address"
|
||||
ref={emailEl}
|
||||
{...register("email")}
|
||||
/>
|
||||
<SigninButton data-cy="signin-email-button" leftIcon={<FaEnvelope />}>
|
||||
Continue with Email
|
||||
@@ -174,23 +174,35 @@ const SigninButton = (props: ButtonProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
interface DebugSigninFormData {
|
||||
username: string;
|
||||
role: Role;
|
||||
}
|
||||
|
||||
const DebugSigninForm = ({ credentials, bgColorClass }: { credentials: ClientSafeProvider; bgColorClass: string }) => {
|
||||
const debugUsernameEl = useRef(null);
|
||||
const roleRef = useRef(null);
|
||||
function signinWithDebugCredentials(ev: React.FormEvent) {
|
||||
ev.preventDefault();
|
||||
const { register, handleSubmit } = useForm<DebugSigninFormData>({
|
||||
defaultValues: {
|
||||
role: "general",
|
||||
username: "dev",
|
||||
},
|
||||
});
|
||||
|
||||
function signinWithDebugCredentials(data: DebugSigninFormData) {
|
||||
signIn(credentials.id, {
|
||||
callbackUrl: "/dashboard",
|
||||
username: debugUsernameEl.current.value,
|
||||
role: roleRef.current.value,
|
||||
...data,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={signinWithDebugCredentials} className="border-2 border-orange-600 rounded-md p-4 relative">
|
||||
<form
|
||||
onSubmit={handleSubmit(signinWithDebugCredentials)}
|
||||
className="border-2 border-orange-600 rounded-md p-4 relative"
|
||||
>
|
||||
<span className={`text-orange-600 absolute -top-3 left-5 ${bgColorClass} px-1`}>For Debugging Only</span>
|
||||
<Stack>
|
||||
<Input variant="outline" size="lg" placeholder="Username" ref={debugUsernameEl} />
|
||||
<RoleSelect defaultValue={"general"} ref={roleRef}></RoleSelect>
|
||||
<Input variant="outline" size="lg" placeholder="Username" {...register("username")} />
|
||||
<RoleSelect {...register("role")}></RoleSelect>
|
||||
<SigninButton leftIcon={<FaBug />}>Continue with Debug User</SigninButton>
|
||||
</Stack>
|
||||
</form>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { Box } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import { useEffect } from "react";
|
||||
import { CallToAction } from "src/components/CallToAction";
|
||||
import { Faq } from "src/components/Faq";
|
||||
@@ -10,6 +13,7 @@ import { getTransparentHeaderLayout } from "src/components/Layout";
|
||||
const Home = () => {
|
||||
const router = useRouter();
|
||||
const { status } = useSession();
|
||||
const { t } = useTranslation("index");
|
||||
useEffect(() => {
|
||||
if (status === "authenticated") {
|
||||
router.push("/dashboard");
|
||||
@@ -19,21 +23,24 @@ const Home = () => {
|
||||
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."
|
||||
/>
|
||||
<title>{t("title")}</title>
|
||||
<meta name="description" content={t("description")} />
|
||||
</Head>
|
||||
<main className="oa-basic-theme">
|
||||
<Box as="main" className="oa-basic-theme">
|
||||
<Hero />
|
||||
<CallToAction />
|
||||
<Faq />
|
||||
</main>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Home.getLayout = getTransparentHeaderLayout;
|
||||
|
||||
export const getStaticProps = async ({ locale }) => ({
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ["index", "common"])),
|
||||
},
|
||||
});
|
||||
|
||||
export default Home;
|
||||
|
||||
Reference in New Issue
Block a user