Login API
import { Body, Controller, Post, UsePipes, ValidationPipe } from '@nestjs/common'; import { DataResponse } from 'src/core'; import { LoginPasswordPayload } from 'src/payloads'; import { AuthService } from 'src/services'; @Controller('/auth/login') export class LoginController { constructor( private authService: AuthService ) {} @Post() @UsePipes(new ValidationPipe({ transform: true, whitelist: true })) async register( @Body() payload: LoginPasswordPayload ) { const res = await this.authService.loginWithPassword(payload); return DataResponse.ok(res); } }
Login form
'use client'; import Link from 'next/link'; import { Formik, Form, Field, FieldProps } from 'formik'; import { signIn } from 'next-auth/react'; import { useTranslation } from 'react-i18next'; import * as yup from 'yup'; import { FormItem, Input } from '@components/ui/form'; import { useState } from 'react'; import { FormattedMessage } from '@components/i18n'; import { useMainThemeLayout } from 'src/providers/main-layout.provider'; const schema = yup.object().shape({ username: yup.string().required('Username is required!'), password: yup.string().required('Password is required!') }); export default function Login() { const { t } = useTranslation(); const [error, setError] = useState<string | null>(); const { register, forgotPassword, closePopup } = useMainThemeLayout(); return ( <div className="w-full space-y-5 p-10 text-center"> <Link href="/" className="font-bold text-4xl text-center"> xStreamer </Link> <h2 className="text-center text-2xl font-bold leading-9 tracking-tigh"> {t('signin', 'Sign in')} </h2> <Formik initialValues={{ username: '', password: '' }} validationSchema={schema} onSubmit={async (values, actions) => { const res = await signIn('credentials', { ...values, redirect: false }); if (res && res.ok) { closePopup(); // loadProfile(); return; } setError(res!.error); actions.setSubmitting(false); }} > {({ handleSubmit }) => ( <Form className="space-y-3" onSubmit={handleSubmit}> <Field name="username"> {({ field, meta }: FieldProps) => ( <FormItem> <Input placeholder="Username" {...field} /> {meta.touched && !!meta.error && ( <FormItem.Error>{meta.error}</FormItem.Error> )} </FormItem> )} </Field> <div> <Field name="password"> {({ field, meta }: FieldProps) => ( <FormItem> <Input type="password" placeholder="Password" {...field} /> {meta.touched && !!meta.error && ( <FormItem.Error>{meta.error}</FormItem.Error> )} </FormItem> )} </Field> </div> <div className="flex items-center justify-between"> <div className="text-sm text-primary"> <button onClick={forgotPassword} role="button" type="button"> <FormattedMessage id="forgotPassword" defaultValue="Forgot password" subifx="?" /> </button> </div> </div> {error && ( <div className="bg-red-100 dark:bg-red-950 border border-red-900 text-red-700 dark:text-white px-4 py-3 rounded relative" role="alert" > <span className="text-sm font-medium">{error}</span> </div> )} <div className="text-center"> <button type="submit" className="rounded-md bg-orange-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-orange-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-600" > {t('signin', 'Sign in')} </button> </div> </Form> )} </Formik> <p className="mt-10 text-center text-sm text-gray-500"> Don't have an account? {' '} <a onClick={register} className="font-semibold leading-6 cursor-pointer hover:text-primary" > {t('signup', 'Sign Up')} </a> </p> </div> ); }