task-managment / src / server / user / infrastructure / repositories-impl.ts
repositories-impl.ts
Raw
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();