import { db } from "@/database/db";
import { task, user } from "@/database/schema";
import { dbGetAll } from "@/lib/db/drizzle-client";
import { getRangeValue } from "@/lib/utils";
import { and, eq, gte, lte, ne } from "drizzle-orm";
import {
SendInvitation,
UpdateUserMetadata,
User,
UserById,
UserId,
} from "../domain/models";
import { FilterTasks } from "@/server/task/domain/models";
import { createLogRecord } from "@/server/log";
import { UserRepository } from "../domain/repositories";
import { createClerkClient } from "@clerk/backend";
import { VARIABLES_CONFIG } from "@/lib/db/util";
const clerkClient = createClerkClient({
secretKey: process.env.CLERK_SECRET_KEY,
});
class UserRepositoryImpl implements UserRepository {
async getAll(userId: UserId): Promise<User[]> {
const data = await dbGetAll("user", {
where: and(ne(user.id, userId.value), ne(user.isDeleted, true)),
});
return data as User[];
}
async getBy(params: Partial<User>): Promise<User> {
const whereClouse = Object.entries(params).map(([key, value]) =>
eq(user[key as keyof unknown], value)
);
const data = await db.query.user.findFirst({
where: and(...whereClouse),
columns: {
id: true,
firstName: true,
lastName: true,
email: true,
createdAt: true,
rol: true,
},
});
return data as User;
}
async getWithTaskById(
userId: UserId,
filter: FilterTasks
): Promise<UserById> {
const taskRange = filter?.range ? getRangeValue(filter.range!) : undefined;
const data = await db.query.user.findFirst({
where: eq(user.id, userId.value),
columns: {
id: true,
firstName: true,
lastName: true,
email: true,
createdAt: true,
rol: true,
},
with: {
tasks: {
columns: {
id: true,
status: true,
priority: true,
startDate: true,
floorName: true,
projectName: true,
projectId: true,
areaName: true,
startedTime: true,
endedTime: true,
expireDate: true,
comments: true,
createdAT: true,
updatedAt: true,
areaId: true,
userId: true,
},
where: and(
taskRange
? and(
gte(task.createdAT, taskRange.start),
lte(task.createdAT, taskRange.end)
)
: undefined,
filter?.status ? eq(task.status, filter.status! as any) : undefined
),
},
userTool: {
columns: { id: true, quantity: true },
with: { tool: { columns: { id: true, name: true, quantity: true } } },
},
},
});
const filteredUserTools = data?.userTool.filter(
(item) => item.quantity > 0
);
return {
...data,
userTools: filteredUserTools,
} as UserById;
}
async createDb(data: User): Promise<User> {
return await db.transaction(async (tx) => {
const [userCreated] = await tx
.insert(user)
.values({ ...data, rol: data.rol as any, id: data.id })
.returning()
.onConflictDoUpdate({
target:user.email,
set: { isDeleted: false },
});
if (userCreated) {
await createLogRecord(tx, {
userId: data.id,
eventType: "crear",
tableName: "user",
item: data,
modifiedItem: `${data.firstName} ${data.lastName}`,
});
}
return userCreated as User;
});
}
async sendInvitation(data: SendInvitation): Promise<void> {
const response = await clerkClient.invitations.createInvitation({
emailAddress: data.emailAddress,
redirectUrl: `${VARIABLES_CONFIG.APP_URL}/auth/accept-invitation`,
publicMetadata: {
role: data.rol,
},
ignoreExisting: true,
notify: true,
});
return (await response) as any;
}
async banUser(userId: UserId): Promise<void> {
try {
await clerkClient.users.lockUser(userId.value);
await db
.update(user)
.set({ isActive: false })
.where(eq(user.id, userId.value));
} catch (error) {
throw error;
}
}
async unbanUser(userId: UserId): Promise<void> {
try {
await clerkClient.users.unlockUser(userId.value);
await db
.update(user)
.set({ isActive: true })
.where(eq(user.id, userId.value));
} catch (error) {
throw error;
}
}
async updateClerk(data: Partial<User>): Promise<User> {
try {
await clerkClient.users.updateUser(data.id!, {
...data,
publicMetadata: { role: data.rol },
});
return data as any;
} catch (error) {
throw error;
}
}
async updateUserMetadataClerk(data: UpdateUserMetadata): Promise<void> {
try {
await clerkClient.users.updateUserMetadata(data.seccionId, {
publicMetadata: {
...(data.existInDb && { existInDb: data.existInDb }),
...(data.rol && { role: data.rol }),
},
});
} catch (error) {
throw error;
}
}
async updateDb(data: User): Promise<User> {
await db.transaction(async (tx) => {
const updateDbPromise = tx
.update(user)
.set({ ...data })
.where(eq(user.id, data.id));
const createLogRecordPrimise = createLogRecord(tx, {
userId: data.id!,
eventType: "modificar",
tableName: "user",
item: data,
modifiedItem: `${data?.firstName} ${data?.lastName}`,
});
await Promise.all([updateDbPromise, createLogRecordPrimise]);
});
return data;
}
async deleteClerk(userId: UserId): Promise<void> {
await clerkClient.users.deleteUser(userId.value);
}
async deleteDb(userId: UserId): Promise<User> {
return await db.transaction(async (tx) => {
const [deletedUser] = await tx
.update(user)
.set({ isDeleted: true })
.where(eq(user.id, userId.value))
.returning();
if (deletedUser) {
await createLogRecord(tx, {
userId: userId.value!,
eventType: "eliminar",
tableName: "user",
item: deletedUser,
modifiedItem: `usuario ${deletedUser.firstName} ${deletedUser.lastName} eliminado`,
});
}
return deletedUser as User;
});
}
}
export const defaultUserRepository = new UserRepositoryImpl();