import { prisma } from '@/db'; import { OrganizationWhereInput } from '@/generated/prisma/models'; import { DB_TABLE, LOG_ACTION } from '@/types/enum'; import { auth } from '@lib/auth'; import { parseError } from '@lib/errors'; import { authMiddleware } from '@lib/middleware'; import { createServerFn } from '@tanstack/react-start'; import { getRequestHeaders } from '@tanstack/react-start/server'; import { baseHouse, houseCreateBESchema, houseEditBESchema, houseListSchema, invitationCreateBESchema, } from './house.schema'; import { createAuditLog } from './repository'; export const getAllHouse = createServerFn({ method: 'GET' }) .middleware([authMiddleware]) .inputValidator(houseListSchema) .handler(async ({ data }) => { try { const { page, limit, keyword } = data; const skip = (page - 1) * limit; const where: OrganizationWhereInput = { OR: [ { name: { contains: keyword, mode: 'insensitive', }, }, ], }; const [list, total]: [HouseWithMembers[], number] = await Promise.all([ await prisma.organization.findMany({ where, orderBy: { createdAt: 'desc' }, include: { members: { select: { role: true, user: { select: { id: true, name: true, email: true, image: true, }, }, }, }, }, take: limit, skip, }), await prisma.organization.count({ where }), ]); const totalPage = Math.ceil(+total / limit); return { result: list, pagination: { currentPage: page, totalPage, totalItem: total, }, }; } catch (error) { console.error(error); const { message, code } = parseError(error); throw { message, code }; } }); export const getCurrentUserHouses = createServerFn({ method: 'GET' }) .middleware([authMiddleware]) .handler(async ({ context: { user } }) => { try { const houses = await prisma.organization.findMany({ where: { members: { some: { userId: user.id } } }, orderBy: { createdAt: 'asc' }, include: { _count: { select: { members: true, }, }, members: { select: { role: true, user: { select: { id: true, name: true, email: true, image: true, }, }, }, }, }, }); return houses; } catch (error) { console.error(error); const { message, code } = parseError(error); throw { message, code }; } }); export const createHouse = createServerFn({ method: 'POST' }) .middleware([authMiddleware]) .inputValidator(houseCreateBESchema) .handler(async ({ data, context: { user } }) => { try { const result = await auth.api.createOrganization({ body: data, }); if (!result) throw Error('Failed to create house'); await createAuditLog({ action: LOG_ACTION.CREATE, tableName: DB_TABLE.ORGANIZATION, recordId: result.id, oldValue: '', newValue: JSON.stringify(result), userId: user.id, }); return result; } catch (error) { console.error(error); const { message, code } = parseError(error); throw { message, code }; } }); export const updateHouse = createServerFn({ method: 'POST' }) .middleware([authMiddleware]) .inputValidator(houseEditBESchema) .handler(async ({ data, context: { user } }) => { try { const currentHouse = await prisma.organization.findUnique({ where: { id: data.id }, }); if (!currentHouse) throw Error('House not found'); const { id, slug, name, color } = data; const result = await prisma.organization.update({ where: { id }, data: { name, slug, color, }, }); await createAuditLog({ action: LOG_ACTION.UPDATE, tableName: DB_TABLE.ORGANIZATION, recordId: id, oldValue: JSON.stringify(currentHouse), newValue: JSON.stringify(result), userId: user.id, }); return result; } catch (error) { console.error(error); const { message, code } = parseError(error); throw { message, code }; } }); export const deleteHouse = createServerFn({ method: 'POST' }) .middleware([authMiddleware]) .inputValidator(baseHouse) .handler(async ({ data, context: { user } }) => { try { const currentHouse = await prisma.organization.findUnique({ where: { id: data.id }, }); if (!currentHouse) throw Error('House not found'); const result = await Promise.all([ prisma.organization.delete({ where: { id: data.id }, }), prisma.member.deleteMany({ where: { organizationId: data.id }, }), prisma.invitation.deleteMany({ where: { organizationId: data.id }, }), ]); await createAuditLog({ action: LOG_ACTION.DELETE, tableName: DB_TABLE.ORGANIZATION, recordId: result[0]?.id, oldValue: JSON.stringify(currentHouse), newValue: '', userId: user.id, }); return result; } catch (error) { console.error(error); const { message, code } = parseError(error); throw { message, code }; } }); export const deleteUserHouse = createServerFn({ method: 'POST' }) .middleware([authMiddleware]) .inputValidator(baseHouse) .handler(async ({ data, context: { user } }) => { try { const currentHouse = await prisma.organization.findUnique({ where: { id: data.id }, }); if (!currentHouse) throw Error('House not found'); const headers = getRequestHeaders(); const result = await auth.api.deleteOrganization({ body: { organizationId: data.id, // required }, headers, }); if (result) { await createAuditLog({ action: LOG_ACTION.DELETE, tableName: DB_TABLE.ORGANIZATION, recordId: result?.id, oldValue: JSON.stringify(currentHouse), newValue: '', userId: user.id, }); } return result; } catch (error) { console.error(error); const { message, code } = parseError(error); throw { message, code }; } }); export const invitationMember = createServerFn({ method: 'POST' }) .middleware([authMiddleware]) .inputValidator(invitationCreateBESchema) .handler(async ({ data, context: { user } }) => { try { const headers = getRequestHeaders(); const body = { email: data.email, role: data.role, organizationId: data.houseId, }; const result = await auth.api.createInvitation({ body, headers, }); if (result) { await createAuditLog({ action: LOG_ACTION.CREATE, tableName: DB_TABLE.INVITATION, recordId: result.id, oldValue: '', newValue: JSON.stringify(body), userId: user.id, }); } return result; } catch (error) { console.error(error); const { message, code } = parseError(error); throw { message, code }; } }); export const cancelInvitation = createServerFn({ method: 'POST' }) .middleware([authMiddleware]) .inputValidator(baseHouse) .handler(async ({ data }) => { try { const headers = getRequestHeaders(); const result = await auth.api.cancelInvitation({ body: { invitationId: data.id, // required }, headers, }); return result; } catch (error) { console.error(error); const { message, code } = parseError(error); throw { message, code }; } });