Files
fullstack-fuware/src/service/house.api.ts
2026-02-11 22:45:33 +07:00

312 lines
8.1 KiB
TypeScript

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 };
}
});