Files
fullstack-fuware/src/lib/auth.ts
2026-02-05 21:10:45 +07:00

150 lines
4.1 KiB
TypeScript

import { prisma } from '@/db';
import { User } from '@/generated/prisma/client';
import { SessionModel } from '@/generated/prisma/models';
import { DB_TABLE, LOG_ACTION } from '@/types/enum';
import {
acOrg,
adminOrg,
member,
owner,
} from '@lib/auth/organization-permissions';
import { ac, admin, user } from '@lib/auth/permissions';
import { createAuditLog } from '@service/repository';
import { betterAuth } from 'better-auth';
import { prismaAdapter } from 'better-auth/adapters/prisma';
import { admin as adminPlugin, organization } from 'better-auth/plugins';
export interface Session {
session: SessionModel | null | undefined;
user?: User;
}
export const auth = betterAuth({
database: prismaAdapter(prisma, {
provider: 'postgresql',
}),
experimental: { joins: true },
emailAndPassword: {
enabled: true,
requireEmailVerification: false,
},
trustedOrigins: ['http://localhost:3001'],
advanced: {
database: {
generateId: false,
},
},
plugins: [
adminPlugin({
ac,
roles: { admin, user },
defaultRole: 'user',
}),
organization({
ac: acOrg,
roles: { owner, admin: adminOrg, member },
schema: {
organization: {
additionalFields: {
color: {
type: 'string',
defaultValue: '#000000',
},
},
},
},
}),
],
databaseHooks: {
user: {
create: {
after: async (user) => {
await auth.api.createOrganization({
body: {
name: `${user.name || 'User'}'s House`,
slug: `${user.name?.toLowerCase().replace(/\s+/g, '-')}-house-${Date.now()}`,
userId: user.id,
color: '#000000',
},
});
await prisma.setting.create({
data: {
key: user.id,
value: '{"language": "en"}',
description: '',
relation: 'user',
},
});
},
},
update: {
before: async (user, ctx) => {
if (ctx?.context.session && ctx?.path === '/update-user') {
const newUser = JSON.parse(JSON.stringify(user));
const keys = Object.keys(newUser);
const oldUser = Object.fromEntries(
Object.entries(ctx?.context.session?.user).filter(([key]) =>
keys.includes(key),
),
);
await createAuditLog({
action: LOG_ACTION.UPDATE,
tableName: DB_TABLE.USER,
recordId: ctx?.context.session?.user.id,
oldValue: JSON.stringify(oldUser),
newValue: JSON.stringify(newUser),
userId: ctx?.context.session?.user.id,
});
}
},
},
},
account: {
update: {
after: async (account, context) => {
if (context?.path === '/change-password') {
await createAuditLog({
action: LOG_ACTION.CHANGE_PASSWORD,
tableName: DB_TABLE.ACCOUNT,
recordId: account.id,
oldValue: 'Change Password',
newValue: 'Change Password',
userId: account.userId,
});
}
},
},
},
session: {
create: {
after: async (session, context) => {
if (context?.path.includes('/sign-in')) {
await createAuditLog({
action: LOG_ACTION.SIGN_IN,
tableName: DB_TABLE.SESSION,
recordId: session.id,
oldValue: '',
newValue: JSON.stringify(session),
userId: session.userId,
});
}
},
},
delete: {
after: async (session, context) => {
if (context?.path === '/sign-out') {
await createAuditLog({
action: LOG_ACTION.SIGN_OUT,
tableName: DB_TABLE.SESSION,
recordId: session.id,
oldValue: JSON.stringify(session),
newValue: '',
userId: session.userId,
});
}
},
},
},
},
});