diff --git a/api/domain/entities/User.ts b/api/domain/entities/User.ts new file mode 100644 index 0000000..9374179 --- /dev/null +++ b/api/domain/entities/User.ts @@ -0,0 +1,48 @@ +export class User { + private email: string; + private password: string; + private name: string; + private createdAt: Date; + private updatedAt: Date; + + constructor(email: string, password: string, name: string) { + this.email = email; + this.password = password; + this.name = name; + this.createdAt = new Date(); + this.updatedAt = new Date(); + } + + public getEmail(): string { + return this.email; + } + + public getPassword(): string { + return this.password; + } + + public getName(): string { + return this.name; + } + + public getCreatedAt(): Date { + return this.createdAt; + } + + public getUpdatedAt(): Date { + return this.updatedAt; + } + + public static create(email: string, password: string, name: string): User { + return new User(email, password, name); + } + + public toPlainObject(): { [key: string]: any } { + return { + email: this.email, + name: this.name, + createdAt: this.createdAt.toISOString(), + updatedAt: this.updatedAt.toISOString(), + }; + } +} diff --git a/api/domain/repositories/AuthRepository.ts b/api/domain/repositories/AuthRepository.ts new file mode 100644 index 0000000..119c4c0 --- /dev/null +++ b/api/domain/repositories/AuthRepository.ts @@ -0,0 +1,26 @@ +import { Auth } from 'firebase-admin/auth'; +import { auth } from '../../../lib/main.admin'; + +export class AuthRepository { + private auth!: Auth; + + constructor() { + this.initializeAuth(); + } + + private async initializeAuth() { + this.auth = await auth(); + } + + public async createUser(email: string, password: string): Promise { + try { + const userRecord = await this.auth.createUser({ + email, + password, + }); + return userRecord; + } catch (error) { + throw new Error('Error creating user: ' + (error as any).message); + } + } +} diff --git a/api/domain/repositories/UserRepository.ts b/api/domain/repositories/UserRepository.ts new file mode 100644 index 0000000..1061aad --- /dev/null +++ b/api/domain/repositories/UserRepository.ts @@ -0,0 +1,23 @@ +import { Firestore } from 'firebase-admin/firestore'; +import { User } from '../entities/User'; +import { firestore } from '../../../lib/main.admin'; + +export class UserRepository { + private db!: Firestore; + + constructor() { + this.initializeFirestore(); + } + + private async initializeFirestore() { + this.db = await firestore(); + } + + public async save(user: User): Promise { + try { + await this.db.collection('users').doc().set(user.toPlainObject()); + } catch (error) { + throw new Error('Error saving user: ' + (error as Error).message); + } + } +} diff --git a/api/usecases/RegisterUser.ts b/api/usecases/RegisterUser.ts new file mode 100644 index 0000000..5684d1a --- /dev/null +++ b/api/usecases/RegisterUser.ts @@ -0,0 +1,24 @@ +import { User } from '../domain/entities/User'; +import { AuthRepository } from '../domain/repositories/AuthRepository'; +import { UserRepository } from '../domain/repositories/UserRepository'; + +export class RegisterUserUseCase { + private userRepository: UserRepository; + private authRepository: AuthRepository; + + constructor(userRepository: UserRepository, authRepository: AuthRepository) { + this.userRepository = userRepository; + this.authRepository = authRepository; + } + + public async execute( + userEmail: string, + userPassword: string, + userName: string + ): Promise { + const user = User.create(userEmail, userPassword, userName); + await this.authRepository.createUser(userEmail, userPassword); + await this.userRepository.save(user); + return user; + } +} diff --git a/lib/configureFirebaseServer.ts b/lib/configureFirebaseServer.ts deleted file mode 100644 index 8df19d9..0000000 --- a/lib/configureFirebaseServer.ts +++ /dev/null @@ -1,28 +0,0 @@ -'use server'; - -import { initializeApp } from 'firebase-admin'; -import { cert, getApp, getApps } from 'firebase-admin/app'; -import { getFirestore, initializeFirestore } from 'firebase-admin/firestore'; - -let app = null; -let firestore = null; -app = !getApps().length - ? initializeApp({ - credential: cert({ - projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID!, - clientEmail: process.env.PRIVATE_FIREBASE_CLIENT_EMAIL!, - // Using JSON to handle newline problems when storing the key as a secret in Vercel. See: - // https://github.com/vercel/vercel/issues/749#issuecomment-707515089 - privateKey: JSON.parse(process.env.PRIVATE_FIREBASE_PRIVATE_KEY!), - }), - databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL!, - }) - : getApp(); - -getFirestore().settings({ ignoreUndefinedProperties: true }); - -if (app != null) { - firestore = initializeFirestore(app); -} - -export { firestore }; diff --git a/lib/firebase.admin.config.ts b/lib/firebase.admin.config.ts new file mode 100644 index 0000000..a4f897b --- /dev/null +++ b/lib/firebase.admin.config.ts @@ -0,0 +1,24 @@ +'use server'; +// firebase.config.ts +import { initializeApp, getApps, getApp, cert } from 'firebase-admin/app'; +import { getFirestore as getFirestoreInstance } from 'firebase-admin/firestore'; +import { getAuth as getAuthInstance } from 'firebase-admin/auth'; + +const projectId = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID!; +const clientEmail = process.env.PRIVATE_FIREBASE_CLIENT_EMAIL!; +const privateKey = JSON.parse(process.env.PRIVATE_FIREBASE_PRIVATE_KEY!); +const databaseURL = process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL!; + +const app = !getApps().length + ? initializeApp({ + credential: cert({ + projectId, + clientEmail, + privateKey, + }), + databaseURL, + }) + : getApp(); + +export const getFirestore = () => getFirestoreInstance(app); +export const getAuth = () => getAuthInstance(app); diff --git a/lib/main.admin.ts b/lib/main.admin.ts new file mode 100644 index 0000000..d3bceb7 --- /dev/null +++ b/lib/main.admin.ts @@ -0,0 +1,6 @@ +import { getAuth, getFirestore } from './firebase.admin.config'; + +const firestore = async () => await getFirestore(); +const auth = async () => await getAuth(); + +export { firestore, auth }; diff --git a/src/app/api/register/route.tsx b/src/app/api/register/route.tsx new file mode 100644 index 0000000..e26a75f --- /dev/null +++ b/src/app/api/register/route.tsx @@ -0,0 +1,28 @@ +import { User } from '../../../../api/domain/entities/User'; +import { UserRepository } from '../../../../api/domain/repositories/UserRepository'; +import { AuthRepository } from '../../../../api/domain/repositories/AuthRepository'; +import { RegisterUserUseCase } from '../../../../api/usecases/RegisterUser'; + +const userRepository = new UserRepository(); +const authRepository = new AuthRepository(); +const registerUserUseCase = new RegisterUserUseCase( + userRepository, + authRepository +); + +export async function POST(req: Request) { + const { userEmail, userPassword, userName } = await req.json(); + + try { + const user = await registerUserUseCase.execute( + userEmail, + userPassword, + userName + ); + + return Response.json(user); + } catch (error) { + console.error('Error creating user:', error); + return Response.json({ error: 'Failed to create user' }, { status: 500 }); + } +}