add sign in with password
This commit is contained in:
parent
741c44ebe5
commit
08ee186737
|
@ -9,7 +9,7 @@ import Link from "next/link";
|
||||||
import { FormField } from "@/app/_components/form-field";
|
import { FormField } from "@/app/_components/form-field";
|
||||||
// import { useSignInController } from "@/src/interface-adapters/controllers/auth/sign-in.controller";
|
// import { useSignInController } from "@/src/interface-adapters/controllers/auth/sign-in.controller";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { signIn } from "../action";
|
|
||||||
import { useSignInHandler } from "../_handlers/use-sign-in";
|
import { useSignInHandler } from "../_handlers/use-sign-in";
|
||||||
|
|
||||||
export function SignInForm({
|
export function SignInForm({
|
||||||
|
@ -98,7 +98,7 @@ export function SignInForm({
|
||||||
{isPending ? (
|
{isPending ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="h-5 w-5 animate-spin" />
|
<Loader2 className="h-5 w-5 animate-spin" />
|
||||||
Signing in...
|
Sign in
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
"Sign in"
|
"Sign in"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { useNavigations } from "@/app/_hooks/use-navigations";
|
import { useNavigations } from "@/app/_hooks/use-navigations";
|
||||||
import { useSignInMutation } from "../_queries/mutations";
|
import { useSignInPasswordlessMutation } from "../_queries/mutations";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
@ -7,7 +7,7 @@ import { ISignInPasswordlessSchema, SignInPasswordlessSchema } from "@/src/entit
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
|
||||||
export function useSignInHandler() {
|
export function useSignInHandler() {
|
||||||
const { mutateAsync: signIn, isPending, error: errors } = useSignInMutation();
|
const { mutateAsync: signIn, isPending, error: errors } = useSignInPasswordlessMutation();
|
||||||
const { router } = useNavigations();
|
const { router } = useNavigations();
|
||||||
|
|
||||||
const [error, setError] = useState<string>();
|
const [error, setError] = useState<string>();
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
import { useMutation } from "@tanstack/react-query"
|
import { useMutation } from "@tanstack/react-query"
|
||||||
import { sendMagicLink, sendPasswordRecovery, signIn, signOut, verifyOtp } from "../action"
|
import { sendMagicLink, sendPasswordRecovery, signInPasswordless, signInWithPassword, signOut, verifyOtp } from "../action"
|
||||||
|
|
||||||
export const useSignInMutation = () => {
|
export const useSignInPasswordlessMutation = () => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationKey: ["signIn"],
|
mutationKey: ["signIn"],
|
||||||
mutationFn: async (formData: FormData) => await signIn(formData),
|
mutationFn: async (formData: FormData) => await signInPasswordless(formData),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSignInWithPasswordMutation = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationKey: ["signInWithCredentials"],
|
||||||
|
mutationFn: async (formData: FormData) => await signInWithPassword(formData),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,17 +8,18 @@ import { InputParseError, NotFoundError } from "@/src/entities/errors/common"
|
||||||
import { AuthenticationError, UnauthenticatedError } from "@/src/entities/errors/auth"
|
import { AuthenticationError, UnauthenticatedError } from "@/src/entities/errors/auth"
|
||||||
import { createClient } from "@/app/_utils/supabase/server"
|
import { createClient } from "@/app/_utils/supabase/server"
|
||||||
|
|
||||||
export async function signIn(formData: FormData) {
|
export async function signInPasswordless(formData: FormData) {
|
||||||
const instrumentationService = getInjection("IInstrumentationService")
|
const instrumentationService = getInjection("IInstrumentationService")
|
||||||
return await instrumentationService.instrumentServerAction("signIn", {
|
return await instrumentationService.instrumentServerAction("signIn", {
|
||||||
recordResponse: true
|
recordResponse: true
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
const email = formData.get("email")?.toString()
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const signInController = getInjection("ISignInController")
|
const email = formData.get("email")?.toString()
|
||||||
return await signInController({ email })
|
|
||||||
|
const signInPasswordlessController = getInjection("ISignInPasswordlessController")
|
||||||
|
return await signInPasswordlessController({ email })
|
||||||
|
|
||||||
// if (email) {
|
// if (email) {
|
||||||
// redirect(`/verify-otp?email=${encodeURIComponent(email)}`)
|
// redirect(`/verify-otp?email=${encodeURIComponent(email)}`)
|
||||||
|
@ -51,6 +52,43 @@ export async function signIn(formData: FormData) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function signInWithPassword(formData: FormData) {
|
||||||
|
const instrumentationService = getInjection("IInstrumentationService")
|
||||||
|
return await instrumentationService.instrumentServerAction("signInWithPassword", {
|
||||||
|
recordResponse: true
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const email = formData.get("email")?.toString()
|
||||||
|
const password = formData.get("password")?.toString()
|
||||||
|
|
||||||
|
const signInWithPasswordController = getInjection("ISignInWithPasswordController")
|
||||||
|
return await signInWithPasswordController({ email, password })
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof InputParseError) {
|
||||||
|
return { error: err.message }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err instanceof AuthenticationError) {
|
||||||
|
return { error: "Invalid credential. Please try again." }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err instanceof UnauthenticatedError || err instanceof NotFoundError) {
|
||||||
|
return {
|
||||||
|
error: 'User not found. Please tell your admin to create an account for you.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const crashReporterService = getInjection('ICrashReporterService');
|
||||||
|
crashReporterService.report(err);
|
||||||
|
|
||||||
|
return {
|
||||||
|
error:
|
||||||
|
'An error happened. The developers have been notified. Please try again later.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
// export async function signUp(formData: FormData) {
|
// export async function signUp(formData: FormData) {
|
||||||
// const instrumentationService = getInjection("IInstrumentationService")
|
// const instrumentationService = getInjection("IInstrumentationService")
|
||||||
// return await instrumentationService.instrumentServerAction("signUp", { recordResponse: true }, async () => {
|
// return await instrumentationService.instrumentServerAction("signUp", { recordResponse: true }, async () => {
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
// import { AuthenticationError } from "@/src/entities/errors/auth";
|
||||||
|
// import { useState } from "react";
|
||||||
|
// import { useAuthActions } from './queries';
|
||||||
|
// import { useForm } from 'react-hook-form';
|
||||||
|
// import { zodResolver } from '@hookform/resolvers/zod';;
|
||||||
|
// import { toast } from 'sonner';
|
||||||
|
// import { useNavigations } from '@/app/_hooks/use-navigations';
|
||||||
|
// import {
|
||||||
|
// IVerifyOtpSchema,
|
||||||
|
// verifyOtpSchema,
|
||||||
|
// } from '@/src/entities/models/auth/verify-otp.model';
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Hook untuk menangani proses sign in
|
||||||
|
// *
|
||||||
|
// * @returns {Object} Object berisi handler dan state untuk form sign in
|
||||||
|
// * @example
|
||||||
|
// * const { handleSubmit, isPending, error } = useSignInHandler();
|
||||||
|
// * <form onSubmit={handleSubmit}>...</form>
|
||||||
|
// */
|
||||||
|
// export function useSignInHandler() {
|
||||||
|
// const { signIn } = useAuthActions();
|
||||||
|
// const { router } = useNavigations();
|
||||||
|
|
||||||
|
// const [error, setError] = useState<string>();
|
||||||
|
|
||||||
|
// const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
// event.preventDefault();
|
||||||
|
// if (signIn.isPending) return;
|
||||||
|
|
||||||
|
// setError(undefined);
|
||||||
|
|
||||||
|
// const formData = new FormData(event.currentTarget);
|
||||||
|
// const email = formData.get('email')?.toString();
|
||||||
|
|
||||||
|
// const res = await signIn.mutateAsync(formData);
|
||||||
|
|
||||||
|
// if (!res?.error) {
|
||||||
|
// toast('An email has been sent to you. Please check your inbox.');
|
||||||
|
// if (email) router.push(`/verify-otp?email=${encodeURIComponent(email)}`);
|
||||||
|
// } else {
|
||||||
|
// setError(res.error);
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// };
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// // formData,
|
||||||
|
// // handleChange,
|
||||||
|
// handleSignIn: handleSubmit,
|
||||||
|
// error,
|
||||||
|
// isPending: signIn.isPending,
|
||||||
|
// errors: !!error || signIn.error,
|
||||||
|
// clearError: () => setError(undefined),
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function useVerifyOtpHandler(email: string) {
|
||||||
|
// const { router } = useNavigations();
|
||||||
|
// const { verifyOtp } = useAuthActions();
|
||||||
|
// const [error, setError] = useState<string>();
|
||||||
|
|
||||||
|
// const {
|
||||||
|
// register,
|
||||||
|
// handleSubmit: hookFormSubmit,
|
||||||
|
// control,
|
||||||
|
// formState: { errors },
|
||||||
|
// setValue,
|
||||||
|
// } = useForm<IVerifyOtpSchema>({
|
||||||
|
// resolver: zodResolver(verifyOtpSchema),
|
||||||
|
// defaultValues: {
|
||||||
|
// email,
|
||||||
|
// token: '',
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const handleOtpChange = (
|
||||||
|
// value: string,
|
||||||
|
// onChange: (value: string) => void
|
||||||
|
// ) => {
|
||||||
|
// onChange(value);
|
||||||
|
|
||||||
|
// if (value.length === 6) {
|
||||||
|
// handleSubmit();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Clear error when user starts typing
|
||||||
|
// if (error) {
|
||||||
|
// setError(undefined);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const handleSubmit = hookFormSubmit(async (data) => {
|
||||||
|
// if (verifyOtp.isPending) return;
|
||||||
|
|
||||||
|
// setError(undefined);
|
||||||
|
|
||||||
|
// // Create FormData object
|
||||||
|
// const formData = new FormData();
|
||||||
|
// formData.append('email', data.email);
|
||||||
|
// formData.append('token', data.token);
|
||||||
|
|
||||||
|
// await verifyOtp.mutateAsync(formData, {
|
||||||
|
// onSuccess: () => {
|
||||||
|
// toast.success('OTP verified successfully');
|
||||||
|
// // Navigate to dashboard on success
|
||||||
|
// router.push('/dashboard');
|
||||||
|
// },
|
||||||
|
// onError: (error) => {
|
||||||
|
// setError(error.message);
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// register,
|
||||||
|
// control,
|
||||||
|
// handleVerifyOtp: handleSubmit,
|
||||||
|
// handleOtpChange,
|
||||||
|
// errors: {
|
||||||
|
// ...errors,
|
||||||
|
// token: error ? { message: error } : errors.token,
|
||||||
|
// },
|
||||||
|
// isPending: verifyOtp.isPending,
|
||||||
|
// clearError: () => setError(undefined),
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function useSignOutHandler() {
|
||||||
|
// const { signOut } = useAuthActions();
|
||||||
|
// const { router } = useNavigations();
|
||||||
|
// const [error, setError] = useState<string>();
|
||||||
|
|
||||||
|
// const handleSignOut = async () => {
|
||||||
|
// if (signOut.isPending) return;
|
||||||
|
|
||||||
|
// setError(undefined);
|
||||||
|
|
||||||
|
// await signOut.mutateAsync(undefined, {
|
||||||
|
// onSuccess: () => {
|
||||||
|
// toast.success('You have been signed out successfully');
|
||||||
|
// router.push('/sign-in');
|
||||||
|
// },
|
||||||
|
// onError: (error) => {
|
||||||
|
// if (error instanceof AuthenticationError) {
|
||||||
|
// setError(error.message);
|
||||||
|
// toast.error(error.message);
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// handleSignOut,
|
||||||
|
// error,
|
||||||
|
// isPending: signOut.isPending,
|
||||||
|
// errors: !!error || signOut.error,
|
||||||
|
// clearError: () => setError(undefined),
|
||||||
|
// };
|
||||||
|
// }
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { Carousel, CarouselContent, CarouselItem } from "@/app/_components/ui/carousal"
|
||||||
|
import { QuoteIcon } from "lucide-react"
|
||||||
|
|
||||||
|
export const CarousalQuotes = () => {
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
quote: "Tried @supabase for the first time yesterday. Amazing tool! I was able to get my Posgres DB up in no time and their documentation on operating on the DB is super easy! 👏 Can't wait for Cloud functions to arrive! It's gonna be a great Firebase alternative!",
|
||||||
|
author: "@codewithbhargav",
|
||||||
|
image: "https://github.com/shadcn.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
quote: "Check out this amazing product @supabase. A must give try #newidea #opportunity",
|
||||||
|
author: "@techenthusiast",
|
||||||
|
image: "https://github.com/shadcn.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
quote: "Check out this amazing product @supabase. A must give try #newidea #opportunity",
|
||||||
|
author: "@dataguru",
|
||||||
|
image: "https://github.com/shadcn.png",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Carousel showDots autoPlay autoPlayInterval={10000} className="w-full max-w-md" >
|
||||||
|
<CarouselContent className="py-8">
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<CarouselItem key={index} className="flex flex-col items-center justify-center">
|
||||||
|
<div className="relative flex flex-col items-start text-start">
|
||||||
|
{/* <QuoteIcon className="absolute h-20 w-20 text-muted opacity-80 -z-10 top-[-30px] transform rotate-180 " /> */}
|
||||||
|
<h2 className="text-3xl font-medium text-white mb-8">{item.quote}</h2>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<img
|
||||||
|
src={item.image}
|
||||||
|
alt="Profile"
|
||||||
|
className="w-12 h-12 rounded-full"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<p className="text-white font-medium">{item.author}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CarouselItem>
|
||||||
|
))}
|
||||||
|
</CarouselContent>
|
||||||
|
</Carousel>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
|
@ -2,25 +2,9 @@ import { SignInForm } from "@/app/(pages)/(auth)/_components/signin-form";
|
||||||
import { Message } from "@/app/_components/form-message";
|
import { Message } from "@/app/_components/form-message";
|
||||||
import { Button } from "@/app/_components/ui/button";
|
import { Button } from "@/app/_components/ui/button";
|
||||||
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/app/_components/ui/carousal";
|
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/app/_components/ui/carousal";
|
||||||
|
import { IconQuoteFilled } from "@tabler/icons-react";
|
||||||
import { GalleryVerticalEnd, Globe, QuoteIcon } from "lucide-react";
|
import { GalleryVerticalEnd, Globe, QuoteIcon } from "lucide-react";
|
||||||
|
import { CarousalQuotes } from "./_components/carousal-quote";
|
||||||
const carouselContent = [
|
|
||||||
{
|
|
||||||
quote: "Tried @supabase for the first time yesterday. Amazing tool! I was able to get my Posgres DB up in no time and their documentation on operating on the DB is super easy! 👏 Can't wait for Cloud functions to arrive! It's gonna be a great Firebase alternative!",
|
|
||||||
author: "@codewithbhargav",
|
|
||||||
image: "https://github.com/shadcn.png",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
quote: "Check out this amazing product @supabase. A must give try #newidea #opportunity",
|
|
||||||
author: "@techenthusiast",
|
|
||||||
image: "https://github.com/shadcn.png",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
quote: "Check out this amazing product @supabase. A must give try #newidea #opportunity",
|
|
||||||
author: "@dataguru",
|
|
||||||
image: "https://github.com/shadcn.png",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default async function Login(props: { searchParams: Promise<Message> }) {
|
export default async function Login(props: { searchParams: Promise<Message> }) {
|
||||||
return (
|
return (
|
||||||
|
@ -49,28 +33,7 @@ export default async function Login(props: { searchParams: Promise<Message> }) {
|
||||||
<Globe className="mr-0 h-4 w-4" />
|
<Globe className="mr-0 h-4 w-4" />
|
||||||
Showcase
|
Showcase
|
||||||
</Button>
|
</Button>
|
||||||
<Carousel showDots autoPlay autoPlayInterval={10000} className="w-full max-w-md" >
|
<CarousalQuotes />
|
||||||
<CarouselContent className="py-8">
|
|
||||||
{carouselContent.map((item, index) => (
|
|
||||||
<CarouselItem key={index} className="flex flex-col items-center justify-center">
|
|
||||||
<div className="relative flex flex-col items-start text-start">
|
|
||||||
<QuoteIcon className="absolute h-20 w-20 text-primary opacity-10 -z-10 top-[-30px] transform rotate-180 " />
|
|
||||||
<h2 className="text-3xl font-medium text-white mb-8">{item.quote}</h2>
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<img
|
|
||||||
src={item.image}
|
|
||||||
alt="Profile"
|
|
||||||
className="w-12 h-12 rounded-full"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<p className="text-white font-medium">{item.author}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CarouselItem>
|
|
||||||
))}
|
|
||||||
</CarouselContent>
|
|
||||||
</Carousel>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,12 +2,12 @@ import { createModule } from '@evyweb/ioctopus';
|
||||||
|
|
||||||
import { AuthenticationService } from '@/src/infrastructure/services/authentication.service';
|
import { AuthenticationService } from '@/src/infrastructure/services/authentication.service';
|
||||||
|
|
||||||
import { signInUseCase } from '@/src/application/use-cases/auth/sign-in.use-case';
|
import { signInUseCase } from '@/src/application/use-cases/auth/sign-in-passwordless.use-case';
|
||||||
import { signUpUseCase } from '@/src/application/use-cases/auth/sign-up.use-case';
|
import { signUpUseCase } from '@/src/application/use-cases/auth/sign-up.use-case';
|
||||||
import { signOutUseCase } from '@/src/application/use-cases/auth/sign-out.use-case';
|
import { signOutUseCase } from '@/src/application/use-cases/auth/sign-out.use-case';
|
||||||
|
|
||||||
import { DI_SYMBOLS } from '@/di/types';
|
import { DI_SYMBOLS } from '@/di/types';
|
||||||
import { signInController } from '@/src/interface-adapters/controllers/auth/sign-in.controller';
|
import { signInPasswordlessController } from '@/src/interface-adapters/controllers/auth/sign-in-passwordless.controller';
|
||||||
import { signOutController } from '@/src/interface-adapters/controllers/auth/sign-out.controller';
|
import { signOutController } from '@/src/interface-adapters/controllers/auth/sign-out.controller';
|
||||||
import { verifyOtpUseCase } from '@/src/application/use-cases/auth/verify-otp.use-case';
|
import { verifyOtpUseCase } from '@/src/application/use-cases/auth/verify-otp.use-case';
|
||||||
import { verifyOtpController } from '@/src/interface-adapters/controllers/auth/verify-otp.controller';
|
import { verifyOtpController } from '@/src/interface-adapters/controllers/auth/verify-otp.controller';
|
||||||
|
@ -15,6 +15,8 @@ import { sendMagicLinkUseCase } from '@/src/application/use-cases/auth/send-magi
|
||||||
import { sendPasswordRecoveryUseCase } from '@/src/application/use-cases/auth/send-password-recovery.use-case';
|
import { sendPasswordRecoveryUseCase } from '@/src/application/use-cases/auth/send-password-recovery.use-case';
|
||||||
import { sendMagicLinkController } from '@/src/interface-adapters/controllers/auth/send-magic-link.controller';
|
import { sendMagicLinkController } from '@/src/interface-adapters/controllers/auth/send-magic-link.controller';
|
||||||
import { sendPasswordRecoveryController } from '@/src/interface-adapters/controllers/auth/send-password-recovery.controller';
|
import { sendPasswordRecoveryController } from '@/src/interface-adapters/controllers/auth/send-password-recovery.controller';
|
||||||
|
import { signInWithPasswordUseCase } from '@/src/application/use-cases/auth/sign-in-with-password.use-case';
|
||||||
|
import { signInWithPasswordController } from '@/src/interface-adapters/controllers/auth/sign-in-with-password.controller';
|
||||||
|
|
||||||
export function createAuthenticationModule() {
|
export function createAuthenticationModule() {
|
||||||
const authenticationModule = createModule();
|
const authenticationModule = createModule();
|
||||||
|
@ -40,13 +42,21 @@ export function createAuthenticationModule() {
|
||||||
|
|
||||||
// Use Cases
|
// Use Cases
|
||||||
authenticationModule
|
authenticationModule
|
||||||
.bind(DI_SYMBOLS.ISignInUseCase)
|
.bind(DI_SYMBOLS.ISignInPasswordlessUseCase)
|
||||||
.toHigherOrderFunction(signInUseCase, [
|
.toHigherOrderFunction(signInUseCase, [
|
||||||
DI_SYMBOLS.IInstrumentationService,
|
DI_SYMBOLS.IInstrumentationService,
|
||||||
DI_SYMBOLS.IAuthenticationService,
|
DI_SYMBOLS.IAuthenticationService,
|
||||||
DI_SYMBOLS.IUsersRepository,
|
DI_SYMBOLS.IUsersRepository,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
authenticationModule
|
||||||
|
.bind(DI_SYMBOLS.ISignInWithPasswordUseCase)
|
||||||
|
.toHigherOrderFunction(signInWithPasswordUseCase, [
|
||||||
|
DI_SYMBOLS.IInstrumentationService,
|
||||||
|
DI_SYMBOLS.IAuthenticationService,
|
||||||
|
DI_SYMBOLS.IUsersRepository,
|
||||||
|
]);
|
||||||
|
|
||||||
authenticationModule
|
authenticationModule
|
||||||
.bind(DI_SYMBOLS.ISignUpUseCase)
|
.bind(DI_SYMBOLS.ISignUpUseCase)
|
||||||
.toHigherOrderFunction(signUpUseCase, [
|
.toHigherOrderFunction(signUpUseCase, [
|
||||||
|
@ -89,10 +99,17 @@ export function createAuthenticationModule() {
|
||||||
|
|
||||||
// Controllers
|
// Controllers
|
||||||
authenticationModule
|
authenticationModule
|
||||||
.bind(DI_SYMBOLS.ISignInController)
|
.bind(DI_SYMBOLS.ISignInPasswordlessController)
|
||||||
.toHigherOrderFunction(signInController, [
|
.toHigherOrderFunction(signInPasswordlessController, [
|
||||||
DI_SYMBOLS.IInstrumentationService,
|
DI_SYMBOLS.IInstrumentationService,
|
||||||
DI_SYMBOLS.ISignInUseCase,
|
DI_SYMBOLS.ISignInPasswordlessUseCase,
|
||||||
|
]);
|
||||||
|
|
||||||
|
authenticationModule
|
||||||
|
.bind(DI_SYMBOLS.ISignInWithPasswordController)
|
||||||
|
.toHigherOrderFunction(signInWithPasswordController, [
|
||||||
|
DI_SYMBOLS.IInstrumentationService,
|
||||||
|
DI_SYMBOLS.ISignInWithPasswordUseCase,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
authenticationModule
|
authenticationModule
|
||||||
|
|
|
@ -3,12 +3,12 @@ import { ITransactionManagerService } from '@/src/application/services/transacti
|
||||||
import { IInstrumentationService } from '@/src/application/services/instrumentation.service.interface';
|
import { IInstrumentationService } from '@/src/application/services/instrumentation.service.interface';
|
||||||
import { ICrashReporterService } from '@/src/application/services/crash-reporter.service.interface';
|
import { ICrashReporterService } from '@/src/application/services/crash-reporter.service.interface';
|
||||||
|
|
||||||
import { ISignInUseCase } from '@/src/application/use-cases/auth/sign-in.use-case';
|
import { ISignInPasswordlessUseCase } from '@/src/application/use-cases/auth/sign-in-passwordless.use-case';
|
||||||
import { ISignUpUseCase } from '@/src/application/use-cases/auth/sign-up.use-case';
|
import { ISignUpUseCase } from '@/src/application/use-cases/auth/sign-up.use-case';
|
||||||
import { ISignOutUseCase } from '@/src/application/use-cases/auth/sign-out.use-case';
|
import { ISignOutUseCase } from '@/src/application/use-cases/auth/sign-out.use-case';
|
||||||
import { IUsersRepository } from '@/src/application/repositories/users.repository.interface';
|
import { IUsersRepository } from '@/src/application/repositories/users.repository.interface';
|
||||||
import { IVerifyOtpUseCase } from '@/src/application/use-cases/auth/verify-otp.use-case';
|
import { IVerifyOtpUseCase } from '@/src/application/use-cases/auth/verify-otp.use-case';
|
||||||
import { ISignInController } from '@/src/interface-adapters/controllers/auth/sign-in.controller';
|
import { ISignInPasswordlessController } from '@/src/interface-adapters/controllers/auth/sign-in-passwordless.controller';
|
||||||
import { ISignOutController } from '@/src/interface-adapters/controllers/auth/sign-out.controller';
|
import { ISignOutController } from '@/src/interface-adapters/controllers/auth/sign-out.controller';
|
||||||
import { IVerifyOtpController } from '@/src/interface-adapters/controllers/auth/verify-otp.controller';
|
import { IVerifyOtpController } from '@/src/interface-adapters/controllers/auth/verify-otp.controller';
|
||||||
import { IBanUserUseCase } from '@/src/application/use-cases/users/ban-user.use-case';
|
import { IBanUserUseCase } from '@/src/application/use-cases/users/ban-user.use-case';
|
||||||
|
@ -39,6 +39,7 @@ import { ISendMagicLinkController } from '@/src/interface-adapters/controllers/a
|
||||||
import { ISendPasswordRecoveryController } from '@/src/interface-adapters/controllers/auth/send-password-recovery.controller';
|
import { ISendPasswordRecoveryController } from '@/src/interface-adapters/controllers/auth/send-password-recovery.controller';
|
||||||
import { IUploadAvatarController } from '@/src/interface-adapters/controllers/users/upload-avatar.controller';
|
import { IUploadAvatarController } from '@/src/interface-adapters/controllers/users/upload-avatar.controller';
|
||||||
import { IUploadAvatarUseCase } from '@/src/application/use-cases/users/upload-avatar.use-case';
|
import { IUploadAvatarUseCase } from '@/src/application/use-cases/users/upload-avatar.use-case';
|
||||||
|
import { ISignInWithPasswordController } from '@/src/interface-adapters/controllers/auth/sign-in-with-password.controller';
|
||||||
|
|
||||||
export const DI_SYMBOLS = {
|
export const DI_SYMBOLS = {
|
||||||
// Services
|
// Services
|
||||||
|
@ -51,7 +52,8 @@ export const DI_SYMBOLS = {
|
||||||
IUsersRepository: Symbol.for('IUsersRepository'),
|
IUsersRepository: Symbol.for('IUsersRepository'),
|
||||||
|
|
||||||
// Use Cases
|
// Use Cases
|
||||||
ISignInUseCase: Symbol.for('ISignInUseCase'),
|
ISignInPasswordlessUseCase: Symbol.for('ISignInPasswordlessUseCase'),
|
||||||
|
ISignInWithPasswordUseCase: Symbol.for('ISignInWithPasswordUseCase'),
|
||||||
ISignUpUseCase: Symbol.for('ISignUpUseCase'),
|
ISignUpUseCase: Symbol.for('ISignUpUseCase'),
|
||||||
IVerifyOtpUseCase: Symbol.for('IVerifyOtpUseCase'),
|
IVerifyOtpUseCase: Symbol.for('IVerifyOtpUseCase'),
|
||||||
ISignOutUseCase: Symbol.for('ISignOutUseCase'),
|
ISignOutUseCase: Symbol.for('ISignOutUseCase'),
|
||||||
|
@ -72,7 +74,8 @@ export const DI_SYMBOLS = {
|
||||||
IUploadAvatarUseCase: Symbol.for('IUploadAvatarUseCase'),
|
IUploadAvatarUseCase: Symbol.for('IUploadAvatarUseCase'),
|
||||||
|
|
||||||
// Controllers
|
// Controllers
|
||||||
ISignInController: Symbol.for('ISignInController'),
|
ISignInPasswordlessController: Symbol.for('ISignInPasswordlessController'),
|
||||||
|
ISignInWithPasswordController: Symbol.for('ISignInWithPasswordController'),
|
||||||
ISignOutController: Symbol.for('ISignOutController'),
|
ISignOutController: Symbol.for('ISignOutController'),
|
||||||
IVerifyOtpController: Symbol.for('IVerifyOtpController'),
|
IVerifyOtpController: Symbol.for('IVerifyOtpController'),
|
||||||
ISendMagicLinkController: Symbol.for('ISendMagicLinkController'),
|
ISendMagicLinkController: Symbol.for('ISendMagicLinkController'),
|
||||||
|
@ -103,7 +106,7 @@ export interface DI_RETURN_TYPES {
|
||||||
IUsersRepository: IUsersRepository;
|
IUsersRepository: IUsersRepository;
|
||||||
|
|
||||||
// Use Cases
|
// Use Cases
|
||||||
ISignInUseCase: ISignInUseCase;
|
ISignInPasswordlessUseCase: ISignInPasswordlessUseCase;
|
||||||
ISignUpUseCase: ISignUpUseCase;
|
ISignUpUseCase: ISignUpUseCase;
|
||||||
IVerifyOtpUseCase: IVerifyOtpUseCase;
|
IVerifyOtpUseCase: IVerifyOtpUseCase;
|
||||||
ISignOutUseCase: ISignOutUseCase;
|
ISignOutUseCase: ISignOutUseCase;
|
||||||
|
@ -124,7 +127,8 @@ export interface DI_RETURN_TYPES {
|
||||||
IUploadAvatarUseCase: IUploadAvatarUseCase;
|
IUploadAvatarUseCase: IUploadAvatarUseCase;
|
||||||
|
|
||||||
// Controllers
|
// Controllers
|
||||||
ISignInController: ISignInController;
|
ISignInPasswordlessController: ISignInPasswordlessController;
|
||||||
|
ISignInWithPasswordController: ISignInWithPasswordController;
|
||||||
IVerifyOtpController: IVerifyOtpController;
|
IVerifyOtpController: IVerifyOtpController;
|
||||||
ISignOutController: ISignOutController;
|
ISignOutController: ISignOutController;
|
||||||
ISendMagicLinkController: ISendMagicLinkController;
|
ISendMagicLinkController: ISendMagicLinkController;
|
||||||
|
|
|
@ -9,9 +9,9 @@ import { IUserSchema } from "@/src/entities/models/users/users.model"
|
||||||
|
|
||||||
export interface IAuthenticationService {
|
export interface IAuthenticationService {
|
||||||
signInPasswordless(credentials: ISignInPasswordlessSchema): Promise<void>
|
signInPasswordless(credentials: ISignInPasswordlessSchema): Promise<void>
|
||||||
SignInWithPasswordSchema(credentials: ISignInWithPasswordSchema): Promise<void>
|
signInWithPassword(credentials: ISignInWithPasswordSchema): Promise<void>
|
||||||
SignUpWithEmailSchema(credentials: ISignUpWithEmailSchema): Promise<IUserSchema>
|
SignUpWithEmail(credentials: ISignUpWithEmailSchema): Promise<IUserSchema>
|
||||||
SignUpWithPhoneSchema(credentials: ISignUpWithPhoneSchema): Promise<IUserSchema>
|
SignUpWithPhone(credentials: ISignUpWithPhoneSchema): Promise<IUserSchema>
|
||||||
getSession(): Promise<Session | null>
|
getSession(): Promise<Session | null>
|
||||||
signOut(): Promise<void>
|
signOut(): Promise<void>
|
||||||
sendMagicLink(credentials: ISendMagicLinkSchema): Promise<void>
|
sendMagicLink(credentials: ISendMagicLinkSchema): Promise<void>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { type TSignInSchema, ISignInPasswordlessSchema, SignInSchema } from "@/s
|
||||||
import { IAuthenticationService } from "@/src/application/services/authentication.service.interface";
|
import { IAuthenticationService } from "@/src/application/services/authentication.service.interface";
|
||||||
import { IUsersRepository } from "../../repositories/users.repository.interface";
|
import { IUsersRepository } from "../../repositories/users.repository.interface";
|
||||||
|
|
||||||
export type ISignInUseCase = ReturnType<typeof signInUseCase>
|
export type ISignInPasswordlessUseCase = ReturnType<typeof signInUseCase>
|
||||||
|
|
||||||
export const signInUseCase =
|
export const signInUseCase =
|
||||||
(
|
(
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { UnauthenticatedError } from "@/src/entities/errors/auth";
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository.interface";
|
||||||
|
import { IAuthenticationService } from "../../services/authentication.service.interface";
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface";
|
||||||
|
|
||||||
|
export type ISignInWithPasswordUseCase = ReturnType<typeof signInWithPasswordUseCase>
|
||||||
|
|
||||||
|
export const signInWithPasswordUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
authenticationService: IAuthenticationService,
|
||||||
|
usersRepository: IUsersRepository
|
||||||
|
) => async (input: { email: string; password: string }): Promise<void> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "signInWithPassword Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
|
||||||
|
const existingUser = await usersRepository.getUserByEmail({ email: input.email })
|
||||||
|
|
||||||
|
if (!existingUser) {
|
||||||
|
throw new UnauthenticatedError("User not found. Please tell your admin to create an account for you.")
|
||||||
|
}
|
||||||
|
|
||||||
|
await authenticationService.signInWithPassword({ email: input.email, password: input.password })
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -16,7 +16,6 @@ export type TSignInSchema = z.infer<typeof SignInSchema>;
|
||||||
export const SignInWithPasswordSchema = SignInSchema.pick({
|
export const SignInWithPasswordSchema = SignInSchema.pick({
|
||||||
email: true,
|
email: true,
|
||||||
password: true,
|
password: true,
|
||||||
phone: true
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,7 +25,6 @@ export type ISignInWithPasswordSchema = z.infer<typeof SignInWithPasswordSchema>
|
||||||
export const defaulISignInWithPasswordSchemaValues: ISignInWithPasswordSchema = {
|
export const defaulISignInWithPasswordSchemaValues: ISignInWithPasswordSchema = {
|
||||||
email: "",
|
email: "",
|
||||||
password: "",
|
password: "",
|
||||||
phone: ""
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SignInPasswordlessSchema = SignInSchema.pick({
|
export const SignInPasswordlessSchema = SignInSchema.pick({
|
||||||
|
@ -40,9 +38,13 @@ export const defaulISignInPasswordlessSchemaValues: ISignInPasswordlessSchema =
|
||||||
email: "",
|
email: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Define the sign-in response schema using Zod
|
// Define the sign-in response schema using Zod
|
||||||
export const SignInResponseSchema = z.object({
|
export const SignInResponseSchema = z.object({
|
||||||
success: z.boolean(),
|
success: z.boolean(),
|
||||||
message: z.string(),
|
message: z.string(),
|
||||||
redirectTo: z.string().optional(),
|
redirectTo: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ export class AuthenticationService implements IAuthenticationService {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async SignInWithPasswordSchema(credentials: ISignInWithPasswordSchema): Promise<void> {
|
async signInWithPassword(credentials: ISignInWithPasswordSchema): Promise<void> {
|
||||||
return await this.instrumentationService.startSpan({
|
return await this.instrumentationService.startSpan({
|
||||||
name: "SignInWithPasswordSchema Use Case",
|
name: "SignInWithPasswordSchema Use Case",
|
||||||
}, async () => {
|
}, async () => {
|
||||||
|
@ -79,7 +79,7 @@ export class AuthenticationService implements IAuthenticationService {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async SignUpWithEmailSchema(credentials: ISignUpWithEmailSchema): Promise<IUserSchema> {
|
async SignUpWithEmail(credentials: ISignUpWithEmailSchema): Promise<IUserSchema> {
|
||||||
return await this.instrumentationService.startSpan({
|
return await this.instrumentationService.startSpan({
|
||||||
name: "SignUpWithEmailSchema Use Case",
|
name: "SignUpWithEmailSchema Use Case",
|
||||||
}, async () => {
|
}, async () => {
|
||||||
|
@ -124,7 +124,7 @@ export class AuthenticationService implements IAuthenticationService {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async SignUpWithPhoneSchema(credentials: ISignUpWithPhoneSchema): Promise<IUserSchema> {
|
async SignUpWithPhone(credentials: ISignUpWithPhoneSchema): Promise<IUserSchema> {
|
||||||
throw new Error("Method not implemented.");
|
throw new Error("Method not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { ISignInUseCase } from "@/src/application/use-cases/auth/sign-in.use-case";
|
import { ISignInPasswordlessUseCase } from "@/src/application/use-cases/auth/sign-in-passwordless.use-case";
|
||||||
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
|
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
|
||||||
import { InputParseError } from "@/src/entities/errors/common";
|
import { InputParseError } from "@/src/entities/errors/common";
|
||||||
|
|
||||||
|
@ -8,12 +8,12 @@ const signInInputSchema = z.object({
|
||||||
email: z.string().min(1, "Email is Required").email("Please enter a valid email address"),
|
email: z.string().min(1, "Email is Required").email("Please enter a valid email address"),
|
||||||
})
|
})
|
||||||
|
|
||||||
export type ISignInController = ReturnType<typeof signInController>
|
export type ISignInPasswordlessController = ReturnType<typeof signInPasswordlessController>
|
||||||
|
|
||||||
export const signInController =
|
export const signInPasswordlessController =
|
||||||
(
|
(
|
||||||
instrumentationService: IInstrumentationService,
|
instrumentationService: IInstrumentationService,
|
||||||
signInUseCase: ISignInUseCase
|
signInUseCase: ISignInPasswordlessUseCase
|
||||||
) =>
|
) =>
|
||||||
async (input: Partial<z.infer<typeof signInInputSchema>>) => {
|
async (input: Partial<z.infer<typeof signInInputSchema>>) => {
|
||||||
return await instrumentationService.startSpan({ name: "signIn Controller" }, async () => {
|
return await instrumentationService.startSpan({ name: "signIn Controller" }, async () => {
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
|
||||||
|
import { ISignInWithPasswordUseCase } from "@/src/application/use-cases/auth/sign-in-with-password.use-case";
|
||||||
|
import { InputParseError } from "@/src/entities/errors/common";
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// Sign In Controller
|
||||||
|
const signInWithPasswordInputSchema = z.object({
|
||||||
|
email: z.string().min(1, "Email is Required").email("Please enter a valid email address"),
|
||||||
|
password: z.string().min(1, "Password is Required")
|
||||||
|
})
|
||||||
|
|
||||||
|
export type ISignInWithPasswordController = ReturnType<typeof signInWithPasswordController>
|
||||||
|
|
||||||
|
export const signInWithPasswordController = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
signInWithPasswordUseCase: ISignInWithPasswordUseCase
|
||||||
|
) =>
|
||||||
|
async (input: Partial<z.infer<typeof signInWithPasswordInputSchema>>) => {
|
||||||
|
return await instrumentationService.startSpan({ name: "signInWithPassword Controller" },
|
||||||
|
async () => {
|
||||||
|
const { data, error: inputParseError } = signInWithPasswordInputSchema.safeParse(input)
|
||||||
|
|
||||||
|
if (inputParseError) {
|
||||||
|
throw new InputParseError(inputParseError.errors[0].message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return await signInWithPasswordUseCase({
|
||||||
|
email: data.email,
|
||||||
|
password: data.password
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue