feat: integrate Prisma for database management
This commit is contained in:
2
.env.example
Normal file
2
.env.example
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
DATABASE_URL="postgres://user:secret123@postgres:5432/db"
|
||||||
|
PORT=3000
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -54,3 +54,5 @@ pids
|
|||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
/generated/prisma
|
||||||
|
|||||||
944
package-lock.json
generated
944
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,8 @@
|
|||||||
"@nestjs/core": "^11.0.1",
|
"@nestjs/core": "^11.0.1",
|
||||||
"@nestjs/mapped-types": "*",
|
"@nestjs/mapped-types": "*",
|
||||||
"@nestjs/platform-express": "^11.0.1",
|
"@nestjs/platform-express": "^11.0.1",
|
||||||
|
"@prisma/client": "^7.4.1",
|
||||||
|
"dotenv": "^17.3.1",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1"
|
"rxjs": "^7.8.1"
|
||||||
},
|
},
|
||||||
@@ -43,6 +45,7 @@
|
|||||||
"globals": "^16.0.0",
|
"globals": "^16.0.0",
|
||||||
"jest": "^30.0.0",
|
"jest": "^30.0.0",
|
||||||
"prettier": "^3.4.2",
|
"prettier": "^3.4.2",
|
||||||
|
"prisma": "^7.4.1",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"supertest": "^7.0.0",
|
"supertest": "^7.0.0",
|
||||||
"ts-jest": "^29.2.5",
|
"ts-jest": "^29.2.5",
|
||||||
|
|||||||
14
prisma.config.ts
Normal file
14
prisma.config.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// This file was generated by Prisma, and assumes you have installed the following:
|
||||||
|
// npm install --save-dev prisma dotenv
|
||||||
|
import "dotenv/config";
|
||||||
|
import { defineConfig } from "prisma/config";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
schema: "prisma/schema.prisma",
|
||||||
|
migrations: {
|
||||||
|
path: "prisma/migrations",
|
||||||
|
},
|
||||||
|
datasource: {
|
||||||
|
url: process.env["DATABASE_URL"],
|
||||||
|
},
|
||||||
|
});
|
||||||
229
prisma/migrations/20260226161313_init/migration.sql
Normal file
229
prisma/migrations/20260226161313_init/migration.sql
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "categories" (
|
||||||
|
"id" BIGSERIAL NOT NULL,
|
||||||
|
"name" VARCHAR(100) NOT NULL,
|
||||||
|
"description" TEXT,
|
||||||
|
|
||||||
|
CONSTRAINT "categories_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "roles" (
|
||||||
|
"id" BIGSERIAL NOT NULL,
|
||||||
|
"name" VARCHAR(50) NOT NULL,
|
||||||
|
"description" TEXT,
|
||||||
|
|
||||||
|
CONSTRAINT "roles_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "users" (
|
||||||
|
"id" BIGSERIAL NOT NULL,
|
||||||
|
"external_uid" VARCHAR(255) NOT NULL,
|
||||||
|
"role_id" BIGINT NOT NULL,
|
||||||
|
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "tickets" (
|
||||||
|
"id" BIGSERIAL NOT NULL,
|
||||||
|
"title" VARCHAR(255) NOT NULL,
|
||||||
|
"description" TEXT,
|
||||||
|
"status" VARCHAR(30) NOT NULL,
|
||||||
|
"priority" VARCHAR(30),
|
||||||
|
"category_id" BIGINT,
|
||||||
|
"created_by_user_id" BIGINT NOT NULL,
|
||||||
|
"assigned_to_user_id" BIGINT,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"closed_at" TIMESTAMPTZ,
|
||||||
|
"ticket_number" VARCHAR(30) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "tickets_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ticket_messages" (
|
||||||
|
"id" BIGSERIAL NOT NULL,
|
||||||
|
"ticket_id" BIGINT NOT NULL,
|
||||||
|
"author_id" BIGINT NOT NULL,
|
||||||
|
"message" TEXT NOT NULL,
|
||||||
|
"is_internal" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "ticket_messages_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ticket_attachments" (
|
||||||
|
"id" BIGSERIAL NOT NULL,
|
||||||
|
"ticket_id" BIGINT NOT NULL,
|
||||||
|
"message_id" BIGINT,
|
||||||
|
"uploaded_by" BIGINT NOT NULL,
|
||||||
|
"file_name" VARCHAR(255),
|
||||||
|
"file_url" TEXT NOT NULL,
|
||||||
|
"file_size" BIGINT,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "ticket_attachments_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ticket_invitations" (
|
||||||
|
"id" BIGSERIAL NOT NULL,
|
||||||
|
"ticket_id" BIGINT NOT NULL,
|
||||||
|
"inviter_id" BIGINT NOT NULL,
|
||||||
|
"invitee_id" BIGINT NOT NULL,
|
||||||
|
"status" VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||||
|
"message" TEXT,
|
||||||
|
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"responded_at" TIMESTAMPTZ,
|
||||||
|
|
||||||
|
CONSTRAINT "ticket_invitations_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ticket_participants" (
|
||||||
|
"id" BIGSERIAL NOT NULL,
|
||||||
|
"ticket_id" BIGINT NOT NULL,
|
||||||
|
"user_id" BIGINT NOT NULL,
|
||||||
|
"added_by" BIGINT NOT NULL,
|
||||||
|
"added_via" VARCHAR(20) NOT NULL,
|
||||||
|
"can_edit" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"can_comment" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"joined_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "ticket_participants_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ticket_status_history" (
|
||||||
|
"id" BIGSERIAL NOT NULL,
|
||||||
|
"ticket_id" BIGINT NOT NULL,
|
||||||
|
"old_status" VARCHAR(30),
|
||||||
|
"new_status" VARCHAR(30) NOT NULL,
|
||||||
|
"changed_by" BIGINT NOT NULL,
|
||||||
|
"changed_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "ticket_status_history_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "categories_name_key" ON "categories"("name");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "roles_name_key" ON "roles"("name");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "users_external_uid_key" ON "users"("external_uid");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_users_external_uid" ON "users"("external_uid");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "tickets_ticket_number_key" ON "tickets"("ticket_number");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_tickets_assigned_to" ON "tickets"("assigned_to_user_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_tickets_created_at" ON "tickets"("created_at");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_tickets_created_by" ON "tickets"("created_by_user_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_tickets_status" ON "tickets"("status");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_tickets_ticket_number" ON "tickets"("ticket_number");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_messages_ticket_id" ON "ticket_messages"("ticket_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_invitations_created_at" ON "ticket_invitations"("created_at");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_invitations_invitee_id" ON "ticket_invitations"("invitee_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_invitations_inviter_id" ON "ticket_invitations"("inviter_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_invitations_status" ON "ticket_invitations"("status");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_invitations_ticket_id" ON "ticket_invitations"("ticket_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "ticket_invitations_ticket_id_invitee_id_status_key" ON "ticket_invitations"("ticket_id", "invitee_id", "status");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_participants_added_by" ON "ticket_participants"("added_by");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_participants_ticket_id" ON "ticket_participants"("ticket_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_participants_user_id" ON "ticket_participants"("user_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "ticket_participants_ticket_id_user_id_key" ON "ticket_participants"("ticket_id", "user_id");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "idx_history_ticket_id" ON "ticket_status_history"("ticket_id");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "users" ADD CONSTRAINT "users_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "roles"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "tickets" ADD CONSTRAINT "tickets_category_id_fkey" FOREIGN KEY ("category_id") REFERENCES "categories"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "tickets" ADD CONSTRAINT "tickets_created_by_user_id_fkey" FOREIGN KEY ("created_by_user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "tickets" ADD CONSTRAINT "tickets_assigned_to_user_id_fkey" FOREIGN KEY ("assigned_to_user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ticket_messages" ADD CONSTRAINT "ticket_messages_ticket_id_fkey" FOREIGN KEY ("ticket_id") REFERENCES "tickets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ticket_messages" ADD CONSTRAINT "ticket_messages_author_id_fkey" FOREIGN KEY ("author_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ticket_attachments" ADD CONSTRAINT "ticket_attachments_ticket_id_fkey" FOREIGN KEY ("ticket_id") REFERENCES "tickets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ticket_attachments" ADD CONSTRAINT "ticket_attachments_message_id_fkey" FOREIGN KEY ("message_id") REFERENCES "ticket_messages"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ticket_attachments" ADD CONSTRAINT "ticket_attachments_uploaded_by_fkey" FOREIGN KEY ("uploaded_by") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ticket_invitations" ADD CONSTRAINT "ticket_invitations_ticket_id_fkey" FOREIGN KEY ("ticket_id") REFERENCES "tickets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ticket_invitations" ADD CONSTRAINT "ticket_invitations_inviter_id_fkey" FOREIGN KEY ("inviter_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ticket_invitations" ADD CONSTRAINT "ticket_invitations_invitee_id_fkey" FOREIGN KEY ("invitee_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ticket_participants" ADD CONSTRAINT "ticket_participants_ticket_id_fkey" FOREIGN KEY ("ticket_id") REFERENCES "tickets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ticket_participants" ADD CONSTRAINT "ticket_participants_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ticket_participants" ADD CONSTRAINT "ticket_participants_added_by_fkey" FOREIGN KEY ("added_by") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ticket_status_history" ADD CONSTRAINT "ticket_status_history_ticket_id_fkey" FOREIGN KEY ("ticket_id") REFERENCES "tickets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ticket_status_history" ADD CONSTRAINT "ticket_status_history_changed_by_fkey" FOREIGN KEY ("changed_by") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
3
prisma/migrations/migration_lock.toml
Normal file
3
prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (e.g., Git)
|
||||||
|
provider = "postgresql"
|
||||||
184
prisma/schema.prisma
Normal file
184
prisma/schema.prisma
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
// This is your Prisma schema file,
|
||||||
|
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||||
|
|
||||||
|
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
||||||
|
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
||||||
|
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client"
|
||||||
|
output = "../generated/prisma"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
}
|
||||||
|
|
||||||
|
model Category {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
name String @unique @db.VarChar(100)
|
||||||
|
description String?
|
||||||
|
|
||||||
|
tickets Ticket[]
|
||||||
|
|
||||||
|
@@map("categories")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Role {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
name String @unique @db.VarChar(50)
|
||||||
|
description String?
|
||||||
|
|
||||||
|
users User[]
|
||||||
|
|
||||||
|
@@map("roles")
|
||||||
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
external_uid String @unique @db.VarChar(255)
|
||||||
|
role_id BigInt
|
||||||
|
is_active Boolean @default(true)
|
||||||
|
created_at DateTime @default(now()) @db.Timestamptz
|
||||||
|
|
||||||
|
role Role @relation(fields: [role_id], references: [id])
|
||||||
|
tickets_created Ticket[] @relation("CreatedBy")
|
||||||
|
tickets_assigned Ticket[] @relation("AssignedTo")
|
||||||
|
messages TicketMessage[]
|
||||||
|
attachments_uploaded TicketAttachment[]
|
||||||
|
invitations_sent TicketInvitation[] @relation("Inviter")
|
||||||
|
invitations_received TicketInvitation[] @relation("Invitee")
|
||||||
|
participants TicketParticipant[] @relation("Participant")
|
||||||
|
participants_added TicketParticipant[] @relation("AddedBy")
|
||||||
|
status_history_changed TicketStatusHistory[]
|
||||||
|
|
||||||
|
@@index([external_uid], name: "idx_users_external_uid")
|
||||||
|
@@map("users")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Ticket {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
title String @db.VarChar(255)
|
||||||
|
description String?
|
||||||
|
status String @db.VarChar(30)
|
||||||
|
priority String? @db.VarChar(30)
|
||||||
|
category_id BigInt?
|
||||||
|
created_by_user_id BigInt
|
||||||
|
assigned_to_user_id BigInt?
|
||||||
|
created_at DateTime @default(now()) @db.Timestamptz
|
||||||
|
updated_at DateTime @default(now()) @db.Timestamptz
|
||||||
|
closed_at DateTime? @db.Timestamptz
|
||||||
|
/// Número de ticket legible para usuarios y APIs públicas (formato: TCK-YYYY-NNNNN). Este identificador se usa en lugar del ID interno en todas las comunicaciones externas.
|
||||||
|
ticket_number String @unique @db.VarChar(30)
|
||||||
|
|
||||||
|
category Category? @relation(fields: [category_id], references: [id])
|
||||||
|
created_by User @relation("CreatedBy", fields: [created_by_user_id], references: [id])
|
||||||
|
assigned_to User? @relation("AssignedTo", fields: [assigned_to_user_id], references: [id])
|
||||||
|
messages TicketMessage[]
|
||||||
|
attachments TicketAttachment[]
|
||||||
|
invitations TicketInvitation[]
|
||||||
|
participants TicketParticipant[]
|
||||||
|
status_history TicketStatusHistory[]
|
||||||
|
|
||||||
|
@@index([assigned_to_user_id], name: "idx_tickets_assigned_to")
|
||||||
|
@@index([created_at], name: "idx_tickets_created_at")
|
||||||
|
@@index([created_by_user_id], name: "idx_tickets_created_by")
|
||||||
|
@@index([status], name: "idx_tickets_status")
|
||||||
|
@@index([ticket_number], name: "idx_tickets_ticket_number")
|
||||||
|
@@map("tickets")
|
||||||
|
}
|
||||||
|
|
||||||
|
model TicketMessage {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
ticket_id BigInt
|
||||||
|
author_id BigInt
|
||||||
|
message String
|
||||||
|
is_internal Boolean @default(false)
|
||||||
|
created_at DateTime @default(now()) @db.Timestamptz
|
||||||
|
|
||||||
|
ticket Ticket @relation(fields: [ticket_id], references: [id], onDelete: Cascade)
|
||||||
|
author User @relation(fields: [author_id], references: [id])
|
||||||
|
attachments TicketAttachment[]
|
||||||
|
|
||||||
|
@@index([ticket_id], name: "idx_messages_ticket_id")
|
||||||
|
@@map("ticket_messages")
|
||||||
|
}
|
||||||
|
|
||||||
|
model TicketAttachment {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
ticket_id BigInt
|
||||||
|
message_id BigInt?
|
||||||
|
uploaded_by BigInt
|
||||||
|
file_name String? @db.VarChar(255)
|
||||||
|
file_url String
|
||||||
|
file_size BigInt?
|
||||||
|
created_at DateTime @default(now()) @db.Timestamptz
|
||||||
|
|
||||||
|
ticket Ticket @relation(fields: [ticket_id], references: [id], onDelete: Cascade)
|
||||||
|
message TicketMessage? @relation(fields: [message_id], references: [id])
|
||||||
|
uploader User @relation(fields: [uploaded_by], references: [id])
|
||||||
|
|
||||||
|
@@map("ticket_attachments")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invitaciones pendientes, aceptadas o rechazadas para unirse a un ticket
|
||||||
|
model TicketInvitation {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
ticket_id BigInt
|
||||||
|
inviter_id BigInt
|
||||||
|
invitee_id BigInt
|
||||||
|
/// Estado de la invitación: pending, accepted, rejected, cancelled
|
||||||
|
status String @default("pending") @db.VarChar(20)
|
||||||
|
message String?
|
||||||
|
created_at DateTime @default(now()) @db.Timestamptz
|
||||||
|
responded_at DateTime? @db.Timestamptz
|
||||||
|
|
||||||
|
ticket Ticket @relation(fields: [ticket_id], references: [id], onDelete: Cascade)
|
||||||
|
inviter User @relation("Inviter", fields: [inviter_id], references: [id])
|
||||||
|
invitee User @relation("Invitee", fields: [invitee_id], references: [id])
|
||||||
|
|
||||||
|
@@unique([ticket_id, invitee_id, status], name: "unique_pending_invitation")
|
||||||
|
@@index([created_at], name: "idx_invitations_created_at")
|
||||||
|
@@index([invitee_id], name: "idx_invitations_invitee_id")
|
||||||
|
@@index([inviter_id], name: "idx_invitations_inviter_id")
|
||||||
|
@@index([status], name: "idx_invitations_status")
|
||||||
|
@@index([ticket_id], name: "idx_invitations_ticket_id")
|
||||||
|
@@map("ticket_invitations")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Usuarios que tienen acceso a un ticket específico
|
||||||
|
model TicketParticipant {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
ticket_id BigInt
|
||||||
|
user_id BigInt
|
||||||
|
added_by BigInt
|
||||||
|
/// Método de adición: creator, invitation, direct_add, assignment, staff_access
|
||||||
|
added_via String @db.VarChar(20)
|
||||||
|
can_edit Boolean @default(false)
|
||||||
|
can_comment Boolean @default(true)
|
||||||
|
joined_at DateTime @default(now()) @db.Timestamptz
|
||||||
|
|
||||||
|
ticket Ticket @relation(fields: [ticket_id], references: [id], onDelete: Cascade)
|
||||||
|
user User @relation("Participant", fields: [user_id], references: [id])
|
||||||
|
added_by_user User @relation("AddedBy", fields: [added_by], references: [id])
|
||||||
|
|
||||||
|
@@unique([ticket_id, user_id])
|
||||||
|
@@index([added_by], name: "idx_participants_added_by")
|
||||||
|
@@index([ticket_id], name: "idx_participants_ticket_id")
|
||||||
|
@@index([user_id], name: "idx_participants_user_id")
|
||||||
|
@@map("ticket_participants")
|
||||||
|
}
|
||||||
|
|
||||||
|
model TicketStatusHistory {
|
||||||
|
id BigInt @id @default(autoincrement())
|
||||||
|
ticket_id BigInt
|
||||||
|
old_status String? @db.VarChar(30)
|
||||||
|
new_status String @db.VarChar(30)
|
||||||
|
changed_by BigInt
|
||||||
|
changed_at DateTime @default(now()) @db.Timestamptz
|
||||||
|
|
||||||
|
ticket Ticket @relation(fields: [ticket_id], references: [id], onDelete: Cascade)
|
||||||
|
changed_by_user User @relation(fields: [changed_by], references: [id])
|
||||||
|
|
||||||
|
@@index([ticket_id], name: "idx_history_ticket_id")
|
||||||
|
@@map("ticket_status_history")
|
||||||
|
}
|
||||||
@@ -2,9 +2,10 @@ import { Module } from '@nestjs/common';
|
|||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { CategoriesModule } from './categories/categories.module';
|
import { CategoriesModule } from './categories/categories.module';
|
||||||
|
import { PrismaModule } from './prisma/prisma.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [CategoriesModule],
|
imports: [PrismaModule, CategoriesModule],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService],
|
providers: [AppService],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ import { AppModule } from './app.module';
|
|||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
await app.listen(process.env.PORT ?? 3020);
|
await app.listen(process.env.PORT ?? 3000);
|
||||||
}
|
}
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|||||||
9
src/prisma/prisma.module.ts
Normal file
9
src/prisma/prisma.module.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Global, Module } from '@nestjs/common';
|
||||||
|
import { PrismaService } from './prisma.service';
|
||||||
|
|
||||||
|
@Global()
|
||||||
|
@Module({
|
||||||
|
providers: [PrismaService],
|
||||||
|
exports: [PrismaService],
|
||||||
|
})
|
||||||
|
export class PrismaModule {}
|
||||||
9
src/prisma/prisma.service.ts
Normal file
9
src/prisma/prisma.service.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Injectable, OnModuleInit } from '@nestjs/common';
|
||||||
|
import { PrismaClient } from 'generated/prisma/client';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PrismaService extends PrismaClient implements OnModuleInit {
|
||||||
|
async onModuleInit() {
|
||||||
|
await this.$connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user