From af9c090d7da84adea9c6b58e45783f872277e83e Mon Sep 17 00:00:00 2001 From: Laksitha Liyanaarachchi Date: Fri, 14 Mar 2025 08:47:06 +0530 Subject: [PATCH 1/3] =?UTF-8?q?k5p7x0r9fd:=20create=20backend=20methods=20?= =?UTF-8?q?to=20observe=20changes=20in=20activity=20and=20mentorfeedback?= =?UTF-8?q?=C2=A0tables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/prisma/schema.prisma | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index 0e8d5b8f..ea3a6702 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -1,7 +1,6 @@ generator client { - provider = "prisma-client-js" - binaryTargets = ["native", "linux-musl"] - + provider = "prisma-client-js" + binaryTargets = ["native", "linux-musl"] } datasource db { @@ -26,6 +25,7 @@ model User { mentorRoles Mentorship[] @relation(name: "mentorRole") studentRoles Mentorship[] @relation(name: "studentRole") mentorActivities MentorActivity[] @relation(name: "UserMentorActivities") + logs Log[] @relation(name: "mentorLogs") @@map("users") } @@ -40,6 +40,7 @@ model Activity { updatedAt DateTime @updatedAt student User @relation(fields: [studentId], references: [id]) feedback MentorFeedback[] + logs Log[] @@map("activities") } @@ -92,6 +93,22 @@ model MentorActivity { @@map("mentoractivities") } +model Log { + id String @id @default(uuid()) // Primary Key + activityId String // Foreign Key (Activity) + mentorId String // Foreign Key (Mentor) + notes String? + feedbackStatus FeedbackStatus @default(pending) // Enum + feedback String? + createdAt DateTime @default(now()) // Timestamp + + // Relations + activity Activity @relation(fields: [activityId], references: [id]) + mentor User @relation(fields: [mentorId], references: [id], name: "mentorLogs") + + @@map("logs") // Map to the actual database table name +} + enum Role { student mentor @@ -109,4 +126,4 @@ enum ProcessStatus { error completed wip -} \ No newline at end of file +} From 0656cc93edd22ad9e90e44e6f7b9d90d9346ffa8 Mon Sep 17 00:00:00 2001 From: Laksitha Liyanaarachchi Date: Fri, 4 Apr 2025 08:48:22 +0530 Subject: [PATCH 2/3] Add files via upload --- server/prisma/executeTriggers.ts | 45 ++++++++++++++++++++++++++++++ server/prisma/schema.prisma | 47 ++++++++++++++------------------ 2 files changed, 66 insertions(+), 26 deletions(-) create mode 100644 server/prisma/executeTriggers.ts diff --git a/server/prisma/executeTriggers.ts b/server/prisma/executeTriggers.ts new file mode 100644 index 00000000..f8419f90 --- /dev/null +++ b/server/prisma/executeTriggers.ts @@ -0,0 +1,45 @@ +import pg from "pg"; +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; +import dotenv from "dotenv"; + +dotenv.config(); + +const { Client } = pg; + +// Fix __dirname for ES Modules +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Path to triggers.sql +const filePath = path.join(__dirname, "scripts", "triggers.sql"); + +const client = new Client({ + connectionString: process.env.DATABASE_URL, +}); + +async function executeTriggers() { + try { + await client.connect(); + console.log(" Connected to the database."); + + // Ensure triggers.sql exists + if (!fs.existsSync(filePath)) { + throw new Error(` triggers.sql file not found at: ${filePath}`); + } + + const sql = fs.readFileSync(filePath, "utf8"); + + console.log(" Executing triggers.sql..."); + await client.query(sql); + console.log(" Triggers executed successfully."); + } catch (error) { + console.error(" Error executing triggers.sql:", error); + } finally { + await client.end(); + console.log(" Database connection closed."); + } +} + +executeTriggers(); diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index ea3a6702..d7ad312c 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -1,6 +1,6 @@ generator client { provider = "prisma-client-js" - binaryTargets = ["native", "linux-musl"] + binaryTargets = ["native", "linux-musl"] } datasource db { @@ -20,16 +20,16 @@ model User { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt activities Activity[] + mentorActivities MentorActivity[] @relation("UserMentorActivities") mentorFeedback MentorFeedback[] - mentorReports Report[] @relation(name: "mentorReports") - mentorRoles Mentorship[] @relation(name: "mentorRole") - studentRoles Mentorship[] @relation(name: "studentRole") - mentorActivities MentorActivity[] @relation(name: "UserMentorActivities") - logs Log[] @relation(name: "mentorLogs") + mentorRoles Mentorship[] @relation("mentorRole") + studentRoles Mentorship[] @relation("studentRole") + mentorReports Report[] @relation("mentorReports") @@map("users") } + model Activity { id String @id @default(uuid()) studentId String @@ -40,7 +40,6 @@ model Activity { updatedAt DateTime @updatedAt student User @relation(fields: [studentId], references: [id]) feedback MentorFeedback[] - logs Log[] @@map("activities") } @@ -60,12 +59,12 @@ model MentorFeedback { } model Report { - id String @id @default(uuid()) + id String @id @default(uuid()) mentorId String reportData Json status ProcessStatus @default(pending) - generatedAt DateTime @default(now()) - mentor User @relation(fields: [mentorId], references: [id], name: "mentorReports") + generatedAt DateTime @default(now()) + mentor User @relation("mentorReports", fields: [mentorId], references: [id]) @@map("reports") } @@ -74,8 +73,8 @@ model Mentorship { id String @id @default(uuid()) mentorId String studentId String - mentor User @relation(fields: [mentorId], references: [id], name: "mentorRole") - student User @relation(fields: [studentId], references: [id], name: "studentRole") + mentor User @relation("mentorRole", fields: [mentorId], references: [id]) + student User @relation("studentRole", fields: [studentId], references: [id]) @@map("mentorship") } @@ -88,26 +87,22 @@ model MentorActivity { activities String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - mentor User @relation(fields: [mentorId], references: [id], name: "UserMentorActivities") + mentor User @relation("UserMentorActivities", fields: [mentorId], references: [id]) @@map("mentoractivities") } -model Log { - id String @id @default(uuid()) // Primary Key - activityId String // Foreign Key (Activity) - mentorId String // Foreign Key (Mentor) - notes String? - feedbackStatus FeedbackStatus @default(pending) // Enum - feedback String? - createdAt DateTime @default(now()) // Timestamp +model AuditLog { + id String @id @default(uuid()) + action String + tableName String + recordId String + oldData Json? + timestamp DateTime @default(now()) + @@map("audit_logs") +} - // Relations - activity Activity @relation(fields: [activityId], references: [id]) - mentor User @relation(fields: [mentorId], references: [id], name: "mentorLogs") - @@map("logs") // Map to the actual database table name -} enum Role { student From 49c862139cca3d9f9f7da3978b50cf0191f1890a Mon Sep 17 00:00:00 2001 From: Laksitha Liyanaarachchi Date: Fri, 4 Apr 2025 09:00:01 +0530 Subject: [PATCH 3/3] Create triggers.sql --- server/prisma/scripts/triggers.sql | 52 ++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 server/prisma/scripts/triggers.sql diff --git a/server/prisma/scripts/triggers.sql b/server/prisma/scripts/triggers.sql new file mode 100644 index 00000000..b33d0588 --- /dev/null +++ b/server/prisma/scripts/triggers.sql @@ -0,0 +1,52 @@ +-- Drop existing function if it exists +DROP FUNCTION IF EXISTS audit_all_tables CASCADE; + +-- Create the audit function without current_user_id +CREATE OR REPLACE FUNCTION audit_all_tables() RETURNS TRIGGER AS $$ +BEGIN + -- Insert into audit_logs (without current_user_id) + INSERT INTO audit_logs ("id", "action", "tableName", "recordId", "oldData", "timestamp") + VALUES ( + gen_random_uuid(), + TG_OP, + TG_TABLE_NAME, + CASE WHEN TG_OP = 'DELETE' THEN OLD.id ELSE NEW.id END, + CASE WHEN TG_OP IN ('UPDATE', 'DELETE') THEN row_to_json(OLD) ELSE NULL END, + now() + ); + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- Create triggers for all tracked tables + +-- Activities table trigger +DROP TRIGGER IF EXISTS audit_trigger_activities ON activities; +CREATE TRIGGER audit_trigger_activities +AFTER INSERT OR UPDATE OR DELETE ON activities +FOR EACH ROW EXECUTE FUNCTION audit_all_tables(); + +-- Mentor Feedback table trigger +DROP TRIGGER IF EXISTS audit_trigger_mentorfeedback ON mentorfeedback; +CREATE TRIGGER audit_trigger_mentorfeedback +AFTER INSERT OR UPDATE OR DELETE ON mentorfeedback +FOR EACH ROW EXECUTE FUNCTION audit_all_tables(); + +-- Mentorship table trigger +DROP TRIGGER IF EXISTS audit_trigger_mentorship ON mentorship; +CREATE TRIGGER audit_trigger_mentorship +AFTER INSERT OR UPDATE OR DELETE ON mentorship +FOR EACH ROW EXECUTE FUNCTION audit_all_tables(); + +-- Users table trigger +DROP TRIGGER IF EXISTS audit_trigger_users ON users; +CREATE TRIGGER audit_trigger_users +AFTER INSERT OR UPDATE OR DELETE ON users +FOR EACH ROW EXECUTE FUNCTION audit_all_tables(); + +-- Mentor Activity table trigger +DROP TRIGGER IF EXISTS audit_trigger_mentoractivities ON mentoractivities; +CREATE TRIGGER audit_trigger_mentoractivities +AFTER INSERT OR UPDATE OR DELETE ON mentoractivities +FOR EACH ROW EXECUTE FUNCTION audit_all_tables();