refactor project structure (backups)
This commit is contained in:
parent
5830eedb18
commit
693f5d265e
|
@ -6,10 +6,10 @@ export default async function DashboardPage() {
|
||||||
const supabase = await createClient();
|
const supabase = await createClient();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: { user },
|
data: { session },
|
||||||
} = await supabase.auth.getUser();
|
} = await supabase.auth.getSession();
|
||||||
|
|
||||||
if (!user) {
|
if (!session) {
|
||||||
return redirect("/sign-in");
|
return redirect("/sign-in");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ export default async function DashboardPage() {
|
||||||
<div className="grid auto-rows-min gap-4 md:grid-cols-3">
|
<div className="grid auto-rows-min gap-4 md:grid-cols-3">
|
||||||
<div className="aspect-video rounded-xl bg-muted/50">
|
<div className="aspect-video rounded-xl bg-muted/50">
|
||||||
<pre className="text-xs font-mono p-3 rounded border overflow-auto">
|
<pre className="text-xs font-mono p-3 rounded border overflow-auto">
|
||||||
{JSON.stringify(user, null, 2)}
|
{JSON.stringify(session, null, 2)}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,13 +8,33 @@ import { SubmitButton } from "@/app/_components/submit-button";
|
||||||
import Link from "next/link";
|
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 { signIn } from "../action";
|
||||||
|
|
||||||
export function SignInForm({
|
export function SignInForm({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentPropsWithoutRef<"form">) {
|
}: React.ComponentPropsWithoutRef<"form">) {
|
||||||
|
|
||||||
const { register, isPending, handleSubmit, errors } = useSignInController();
|
const [error, setError] = useState<string>();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
if (loading) return;
|
||||||
|
|
||||||
|
const formData = new FormData(event.currentTarget);
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
const res = await signIn(formData);
|
||||||
|
if (res && res.error) {
|
||||||
|
setError(res.error);
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// const { register, isPending, handleSubmit, errors } = useSignInController();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -41,7 +61,7 @@ export function SignInForm({
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="w-full bg-[#1C1C1C] text-white border-gray-800 hover:bg-[#2C2C2C] hover:border-gray-700"
|
className="w-full bg-[#1C1C1C] text-white border-gray-800 hover:bg-[#2C2C2C] hover:border-gray-700"
|
||||||
size="lg"
|
size="lg"
|
||||||
disabled={isPending}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
<Lock className="mr-2 h-5 w-5" />
|
<Lock className="mr-2 h-5 w-5" />
|
||||||
Continue with SSO
|
Continue with SSO
|
||||||
|
@ -57,28 +77,28 @@ export function SignInForm({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4" {...props} noValidate>
|
<form onSubmit={onSubmit} className="space-y-4" {...props} noValidate>
|
||||||
<FormField
|
<FormField
|
||||||
label="Email"
|
label="Email"
|
||||||
input={
|
input={
|
||||||
<Input
|
<Input
|
||||||
id="email"
|
id="email"
|
||||||
type="email"
|
type="email"
|
||||||
{...register("email")}
|
name="email"
|
||||||
placeholder="you@example.com"
|
placeholder="you@example.com"
|
||||||
className={`bg-[#1C1C1C] border-gray-800 ${errors.email ? "ring-red-500 focus-visible:ring-red-500" : ""
|
className={`bg-[#1C1C1C] border-gray-800 ${error ? "ring-red-500 focus-visible:ring-red-500" : ""
|
||||||
}`}
|
}`}
|
||||||
disabled={isPending}
|
disabled={loading}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
error={errors.email ? errors.email.message : undefined}
|
error={error ? error : undefined}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
className="w-full bg-emerald-600 hover:bg-emerald-700 text-white"
|
className="w-full bg-emerald-600 hover:bg-emerald-700 text-white"
|
||||||
size="lg"
|
size="lg"
|
||||||
disabled={isPending}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
{isPending ? (
|
{loading ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="h-5 w-5 animate-spin" />
|
<Loader2 className="h-5 w-5 animate-spin" />
|
||||||
Signing in...
|
Signing in...
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
"use server";
|
||||||
|
|
||||||
|
import { redirect } from "next/navigation"
|
||||||
|
import { getInjection } from "@/di/container"
|
||||||
|
import { revalidatePath } from "next/cache"
|
||||||
|
|
||||||
|
import { InputParseError } from "@/src/entities/errors/common"
|
||||||
|
import { AuthenticationError, UnauthenticatedError } from "@/src/entities/errors/auth"
|
||||||
|
import { createClient } from "@/app/_utils/supabase/server"
|
||||||
|
|
||||||
|
export async function signIn(formData: FormData) {
|
||||||
|
const instrumentationService = getInjection("IInstrumentationService")
|
||||||
|
return await instrumentationService.instrumentServerAction("signIn", {
|
||||||
|
recordResponse: true
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
try {
|
||||||
|
const email = formData.get("email")?.toString()
|
||||||
|
|
||||||
|
const signInController = getInjection("ISignInController")
|
||||||
|
await signInController({ email })
|
||||||
|
|
||||||
|
if (email) redirect(`/verify-otp?email=${encodeURIComponent(email)}`)
|
||||||
|
} catch (err) {
|
||||||
|
if (
|
||||||
|
err instanceof InputParseError ||
|
||||||
|
err instanceof AuthenticationError
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
error: 'Incorrect credential. Please try again.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// const instrumentationService = getInjection("IInstrumentationService")
|
||||||
|
// return await instrumentationService.instrumentServerAction("signUp", { recordResponse: true }, async () => {
|
||||||
|
// try {
|
||||||
|
// const data = Object.fromEntries(formData.entries())
|
||||||
|
// const signUpController = getInjection("ISignUpController")
|
||||||
|
// await signUpController(data)
|
||||||
|
// } catch (err) {
|
||||||
|
// if (err instanceof InputParseError) {
|
||||||
|
// return { error: err.message, success: false }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const crashReporterService = getInjection("ICrashReporterService")
|
||||||
|
// crashReporterService.report(err)
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// error: "An error occurred during sign up. Please try again later.",
|
||||||
|
// success: false,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
export async function signOut() {
|
||||||
|
const instrumentationService = getInjection("IInstrumentationService")
|
||||||
|
return await instrumentationService.instrumentServerAction("signOut", {
|
||||||
|
recordResponse: true
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const signOutController = getInjection("ISignOutController")
|
||||||
|
await signOutController()
|
||||||
|
|
||||||
|
revalidatePath("/")
|
||||||
|
redirect("/sign-in") // Updated to match your route
|
||||||
|
} catch (err) {
|
||||||
|
const crashReporterService = getInjection("ICrashReporterService")
|
||||||
|
crashReporterService.report(err)
|
||||||
|
|
||||||
|
return {
|
||||||
|
error: "An error occurred during sign out. Please try again later.",
|
||||||
|
success: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verifyOtp(formData: FormData) {
|
||||||
|
const instrumentationService = getInjection("IInstrumentationService")
|
||||||
|
return await instrumentationService.instrumentServerAction("verifyOtp", {
|
||||||
|
recordResponse: true
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const email = formData.get("email")?.toString()
|
||||||
|
const token = formData.get("token")?.toString()
|
||||||
|
|
||||||
|
const verifyOtpController = getInjection("IVerifyOtpController")
|
||||||
|
await verifyOtpController({ email, token })
|
||||||
|
|
||||||
|
redirect("/dashboard") // Updated to match your route
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof InputParseError || err instanceof AuthenticationError) {
|
||||||
|
return { error: err.message, success: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
const crashReporterService = getInjection("ICrashReporterService")
|
||||||
|
crashReporterService.report(err)
|
||||||
|
|
||||||
|
return {
|
||||||
|
error: "An error occurred during OTP verification. Please try again later.",
|
||||||
|
success: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { checkSession } from "./_actions/session";
|
// import { checkSession } from "./_actions/session";
|
||||||
import { createClient } from "@/app/_utils/supabase/client";
|
import { createClient } from "@/app/_utils/supabase/client";
|
||||||
|
|
||||||
export default async function Layout({
|
export default async function Layout({
|
||||||
|
|
|
@ -27,3 +27,5 @@ export const createClient = async () => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { createContainer } from '@evyweb/ioctopus';
|
||||||
|
import { DI_RETURN_TYPES, DI_SYMBOLS } from '@/di/types';
|
||||||
|
import { IInstrumentationService } from '@/src/application/services/instrumentation.service.interface';
|
||||||
|
import { createAuthenticationModule } from './modules/authentication.module';
|
||||||
|
|
||||||
|
const ApplicationContainer = createContainer();
|
||||||
|
ApplicationContainer.load(Symbol('AuthenticationModule'), createAuthenticationModule());
|
||||||
|
|
||||||
|
export function getInjection<K extends keyof typeof DI_SYMBOLS>(
|
||||||
|
symbol: K
|
||||||
|
): DI_RETURN_TYPES[K] {
|
||||||
|
const instrumentationService =
|
||||||
|
ApplicationContainer.get<IInstrumentationService>(
|
||||||
|
DI_SYMBOLS.IInstrumentationService
|
||||||
|
);
|
||||||
|
|
||||||
|
return instrumentationService.startSpan(
|
||||||
|
{
|
||||||
|
name: '(di) getInjection',
|
||||||
|
op: 'function',
|
||||||
|
attributes: { symbol: symbol.toString() },
|
||||||
|
},
|
||||||
|
() => ApplicationContainer.get(DI_SYMBOLS[symbol])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { createModule } from '@evyweb/ioctopus';
|
||||||
|
|
||||||
|
import { DI_SYMBOLS } from '@/di/types';
|
||||||
|
import { signInController, signOutController, verifyOtpController } from '@/src/interface-adapters/controllers/auth/authentication-controller';
|
||||||
|
import { AuthenticationService } from '@/src/infrastructure/services/authentication.service';
|
||||||
|
import { IInstrumentationServiceImpl } from '@/src/application/services/instrumentation.service.interface';
|
||||||
|
|
||||||
|
export function createAuthenticationModule() {
|
||||||
|
const authenticationModule = createModule();
|
||||||
|
|
||||||
|
authenticationModule
|
||||||
|
.bind(DI_SYMBOLS.IAuthenticationService)
|
||||||
|
.toClass(AuthenticationService, [
|
||||||
|
DI_SYMBOLS.IUsersRepository,
|
||||||
|
DI_SYMBOLS.IInstrumentationService,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Rest of your bindings remain the same
|
||||||
|
authenticationModule
|
||||||
|
.bind(DI_SYMBOLS.ISignInController)
|
||||||
|
.toHigherOrderFunction(signInController, [
|
||||||
|
DI_SYMBOLS.IInstrumentationService,
|
||||||
|
DI_SYMBOLS.ISignInUseCase,
|
||||||
|
]);
|
||||||
|
|
||||||
|
authenticationModule
|
||||||
|
.bind(DI_SYMBOLS.IVerifyOtpController)
|
||||||
|
.toHigherOrderFunction(verifyOtpController, [
|
||||||
|
DI_SYMBOLS.IInstrumentationService,
|
||||||
|
DI_SYMBOLS.IVerifyOtpUseCase,
|
||||||
|
]);
|
||||||
|
|
||||||
|
authenticationModule
|
||||||
|
.bind(DI_SYMBOLS.ISignOutController)
|
||||||
|
.toHigherOrderFunction(signOutController, [
|
||||||
|
DI_SYMBOLS.IInstrumentationService,
|
||||||
|
DI_SYMBOLS.ISignOutUseCase,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return authenticationModule;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { createModule } from '@evyweb/ioctopus';
|
||||||
|
|
||||||
|
import { DI_SYMBOLS } from '@/di/types';
|
||||||
|
|
||||||
|
export function createMonitoringModule() {
|
||||||
|
const monitoringModule = createModule();
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'test') {
|
||||||
|
monitoringModule
|
||||||
|
.bind(DI_SYMBOLS.IInstrumentationService)
|
||||||
|
.toClass(MockInstrumentationService);
|
||||||
|
monitoringModule
|
||||||
|
.bind(DI_SYMBOLS.ICrashReporterService)
|
||||||
|
.toClass(MockCrashReporterService);
|
||||||
|
} else {
|
||||||
|
monitoringModule
|
||||||
|
.bind(DI_SYMBOLS.IInstrumentationService)
|
||||||
|
.toClass(InstrumentationService);
|
||||||
|
monitoringModule
|
||||||
|
.bind(DI_SYMBOLS.ICrashReporterService)
|
||||||
|
.toClass(CrashReporterService);
|
||||||
|
}
|
||||||
|
|
||||||
|
return monitoringModule;
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { IAuthenticationService } from '@/src/application/services/authentication.service.interface';
|
||||||
|
import { ITransactionManagerService } from '@/src/application/services/transaction-manager.service.interface';
|
||||||
|
import { IInstrumentationService } from '@/src/application/services/instrumentation.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 { ISignUpUseCase } from '@/src/application/use-cases/auth/sign-up.use-case';
|
||||||
|
import { ISignOutUseCase } from '@/src/application/use-cases/auth/sign-out.use-case';
|
||||||
|
import { IUsersRepository } from '@/src/application/repositories/users.repository';
|
||||||
|
import { ISignInController, ISignOutController, IVerifyOtpController } from '@/src/interface-adapters/controllers/auth/authentication-controller';
|
||||||
|
import { IVerifyOtpUseCase } from '@/src/application/use-cases/auth/verify-otp.use-case';
|
||||||
|
import { IInviteUserUseCase } from '@/src/application/use-cases/users/invite-user.use-case';
|
||||||
|
|
||||||
|
export const DI_SYMBOLS = {
|
||||||
|
// Services
|
||||||
|
IAuthenticationService: Symbol.for('IAuthenticationService'),
|
||||||
|
ITransactionManagerService: Symbol.for('ITransactionManagerService'),
|
||||||
|
IInstrumentationService: Symbol.for('IInstrumentationService'),
|
||||||
|
ICrashReporterService: Symbol.for('ICrashReporterService'),
|
||||||
|
|
||||||
|
// Repositories
|
||||||
|
IUsersRepository: Symbol.for('IUsersRepository'),
|
||||||
|
|
||||||
|
// Use Cases
|
||||||
|
ISignInUseCase: Symbol.for('ISignInUseCase'),
|
||||||
|
ISignOutUseCase: Symbol.for('ISignOutUseCase'),
|
||||||
|
IVerifyOtpUseCase: Symbol.for('IVerifyOtpUseCase'),
|
||||||
|
|
||||||
|
// Controllers
|
||||||
|
ISignInController: Symbol.for('ISignInController'),
|
||||||
|
ISignOutController: Symbol.for('ISignOutController'),
|
||||||
|
IVerifyOtpController: Symbol.for('IVerifyOtpController'),
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface DI_RETURN_TYPES {
|
||||||
|
// Services
|
||||||
|
IAuthenticationService: IAuthenticationService;
|
||||||
|
ITransactionManagerService: ITransactionManagerService;
|
||||||
|
IInstrumentationService: IInstrumentationService;
|
||||||
|
ICrashReporterService: ICrashReporterService;
|
||||||
|
|
||||||
|
// Repositories
|
||||||
|
IUsersRepository: IUsersRepository;
|
||||||
|
|
||||||
|
// Use Cases
|
||||||
|
ISignInUseCase: ISignInUseCase;
|
||||||
|
ISignOutUseCase: ISignOutUseCase;
|
||||||
|
IVerifyOtpUseCase: IVerifyOtpUseCase;
|
||||||
|
|
||||||
|
// Controllers
|
||||||
|
ISignInController: ISignInController;
|
||||||
|
ISignOutController: ISignOutController;
|
||||||
|
IVerifyOtpController: IVerifyOtpController;
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@evyweb/ioctopus": "^1.2.0",
|
||||||
"@hookform/resolvers": "^4.1.2",
|
"@hookform/resolvers": "^4.1.2",
|
||||||
"@prisma/client": "^6.4.1",
|
"@prisma/client": "^6.4.1",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.6",
|
"@radix-ui/react-alert-dialog": "^1.1.6",
|
||||||
|
@ -1054,6 +1055,12 @@
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@evyweb/ioctopus": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@evyweb/ioctopus/-/ioctopus-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-OIISYUx7WZDm6uxQkVsKmNF13tEiA3gbUeboTkr4LUTmJffhSVswiWAs8Ng5DoyvUlmgteTYcHP5XzOtrPTxLw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@floating-ui/core": {
|
"node_modules/@floating-ui/core": {
|
||||||
"version": "1.6.9",
|
"version": "1.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz",
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
"seed": "ts-node prisma/seed.ts"
|
"seed": "ts-node prisma/seed.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@evyweb/ioctopus": "^1.2.0",
|
||||||
"@hookform/resolvers": "^4.1.2",
|
"@hookform/resolvers": "^4.1.2",
|
||||||
"@prisma/client": "^6.4.1",
|
"@prisma/client": "^6.4.1",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.6",
|
"@radix-ui/react-alert-dialog": "^1.1.6",
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import { PrismaClient } from "@prisma/client";
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
|
||||||
const prismaClientSingleton = () => {
|
const prismaClientSingleton = () => {
|
||||||
return new PrismaClient();
|
return new PrismaClient({
|
||||||
|
log: [
|
||||||
|
"query",
|
||||||
|
]
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
declare const globalThis: {
|
declare const globalThis: {
|
||||||
|
@ -16,3 +20,4 @@ if (process.env.NODE_ENV !== "production") globalThis.prismaGlobal = db;
|
||||||
|
|
||||||
|
|
||||||
export type Transaction = PrismaClient['$transaction'];
|
export type Transaction = PrismaClient['$transaction'];
|
||||||
|
|
||||||
|
|
|
@ -1,179 +1,6 @@
|
||||||
// // src/repositories/auth.repository.ts
|
|
||||||
// "use server";
|
|
||||||
|
|
||||||
// import { createClient } from "@/app/_utils/supabase/server";
|
|
||||||
// import { SignInFormData } from "@/src/entities/models/auth/sign-in.model";
|
|
||||||
// import { VerifyOtpFormData } from "@/src/entities/models/auth/verify-otp.model";
|
|
||||||
// import { AuthenticationError } from "@/src/entities/errors/auth";
|
|
||||||
// import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
|
|
||||||
// import { ICrashReporterService } from "@/src/application/services/crash-reporter.service.interface";
|
|
||||||
// import { createAdminClient } from "@/app/_utils/supabase/admin";
|
|
||||||
// import { DatabaseOperationError } from "@/src/entities/errors/common";
|
|
||||||
|
|
||||||
// export class AuthRepository {
|
|
||||||
// private static instance: AuthRepository;
|
|
||||||
|
|
||||||
// private constructor(
|
|
||||||
// private readonly instrumentationService: IInstrumentationService,
|
|
||||||
// private readonly crashReporterService: ICrashReporterService,
|
|
||||||
// private readonly supabaseAdmin = createAdminClient(),
|
|
||||||
// private readonly supabaseServer = createClient()
|
|
||||||
// ) { }
|
|
||||||
|
|
||||||
// // Method untuk mendapatkan singleton instance
|
|
||||||
// public static getInstance(
|
|
||||||
// instrumentationService: IInstrumentationService,
|
|
||||||
// crashReporterService: ICrashReporterService
|
|
||||||
// ): AuthRepository {
|
|
||||||
// if (!AuthRepository.instance) {
|
|
||||||
// AuthRepository.instance = new AuthRepository(instrumentationService, crashReporterService);
|
|
||||||
// }
|
|
||||||
// return AuthRepository.instance;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async signIn({ email }: SignInFormData) {
|
|
||||||
// return await this.instrumentationService.startSpan({
|
|
||||||
// name: "UsersRepository > signIn",
|
|
||||||
// op: 'db.query',
|
|
||||||
// attributes: { 'db.system': 'postgres' },
|
|
||||||
// }, async () => {
|
|
||||||
// try {
|
|
||||||
// const supabase = await this.supabaseServer;
|
|
||||||
// const { data, error } = await supabase.auth.signInWithOtp({
|
|
||||||
// email,
|
|
||||||
// options: {
|
|
||||||
// shouldCreateUser: false,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (error) {
|
|
||||||
// console.error("Error signing in:", error);
|
|
||||||
// throw new AuthenticationError(error.message);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return {
|
|
||||||
// data,
|
|
||||||
// redirectTo: `/verify-otp?email=${encodeURIComponent(email)}`,
|
|
||||||
// };
|
|
||||||
// } catch (err) {
|
|
||||||
// this.crashReporterService.report(err);
|
|
||||||
// throw err;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async verifyOtp({ email, token }: VerifyOtpFormData) {
|
|
||||||
// return await this.instrumentationService.startSpan({
|
|
||||||
// name: "UsersRepository > verifyOtp",
|
|
||||||
// op: 'db.query',
|
|
||||||
// attributes: { 'db.system': 'postgres' },
|
|
||||||
// }, async () => {
|
|
||||||
// try {
|
|
||||||
// const supabase = await this.supabaseServer;
|
|
||||||
// const { data, error } = await supabase.auth.verifyOtp({
|
|
||||||
// email,
|
|
||||||
// token,
|
|
||||||
// type: "email",
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (error) {
|
|
||||||
// console.error("Error verifying OTP:", error);
|
|
||||||
// throw new AuthenticationError(error.message);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return {
|
|
||||||
// data,
|
|
||||||
// redirectTo: "/dashboard",
|
|
||||||
// };
|
|
||||||
// } catch (err) {
|
|
||||||
// this.crashReporterService.report(err);
|
|
||||||
// throw err;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async signOut() {
|
|
||||||
// return await this.instrumentationService.startSpan({
|
|
||||||
// name: "UsersRepository > signOut",
|
|
||||||
// op: 'db.query',
|
|
||||||
// attributes: { 'db.system': 'postgres' },
|
|
||||||
// }, async () => {
|
|
||||||
// try {
|
|
||||||
// const supabase = await this.supabaseServer;
|
|
||||||
// const { error } = await supabase.auth.signOut();
|
|
||||||
|
|
||||||
// if (error) {
|
|
||||||
// console.error("Error signing out:", error);
|
|
||||||
// throw new AuthenticationError(error.message);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return {
|
|
||||||
// success: true,
|
|
||||||
// redirectTo: "/",
|
|
||||||
// };
|
|
||||||
// } catch (err) {
|
|
||||||
// this.crashReporterService.report(err);
|
|
||||||
// throw err;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async sendPasswordRecovery(email: string): Promise<void> {
|
|
||||||
// return await this.instrumentationService.startSpan({
|
|
||||||
// name: "UsersRepository > sendPasswordRecovery",
|
|
||||||
// op: 'db.query',
|
|
||||||
// attributes: { 'db.system': 'postgres' },
|
|
||||||
// }, async () => {
|
|
||||||
// try {
|
|
||||||
// const supabase = this.supabaseAdmin;
|
|
||||||
|
|
||||||
// const { error } = await supabase.auth.resetPasswordForEmail(email, {
|
|
||||||
// redirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/reset-password`,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (error) {
|
|
||||||
// console.error("Error sending password recovery:", error);
|
|
||||||
// throw new DatabaseOperationError(error.message);
|
|
||||||
// }
|
|
||||||
// } catch (err) {
|
|
||||||
// this.crashReporterService.report(err);
|
|
||||||
// throw err;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// async sendMagicLink(email: string): Promise<void> {
|
|
||||||
// return await this.instrumentationService.startSpan({
|
|
||||||
// name: "UsersRepository > sendMagicLink",
|
|
||||||
// op: 'db.query',
|
|
||||||
// attributes: { 'db.system': 'postgres' },
|
|
||||||
// }, async () => {
|
|
||||||
// try {
|
|
||||||
// const supabase = this.supabaseAdmin;
|
|
||||||
|
|
||||||
// const { error } = await supabase.auth.signInWithOtp({
|
|
||||||
// email,
|
|
||||||
// options: {
|
|
||||||
// emailRedirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/callback`,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (error) {
|
|
||||||
// console.error("Error sending magic link:", error);
|
|
||||||
// throw new DatabaseOperationError(error.message);
|
|
||||||
// }
|
|
||||||
// } catch (err) {
|
|
||||||
// this.crashReporterService.report(err);
|
|
||||||
// throw err;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// src/app/_actions/auth.actions.ts
|
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import { createClient } from "@/app/_utils/supabase/server";
|
import { createClient as createServerClient } from "@/app/_utils/supabase/server";
|
||||||
import { SignInFormData } from "@/src/entities/models/auth/sign-in.model";
|
import { SignInFormData } from "@/src/entities/models/auth/sign-in.model";
|
||||||
import { VerifyOtpFormData } from "@/src/entities/models/auth/verify-otp.model";
|
import { VerifyOtpFormData } from "@/src/entities/models/auth/verify-otp.model";
|
||||||
import { AuthenticationError } from "@/src/entities/errors/auth";
|
import { AuthenticationError } from "@/src/entities/errors/auth";
|
||||||
|
@ -182,6 +9,9 @@ import { createAdminClient } from "@/app/_utils/supabase/admin";
|
||||||
import { IInstrumentationServiceImpl } from "@/src/application/services/instrumentation.service.interface";
|
import { IInstrumentationServiceImpl } from "@/src/application/services/instrumentation.service.interface";
|
||||||
import { ICrashReporterServiceImpl } from "@/src/application/services/crash-reporter.service.interface";
|
import { ICrashReporterServiceImpl } from "@/src/application/services/crash-reporter.service.interface";
|
||||||
|
|
||||||
|
let supabaseAdmin = createAdminClient();
|
||||||
|
let supabaseServer = createServerClient();
|
||||||
|
|
||||||
// Server actions for authentication
|
// Server actions for authentication
|
||||||
export async function signIn({ email }: SignInFormData) {
|
export async function signIn({ email }: SignInFormData) {
|
||||||
return await IInstrumentationServiceImpl.instrumentServerAction(
|
return await IInstrumentationServiceImpl.instrumentServerAction(
|
||||||
|
@ -189,7 +19,7 @@ export async function signIn({ email }: SignInFormData) {
|
||||||
{ email },
|
{ email },
|
||||||
async () => {
|
async () => {
|
||||||
try {
|
try {
|
||||||
const supabase = await createClient();
|
const supabase = await supabaseServer;
|
||||||
const { data, error } = await supabase.auth.signInWithOtp({
|
const { data, error } = await supabase.auth.signInWithOtp({
|
||||||
email,
|
email,
|
||||||
options: {
|
options: {
|
||||||
|
@ -225,7 +55,7 @@ export async function verifyOtp({ email, token }: VerifyOtpFormData) {
|
||||||
{ email },
|
{ email },
|
||||||
async () => {
|
async () => {
|
||||||
try {
|
try {
|
||||||
const supabase = await createClient();
|
const supabase = await supabaseServer;
|
||||||
const { data, error } = await supabase.auth.verifyOtp({
|
const { data, error } = await supabase.auth.verifyOtp({
|
||||||
email,
|
email,
|
||||||
token,
|
token,
|
||||||
|
@ -260,7 +90,7 @@ export async function signOut() {
|
||||||
{},
|
{},
|
||||||
async () => {
|
async () => {
|
||||||
try {
|
try {
|
||||||
const supabase = await createClient();
|
const supabase = await supabaseServer;
|
||||||
const { error } = await supabase.auth.signOut();
|
const { error } = await supabase.auth.signOut();
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,18 @@
|
||||||
|
import { AuthResult } from "@/src/entities/models/auth/auth-result.model"
|
||||||
|
import { Session } from "@/src/entities/models/auth/session.model"
|
||||||
|
import { SignInFormData, SignInPasswordless, SignInWithPassword } from "@/src/entities/models/auth/sign-in.model"
|
||||||
|
import { SignUpWithEmail, SignUpWithPhone } from "@/src/entities/models/auth/sign-up.model"
|
||||||
|
import { VerifyOtpFormData } from "@/src/entities/models/auth/verify-otp.model"
|
||||||
|
import { User } from "@/src/entities/models/users/users.model"
|
||||||
|
|
||||||
|
export interface IAuthenticationService {
|
||||||
|
signInPasswordless(credentials: SignInPasswordless): Promise<void>
|
||||||
|
signInWithPassword(credentials: SignInWithPassword): Promise<void>
|
||||||
|
signUpWithEmail(credentials: SignUpWithEmail): Promise<User>
|
||||||
|
signUpWithPhone(credentials: SignUpWithPhone): Promise<User>
|
||||||
|
getSession(): Promise<Session | null>
|
||||||
|
signOut(): Promise<void>
|
||||||
|
sendMagicLink(email: string): Promise<void>
|
||||||
|
sendPasswordRecovery(email: string): Promise<void>
|
||||||
|
verifyOtp(credentials: VerifyOtpFormData): Promise<void>
|
||||||
|
}
|
|
@ -12,11 +12,16 @@ export interface IInstrumentationService {
|
||||||
|
|
||||||
class InstrumentationService implements IInstrumentationService {
|
class InstrumentationService implements IInstrumentationService {
|
||||||
startSpan<T>(
|
startSpan<T>(
|
||||||
options: { name: string; op?: string; attributes?: Record<string, any> },
|
spanAttributes: { name: string; op: string; attributes: Record<string, string> },
|
||||||
callback: () => T
|
callback: () => T
|
||||||
): T {
|
): T {
|
||||||
// Implementation of the startSpan method
|
// Your implementation here
|
||||||
|
console.log(`Starting span: ${spanAttributes.name}`);
|
||||||
|
try {
|
||||||
return callback();
|
return callback();
|
||||||
|
} finally {
|
||||||
|
console.log(`Ending span: ${spanAttributes.name}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async instrumentServerAction<T>(
|
async instrumentServerAction<T>(
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { AuthenticationError } from "@/src/entities/errors/auth"
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository"
|
||||||
|
import { IAuthenticationService } from "../../services/authentication.service.interface"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
import { NotFoundError } from "@/src/entities/errors/common"
|
||||||
|
|
||||||
|
export type ISendMagicLinkUseCase = ReturnType<typeof sendMagicLinkUseCase>
|
||||||
|
|
||||||
|
export const sendMagicLinkUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
authenticationService: IAuthenticationService,
|
||||||
|
usersRepository: IUsersRepository
|
||||||
|
) => async (input: { email: string }): Promise<void> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "sendMagicLink Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
const user = await usersRepository.getUserByEmail(input.email)
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new NotFoundError("User not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
await authenticationService.sendMagicLink(input.email)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { AuthenticationError } from "@/src/entities/errors/auth"
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository"
|
||||||
|
import { IAuthenticationService } from "../../services/authentication.service.interface"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
import { NotFoundError } from "@/src/entities/errors/common"
|
||||||
|
|
||||||
|
export type ISendPasswordRecoveryUseCase = ReturnType<typeof sendPasswordRecoveryUseCase>
|
||||||
|
|
||||||
|
export const sendPasswordRecoveryUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
authenticationService: IAuthenticationService,
|
||||||
|
usersRepository: IUsersRepository
|
||||||
|
) => async (input: { email: string }): Promise<void> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "sendPasswordRecovery Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
const user = await usersRepository.getUserByEmail(input.email)
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new NotFoundError("User not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
await authenticationService.sendPasswordRecovery(input.email)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
import type { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface"
|
||||||
|
import { AuthenticationError } from "@/src/entities/errors/auth";
|
||||||
|
import { InputParseError, NotFoundError } from "@/src/entities/errors/common";
|
||||||
|
import { type SignInFormData, SignInPasswordless, SignInSchema } from "@/src/entities/models/auth/sign-in.model"
|
||||||
|
import { IAuthenticationService } from "@/src/application/services/authentication.service.interface";
|
||||||
|
import { User } from "@/src/entities/models/users/users.model";
|
||||||
|
import { Session } from "@/src/entities/models/auth/session.model";
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository";
|
||||||
|
import { AuthResult } from "@/src/entities/models/auth/auth-result.model";
|
||||||
|
|
||||||
|
export type ISignInUseCase = ReturnType<typeof signInUseCase>
|
||||||
|
|
||||||
|
export const signInUseCase =
|
||||||
|
(
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
authenticationService: IAuthenticationService,
|
||||||
|
usersRepository: IUsersRepository
|
||||||
|
) =>
|
||||||
|
async (input: SignInPasswordless): Promise<void> => {
|
||||||
|
return instrumentationService.startSpan({ name: "signIn Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
|
||||||
|
const existingUser = await usersRepository.getUserByEmail(input.email)
|
||||||
|
|
||||||
|
if (!existingUser) {
|
||||||
|
throw new NotFoundError("User does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to sign in
|
||||||
|
await authenticationService.signInPasswordless({
|
||||||
|
email: input.email
|
||||||
|
})
|
||||||
|
|
||||||
|
const session = await authenticationService.getSession();
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
throw new NotFoundError("Session not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { IAuthenticationService } from "../../services/authentication.service.interface"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
|
||||||
|
export type ISignOutUseCase = ReturnType<typeof signOutUseCase>
|
||||||
|
|
||||||
|
export const signOutUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
authenticationService: IAuthenticationService
|
||||||
|
) => async (): Promise<void> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "signOut Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
await authenticationService.signOut()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { CreateUser, User } from "@/src/entities/models/users/users.model"
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository"
|
||||||
|
import { IAuthenticationService } from "../../services/authentication.service.interface"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
import { AuthenticationError } from "@/src/entities/errors/auth"
|
||||||
|
import { SignUpFormData } from "@/src/entities/models/auth/sign-up.model"
|
||||||
|
import { AuthResult } from "@/src/entities/models/auth/auth-result.model"
|
||||||
|
|
||||||
|
|
||||||
|
export type ISignUpUseCase = ReturnType<typeof signUpUseCase>
|
||||||
|
|
||||||
|
export const signUpUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
authenticationService: IAuthenticationService,
|
||||||
|
usersRepository: IUsersRepository
|
||||||
|
) => async (input: SignUpFormData): Promise<User> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "signUp Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
const existingUser = await usersRepository.getUserByEmail(input.email)
|
||||||
|
|
||||||
|
if (existingUser) {
|
||||||
|
throw new AuthenticationError("User already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
const newUser = await authenticationService.signUpWithEmail({
|
||||||
|
email: input.email,
|
||||||
|
password: input.password
|
||||||
|
})
|
||||||
|
|
||||||
|
await authenticationService.signInWithPassword({
|
||||||
|
email: input.email,
|
||||||
|
password: input.password
|
||||||
|
})
|
||||||
|
|
||||||
|
const session = await authenticationService.getSession();
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
throw new AuthenticationError("Session not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...newUser
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { VerifyOtpFormData } from "@/src/entities/models/auth/verify-otp.model"
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository"
|
||||||
|
import { IAuthenticationService } from "../../services/authentication.service.interface"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
import { AuthenticationError } from "@/src/entities/errors/auth"
|
||||||
|
import { NotFoundError } from "@/src/entities/errors/common"
|
||||||
|
|
||||||
|
|
||||||
|
export type IVerifyOtpUseCase = ReturnType<typeof verifyOtpUseCase>
|
||||||
|
|
||||||
|
export const verifyOtpUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
authenticationService: IAuthenticationService,
|
||||||
|
usersRepository: IUsersRepository
|
||||||
|
) => async (input: VerifyOtpFormData): Promise<void> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "verifyOtp Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
const user = await usersRepository.getUserByEmail(input.email)
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new NotFoundError("User not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
await authenticationService.verifyOtp({
|
||||||
|
email: input.email,
|
||||||
|
token: input.token
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { User } from "@/src/entities/models/users/users.model"
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
import { NotFoundError } from "@/src/entities/errors/common"
|
||||||
|
|
||||||
|
export type IBanUserUseCase = ReturnType<typeof banUserUseCase>
|
||||||
|
|
||||||
|
export const banUserUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
usersRepository: IUsersRepository
|
||||||
|
) => async (id: string, ban_duration: string): Promise<User> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "banUser Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
const existingUser = await usersRepository.getUserById(id)
|
||||||
|
|
||||||
|
if (!existingUser) {
|
||||||
|
throw new NotFoundError("User not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
const bannedUser = await usersRepository.banUser(id, ban_duration)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...bannedUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { AuthenticationError } from "@/src/entities/errors/auth"
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository"
|
||||||
|
import { IAuthenticationService } from "../../services/authentication.service.interface"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
import { CreateUser, User } from "@/src/entities/models/users/users.model"
|
||||||
|
import { InputParseError } from "@/src/entities/errors/common"
|
||||||
|
|
||||||
|
|
||||||
|
export type ICreateUserUseCase = ReturnType<typeof createUserUseCase>
|
||||||
|
|
||||||
|
export const createUserUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
usersRepository: IUsersRepository,
|
||||||
|
) => async (input: CreateUser): Promise<User> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "createUser Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
const existingUser = await usersRepository.getUserByEmail(input.email)
|
||||||
|
|
||||||
|
if (existingUser) {
|
||||||
|
throw new AuthenticationError("User already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
const newUser = await usersRepository.createUser({
|
||||||
|
email: input.email,
|
||||||
|
password: input.password,
|
||||||
|
email_confirm: true
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!newUser) {
|
||||||
|
throw new InputParseError("User not created")
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...newUser
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
|
||||||
|
export type IDeleteUserUseCase = ReturnType<typeof deleteUserUseCase>
|
||||||
|
|
||||||
|
const deleteUserUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
usersRepository: IUsersRepository
|
||||||
|
) => async (id: string): Promise<void> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "deleteUser Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
await usersRepository.deleteUser(id)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { NotFoundError } from "@/src/entities/errors/common"
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
import { UserResponse } from "@/src/entities/models/users/users.model"
|
||||||
|
|
||||||
|
|
||||||
|
export type IGetCurrentUserUseCase = ReturnType<typeof getCurrentUserUseCase>
|
||||||
|
|
||||||
|
export const getCurrentUserUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
usersRepository: IUsersRepository
|
||||||
|
) => async (): Promise<UserResponse> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "getCurrentUser Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
|
||||||
|
const existingUser = await usersRepository.getCurrentUser()
|
||||||
|
|
||||||
|
if (!existingUser) {
|
||||||
|
throw new NotFoundError("User not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...existingUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { User } from "@/src/entities/models/users/users.model"
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
|
||||||
|
export type IGetListUsersUseCase = ReturnType<typeof getListUsersUseCase>
|
||||||
|
|
||||||
|
export const getListUsersUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
usersRepository: IUsersRepository
|
||||||
|
) => async (): Promise<User[]> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "getListUsers Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
return await usersRepository.listUsers()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { User } from "@/src/entities/models/users/users.model"
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
import { NotFoundError } from "@/src/entities/errors/common"
|
||||||
|
|
||||||
|
export type IGetUserByEmailUseCase = ReturnType<typeof getUserByEmailUseCase>
|
||||||
|
|
||||||
|
const getUserByEmailUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
usersRepository: IUsersRepository
|
||||||
|
) => async (email: string): Promise<User> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "getUserByEmail Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
|
||||||
|
const existingUser = await usersRepository.getUserByEmail(email)
|
||||||
|
|
||||||
|
if (!existingUser) {
|
||||||
|
throw new NotFoundError("User not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...existingUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { User } from "@/src/entities/models/users/users.model"
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
import { AuthenticationError } from "@/src/entities/errors/auth"
|
||||||
|
import { NotFoundError } from "@/src/entities/errors/common"
|
||||||
|
|
||||||
|
|
||||||
|
export type IGetUserByIdUseCase = ReturnType<typeof getUserByIdUseCase>
|
||||||
|
|
||||||
|
export const getUserByIdUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
usersRepository: IUsersRepository
|
||||||
|
) => async (id: string): Promise<User> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "getUserById Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
|
||||||
|
const existingUser = await usersRepository.getUserById(id)
|
||||||
|
|
||||||
|
if (!existingUser) {
|
||||||
|
throw new NotFoundError("User not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...existingUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { NotFoundError } from "@/src/entities/errors/common"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository"
|
||||||
|
import { User } from "@/src/entities/models/users/users.model"
|
||||||
|
|
||||||
|
export type IGetUserByUsernameUseCase = ReturnType<typeof getUserByUsernameUseCase>
|
||||||
|
|
||||||
|
const getUserByUsernameUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
usersRepository: IUsersRepository
|
||||||
|
) => async (username: string): Promise<User> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "getUserByUsername Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
|
||||||
|
const existingUser = await usersRepository.getUserByUsername(username)
|
||||||
|
|
||||||
|
if (!existingUser) {
|
||||||
|
throw new NotFoundError("User not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...existingUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { AuthenticationError } from "@/src/entities/errors/auth"
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository"
|
||||||
|
import { IAuthenticationService } from "../../services/authentication.service.interface"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
import { User } from "@/src/entities/models/users/users.model"
|
||||||
|
|
||||||
|
|
||||||
|
export type IInviteUserUseCase = ReturnType<typeof inviteUserUseCase>
|
||||||
|
|
||||||
|
export const inviteUserUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
usersRepository: IUsersRepository,
|
||||||
|
authenticationService: IAuthenticationService,
|
||||||
|
) => async (input: { email: string }): Promise<User> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "inviteUser Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
const existingUser = await usersRepository.getUserByEmail(input.email)
|
||||||
|
|
||||||
|
if (existingUser) {
|
||||||
|
throw new AuthenticationError("User already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
const newUser = await usersRepository.inviteUser(input.email)
|
||||||
|
|
||||||
|
if (!newUser) {
|
||||||
|
throw new AuthenticationError("User not invited")
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...newUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { UpdateUser, User, UserResponse } from "@/src/entities/models/users/users.model"
|
||||||
|
import { IUsersRepository } from "../../repositories/users.repository"
|
||||||
|
import { IInstrumentationService } from "../../services/instrumentation.service.interface"
|
||||||
|
import { NotFoundError } from "@/src/entities/errors/common"
|
||||||
|
|
||||||
|
export type IUpdateUserUseCase = ReturnType<typeof updateUserUseCase>
|
||||||
|
|
||||||
|
const updateUserUseCase = (
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
usersRepository: IUsersRepository
|
||||||
|
) => async (id: string, input: UpdateUser): Promise<UserResponse> => {
|
||||||
|
return await instrumentationService.startSpan({ name: "updateUser Use Case", op: "function" },
|
||||||
|
async () => {
|
||||||
|
|
||||||
|
const existingUser = await usersRepository.getUserById(id)
|
||||||
|
|
||||||
|
if (!existingUser) {
|
||||||
|
throw new NotFoundError("User not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedUser = await usersRepository.updateUser(id, input)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...updatedUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { Session } from '@/src/entities/models/auth/session.model';
|
||||||
|
|
||||||
|
export interface AuthResult {
|
||||||
|
data: {
|
||||||
|
user: null;
|
||||||
|
session: Session | null;
|
||||||
|
messageId?: string | null;
|
||||||
|
} | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { z } from "zod";
|
||||||
|
import { UserSchema } from "@/src/entities/models/users/users.model";
|
||||||
|
|
||||||
|
export const SessionSchema = z.object({
|
||||||
|
user: UserSchema.pick({
|
||||||
|
id: true,
|
||||||
|
email: true,
|
||||||
|
role: true,
|
||||||
|
}),
|
||||||
|
expiresAt: z.number().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Session = z.infer<typeof SessionSchema>
|
|
@ -6,12 +6,43 @@ export const SignInSchema = z.object({
|
||||||
.string()
|
.string()
|
||||||
.min(1, { message: "Email is required" })
|
.min(1, { message: "Email is required" })
|
||||||
.email({ message: "Please enter a valid email address" }),
|
.email({ message: "Please enter a valid email address" }),
|
||||||
|
password: z.string().min(1, { message: "Password is required" }),
|
||||||
|
phone: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Export the type derived from the schema
|
// Export the type derived from the schema
|
||||||
export type SignInFormData = z.infer<typeof SignInSchema>;
|
export type SignInFormData = z.infer<typeof SignInSchema>;
|
||||||
|
|
||||||
|
export const SignInWithPassword = SignInSchema.pick({
|
||||||
|
email: true,
|
||||||
|
password: true,
|
||||||
|
phone: true
|
||||||
|
})
|
||||||
|
|
||||||
// Default values for the form
|
// Default values for the form
|
||||||
export const defaultSignInValues: SignInFormData = {
|
export const defaultSignInWithPasswordValues: SignInWithPassword = {
|
||||||
email: "",
|
email: "",
|
||||||
|
password: "",
|
||||||
|
phone: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SignInWithPassword = z.infer<typeof SignInWithPassword>
|
||||||
|
|
||||||
|
export const SignInPasswordless = SignInSchema.pick({
|
||||||
|
email: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Default values for the form
|
||||||
|
export const defaultSignInPasswordlessValues: SignInPasswordless = {
|
||||||
|
email: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SignInPasswordless = z.infer<typeof SignInPasswordless>
|
||||||
|
|
||||||
|
|
||||||
|
// Define the sign-in response schema using Zod
|
||||||
|
export const SignInResponseSchema = z.object({
|
||||||
|
success: z.boolean(),
|
||||||
|
message: z.string(),
|
||||||
|
redirectTo: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const SignUpSchema = z.object({
|
||||||
|
email: z
|
||||||
|
.string()
|
||||||
|
.min(1, { message: "Email is required" })
|
||||||
|
.email({ message: "Please enter a valid email address" }),
|
||||||
|
password: z.string().min(1, { message: "Password is required" }),
|
||||||
|
phone: z.string().optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type SignUpFormData = z.infer<typeof SignUpSchema>;
|
||||||
|
|
||||||
|
export const SignUpWithEmail = SignUpSchema.pick({
|
||||||
|
email: true,
|
||||||
|
password: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const defaultSignUpWithEmailValues: SignUpWithEmail = {
|
||||||
|
email: "",
|
||||||
|
password: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SignUpWithEmail = z.infer<typeof SignUpWithEmail>
|
||||||
|
|
||||||
|
export const SignUpWithPhone = SignUpSchema.pick({
|
||||||
|
phone: true,
|
||||||
|
password: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const defaultSignUpWithPhoneValues: SignUpWithPhone = {
|
||||||
|
phone: "",
|
||||||
|
password: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SignUpWithPhone = z.infer<typeof SignUpWithPhone>
|
|
@ -11,3 +11,4 @@ export const defaultVerifyOtpValues: VerifyOtpFormData = {
|
||||||
email: "",
|
email: "",
|
||||||
token: "",
|
token: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -82,17 +82,16 @@ export const ProfileSchema = z.object({
|
||||||
|
|
||||||
export type Profile = z.infer<typeof ProfileSchema>;
|
export type Profile = z.infer<typeof ProfileSchema>;
|
||||||
|
|
||||||
export const CreateUserParamsSchema = z.object({
|
export const CreateUserSchema = z.object({
|
||||||
email: z.string().email(),
|
email: z.string().email(),
|
||||||
password: z.string(),
|
password: z.string().min(8),
|
||||||
phone: z.string().optional(),
|
phone: z.string().optional(),
|
||||||
user_metadata: z.record(z.any()).optional(),
|
|
||||||
email_confirm: z.boolean().optional(),
|
email_confirm: z.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type CreateUserParams = z.infer<typeof CreateUserParamsSchema>;
|
export type CreateUser = z.infer<typeof CreateUserSchema>;
|
||||||
|
|
||||||
export const UpdateUserParamsSchema = z.object({
|
export const UpdateUserSchema = z.object({
|
||||||
email: z.string().email().optional(),
|
email: z.string().email().optional(),
|
||||||
email_confirmed_at: z.boolean().optional(),
|
email_confirmed_at: z.boolean().optional(),
|
||||||
encrypted_password: z.string().optional(),
|
encrypted_password: z.string().optional(),
|
||||||
|
@ -120,17 +119,15 @@ export const UpdateUserParamsSchema = z.object({
|
||||||
address: z.any().optional(),
|
address: z.any().optional(),
|
||||||
birth_date: z.date().optional(),
|
birth_date: z.date().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
.optional(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type UpdateUserParams = z.infer<typeof UpdateUserParamsSchema>;
|
export type UpdateUser = z.infer<typeof UpdateUserSchema>;
|
||||||
|
|
||||||
export const InviteUserParamsSchema = z.object({
|
export const InviteUserSchema = z.object({
|
||||||
email: z.string().email(),
|
email: z.string().email(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type InviteUserParams = z.infer<typeof InviteUserParamsSchema>;
|
export type InviteUser = z.infer<typeof InviteUserSchema>;
|
||||||
|
|
||||||
export type UserResponse =
|
export type UserResponse =
|
||||||
| {
|
| {
|
||||||
|
|
|
@ -0,0 +1,452 @@
|
||||||
|
import { IUsersRepository } from "@/src/application/repositories/users.repository";
|
||||||
|
import { ICrashReporterService } from "@/src/application/services/crash-reporter.service.interface";
|
||||||
|
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
|
||||||
|
import { createAdminClient } from "@/app/_utils/supabase/admin";
|
||||||
|
import { createClient as createServerClient } from "@/app/_utils/supabase/server";
|
||||||
|
import { CreateUser, UpdateUser, User, UserResponse } from "@/src/entities/models/users/users.model";
|
||||||
|
import { ITransaction } from "@/src/entities/models/transaction.interface";
|
||||||
|
import db from "@/prisma/db";
|
||||||
|
import { NotFoundError } from "@/src/entities/errors/common";
|
||||||
|
import { AuthenticationError } from "@/src/entities/errors/auth";
|
||||||
|
|
||||||
|
export class UsersRepository implements IUsersRepository {
|
||||||
|
constructor(
|
||||||
|
private readonly instrumentationService: IInstrumentationService,
|
||||||
|
private readonly crashReporterService: ICrashReporterService,
|
||||||
|
private readonly supabaseAdmin = createAdminClient(),
|
||||||
|
private readonly supabaseServer = createServerClient()
|
||||||
|
) { }
|
||||||
|
|
||||||
|
async listUsers(): Promise<User[]> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > getUsers",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const query = db.users.findMany({
|
||||||
|
include: {
|
||||||
|
profile: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const users = await this.instrumentationService.startSpan({
|
||||||
|
name: `UsersRepository > getUsers > Prisma: db.users.findMany`,
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "prisma" },
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await query;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return users;
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserById(id: string): Promise<User | undefined> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > getUserById",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const query = db.users.findUnique({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
profile: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const user = await this.instrumentationService.startSpan({
|
||||||
|
name: `UsersRepository > getUserById > Prisma: db.users.findUnique(${id})`,
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "prisma" },
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await query;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (user)
|
||||||
|
return user;
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserByUsername(username: string): Promise<User | undefined> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > getUserByUsername",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const query = db.users.findFirst({
|
||||||
|
where: {
|
||||||
|
profile: {
|
||||||
|
username,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
profile: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const user = await this.instrumentationService.startSpan({
|
||||||
|
name: `UsersRepository > getUserByUsername > Prisma: db.users.findFirst(${username})`,
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "prisma" },
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await query;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (user)
|
||||||
|
return user;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserByEmail(email: string): Promise<User | undefined> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > getUserByEmail",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const query = db.users.findUnique({
|
||||||
|
where: {
|
||||||
|
email,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
profile: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const user = await this.instrumentationService.startSpan({
|
||||||
|
name: `UsersRepository > getUserByEmail > Prisma: db.users.findUnique(${email})`,
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "prisma" },
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await query;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (user)
|
||||||
|
return user;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCurrentUser(): Promise<UserResponse> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > getCurrentUser",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = await this.supabaseServer;
|
||||||
|
|
||||||
|
const query = supabase.auth.getUser();
|
||||||
|
|
||||||
|
const user = await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > getCurrentUser > supabase.auth.getUser",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" },
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await query;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return user;
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async createUser(input: CreateUser, tx?: ITransaction): Promise<User | null> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > createUser",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = this.supabaseAdmin;
|
||||||
|
|
||||||
|
const query = supabase.auth.admin.createUser({
|
||||||
|
email: input.email,
|
||||||
|
password: input.password,
|
||||||
|
email_confirm: input.email_confirm,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { data: { user } } = await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > createUser > supabase.auth.admin.createUser",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" },
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await query;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return user;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async inviteUser(email: string, tx?: ITransaction): Promise<User | null> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > inviteUser",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = this.supabaseAdmin;
|
||||||
|
|
||||||
|
const query = supabase.auth.admin.inviteUserByEmail(email);
|
||||||
|
|
||||||
|
const { data: { user } } = await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > inviteUser > supabase.auth.admin.inviteUserByEmail",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" },
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await query;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return user;
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateUser(id: string, input: Partial<UpdateUser>, tx?: ITransaction): Promise<UserResponse> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > updateUser",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = this.supabaseAdmin;
|
||||||
|
|
||||||
|
const queryUpdateSupabaseUser = supabase.auth.admin.updateUserById(id, {
|
||||||
|
email: input.email,
|
||||||
|
email_confirm: input.email_confirmed_at,
|
||||||
|
password: input.encrypted_password ?? undefined,
|
||||||
|
password_hash: input.encrypted_password ?? undefined,
|
||||||
|
phone: input.phone,
|
||||||
|
phone_confirm: input.phone_confirmed_at,
|
||||||
|
role: input.role,
|
||||||
|
user_metadata: input.user_metadata,
|
||||||
|
app_metadata: input.app_metadata,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data, error } = await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > updateUser > supabase.auth.updateUser",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" },
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await queryUpdateSupabaseUser;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw new AuthenticationError(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryGetUser = db.users.findUnique({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
profile: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const user = await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > updateUser > Prisma: db.users.update",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "prisma" },
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await queryGetUser;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new NotFoundError("User not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryUpdateUser = db.users.update({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
role: input.role || user.role,
|
||||||
|
invited_at: input.invited_at || user.invited_at,
|
||||||
|
confirmed_at: input.confirmed_at || user.confirmed_at,
|
||||||
|
last_sign_in_at: input.last_sign_in_at || user.last_sign_in_at,
|
||||||
|
is_anonymous: input.is_anonymous || user.is_anonymous,
|
||||||
|
created_at: input.created_at || user.created_at,
|
||||||
|
updated_at: input.updated_at || user.updated_at,
|
||||||
|
profile: {
|
||||||
|
update: {
|
||||||
|
avatar: input.profile?.avatar || user.profile?.avatar,
|
||||||
|
username: input.profile?.username || user.profile?.username,
|
||||||
|
first_name: input.profile?.first_name || user.profile?.first_name,
|
||||||
|
last_name: input.profile?.last_name || user.profile?.last_name,
|
||||||
|
bio: input.profile?.bio || user.profile?.bio,
|
||||||
|
address: input.profile?.address || user.profile?.address,
|
||||||
|
birth_date: input.profile?.birth_date || user.profile?.birth_date,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
profile: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const updatedUser = await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > updateUser > Prisma: db.users.update",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "prisma" },
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await queryUpdateUser;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
user: {
|
||||||
|
...data.user,
|
||||||
|
role: updatedUser.role,
|
||||||
|
profile: {
|
||||||
|
user_id: id,
|
||||||
|
...updatedUser.profile,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteUser(id: string, tx?: ITransaction): Promise<void> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > deleteUser",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = this.supabaseAdmin;
|
||||||
|
|
||||||
|
const query = supabase.auth.admin.deleteUser(id);
|
||||||
|
|
||||||
|
await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > deleteUser > supabase.auth.admin.deleteUser",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" },
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await query;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async banUser(id: string, ban_duration: string, tx?: ITransaction): Promise<User> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > banUser",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = this.supabaseAdmin;
|
||||||
|
|
||||||
|
const query = supabase.auth.admin.updateUserById(id, {
|
||||||
|
ban_duration: ban_duration ?? "100h",
|
||||||
|
})
|
||||||
|
|
||||||
|
const { data, error } = await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > banUser > supabase.auth.admin.updateUserById",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" },
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await query;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw new AuthenticationError(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.user;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async unbanUser(id: string, tx?: ITransaction): Promise<User> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > unbanUser",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = this.supabaseAdmin;
|
||||||
|
|
||||||
|
const query = supabase.auth.admin.updateUserById(id, {
|
||||||
|
ban_duration: "none",
|
||||||
|
})
|
||||||
|
|
||||||
|
const { data, error } = await this.instrumentationService.startSpan({
|
||||||
|
name: "UsersRepository > unbanUser > supabase.auth.admin.updateUserById",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" },
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await query;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw new AuthenticationError(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.user;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,273 @@
|
||||||
|
import { createAdminClient } from "@/app/_utils/supabase/admin";
|
||||||
|
import { createClient } from "@/app/_utils/supabase/server";
|
||||||
|
import db from "@/prisma/db";
|
||||||
|
import { IUsersRepository } from "@/src/application/repositories/users.repository";
|
||||||
|
import { IAuthenticationService } from "@/src/application/services/authentication.service.interface";
|
||||||
|
import { ICrashReporterService } from "@/src/application/services/crash-reporter.service.interface";
|
||||||
|
import { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface";
|
||||||
|
import { AuthenticationError } from "@/src/entities/errors/auth";
|
||||||
|
import { Session } from "@/src/entities/models/auth/session.model";
|
||||||
|
import { SignInPasswordless, SignInWithPassword } from "@/src/entities/models/auth/sign-in.model";
|
||||||
|
import { SignUpWithEmail, SignUpWithPhone } from "@/src/entities/models/auth/sign-up.model";
|
||||||
|
import { VerifyOtpFormData } from "@/src/entities/models/auth/verify-otp.model";
|
||||||
|
import { User } from "@/src/entities/models/users/users.model";
|
||||||
|
|
||||||
|
export class AuthenticationService implements IAuthenticationService {
|
||||||
|
constructor(
|
||||||
|
private readonly usersRepository: IUsersRepository,
|
||||||
|
private readonly instrumentationService: IInstrumentationService,
|
||||||
|
private readonly crashReporterService: ICrashReporterService,
|
||||||
|
private readonly supabaseAdmin = createAdminClient(),
|
||||||
|
private readonly supabaseServer = createClient()
|
||||||
|
) { }
|
||||||
|
|
||||||
|
async signInPasswordless(credentials: SignInPasswordless): Promise<void> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "signInPasswordless Use Case",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = await this.supabaseServer
|
||||||
|
|
||||||
|
const { email } = credentials
|
||||||
|
const signIn = supabase.auth.signInWithOtp({ email })
|
||||||
|
|
||||||
|
const { data: { session }, error } = await this.instrumentationService.startSpan({
|
||||||
|
name: "supabase.auth.signInWithOtp",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" }
|
||||||
|
}, async () => {
|
||||||
|
return await signIn
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async signInWithPassword(credentials: SignInWithPassword): Promise<void> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "signInWithPassword Use Case",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = await this.supabaseServer
|
||||||
|
|
||||||
|
const { email, password } = credentials
|
||||||
|
const signIn = supabase.auth.signInWithPassword({ email, password })
|
||||||
|
|
||||||
|
const { data: { session }, error } = await this.instrumentationService.startSpan({
|
||||||
|
name: "supabase.auth.signIn",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" }
|
||||||
|
}, async () => {
|
||||||
|
return await signIn
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async signUpWithEmail(credentials: SignUpWithEmail): Promise<User> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "signUpWithEmail Use Case",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = await this.supabaseServer
|
||||||
|
|
||||||
|
const { email, password } = credentials
|
||||||
|
const signUp = supabase.auth.signUp({ email, password })
|
||||||
|
|
||||||
|
const { data: { user, session }, error } = await this.instrumentationService.startSpan({
|
||||||
|
name: "supabase.auth.signUp",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" }
|
||||||
|
}, async () => {
|
||||||
|
return await signUp
|
||||||
|
})
|
||||||
|
|
||||||
|
const newUser = db.users.findUnique({
|
||||||
|
where: {
|
||||||
|
id: user!.id
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
profile: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const userDetail = await this.instrumentationService.startSpan({
|
||||||
|
name: "db.users.findUnique",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "prisma" }
|
||||||
|
}, async () => {
|
||||||
|
return await newUser
|
||||||
|
})
|
||||||
|
|
||||||
|
return userDetail!;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async signUpWithPhone(credentials: SignUpWithPhone): Promise<User> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSession(): Promise<Session | null> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "getSession Use Case",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = await this.supabaseServer
|
||||||
|
|
||||||
|
const session = supabase.auth.getSession()
|
||||||
|
|
||||||
|
const { data, error } = await this.instrumentationService.startSpan({
|
||||||
|
name: "supabase.auth.session",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" }
|
||||||
|
}, async () => {
|
||||||
|
return await session
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!data.session) {
|
||||||
|
throw new AuthenticationError("Session not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: {
|
||||||
|
id: data.session.user.id,
|
||||||
|
role: data.session.user.role,
|
||||||
|
email: data.session.user.email
|
||||||
|
},
|
||||||
|
expiresAt: data.session.expires_at,
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async signOut(): Promise<void> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "signOut Use Case",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = await this.supabaseServer
|
||||||
|
|
||||||
|
const signOut = supabase.auth.signOut()
|
||||||
|
|
||||||
|
await this.instrumentationService.startSpan({
|
||||||
|
name: "supabase.auth.signOut",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" }
|
||||||
|
}, async () => {
|
||||||
|
return await signOut
|
||||||
|
})
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendMagicLink(email: string): Promise<void> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "sendMagicLink Use Case",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = await this.supabaseServer
|
||||||
|
|
||||||
|
const magicLink = supabase.auth.signInWithOtp({ email })
|
||||||
|
|
||||||
|
await this.instrumentationService.startSpan({
|
||||||
|
name: "supabase.auth.signIn",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" }
|
||||||
|
}, async () => {
|
||||||
|
return await magicLink
|
||||||
|
})
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendPasswordRecovery(email: string): Promise<void> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "sendPasswordRecovery Use Case",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = await this.supabaseServer
|
||||||
|
|
||||||
|
const passwordRecovery = supabase.auth.resetPasswordForEmail(email, {
|
||||||
|
redirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/reset-password`,
|
||||||
|
})
|
||||||
|
|
||||||
|
await this.instrumentationService.startSpan({
|
||||||
|
name: "supabase.auth.resetPasswordForEmail",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" }
|
||||||
|
}, async () => {
|
||||||
|
return await passwordRecovery
|
||||||
|
})
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyOtp(credentials: VerifyOtpFormData): Promise<void> {
|
||||||
|
return await this.instrumentationService.startSpan({
|
||||||
|
name: "verifyOtp Use Case",
|
||||||
|
}, async () => {
|
||||||
|
try {
|
||||||
|
const supabase = await this.supabaseServer
|
||||||
|
|
||||||
|
const { email, token } = credentials
|
||||||
|
const verifyOtp = supabase.auth.verifyOtp({ email, token, type: "email" })
|
||||||
|
|
||||||
|
await this.instrumentationService.startSpan({
|
||||||
|
name: "supabase.auth.verifyOtp",
|
||||||
|
op: "db:query",
|
||||||
|
attributes: { "system": "supabase.auth" }
|
||||||
|
}, async () => {
|
||||||
|
return await verifyOtp
|
||||||
|
})
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
this.crashReporterService.report(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { z } from "zod"
|
||||||
|
import type { IInstrumentationService } from "@/src/application/services/instrumentation.service.interface"
|
||||||
|
import { InputParseError } from "@/src/entities/errors/common"
|
||||||
|
import { ISignInUseCase } from "@/src/application/use-cases/auth/sign-in.use-case"
|
||||||
|
import { IAuthenticationService } from "@/src/application/services/authentication.service.interface"
|
||||||
|
import { ISignUpUseCase } from "@/src/application/use-cases/auth/sign-up.use-case"
|
||||||
|
import { IVerifyOtpUseCase } from "@/src/application/use-cases/auth/verify-otp.use-case"
|
||||||
|
|
||||||
|
// Sign In Controller
|
||||||
|
const signInInputSchema = z.object({
|
||||||
|
email: z.string().email("Please enter a valid email address"),
|
||||||
|
})
|
||||||
|
|
||||||
|
export type ISignInController = ReturnType<typeof signInController>
|
||||||
|
|
||||||
|
export const signInController =
|
||||||
|
(
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
signInUseCase: ISignInUseCase
|
||||||
|
) =>
|
||||||
|
async (input: Partial<z.infer<typeof signInInputSchema>>) => {
|
||||||
|
return await instrumentationService.startSpan({ name: "signIn Controller" }, async () => {
|
||||||
|
const { data, error: inputParseError } = signInInputSchema.safeParse(input)
|
||||||
|
|
||||||
|
if (inputParseError) {
|
||||||
|
throw new InputParseError("Invalid data", { cause: inputParseError })
|
||||||
|
}
|
||||||
|
|
||||||
|
return await signInUseCase({
|
||||||
|
email: data.email
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify OTP Controller
|
||||||
|
const verifyOtpInputSchema = z.object({
|
||||||
|
email: z.string().email("Please enter a valid email address"),
|
||||||
|
token: z.string().min(6, "Please enter a valid OTP")
|
||||||
|
})
|
||||||
|
|
||||||
|
export type IVerifyOtpController = ReturnType<typeof verifyOtpController>
|
||||||
|
|
||||||
|
export const verifyOtpController =
|
||||||
|
(
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
verifyOtpUseCase: IVerifyOtpUseCase
|
||||||
|
) =>
|
||||||
|
async (input: Partial<z.infer<typeof verifyOtpInputSchema>>) => {
|
||||||
|
return await instrumentationService.startSpan({ name: "verifyOtp Controller" }, async () => {
|
||||||
|
const { data, error: inputParseError } = verifyOtpInputSchema.safeParse(input)
|
||||||
|
|
||||||
|
if (inputParseError) {
|
||||||
|
throw new InputParseError("Invalid data", { cause: inputParseError })
|
||||||
|
}
|
||||||
|
|
||||||
|
return await verifyOtpUseCase({
|
||||||
|
email: data.email,
|
||||||
|
token: data.token
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign Out Controller
|
||||||
|
export type ISignOutController = ReturnType<typeof signOutController>
|
||||||
|
|
||||||
|
export const signOutController =
|
||||||
|
(
|
||||||
|
instrumentationService: IInstrumentationService,
|
||||||
|
authenticationService: IAuthenticationService
|
||||||
|
) =>
|
||||||
|
async () => {
|
||||||
|
return await instrumentationService.startSpan({
|
||||||
|
name: "signOut Controller"
|
||||||
|
}, async () => {
|
||||||
|
return await authenticationService.signOut()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import {
|
import {
|
||||||
defaultSignInValues,
|
defaultSignInPasswordlessValues,
|
||||||
SignInFormData,
|
SignInFormData,
|
||||||
SignInSchema,
|
SignInSchema,
|
||||||
} from "@/src/entities/models/auth/sign-in.model";
|
} from "@/src/entities/models/auth/sign-in.model";
|
||||||
|
@ -173,7 +173,7 @@ export function useSignInController() {
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = useForm<SignInFormData>({
|
} = useForm<SignInFormData>({
|
||||||
resolver: zodResolver(SignInSchema),
|
resolver: zodResolver(SignInSchema),
|
||||||
defaultValues: defaultSignInValues,
|
defaultValues: defaultSignInPasswordlessValues,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handler untuk submit form
|
// Handler untuk submit form
|
||||||
|
|
Loading…
Reference in New Issue