Release 2025-05-19

This commit is contained in:
pluja
2025-05-19 10:19:49 +00:00
parent 046c4559e5
commit 2657f936bc
267 changed files with 0 additions and 49432 deletions

View File

@@ -1,265 +0,0 @@
-- This script manages user karma based on comment interactions. It handles karma points
-- for comment approvals, verifications, spam status changes, and votes (upvotes/downvotes).
-- Karma transactions are recorded, and user karma totals are updated accordingly.
-- Drop existing triggers first
DROP TRIGGER IF EXISTS comment_status_change_trigger ON "Comment";
DROP TRIGGER IF EXISTS comment_suspicious_change_trigger ON "Comment";
DROP TRIGGER IF EXISTS comment_upvote_change_trigger ON "Comment";
DROP TRIGGER IF EXISTS comment_vote_change_trigger ON "CommentVote";
DROP TRIGGER IF EXISTS suggestion_status_change_trigger ON "ServiceSuggestion";
-- Drop existing functions
DROP FUNCTION IF EXISTS handle_comment_upvote_change();
DROP FUNCTION IF EXISTS handle_comment_status_change();
DROP FUNCTION IF EXISTS handle_comment_approval();
DROP FUNCTION IF EXISTS handle_comment_verification();
DROP FUNCTION IF EXISTS handle_comment_spam_status();
DROP FUNCTION IF EXISTS handle_comment_vote_change();
DROP FUNCTION IF EXISTS insert_karma_transaction();
DROP FUNCTION IF EXISTS update_user_karma();
DROP FUNCTION IF EXISTS handle_suggestion_status_change();
-- Helper function to insert karma transaction
CREATE OR REPLACE FUNCTION insert_karma_transaction(
p_user_id INT,
p_points INT,
p_action TEXT,
p_comment_id INT,
p_description TEXT,
p_suggestion_id INT DEFAULT NULL
) RETURNS VOID AS $$
BEGIN
INSERT INTO "KarmaTransaction" (
"userId", "points", "action", "commentId",
"suggestionId",
"description", "processed", "createdAt"
)
VALUES (
p_user_id, p_points, p_action, p_comment_id,
p_suggestion_id,
p_description, true, NOW()
);
END;
$$ LANGUAGE plpgsql;
-- Helper function to update user karma
CREATE OR REPLACE FUNCTION update_user_karma(
p_user_id INT,
p_karma_change INT
) RETURNS VOID AS $$
BEGIN
UPDATE "User"
SET "totalKarma" = "totalKarma" + p_karma_change
WHERE id = p_user_id;
END;
$$ LANGUAGE plpgsql;
-- Handle comment approval
CREATE OR REPLACE FUNCTION handle_comment_approval(
NEW RECORD,
OLD RECORD
) RETURNS VOID AS $$
BEGIN
IF OLD.status = 'PENDING' AND NEW.status = 'APPROVED' THEN
PERFORM insert_karma_transaction(
NEW."authorId",
1,
'comment_approved',
NEW.id,
format('Your comment #comment-%s in %s has been approved!',
NEW.id,
(SELECT name FROM "Service" WHERE id = NEW."serviceId"))
);
PERFORM update_user_karma(NEW."authorId", 1);
END IF;
END;
$$ LANGUAGE plpgsql;
-- Handle comment verification
CREATE OR REPLACE FUNCTION handle_comment_verification(
NEW RECORD,
OLD RECORD
) RETURNS VOID AS $$
BEGIN
IF NEW.status = 'VERIFIED' AND OLD.status != 'VERIFIED' THEN
PERFORM insert_karma_transaction(
NEW."authorId",
5,
'comment_verified',
NEW.id,
format('Your comment #comment-%s in %s has been verified!',
NEW.id,
(SELECT name FROM "Service" WHERE id = NEW."serviceId"))
);
PERFORM update_user_karma(NEW."authorId", 5);
END IF;
END;
$$ LANGUAGE plpgsql;
-- Handle spam status changes
CREATE OR REPLACE FUNCTION handle_comment_spam_status(
NEW RECORD,
OLD RECORD
) RETURNS VOID AS $$
BEGIN
-- Handle marking as spam
IF NEW.suspicious = true AND OLD.suspicious = false THEN
PERFORM insert_karma_transaction(
NEW."authorId",
-10,
'comment_spam',
NEW.id,
format('Your comment #comment-%s in %s has been marked as spam.',
NEW.id,
(SELECT name FROM "Service" WHERE id = NEW."serviceId"))
);
PERFORM update_user_karma(NEW."authorId", -10);
-- Handle unmarking as spam
ELSIF NEW.suspicious = false AND OLD.suspicious = true THEN
PERFORM insert_karma_transaction(
NEW."authorId",
10,
'comment_spam_reverted',
NEW.id,
format('Your comment #comment-%s in %s is no longer marked as spam.',
NEW.id,
(SELECT name FROM "Service" WHERE id = NEW."serviceId"))
);
PERFORM update_user_karma(NEW."authorId", 10);
END IF;
END;
$$ LANGUAGE plpgsql;
-- Function for handling vote changes
CREATE OR REPLACE FUNCTION handle_comment_vote_change()
RETURNS TRIGGER AS $$
DECLARE
karma_points INT;
vote_action TEXT;
vote_description TEXT;
comment_author_id INT;
service_name TEXT;
upvote_change INT := 0; -- Variable to track change in upvotes
BEGIN
-- Get comment author and service info
SELECT c."authorId", s.name INTO comment_author_id, service_name
FROM "Comment" c
JOIN "Service" s ON c.id = COALESCE(NEW."commentId", OLD."commentId") AND c."serviceId" = s.id;
-- Calculate karma impact based on vote type
IF TG_OP = 'INSERT' THEN
-- New vote
karma_points := CASE WHEN NEW.downvote THEN -1 ELSE 1 END;
vote_action := CASE WHEN NEW.downvote THEN 'comment_downvote' ELSE 'comment_upvote' END;
vote_description := format('Your comment #comment-%s in %s received %s',
NEW."commentId",
service_name,
CASE WHEN NEW.downvote THEN 'a downvote' ELSE 'an upvote' END);
upvote_change := CASE WHEN NEW.downvote THEN -1 ELSE 1 END; -- -1 for downvote, +1 for upvote
ELSIF TG_OP = 'DELETE' THEN
-- Removed vote
karma_points := CASE WHEN OLD.downvote THEN 1 ELSE -1 END;
vote_action := 'comment_vote_removed';
vote_description := format('A vote was removed from your comment #comment-%s in %s',
OLD."commentId",
service_name);
upvote_change := CASE WHEN OLD.downvote THEN 1 ELSE -1 END; -- +1 if downvote removed, -1 if upvote removed
ELSIF TG_OP = 'UPDATE' THEN
-- Changed vote (from upvote to downvote or vice versa)
karma_points := CASE WHEN NEW.downvote THEN -2 ELSE 2 END;
vote_action := CASE WHEN NEW.downvote THEN 'comment_downvote' ELSE 'comment_upvote' END;
vote_description := format('Your comment #comment-%s in %s vote changed to %s',
NEW."commentId",
service_name,
CASE WHEN NEW.downvote THEN 'downvote' ELSE 'upvote' END);
upvote_change := CASE WHEN NEW.downvote THEN -2 ELSE 2 END; -- -2 if upvote->downvote, +2 if downvote->upvote
END IF;
-- Record karma transaction and update user karma
PERFORM insert_karma_transaction(
comment_author_id,
karma_points,
vote_action,
COALESCE(NEW."commentId", OLD."commentId"),
vote_description
);
PERFORM update_user_karma(comment_author_id, karma_points);
-- Update comment's upvotes count incrementally
UPDATE "Comment"
SET upvotes = upvotes + upvote_change
WHERE id = COALESCE(NEW."commentId", OLD."commentId");
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Main function for handling status changes
CREATE OR REPLACE FUNCTION handle_comment_status_change()
RETURNS TRIGGER AS $$
BEGIN
PERFORM handle_comment_approval(NEW, OLD);
PERFORM handle_comment_verification(NEW, OLD);
PERFORM handle_comment_spam_status(NEW, OLD);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Create triggers
CREATE TRIGGER comment_status_change_trigger
AFTER UPDATE OF status
ON "Comment"
FOR EACH ROW
EXECUTE FUNCTION handle_comment_status_change();
CREATE TRIGGER comment_suspicious_change_trigger
AFTER UPDATE OF suspicious
ON "Comment"
FOR EACH ROW
EXECUTE FUNCTION handle_comment_status_change();
CREATE TRIGGER comment_vote_change_trigger
AFTER INSERT OR UPDATE OR DELETE
ON "CommentVote"
FOR EACH ROW
EXECUTE FUNCTION handle_comment_vote_change();
-- Function to handle suggestion status changes and award karma
CREATE OR REPLACE FUNCTION handle_suggestion_status_change()
RETURNS TRIGGER AS $$
DECLARE
service_name TEXT;
BEGIN
-- Award karma for first approval
-- Check that OLD.status is not NULL to handle the initial creation case if needed,
-- and ensure it wasn't already APPROVED.
IF OLD.status IS DISTINCT FROM 'APPROVED' AND NEW.status = 'APPROVED' THEN
-- Fetch service name for the description
SELECT name INTO service_name FROM "Service" WHERE id = NEW."serviceId";
-- Insert karma transaction, linking it to the suggestion
PERFORM insert_karma_transaction(
NEW."userId",
10,
'suggestion_approved',
NULL, -- p_comment_id (not applicable)
format('Your suggestion for service ''%s'' has been approved!', service_name),
NEW.id -- p_suggestion_id
);
-- Update user's total karma
PERFORM update_user_karma(NEW."userId", 10);
END IF;
RETURN NEW; -- Result is ignored since this is an AFTER trigger
END;
$$ LANGUAGE plpgsql;
-- Create triggers
CREATE TRIGGER suggestion_status_change_trigger
AFTER UPDATE OF status
ON "ServiceSuggestion"
FOR EACH ROW
EXECUTE FUNCTION handle_suggestion_status_change();

View File

@@ -1,264 +0,0 @@
-- This script defines PostgreSQL functions and triggers for managing service scores:
-- 1. Automatically calculates and updates privacy, trust, and overall scores
-- for services when services or their attributes change.
-- 2. Updates the isRecentlyListed flag for services listed within the last 15 days.
-- 3. Queues asynchronous score recalculation in "ServiceScoreRecalculationJob"
-- when an "Attribute" definition (e.g., points) is updated, ensuring
-- efficient handling of widespread score updates.
-- Drop existing triggers first
DROP TRIGGER IF EXISTS service_score_update_trigger ON "Service";
DROP TRIGGER IF EXISTS service_attribute_change_trigger ON "ServiceAttribute";
DROP TRIGGER IF EXISTS attribute_change_trigger ON "Attribute";
-- Drop existing functions
DROP FUNCTION IF EXISTS calculate_service_scores();
DROP FUNCTION IF EXISTS calculate_privacy_score();
DROP FUNCTION IF EXISTS calculate_trust_score();
DROP FUNCTION IF EXISTS calculate_overall_score();
DROP FUNCTION IF EXISTS recalculate_scores_for_attribute();
-- Calculate privacy score based on service attributes and properties
CREATE OR REPLACE FUNCTION calculate_privacy_score(service_id INT)
RETURNS INT AS $$
DECLARE
privacy_score INT := 50; -- Start from middle value (50)
kyc_factor INT;
onion_factor INT := 0;
i2p_factor INT := 0;
monero_factor INT := 0;
open_source_factor INT := 0;
p2p_factor INT := 0;
decentralized_factor INT := 0;
attributes_score INT := 0;
BEGIN
-- Get service data
SELECT
CASE
WHEN "kycLevel" = 0 THEN 25 -- No KYC is best for privacy
WHEN "kycLevel" = 1 THEN 10 -- Minimal KYC
WHEN "kycLevel" = 2 THEN -5 -- Moderate KYC
WHEN "kycLevel" = 3 THEN -15 -- More KYC
WHEN "kycLevel" = 4 THEN -25 -- Full mandatory KYC
ELSE 0 -- Default to no change
END
INTO kyc_factor
FROM "Service"
WHERE "id" = service_id;
-- Check for onion URLs
IF EXISTS (
SELECT 1 FROM "Service"
WHERE "id" = service_id AND array_length("onionUrls", 1) > 0
) THEN
onion_factor := 5;
END IF;
-- Check for i2p URLs
IF EXISTS (
SELECT 1 FROM "Service"
WHERE "id" = service_id AND array_length("i2pUrls", 1) > 0
) THEN
i2p_factor := 5;
END IF;
-- Check for Monero acceptance
IF EXISTS (
SELECT 1 FROM "Service"
WHERE "id" = service_id AND 'MONERO' = ANY("acceptedCurrencies")
) THEN
monero_factor := 5;
END IF;
-- Calculate score from privacy attributes - directly use the points
SELECT COALESCE(SUM(a."privacyPoints"), 0)
INTO attributes_score
FROM "ServiceAttribute" sa
JOIN "Attribute" a ON sa."attributeId" = a."id"
WHERE sa."serviceId" = service_id AND a."category" = 'PRIVACY';
-- Calculate final privacy score (base 100)
privacy_score := privacy_score + kyc_factor + onion_factor + i2p_factor + monero_factor + open_source_factor + p2p_factor + decentralized_factor + attributes_score;
-- Ensure the score is in reasonable bounds (0-100)
privacy_score := GREATEST(0, LEAST(100, privacy_score));
RETURN privacy_score;
END;
$$ LANGUAGE plpgsql;
-- Calculate trust score based on service attributes and verification status
CREATE OR REPLACE FUNCTION calculate_trust_score(service_id INT)
RETURNS INT AS $$
DECLARE
trust_score INT := 50; -- Start from middle value (50)
verification_factor INT;
attributes_score INT := 0;
BEGIN
-- Get verification status factor
SELECT
CASE
WHEN "verificationStatus" = 'VERIFICATION_SUCCESS' THEN 10
WHEN "verificationStatus" = 'APPROVED' THEN 5
WHEN "verificationStatus" = 'COMMUNITY_CONTRIBUTED' THEN 0
WHEN "verificationStatus" = 'VERIFICATION_FAILED' THEN -50
ELSE 0
END
INTO verification_factor
FROM "Service"
WHERE id = service_id;
-- Calculate score from trust attributes - directly use the points
SELECT COALESCE(SUM(a."trustPoints"), 0)
INTO attributes_score
FROM "ServiceAttribute" sa
JOIN "Attribute" a ON sa."attributeId" = a.id
WHERE sa."serviceId" = service_id AND a.category = 'TRUST';
-- Apply penalty if service was listed within the last 15 days
IF EXISTS (
SELECT 1
FROM "Service"
WHERE id = service_id
AND "listedAt" IS NOT NULL
AND "verificationStatus" = 'APPROVED'
AND (NOW() - "listedAt") <= INTERVAL '15 days'
) THEN
trust_score := trust_score - 10;
-- Update the isRecentlyListed flag to true
UPDATE "Service"
SET "isRecentlyListed" = TRUE
WHERE id = service_id;
ELSE
-- Update the isRecentlyListed flag to false
UPDATE "Service"
SET "isRecentlyListed" = FALSE
WHERE id = service_id;
END IF;
-- Calculate final trust score (base 100)
trust_score := trust_score + verification_factor + attributes_score;
-- Ensure the score is in reasonable bounds (0-100)
trust_score := GREATEST(0, LEAST(100, trust_score));
RETURN trust_score;
END;
$$ LANGUAGE plpgsql;
-- Calculate overall score based on weighted average of privacy and trust scores
CREATE OR REPLACE FUNCTION calculate_overall_score(service_id INT, privacy_score INT, trust_score INT)
RETURNS INT AS $$
DECLARE
overall_score INT;
BEGIN
overall_score := CAST(ROUND(((privacy_score * 0.6) + (trust_score * 0.4)) / 10.0) AS INT);
RETURN GREATEST(0, LEAST(10, overall_score));
END;
$$ LANGUAGE plpgsql;
-- Main function to calculate all scores for a service
CREATE OR REPLACE FUNCTION calculate_service_scores()
RETURNS TRIGGER AS $$
DECLARE
privacy_score INT;
trust_score INT;
overall_score INT;
service_id INT;
BEGIN
-- Determine which service ID to use based on the trigger context and table
IF TG_TABLE_NAME = 'Service' THEN
IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
service_id := NEW."id";
END IF;
ELSIF TG_TABLE_NAME = 'ServiceAttribute' THEN
IF TG_OP = 'DELETE' THEN
service_id := OLD."serviceId";
ELSE -- INSERT or UPDATE
service_id := NEW."serviceId";
END IF;
END IF;
-- Calculate each score
privacy_score := calculate_privacy_score(service_id);
trust_score := calculate_trust_score(service_id);
overall_score := calculate_overall_score(service_id, privacy_score, trust_score);
-- Cap score if service is flagged as scam (verificationStatus = 'VERIFICATION_FAILED')
IF (SELECT "verificationStatus" FROM "Service" WHERE "id" = service_id) = 'VERIFICATION_FAILED' THEN
IF overall_score > 3 THEN
overall_score := 3;
ELSIF overall_score < 0 THEN
overall_score := 0;
END IF;
END IF;
-- Update the service with the new scores
UPDATE "Service"
SET
"privacyScore" = privacy_score,
"trustScore" = trust_score,
"overallScore" = overall_score
WHERE "id" = service_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Create trigger to recalculate scores when service is created or updated
CREATE TRIGGER service_score_update_trigger
AFTER INSERT OR UPDATE
ON "Service"
FOR EACH ROW
WHEN (pg_trigger_depth() < 2) -- Prevent recursive triggering
EXECUTE FUNCTION calculate_service_scores();
-- Create trigger to recalculate scores when service attributes change
CREATE TRIGGER service_attribute_change_trigger
AFTER INSERT OR UPDATE OR DELETE
ON "ServiceAttribute"
FOR EACH ROW
WHEN (pg_trigger_depth() < 2) -- Prevent recursive triggering
EXECUTE FUNCTION calculate_service_scores();
-- Function to queue score recalculation for all services with a specific attribute
CREATE OR REPLACE FUNCTION queue_service_score_recalculation_for_attribute()
RETURNS TRIGGER AS $$
DECLARE
service_rec RECORD;
BEGIN
-- Only trigger recalculation if relevant fields changed
IF (TG_OP = 'UPDATE' AND (
OLD."privacyPoints" != NEW."privacyPoints" OR
OLD."trustPoints" != NEW."trustPoints" OR
OLD."type" != NEW."type" OR
OLD."category" != NEW."category"
)) THEN
-- Find all services that have this attribute and queue a recalculation job
FOR service_rec IN
SELECT DISTINCT sa."serviceId"
FROM "ServiceAttribute" sa
WHERE sa."attributeId" = NEW.id
LOOP
-- Insert a job into the queue table
-- ON CONFLICT clause ensures we don't queue the same service multiple times per transaction
INSERT INTO "ServiceScoreRecalculationJob" ("serviceId", "createdAt", "processedAt")
VALUES (service_rec."serviceId", NOW(), NULL)
ON CONFLICT ("serviceId") DO UPDATE SET "processedAt" = NULL, "createdAt" = NOW();
END LOOP;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Create constraint trigger to queue score recalculation when attributes are updated
DROP TRIGGER IF EXISTS attribute_change_trigger ON "Attribute";
CREATE CONSTRAINT TRIGGER attribute_change_trigger
AFTER UPDATE
ON "Attribute"
DEFERRABLE INITIALLY DEFERRED
FOR EACH ROW
WHEN (pg_trigger_depth() < 2)
EXECUTE FUNCTION queue_service_score_recalculation_for_attribute();

View File

@@ -1,57 +0,0 @@
-- This script defines a PostgreSQL function and trigger to automatically calculate
-- and update the average user rating for services based on associated comments.
-- The average rating is recalculated whenever comments are added, updated, or deleted.
-- Drop existing triggers first
DROP TRIGGER IF EXISTS comment_average_rating_trigger ON "Comment";
-- Drop existing functions
DROP FUNCTION IF EXISTS calculate_average_rating();
-- Calculate average rating based on active comments with approved or verified status
CREATE OR REPLACE FUNCTION calculate_average_rating()
RETURNS TRIGGER AS $$
DECLARE
affected_service_id INT;
average_user_rating DECIMAL;
BEGIN
-- Determine which service ID to use based on the trigger context
IF TG_OP = 'DELETE' THEN
affected_service_id := OLD."serviceId";
ELSE -- INSERT or UPDATE
affected_service_id := NEW."serviceId";
END IF;
-- Calculate average rating from active comments with approved or verified status
-- Excluding suspicious comments and replies (comments with parentId not null)
SELECT AVG(rating) INTO average_user_rating
FROM "Comment"
WHERE "serviceId" = affected_service_id
AND "parentId" IS NULL
AND rating IS NOT NULL
AND (status = 'APPROVED' OR status = 'VERIFIED')
AND "ratingActive" = true
AND suspicious = false;
-- Update the service with the new average rating
UPDATE "Service"
SET "averageUserRating" = average_user_rating
WHERE "id" = affected_service_id;
-- Return the appropriate record based on operation
IF TG_OP = 'DELETE' THEN
RETURN OLD;
ELSE
RETURN NEW;
END IF;
END;
$$ LANGUAGE plpgsql;
-- Create trigger to recalculate average rating when comments are created, updated, or deleted
CREATE TRIGGER comment_average_rating_trigger
AFTER INSERT OR UPDATE OR DELETE
ON "Comment"
FOR EACH ROW
WHEN (pg_trigger_depth() < 2) -- Prevent recursive triggering
EXECUTE FUNCTION calculate_average_rating();

View File

@@ -1,48 +0,0 @@
-- This script manages the `listedAt`, `verifiedAt`, and `isRecentlyListed` timestamps
-- for services based on changes to their `verificationStatus`. It ensures these timestamps
-- are set or cleared appropriately when a service's verification status is updated.
CREATE OR REPLACE FUNCTION manage_service_timestamps()
RETURNS TRIGGER AS $$
BEGIN
-- Manage listedAt timestamp
IF NEW."verificationStatus" IN ('APPROVED', 'VERIFICATION_SUCCESS') THEN
-- Set listedAt only on the first time status becomes APPROVED or VERIFICATION_SUCCESS
IF OLD."listedAt" IS NULL THEN
NEW."listedAt" := NOW();
NEW."isRecentlyListed" := TRUE;
END IF;
ELSIF OLD."verificationStatus" IN ('APPROVED', 'VERIFICATION_SUCCESS') THEN
-- Clear listedAt if the status changes FROM APPROVED or VERIFICATION_SUCCESS to something else
-- The trigger's WHEN clause ensures NEW."verificationStatus" is different.
NEW."listedAt" := NULL;
NEW."isRecentlyListed" := FALSE;
END IF;
-- Manage verifiedAt timestamp
IF NEW."verificationStatus" = 'VERIFICATION_SUCCESS' THEN
-- Set verifiedAt when status changes TO VERIFICATION_SUCCESS
NEW."verifiedAt" := NOW();
NEW."isRecentlyListed" := FALSE;
ELSIF OLD."verificationStatus" = 'VERIFICATION_SUCCESS' THEN
-- Clear verifiedAt when status changes FROM VERIFICATION_SUCCESS
-- The trigger's WHEN clause ensures NEW."verificationStatus" is different.
NEW."verifiedAt" := NULL;
NEW."isRecentlyListed" := FALSE;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Drop the old trigger first if it exists under the old name
DROP TRIGGER IF EXISTS trigger_set_service_listed_at ON "Service";
-- Drop the trigger if it exists under the new name
DROP TRIGGER IF EXISTS trigger_manage_service_timestamps ON "Service";
CREATE TRIGGER trigger_manage_service_timestamps
BEFORE UPDATE OF "verificationStatus" ON "Service"
FOR EACH ROW
-- Only execute the function if the verificationStatus value has actually changed
WHEN (OLD."verificationStatus" IS DISTINCT FROM NEW."verificationStatus")
EXECUTE FUNCTION manage_service_timestamps();

View File

@@ -1,399 +0,0 @@
-- Service Events Trigger
-- This trigger automatically creates events when services are updated
-- to track important changes over time
CREATE OR REPLACE FUNCTION trigger_service_events()
RETURNS TRIGGER AS $$
DECLARE
change_descriptions TEXT[] := '{}';
event_title TEXT;
event_content TEXT;
change_type TEXT := NULL;
event_time TIMESTAMP WITH TIME ZONE := transaction_timestamp();
currency_desc TEXT;
BEGIN
-- Only proceed if this is an UPDATE operation
IF TG_OP <> 'UPDATE' THEN
RETURN NEW;
END IF;
-- Check for domain/URL changes
IF OLD."serviceUrls" IS DISTINCT FROM NEW."serviceUrls" THEN
change_descriptions := array_append(change_descriptions,
'Service URLs updated from ' || array_to_string(OLD."serviceUrls", ', ') ||
' to ' || array_to_string(NEW."serviceUrls", ', ')
);
change_type := COALESCE(change_type, 'Domain change');
END IF;
-- Check for KYC level changes
IF OLD."kycLevel" IS DISTINCT FROM NEW."kycLevel" THEN
change_descriptions := array_append(change_descriptions,
'KYC level changed from ' || OLD."kycLevel"::TEXT || ' to ' || NEW."kycLevel"::TEXT
);
change_type := COALESCE(change_type, 'KYC update');
END IF;
-- Check for verification status changes
IF OLD."verificationStatus" IS DISTINCT FROM NEW."verificationStatus" THEN
change_descriptions := array_append(change_descriptions,
'Verification status changed from ' || OLD."verificationStatus"::TEXT || ' to ' || NEW."verificationStatus"::TEXT
);
change_type := COALESCE(change_type, 'Verification update');
END IF;
-- Check for description changes
IF OLD.description IS DISTINCT FROM NEW.description THEN
change_descriptions := array_append(change_descriptions, 'Description was updated');
change_type := COALESCE(change_type, 'Description update');
END IF;
-- Check for currency changes
IF OLD."acceptedCurrencies" IS DISTINCT FROM NEW."acceptedCurrencies" THEN
-- Find currencies added
WITH
old_currencies AS (SELECT unnest(OLD."acceptedCurrencies") AS currency),
new_currencies AS (SELECT unnest(NEW."acceptedCurrencies") AS currency),
added_currencies AS (
SELECT currency FROM new_currencies
EXCEPT
SELECT currency FROM old_currencies
),
removed_currencies AS (
SELECT currency FROM old_currencies
EXCEPT
SELECT currency FROM new_currencies
)
-- Temp variable for currency description
SELECT
CASE
WHEN (SELECT COUNT(*) FROM added_currencies) > 0 AND (SELECT COUNT(*) FROM removed_currencies) > 0 THEN
'Currencies updated: added ' || array_to_string(ARRAY(SELECT currency FROM added_currencies), ', ') ||
', removed ' || array_to_string(ARRAY(SELECT currency FROM removed_currencies), ', ')
WHEN (SELECT COUNT(*) FROM added_currencies) > 0 THEN
'Added currencies: ' || array_to_string(ARRAY(SELECT currency FROM added_currencies), ', ')
WHEN (SELECT COUNT(*) FROM removed_currencies) > 0 THEN
'Removed currencies: ' || array_to_string(ARRAY(SELECT currency FROM removed_currencies), ', ')
ELSE
'Currencies changed'
END
INTO currency_desc;
IF currency_desc IS NOT NULL AND currency_desc <> '' THEN
change_descriptions := array_append(change_descriptions, currency_desc);
change_type := COALESCE(change_type, 'Currency update');
END IF;
END IF;
-- If there are changes, create an event
IF array_length(change_descriptions, 1) > 0 THEN
-- Create a title based on number of changes
IF array_length(change_descriptions, 1) = 1 THEN
event_title := COALESCE(change_type, 'Service updated'); -- Ensure title is not null
ELSE
event_title := 'Service updated';
END IF;
-- Create content with all changes
event_content := array_to_string(change_descriptions, '. ');
-- Ensure content is not null or empty
IF event_content IS NULL OR event_content = '' THEN
event_content := 'Service details changed (content unavailable)';
END IF;
-- Insert the event
INSERT INTO "Event" (
"title",
"content",
"type",
"visible",
"startedAt",
"endedAt",
"serviceId"
) VALUES (
event_title,
event_content,
'UPDATE',
TRUE,
event_time,
event_time,
NEW.id
);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Create a trigger for service updates
DROP TRIGGER IF EXISTS service_events_trigger ON "Service";
CREATE TRIGGER service_events_trigger
AFTER UPDATE OF "serviceUrls", "kycLevel", "verificationStatus", "description", "acceptedCurrencies" ON "Service"
FOR EACH ROW
EXECUTE FUNCTION trigger_service_events();
-- Additional trigger to monitor changes to ServiceAttribute
CREATE OR REPLACE FUNCTION trigger_service_attribute_events()
RETURNS TRIGGER AS $$
DECLARE
attribute_name TEXT;
service_name TEXT;
event_title TEXT := 'Attribute change'; -- Default title
event_content TEXT;
event_time TIMESTAMP WITH TIME ZONE := transaction_timestamp();
target_service_id INT;
service_exists BOOLEAN;
service_created_at TIMESTAMP WITH TIME ZONE;
is_new_service BOOLEAN := FALSE;
BEGIN
-- Determine target service ID and operation type
IF TG_OP = 'INSERT' THEN
target_service_id := NEW."serviceId";
-- Check if this is a new service (created within the last minute)
-- This helps prevent events when attributes are initially added to a new service
SELECT "createdAt" INTO service_created_at FROM "Service" WHERE id = target_service_id;
IF service_created_at IS NOT NULL AND (event_time - service_created_at) < INTERVAL '1 minute' THEN
is_new_service := TRUE;
RETURN NEW; -- Skip event creation for new services
END IF;
SELECT title INTO attribute_name FROM "Attribute" WHERE id = NEW."attributeId";
SELECT name INTO service_name FROM "Service" WHERE id = target_service_id;
IF attribute_name IS NOT NULL AND service_name IS NOT NULL THEN
event_title := 'Attribute added';
event_content := 'Attribute "' || attribute_name || '" was added to ' || service_name;
ELSE
event_content := 'An attribute was added (details unavailable)';
END IF;
ELSIF TG_OP = 'DELETE' THEN
target_service_id := OLD."serviceId";
-- Check if the service still exists before trying to fetch its name or create an event
SELECT EXISTS (SELECT 1 FROM "Service" WHERE id = target_service_id) INTO service_exists;
IF service_exists THEN
SELECT title INTO attribute_name FROM "Attribute" WHERE id = OLD."attributeId";
SELECT name INTO service_name FROM "Service" WHERE id = target_service_id;
IF attribute_name IS NOT NULL AND service_name IS NOT NULL THEN
event_title := 'Attribute removed';
event_content := 'Attribute "' || attribute_name || '" was removed from ' || service_name;
ELSE
-- This case might happen if attribute was deleted concurrently
event_content := 'An attribute was removed (details unavailable)';
END IF;
ELSE
-- Service was deleted, don't create an event
RETURN OLD;
END IF;
END IF;
-- Ensure content is not null/empty and insert
IF event_content IS NOT NULL AND event_content <> '' AND target_service_id IS NOT NULL AND NOT is_new_service THEN
-- Re-check service existence right before insert just in case of concurrency on INSERT
IF TG_OP = 'INSERT' THEN
SELECT EXISTS (SELECT 1 FROM "Service" WHERE id = target_service_id) INTO service_exists;
END IF;
IF service_exists THEN
INSERT INTO "Event" (
"title",
"content",
"type",
"visible",
"startedAt",
"endedAt",
"serviceId"
) VALUES (
event_title,
event_content,
'UPDATE',
TRUE,
event_time,
event_time,
target_service_id
);
END IF;
END IF;
-- Return appropriate record
IF TG_OP = 'INSERT' THEN
RETURN NEW;
ELSE
RETURN OLD;
END IF;
END;
$$ LANGUAGE plpgsql;
-- Create a trigger for service attribute changes
DROP TRIGGER IF EXISTS service_attribute_events_trigger ON "ServiceAttribute";
CREATE TRIGGER service_attribute_events_trigger
AFTER INSERT OR DELETE ON "ServiceAttribute"
FOR EACH ROW
EXECUTE FUNCTION trigger_service_attribute_events();
-- Additional trigger to monitor changes to service categories
CREATE OR REPLACE FUNCTION trigger_service_category_events()
RETURNS TRIGGER AS $$
DECLARE
category_name TEXT;
service_name TEXT;
event_title TEXT := 'Category change'; -- Default title
event_content TEXT;
event_time TIMESTAMP WITH TIME ZONE := transaction_timestamp();
target_service_id INT;
service_exists BOOLEAN;
service_created_at TIMESTAMP WITH TIME ZONE;
is_new_service BOOLEAN := FALSE;
BEGIN
-- Determine target service ID and operation type
IF TG_OP = 'INSERT' THEN
target_service_id := NEW."A";
-- Check if this is a new service (created within the last minute)
-- This helps prevent events when categories are initially added to a new service
SELECT "createdAt" INTO service_created_at FROM "Service" WHERE id = target_service_id;
IF service_created_at IS NOT NULL AND (event_time - service_created_at) < INTERVAL '1 minute' THEN
is_new_service := TRUE;
RETURN NEW; -- Skip event creation for new services
END IF;
SELECT name INTO category_name FROM "Category" WHERE id = NEW."B";
SELECT name INTO service_name FROM "Service" WHERE id = target_service_id;
IF category_name IS NOT NULL AND service_name IS NOT NULL THEN
event_title := 'Category added';
event_content := 'Category "' || category_name || '" was added to ' || service_name;
ELSE
event_content := 'A category was added (details unavailable)';
END IF;
ELSIF TG_OP = 'DELETE' THEN
target_service_id := OLD."A";
-- Check if the service still exists before trying to fetch its name or create an event
SELECT EXISTS (SELECT 1 FROM "Service" WHERE id = target_service_id) INTO service_exists;
IF service_exists THEN
SELECT name INTO category_name FROM "Category" WHERE id = OLD."B";
SELECT name INTO service_name FROM "Service" WHERE id = target_service_id;
IF category_name IS NOT NULL AND service_name IS NOT NULL THEN
event_title := 'Category removed';
event_content := 'Category "' || category_name || '" was removed from ' || service_name;
ELSE
-- This case might happen if category was deleted concurrently
event_content := 'A category was removed (details unavailable)';
END IF;
ELSE
-- Service was deleted, don't create an event
RETURN OLD;
END IF;
END IF;
-- Ensure content is not null/empty and insert
IF event_content IS NOT NULL AND event_content <> '' AND target_service_id IS NOT NULL AND NOT is_new_service THEN
-- Re-check service existence right before insert just in case of concurrency on INSERT
IF TG_OP = 'INSERT' THEN
SELECT EXISTS (SELECT 1 FROM "Service" WHERE id = target_service_id) INTO service_exists;
END IF;
IF service_exists THEN
INSERT INTO "Event" (
"title",
"content",
"type",
"visible",
"startedAt",
"endedAt",
"serviceId"
) VALUES (
event_title,
event_content,
'UPDATE',
TRUE,
event_time,
event_time,
target_service_id
);
END IF;
END IF;
-- Return appropriate record
IF TG_OP = 'INSERT' THEN
RETURN NEW;
ELSE
RETURN OLD;
END IF;
END;
$$ LANGUAGE plpgsql;
-- Create a trigger for service category changes (on the junction table)
DROP TRIGGER IF EXISTS service_category_events_trigger ON "_ServiceToCategory";
CREATE TRIGGER service_category_events_trigger
AFTER INSERT OR DELETE ON "_ServiceToCategory"
FOR EACH ROW
EXECUTE FUNCTION trigger_service_category_events();
-- Verification Steps Trigger
-- This trigger creates events when verification steps are added or status changes
CREATE OR REPLACE FUNCTION trigger_verification_step_events()
RETURNS TRIGGER AS $$
DECLARE
service_name TEXT;
event_title TEXT;
event_content TEXT;
event_time TIMESTAMP WITH TIME ZONE := transaction_timestamp();
service_exists BOOLEAN;
BEGIN
-- Check if the service exists
SELECT EXISTS (SELECT 1 FROM "Service" WHERE id = NEW."serviceId") INTO service_exists;
IF NOT service_exists THEN
-- Service was deleted or doesn't exist, don't create an event
RETURN NEW;
END IF;
-- Get service name
SELECT name INTO service_name FROM "Service" WHERE id = NEW."serviceId";
-- Handle different operations
IF TG_OP = 'INSERT' THEN
event_title := 'Verification step added';
event_content := '"' || NEW.title || '" was added';
ELSIF TG_OP = 'UPDATE' AND OLD.status IS DISTINCT FROM NEW.status THEN
event_title := 'Verification step ' || replace(lower(NEW.status::TEXT), '_', ' ');
event_content := '"' || NEW.title || '" status changed from ' ||
replace(lower(OLD.status::TEXT), '_', ' ') || ' to ' || replace(lower(NEW.status::TEXT), '_', ' ');
ELSE
-- No relevant changes, exit
RETURN NEW;
END IF;
-- Insert the event
INSERT INTO "Event" (
"title",
"content",
"type",
"visible",
"startedAt",
"endedAt",
"serviceId"
) VALUES (
event_title,
event_content,
'UPDATE',
TRUE,
event_time,
event_time,
NEW."serviceId"
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Create trigger for verification step changes
DROP TRIGGER IF EXISTS verification_step_events_trigger ON "VerificationStep";
CREATE TRIGGER verification_step_events_trigger
AFTER INSERT OR UPDATE OF status ON "VerificationStep"
FOR EACH ROW
EXECUTE FUNCTION trigger_verification_step_events();

View File

@@ -1,227 +0,0 @@
-- Function & Trigger for Root Comment Insertions (Approved/Verified)
CREATE OR REPLACE FUNCTION notify_root_comment_inserted()
RETURNS TRIGGER AS $$
DECLARE
watcher_count INT;
BEGIN
RAISE NOTICE '[notify_root_comment_inserted] Trigger fired for comment ID: %', NEW.id;
WITH watchers AS (
SELECT np."userId", np."enableNotifyPendingRepliesOnWatch"
FROM "_onRootCommentCreatedForServices" rc
JOIN "NotificationPreferences" np ON rc."A" = np."id"
WHERE rc."B" = NEW."serviceId"
AND np."userId" <> NEW."authorId"
)
INSERT INTO "Notification" ("userId", "type", "aboutCommentId")
SELECT w."userId",
'ROOT_COMMENT_CREATED',
NEW."id"
FROM watchers w
WHERE (
NEW.status IN ('APPROVED', 'VERIFIED')
OR (NEW.status = 'PENDING' AND w."enableNotifyPendingRepliesOnWatch")
)
ON CONFLICT DO NOTHING;
RAISE NOTICE '[notify_root_comment_inserted] Inserted % notifications for comment ID: %', FOUND, NEW.id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_notify_root_comment_inserted ON "Comment";
CREATE TRIGGER trg_notify_root_comment_inserted
AFTER INSERT ON "Comment"
FOR EACH ROW
WHEN (NEW."parentId" IS NULL)
EXECUTE FUNCTION notify_root_comment_inserted();
-- Function & Trigger for Reply Comment Insertions
CREATE OR REPLACE FUNCTION notify_reply_comment_inserted()
RETURNS TRIGGER AS $$
BEGIN
WITH watchers AS (
SELECT np."userId", np."enableNotifyPendingRepliesOnWatch"
FROM "_watchedComments" w
JOIN "NotificationPreferences" np ON w."B" = np."id"
WHERE w."A" = NEW."parentId"
AND np."userId" <> NEW."authorId"
)
INSERT INTO "Notification" ("userId", "type", "aboutCommentId")
SELECT w."userId",
'REPLY_COMMENT_CREATED',
NEW."id"
FROM watchers w
WHERE (
NEW.status IN ('APPROVED', 'VERIFIED')
OR (NEW.status = 'PENDING' AND w."enableNotifyPendingRepliesOnWatch")
)
ON CONFLICT DO NOTHING;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_notify_reply_comment_inserted ON "Comment";
CREATE TRIGGER trg_notify_reply_comment_inserted
AFTER INSERT ON "Comment"
FOR EACH ROW
WHEN (NEW."parentId" IS NOT NULL)
EXECUTE FUNCTION notify_reply_comment_inserted();
-- Function & Trigger for Reply Approval/Verification
CREATE OR REPLACE FUNCTION notify_reply_approved()
RETURNS TRIGGER AS $$
BEGIN
WITH watchers AS (
SELECT np."userId"
FROM "_watchedComments" w
JOIN "NotificationPreferences" np ON w."B" = np."id"
WHERE w."A" = NEW."parentId"
AND np."userId" <> NEW."authorId"
)
INSERT INTO "Notification" ("userId", "type", "aboutCommentId")
SELECT w."userId",
'REPLY_COMMENT_CREATED',
NEW."id"
FROM watchers w
ON CONFLICT DO NOTHING;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_notify_reply_approved ON "Comment";
CREATE TRIGGER trg_notify_reply_approved
AFTER UPDATE OF status ON "Comment"
FOR EACH ROW
WHEN (NEW."parentId" IS NOT NULL AND NEW.status IN ('APPROVED', 'VERIFIED') AND OLD.status NOT IN ('APPROVED', 'VERIFIED'))
EXECUTE FUNCTION notify_reply_approved();
DROP TRIGGER IF EXISTS trg_notify_root_approved ON "Comment";
CREATE OR REPLACE FUNCTION notify_root_approved()
RETURNS TRIGGER AS $$
BEGIN
WITH watchers AS (
SELECT np."userId"
FROM "_onRootCommentCreatedForServices" rc
JOIN "NotificationPreferences" np ON rc."A" = np."id"
WHERE rc."B" = NEW."serviceId"
AND np."userId" <> NEW."authorId"
AND NOT np."enableNotifyPendingRepliesOnWatch"
)
INSERT INTO "Notification" ("userId", "type", "aboutCommentId")
SELECT w."userId",
'ROOT_COMMENT_CREATED',
NEW."id"
FROM watchers w
ON CONFLICT DO NOTHING;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_notify_root_approved
AFTER UPDATE OF status ON "Comment"
FOR EACH ROW
WHEN (NEW."parentId" IS NULL AND NEW.status IN ('APPROVED', 'VERIFIED') AND OLD.status NOT IN ('APPROVED', 'VERIFIED'))
EXECUTE FUNCTION notify_root_approved();
-- Function & Trigger for Comment Status Changes (Status, Suspicious, AdminReview)
CREATE OR REPLACE FUNCTION notify_comment_status_changed()
RETURNS TRIGGER AS $$
DECLARE
v_status_change "CommentStatusChange" := NULL;
BEGIN
-- Determine the status change type
IF NEW.status <> OLD.status THEN
IF NEW.status = 'APPROVED' THEN v_status_change := 'STATUS_CHANGED_TO_APPROVED';
ELSIF NEW.status = 'VERIFIED' THEN v_status_change := 'STATUS_CHANGED_TO_VERIFIED';
ELSIF NEW.status = 'REJECTED' THEN v_status_change := 'STATUS_CHANGED_TO_REJECTED';
ELSIF (NEW.status = 'PENDING' OR NEW.status = 'HUMAN_PENDING') AND (OLD.status <> 'PENDING' AND OLD.status <> 'HUMAN_PENDING') THEN v_status_change := 'STATUS_CHANGED_TO_PENDING';
END IF;
ELSIF NEW.suspicious <> OLD.suspicious THEN
IF NEW.suspicious = true THEN v_status_change := 'MARKED_AS_SPAM';
ELSE v_status_change := 'UNMARKED_AS_SPAM';
END IF;
ELSIF NEW."requiresAdminReview" <> OLD."requiresAdminReview" THEN
IF NEW."requiresAdminReview" = true THEN v_status_change := 'MARKED_FOR_ADMIN_REVIEW';
ELSE v_status_change := 'UNMARKED_FOR_ADMIN_REVIEW';
END IF;
END IF;
-- If a relevant status change occurred, notify watchers of THIS comment
IF v_status_change IS NOT NULL THEN
WITH watchers AS (
-- Get all watchers excluding author
SELECT np."userId"
FROM "_watchedComments" w
JOIN "NotificationPreferences" np ON w."B" = np."id"
WHERE w."A" = NEW."id"
AND np."userId" <> NEW."authorId"
AND np."enableOnMyCommentStatusChange"
UNION ALL
-- Add author if they have enabled notifications for their own comments
SELECT np."userId"
FROM "NotificationPreferences" np
WHERE np."userId" = NEW."authorId"
AND np."enableOnMyCommentStatusChange"
)
INSERT INTO "Notification" ("userId", "type", "aboutCommentId", "aboutCommentStatusChange")
SELECT w."userId",
'COMMENT_STATUS_CHANGE',
NEW."id",
v_status_change
FROM watchers w
ON CONFLICT DO NOTHING;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_notify_comment_status_changed ON "Comment";
CREATE TRIGGER trg_notify_comment_status_changed
AFTER UPDATE OF status, suspicious, "requiresAdminReview" ON "Comment"
FOR EACH ROW
WHEN (NEW.status <> OLD.status OR NEW.suspicious <> OLD.suspicious OR NEW."requiresAdminReview" <> OLD."requiresAdminReview")
EXECUTE FUNCTION notify_comment_status_changed();
-- Function & Trigger for Community Note Added
CREATE OR REPLACE FUNCTION notify_community_note_added()
RETURNS TRIGGER AS $$
BEGIN
-- Notify watchers of this specific comment (excluding author)
WITH watchers AS (
SELECT np."userId"
FROM "_watchedComments" w
JOIN "NotificationPreferences" np ON w."B" = np."id"
WHERE w."A" = NEW."id"
AND np."userId" <> NEW."authorId"
)
INSERT INTO "Notification" ("userId", "type", "aboutCommentId")
SELECT w."userId",
'COMMUNITY_NOTE_ADDED',
NEW."id"
FROM watchers w
ON CONFLICT DO NOTHING;
-- Always notify the author
INSERT INTO "Notification" ("userId", "type", "aboutCommentId")
VALUES (NEW."authorId", 'COMMUNITY_NOTE_ADDED', NEW."id")
ON CONFLICT DO NOTHING;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_notify_community_note_added ON "Comment";
CREATE TRIGGER trg_notify_community_note_added
AFTER UPDATE OF "communityNote" ON "Comment"
FOR EACH ROW
WHEN (NEW."communityNote" IS NOT NULL AND NEW."communityNote" <> '' AND (OLD."communityNote" IS NULL OR OLD."communityNote" = ''))
EXECUTE FUNCTION notify_community_note_added();
-- Remove the old monolithic trigger and function definition if they still exist
DROP TRIGGER IF EXISTS comment_notifications_trigger ON "Comment";
DROP FUNCTION IF EXISTS trigger_comment_notifications();

View File

@@ -1,72 +0,0 @@
CREATE OR REPLACE FUNCTION trigger_service_suggestion_notifications()
RETURNS TRIGGER AS $$
DECLARE
suggestion_status_change "ServiceSuggestionStatusChange";
BEGIN
IF TG_OP = 'INSERT' THEN -- Corresponds to ServiceSuggestionMessage insert
-- Notify suggestion author (if not the sender)
INSERT INTO "Notification" ("userId", "type", "aboutServiceSuggestionId", "aboutServiceSuggestionMessageId")
SELECT s."userId", 'SUGGESTION_MESSAGE', NEW."suggestionId", NEW."id"
FROM "ServiceSuggestion" s
WHERE s."id" = NEW."suggestionId"
AND s."userId" <> NEW."userId"
AND NOT EXISTS (
SELECT 1 FROM "Notification" n
WHERE n."userId" = s."userId"
AND n."type" = 'SUGGESTION_MESSAGE'
AND n."aboutServiceSuggestionMessageId" = NEW."id"
);
-- Notify all admins (except the sender), but only if sender is not admin
INSERT INTO "Notification" ("userId", "type", "aboutServiceSuggestionId", "aboutServiceSuggestionMessageId")
SELECT u."id", 'SUGGESTION_MESSAGE', NEW."suggestionId", NEW."id"
FROM "User" u
WHERE u."admin" = true
AND u."id" <> NEW."userId"
-- Only notify admins if the message sender is not an admin
AND NOT EXISTS (SELECT 1 FROM "User" WHERE "id" = NEW."userId" AND "admin" = true)
AND NOT EXISTS (
SELECT 1 FROM "Notification" n
WHERE n."userId" = u."id"
AND n."type" = 'SUGGESTION_MESSAGE'
AND n."aboutServiceSuggestionMessageId" = NEW."id"
);
ELSIF TG_OP = 'UPDATE' THEN -- Corresponds to ServiceSuggestion status update
-- Notify suggestion author about status change
IF NEW.status <> OLD.status THEN
IF NEW.status = 'PENDING' THEN
suggestion_status_change := 'STATUS_CHANGED_TO_PENDING';
ELSIF NEW.status = 'APPROVED' THEN
suggestion_status_change := 'STATUS_CHANGED_TO_APPROVED';
ELSIF NEW.status = 'REJECTED' THEN
suggestion_status_change := 'STATUS_CHANGED_TO_REJECTED';
ELSIF NEW.status = 'WITHDRAWN' THEN
suggestion_status_change := 'STATUS_CHANGED_TO_WITHDRAWN';
END IF;
INSERT INTO "Notification" ("userId", "type", "aboutServiceSuggestionId", "aboutSuggestionStatusChange")
VALUES (NEW."userId", 'SUGGESTION_STATUS_CHANGE', NEW."id", suggestion_status_change);
END IF;
END IF;
-- Use RETURN NULL for AFTER triggers as the return value is ignored.
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
-- Trigger for new messages
DROP TRIGGER IF EXISTS service_suggestion_message_notifications_trigger ON "ServiceSuggestionMessage";
CREATE TRIGGER service_suggestion_message_notifications_trigger
AFTER INSERT ON "ServiceSuggestionMessage"
FOR EACH ROW
EXECUTE FUNCTION trigger_service_suggestion_notifications();
-- Trigger for status updates
DROP TRIGGER IF EXISTS service_suggestion_status_notifications_trigger ON "ServiceSuggestion";
CREATE TRIGGER service_suggestion_status_notifications_trigger
AFTER UPDATE OF status ON "ServiceSuggestion"
FOR EACH ROW
-- Only run the function if the status actually changed
WHEN (OLD.status IS DISTINCT FROM NEW.status)
EXECUTE FUNCTION trigger_service_suggestion_notifications();

View File

@@ -1,28 +0,0 @@
CREATE OR REPLACE FUNCTION trigger_service_events_notifications()
RETURNS TRIGGER AS $$
BEGIN
-- Handle new Event insertions
IF TG_TABLE_NAME = 'Event' AND TG_OP = 'INSERT' THEN
INSERT INTO "Notification" ("userId", "type", "aboutServiceId", "aboutEventId")
SELECT np."userId", 'EVENT_CREATED', NEW."serviceId", NEW.id
FROM "_onEventCreatedForServices" oes
JOIN "NotificationPreferences" np ON oes."A" = np.id
WHERE oes."B" = NEW."serviceId"
AND NOT EXISTS (
SELECT 1 FROM "Notification" n
WHERE n."userId" = np."userId"
AND n."type" = 'EVENT_CREATED'
AND n."aboutEventId" = NEW.id
);
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
-- Trigger for new Events
DROP TRIGGER IF EXISTS eVENT_CREATED_notifications_trigger ON "Event";
CREATE TRIGGER eVENT_CREATED_notifications_trigger
AFTER INSERT ON "Event"
FOR EACH ROW
EXECUTE FUNCTION trigger_service_events_notifications();

View File

@@ -1,37 +0,0 @@
CREATE OR REPLACE FUNCTION trigger_service_verification_status_change_notifications()
RETURNS TRIGGER AS $$
DECLARE
v_status_change "ServiceVerificationStatusChange";
BEGIN
-- Check if verificationStatus actually changed
IF OLD."verificationStatus" IS DISTINCT FROM NEW."verificationStatus" THEN
-- Determine the correct ServiceVerificationStatusChange enum value
SELECT CASE NEW."verificationStatus"
WHEN 'COMMUNITY_CONTRIBUTED' THEN 'STATUS_CHANGED_TO_COMMUNITY_CONTRIBUTED'
WHEN 'APPROVED' THEN 'STATUS_CHANGED_TO_APPROVED'
WHEN 'VERIFICATION_SUCCESS' THEN 'STATUS_CHANGED_TO_VERIFICATION_SUCCESS'
WHEN 'VERIFICATION_FAILED' THEN 'STATUS_CHANGED_TO_VERIFICATION_FAILED'
ELSE NULL
END
INTO v_status_change;
-- Only insert if we determined a valid status change enum
IF v_status_change IS NOT NULL THEN
INSERT INTO "Notification" ("userId", "type", "aboutServiceId", "aboutServiceVerificationStatusChange")
SELECT np."userId", 'SERVICE_VERIFICATION_STATUS_CHANGE', NEW.id, v_status_change
FROM "_onVerificationChangeForServices" oes
JOIN "NotificationPreferences" np ON oes."A" = np.id -- A -> NotificationPreferences.id
WHERE oes."B" = NEW.id; -- B -> Service.id
END IF;
END IF;
RETURN NULL; -- Return NULL for AFTER trigger
END;
$$ LANGUAGE plpgsql;
-- Trigger for Service verificationStatus updates
DROP TRIGGER IF EXISTS service_verification_status_change_notifications_trigger ON "Service";
CREATE TRIGGER service_verification_status_change_notifications_trigger
AFTER UPDATE ON "Service"
FOR EACH ROW
EXECUTE FUNCTION trigger_service_verification_status_change_notifications();

View File

@@ -1,62 +0,0 @@
CREATE OR REPLACE FUNCTION trigger_user_status_change_notifications()
RETURNS TRIGGER AS $$
DECLARE
status_change "AccountStatusChange";
BEGIN
-- Check for admin status change
IF OLD.admin IS DISTINCT FROM NEW.admin THEN
IF NEW.admin = true THEN
status_change := 'ADMIN_TRUE';
ELSE
status_change := 'ADMIN_FALSE';
END IF;
INSERT INTO "Notification" ("userId", "type", "aboutAccountStatusChange")
VALUES (NEW.id, 'ACCOUNT_STATUS_CHANGE', status_change);
END IF;
-- Check for verified status change
IF OLD.verified IS DISTINCT FROM NEW.verified THEN
IF NEW.verified = true THEN
status_change := 'VERIFIED_TRUE';
ELSE
status_change := 'VERIFIED_FALSE';
END IF;
INSERT INTO "Notification" ("userId", "type", "aboutAccountStatusChange")
VALUES (NEW.id, 'ACCOUNT_STATUS_CHANGE', status_change);
END IF;
-- Check for verifier status change
IF OLD.verifier IS DISTINCT FROM NEW.verifier THEN
IF NEW.verifier = true THEN
status_change := 'VERIFIER_TRUE';
ELSE
status_change := 'VERIFIER_FALSE';
END IF;
INSERT INTO "Notification" ("userId", "type", "aboutAccountStatusChange")
VALUES (NEW.id, 'ACCOUNT_STATUS_CHANGE', status_change);
END IF;
-- Check for spammer status change
IF OLD.spammer IS DISTINCT FROM NEW.spammer THEN
IF NEW.spammer = true THEN
status_change := 'SPAMMER_TRUE';
ELSE
status_change := 'SPAMMER_FALSE';
END IF;
INSERT INTO "Notification" ("userId", "type", "aboutAccountStatusChange")
VALUES (NEW.id, 'ACCOUNT_STATUS_CHANGE', status_change);
END IF;
-- Return NULL for AFTER triggers as the return value is ignored.
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
-- Drop the trigger if it exists to ensure a clean setup
DROP TRIGGER IF EXISTS user_status_change_notifications_trigger ON "User";
-- Create the trigger to fire after updates on specific status columns
CREATE TRIGGER user_status_change_notifications_trigger
AFTER UPDATE OF admin, verified, verifier, spammer ON "User"
FOR EACH ROW
EXECUTE FUNCTION trigger_user_status_change_notifications();