aegida-console / lib / db / attachments.ts
attachments.ts
Raw
import type { Attachment } from "@/lib/chat/types";
import type { ValidatedAttachment } from "@/lib/chat/attachments";
import type { Queryable } from "@/lib/db/types";

type AttachmentRow = {
  id: string;
  original_name: string;
  content_type: string;
  byte_size: number;
  gate_file_id: string | null;
  object_key: string | null;
};

export type StoredAttachment = Attachment & {
  gateFileId: string | null;
  objectKey: string | null;
};

export type GateAttachmentMetadata = ValidatedAttachment & { gateFileId: string };

export async function createPendingAttachment(
  database: Queryable,
  userId: string,
  attachment: GateAttachmentMetadata,
): Promise<StoredAttachment> {
  const result = await database.query<AttachmentRow>(
    `INSERT INTO chat_attachments (user_id, original_name, content_type, byte_size, gate_file_id, state, expires_at)
     VALUES ($1::uuid, $2, $3, $4, $5, 'pending', now() + interval '24 hours')
     RETURNING id, original_name, content_type, byte_size, gate_file_id, object_key`,
    [userId, attachment.name, attachment.contentType, attachment.size, attachment.gateFileId],
  );
  return mapStoredAttachment(result.rows[0]);
}

export async function getOwnedAttachment(
  database: Queryable,
  userId: string,
  id: string,
): Promise<StoredAttachment | null> {
  const result = await database.query<AttachmentRow>(
    `SELECT id, original_name, content_type, byte_size, gate_file_id, object_key
     FROM chat_attachments WHERE id = $1 AND user_id = $2`,
    [id, userId],
  );
  return result.rows[0] ? mapStoredAttachment(result.rows[0]) : null;
}

export async function attachPendingAttachments(
  database: Queryable,
  userId: string,
  messageId: string,
  ids: string[],
): Promise<StoredAttachment[]> {
  if (ids.length !== new Set(ids).size) throw new Error("Duplicate attachment IDs");
  const result = await database.query<AttachmentRow>(
    `UPDATE chat_attachments
     SET message_id = $3, state = 'attached', expires_at = 'infinity'
     WHERE id = ANY($1::uuid[]) AND user_id = $2 AND state = 'pending' AND expires_at > now()
     RETURNING id, original_name, content_type, byte_size, gate_file_id, object_key`,
    [ids, userId, messageId],
  );
  if (result.rows.length !== ids.length) throw new Error("Attachment not found");
  return result.rows.map(mapStoredAttachment);
}

function mapAttachment(row: AttachmentRow): Attachment {
  return { id: row.id, name: row.original_name, contentType: row.content_type, size: Number(row.byte_size) };
}

function mapStoredAttachment(row: AttachmentRow): StoredAttachment {
  return {
    ...mapAttachment(row),
    gateFileId: row.gate_file_id,
    objectKey: row.object_key,
  };
}