mirror of
https://github.com/wassname/Open-Assistant.git
synced 2026-06-27 16:10:30 +08:00
Merge remote-tracking branch 'origin/main' into react-hook-form
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
import { Select, SelectProps } from "@chakra-ui/react";
|
||||
import { forwardRef } from "react";
|
||||
import { ElementOf } from "src/types/utils";
|
||||
|
||||
export const roles = ["general", "admin", "banned"] as const;
|
||||
export type Role = ElementOf<typeof roles>;
|
||||
|
||||
type RoleSelectProps = Omit<SelectProps, "defaultValue"> & {
|
||||
defaultValue?: Role;
|
||||
value?: Role;
|
||||
};
|
||||
|
||||
export const RoleSelect = forwardRef<HTMLSelectElement, RoleSelectProps>((props, ref) => {
|
||||
return (
|
||||
<Select {...props} ref={ref}>
|
||||
{roles.map((role) => (
|
||||
<option value={role} key={role}>
|
||||
{role}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
});
|
||||
|
||||
RoleSelect.displayName = "RoleSelect";
|
||||
@@ -1,11 +1,12 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { getToken, JWT } from "next-auth/jwt";
|
||||
import { Role } from "src/components/RoleSelect";
|
||||
|
||||
/**
|
||||
* Wraps any API Route handler and verifies that the user does not have the
|
||||
* specified role. Returns a 403 if they do, otherwise runs the handler.
|
||||
*/
|
||||
const withoutRole = (role: string, handler: (arg0: NextApiRequest, arg1: NextApiResponse, arg2: JWT) => void) => {
|
||||
const withoutRole = (role: Role, handler: (arg0: NextApiRequest, arg1: NextApiResponse, arg2: JWT) => void) => {
|
||||
return async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const token = await getToken({ req });
|
||||
if (!token || token.role === role) {
|
||||
@@ -20,7 +21,7 @@ const withoutRole = (role: string, handler: (arg0: NextApiRequest, arg1: NextApi
|
||||
* Wraps any API Route handler and verifies that the user has the appropriate
|
||||
* role before running the handler. Returns a 403 otherwise.
|
||||
*/
|
||||
const withRole = (role: string, handler: (arg0: NextApiRequest, arg1: NextApiResponse) => void) => {
|
||||
const withRole = (role: Role, handler: (arg0: NextApiRequest, arg1: NextApiResponse) => void) => {
|
||||
return async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const token = await getToken({ req });
|
||||
if (!token || token.role !== role) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, Container, FormControl, FormLabel, Input, Select, Stack, useToast } from "@chakra-ui/react";
|
||||
import { Button, Container, FormControl, FormLabel, Input, Stack, useToast } from "@chakra-ui/react";
|
||||
import { Field, Form, Formik } from "formik";
|
||||
import { InferGetServerSidePropsType } from "next";
|
||||
import Head from "next/head";
|
||||
@@ -7,6 +7,7 @@ import { useSession } from "next-auth/react";
|
||||
import { useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { getAdminLayout } from "src/components/Layout";
|
||||
import { RoleSelect } from "src/components/RoleSelect";
|
||||
import { UserMessagesCell } from "src/components/UserMessagesCell";
|
||||
import { post } from "src/lib/api";
|
||||
import { oasstApiClient } from "src/lib/oasst_api_client";
|
||||
@@ -99,11 +100,7 @@ const ManageUser = ({ user }: InferGetServerSidePropsType<typeof getServerSidePr
|
||||
{({ field }) => (
|
||||
<FormControl>
|
||||
<FormLabel>Role</FormLabel>
|
||||
<Select {...field}>
|
||||
<option value="banned">Banned</option>
|
||||
<option value="general">General</option>
|
||||
<option value="admin">Admin</option>
|
||||
</Select>
|
||||
<RoleSelect {...field}></RoleSelect>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
@@ -2,12 +2,13 @@ import { PrismaAdapter } from "@next-auth/prisma-adapter";
|
||||
import { boolean } from "boolean";
|
||||
import type { AuthOptions } from "next-auth";
|
||||
import NextAuth from "next-auth";
|
||||
import { Provider } from "next-auth/providers";
|
||||
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";
|
||||
|
||||
const providers = [];
|
||||
const providers: Provider[] = [];
|
||||
|
||||
// Register an email magic link auth method.
|
||||
providers.push(
|
||||
@@ -39,12 +40,13 @@ if (boolean(process.env.DEBUG_LOGIN) || process.env.NODE_ENV === "development")
|
||||
name: "Debug Credentials",
|
||||
credentials: {
|
||||
username: { label: "Username", type: "text" },
|
||||
role: { label: "Role", type: "text" },
|
||||
},
|
||||
async authorize(credentials) {
|
||||
const user = {
|
||||
id: credentials.username,
|
||||
name: credentials.username,
|
||||
role: "admin",
|
||||
role: credentials.role,
|
||||
};
|
||||
// save the user to the database
|
||||
await prisma.user.upsert({
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { Button, Input, Stack } from "@chakra-ui/react";
|
||||
import { Button, ButtonProps, Input, Stack, useColorModeValue } from "@chakra-ui/react";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import { GetServerSideProps } from "next";
|
||||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { getCsrfToken, getProviders, signIn } from "next-auth/react";
|
||||
import { ClientSafeProvider, getProviders, signIn } from "next-auth/react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
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";
|
||||
|
||||
export type SignInErrorTypes =
|
||||
| "Signin"
|
||||
@@ -37,8 +39,11 @@ const errorMessages: Record<SignInErrorTypes, string> = {
|
||||
default: "Unable to sign in.",
|
||||
};
|
||||
|
||||
interface SigninProps {
|
||||
providers: Awaited<ReturnType<typeof getProviders>>;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function Signin({ csrfToken, providers }) {
|
||||
function Signin({ providers }: SigninProps) {
|
||||
const router = useRouter();
|
||||
const { discord, email, github, credentials } = providers;
|
||||
const emailEl = useRef(null);
|
||||
@@ -60,18 +65,10 @@ function Signin({ csrfToken, providers }) {
|
||||
signIn(email.id, { callbackUrl: "/dashboard", email: emailEl.current.value });
|
||||
};
|
||||
|
||||
const debugUsernameEl = useRef(null);
|
||||
function signinWithDebugCredentials(ev: React.FormEvent) {
|
||||
ev.preventDefault();
|
||||
signIn(credentials.id, { callbackUrl: "/dashboard", username: debugUsernameEl.current.value });
|
||||
}
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const bgColorClass = colorMode === "light" ? "bg-gray-50" : "bg-chakra-gray-900";
|
||||
const buttonBgColor = colorMode === "light" ? "#2563eb" : "#2563eb";
|
||||
|
||||
const buttonColorScheme = colorMode === "light" ? "blue" : "dark-blue-btn";
|
||||
|
||||
return (
|
||||
<div className={bgColorClass}>
|
||||
<Head>
|
||||
@@ -80,17 +77,7 @@ function Signin({ csrfToken, providers }) {
|
||||
</Head>
|
||||
<AuthLayout>
|
||||
<Stack spacing="2">
|
||||
{credentials && (
|
||||
<form onSubmit={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} />
|
||||
<Button size={"lg"} leftIcon={<FaBug />} colorScheme={buttonColorScheme} color="white" type="submit">
|
||||
Continue with Debug User
|
||||
</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
)}
|
||||
{credentials && <DebugSigninForm credentials={credentials} bgColorClass={bgColorClass} />}
|
||||
{email && (
|
||||
<form onSubmit={signinWithEmail}>
|
||||
<Stack>
|
||||
@@ -102,16 +89,9 @@ function Signin({ csrfToken, providers }) {
|
||||
placeholder="Email Address"
|
||||
ref={emailEl}
|
||||
/>
|
||||
<Button
|
||||
data-cy="signin-email-button"
|
||||
size={"lg"}
|
||||
leftIcon={<FaEnvelope />}
|
||||
type="submit"
|
||||
colorScheme={buttonColorScheme}
|
||||
color="white"
|
||||
>
|
||||
<SigninButton data-cy="signin-email-button" leftIcon={<FaEnvelope />}>
|
||||
Continue with Email
|
||||
</Button>
|
||||
</SigninButton>
|
||||
</Stack>
|
||||
</form>
|
||||
)}
|
||||
@@ -179,13 +159,49 @@ Signin.getLayout = (page) => (
|
||||
|
||||
export default Signin;
|
||||
|
||||
export async function getServerSideProps() {
|
||||
const csrfToken = await getCsrfToken();
|
||||
const SigninButton = (props: ButtonProps) => {
|
||||
const buttonColorScheme = useColorModeValue("blue", "dark-blue-btn");
|
||||
|
||||
return (
|
||||
<Button
|
||||
size={"lg"}
|
||||
leftIcon={<FaEnvelope />}
|
||||
type="submit"
|
||||
colorScheme={buttonColorScheme}
|
||||
color="white"
|
||||
{...props}
|
||||
></Button>
|
||||
);
|
||||
};
|
||||
|
||||
const DebugSigninForm = ({ credentials, bgColorClass }: { credentials: ClientSafeProvider; bgColorClass: string }) => {
|
||||
const debugUsernameEl = useRef(null);
|
||||
const roleRef = useRef(null);
|
||||
function signinWithDebugCredentials(ev: React.FormEvent) {
|
||||
ev.preventDefault();
|
||||
signIn(credentials.id, {
|
||||
callbackUrl: "/dashboard",
|
||||
username: debugUsernameEl.current.value,
|
||||
role: roleRef.current.value,
|
||||
});
|
||||
}
|
||||
return (
|
||||
<form onSubmit={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>
|
||||
<SigninButton leftIcon={<FaBug />}>Continue with Debug User</SigninButton>
|
||||
</Stack>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<SigninProps> = async () => {
|
||||
const providers = await getProviders();
|
||||
return {
|
||||
props: {
|
||||
csrfToken,
|
||||
providers,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// https://github.com/ts-essentials/ts-essentials/blob/25cae45c162f8784e3cdae8f43783d0c66370a57/lib/types.ts#L437
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type ElementOf<T extends readonly any[]> = T extends readonly (infer ET)[] ? ET : never;
|
||||
Reference in New Issue
Block a user