MIF_E31221222/sigap-website/prisma/backups/supabase_migration/20250518035335_remote_schem...

1271 lines
40 KiB
PL/PgSQL

set check_function_bodies = off;
CREATE OR REPLACE FUNCTION gis.nearby_units(lat double precision, lon double precision, max_results integer DEFAULT 5)
RETURNS TABLE(code_unit character varying, name text, type text, address text, district_id character varying, lat_unit double precision, lon_unit double precision, distance_km double precision)
LANGUAGE sql
AS $function$
select
u.code_unit,
u.name,
u.type,
u.address,
u.district_id,
gis.ST_Y(u.location::gis.geometry) as lat_unit,
gis.ST_X(u.location::gis.geometry) as lon_unit,
gis.ST_Distance(
u.location::gis.geography,
gis.ST_SetSRID(gis.ST_MakePoint(lon, lat), 4326)::gis.geography
) / 1000 as distance_km
from units u
order by gis.ST_Distance(
u.location::gis.geography,
gis.ST_SetSRID(gis.ST_MakePoint(lon, lat), 4326)::gis.geography
)
limit max_results
$function$
;
CREATE OR REPLACE FUNCTION gis.update_location_distance_to_unit()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
loc_lat FLOAT;
loc_lng FLOAT;
unit_lat FLOAT;
unit_lng FLOAT;
loc_point gis.GEOGRAPHY;
unit_point gis.GEOGRAPHY;
BEGIN
-- Ambil lat/lng dari location yang baru
SELECT gis.ST_Y(NEW.location::gis.geometry), gis.ST_X(NEW.location::gis.geometry)
INTO loc_lat, loc_lng;
-- Ambil lat/lng dari unit di distrik yang sama
SELECT gis.ST_Y(u.location::gis.geometry), gis.ST_X(u.location::gis.geometry)
INTO unit_lat, unit_lng
FROM units u
WHERE u.district_id = NEW.district_id
LIMIT 1;
-- Jika tidak ada unit di distrik yang sama, kembalikan NEW tanpa perubahan
IF unit_lat IS NULL OR unit_lng IS NULL THEN
RETURN NEW;
END IF;
-- Buat point geography dari lat/lng
loc_point := gis.ST_SetSRID(gis.ST_MakePoint(loc_lng, loc_lat), 4326)::gis.geography;
unit_point := gis.ST_SetSRID(gis.ST_MakePoint(unit_lng, unit_lat), 4326)::gis.geography;
-- Update jaraknya ke kolom distance_to_unit
NEW.distance_to_unit := gis.ST_Distance(loc_point, unit_point) / 1000;
RETURN NEW;
END;
$function$
;
CREATE OR REPLACE FUNCTION gis.update_land_area()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
NEW.land_area := ROUND((ST_Area(NEW.geometry::geography) / 1000000.0)::numeric, 2);
RETURN NEW;
END;
$function$
;
drop trigger if exists "trg_update_land_area" on "public"."geographics";
drop trigger if exists "location_distance_to_unit_trigger" on "public"."locations";
-- Drop the trigger first
drop trigger if exists location_distance_to_unit_trigger on public.locations;
-- Drop all triggers on public.locations that use public.update_location_distance_to_unit()
DO $$
DECLARE
trig record;
BEGIN
FOR trig IN
SELECT tgname
FROM pg_trigger
WHERE tgrelid = 'public.locations'::regclass
AND NOT tgisinternal
AND tgfoid = 'public.update_location_distance_to_unit()'::regprocedure
LOOP
EXECUTE format('DROP TRIGGER IF EXISTS %I ON public.locations;', trig.tgname);
END LOOP;
END
$$;
-- Now drop the trigger function
drop function if exists public.update_location_distance_to_unit();
-- Now we can safely alter the column type
alter table "public"."locations" alter column "location" set data type gis.geography using "location"::gis.geography;
-- Recreate the trigger function
CREATE OR REPLACE FUNCTION public.update_location_distance_to_unit()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
loc_lat FLOAT;
loc_lng FLOAT;
unit_lat FLOAT;
unit_lng FLOAT;
loc_point gis.GEOGRAPHY;
unit_point gis.GEOGRAPHY;
BEGIN
-- Ambil lat/lng dari location yang baru
SELECT gis.ST_Y(NEW.location::gis.geometry), gis.ST_X(NEW.location::gis.geometry)
INTO loc_lat, loc_lng;
-- Ambil lat/lng dari unit di distrik yang sama
SELECT gis.ST_Y(u.location::gis.geometry), gis.ST_X(u.location::gis.geometry)
INTO unit_lat, unit_lng
FROM units u
WHERE u.district_id = NEW.district_id
LIMIT 1;
-- Jika tidak ada unit di distrik yang sama, kembalikan NEW tanpa perubahan
IF unit_lat IS NULL OR unit_lng IS NULL THEN
RETURN NEW;
END IF;
-- Buat point geography dari lat/lng
loc_point := gis.ST_SetSRID(gis.ST_MakePoint(loc_lng, loc_lat), 4326)::gis.geography;
unit_point := gis.ST_SetSRID(gis.ST_MakePoint(unit_lng, unit_lat), 4326)::gis.geography;
-- Update jaraknya ke kolom distance_to_unit
NEW.distance_to_unit := gis.ST_Distance(loc_point, unit_point) / 1000;
RETURN NEW;
END;
$function$
;
-- Recreate the trigger
CREATE TRIGGER location_distance_to_unit_trigger
BEFORE INSERT OR UPDATE OF location ON public.locations
FOR EACH ROW
EXECUTE FUNCTION public.update_location_distance_to_unit();
-- drop policy "give all access to users" on "public"."geographics";
-- Check if constraint exists before attempting to drop it
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM information_schema.table_constraints
WHERE constraint_name = 'unit_statistics_unit_id_fkey'
AND table_name = 'unit_statistics'
) THEN
ALTER TABLE "public"."unit_statistics" DROP CONSTRAINT "unit_statistics_unit_id_fkey";
END IF;
END
$$;
drop view if exists "public"."location_paths";
drop function if exists "public"."update_land_area"();
drop function if exists "public"."update_timestamp"();
alter table "public"."units" drop constraint "units_pkey";
drop index if exists "public"."unit_statistics_unit_id_month_year_key";
drop index if exists "public"."units_pkey";
-- Check if _prisma_migrations table exists before trying to create it
DO $$
BEGIN
IF NOT EXISTS (
SELECT FROM pg_tables
WHERE schemaname = 'public'
AND tablename = '_prisma_migrations'
) THEN
CREATE TABLE "public"."_prisma_migrations" (
"id" character varying(36) not null,
"checksum" character varying(64) not null,
"finished_at" timestamp with time zone,
"migration_name" character varying(255) not null,
"logs" text,
"rolled_back_at" timestamp with time zone,
"started_at" timestamp with time zone not null default now(),
"applied_steps_count" integer not null default 0
);
END IF;
END
$$;
-- Also wrap other table creations with existence checks
DO $$
BEGIN
IF NOT EXISTS (
SELECT FROM pg_tables
WHERE schemaname = 'public'
AND tablename = 'evidence'
) THEN
create table "public"."evidence" (
"incident_id" uuid not null,
"type" character varying(50) not null,
"url" text not null,
"uploaded_at" timestamp(6) with time zone default CURRENT_TIMESTAMP,
"caption" character varying(255),
"description" character varying(255),
"metadata" jsonb,
"id" character varying(20) not null
);
END IF;
END
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT FROM pg_tables
WHERE schemaname = 'public'
AND tablename = 'officers'
) THEN
create table "public"."officers" (
"unit_id" character varying(20) not null,
"role_id" uuid not null,
"nrp" character varying(100) not null,
"name" character varying(100) not null,
"rank" character varying(100),
"position" character varying(100),
"phone" character varying(20),
"email" character varying(255),
"avatar" text,
"valid_until" timestamp(3) without time zone,
"qr_code" text,
"created_at" timestamp(6) with time zone default CURRENT_TIMESTAMP,
"updated_at" timestamp(6) with time zone default CURRENT_TIMESTAMP,
"patrol_unit_id" character varying(100) not null,
"id" uuid not null default gen_random_uuid(),
"banned_reason" character varying(255),
"banned_until" timestamp(3) without time zone,
"is_banned" boolean not null default false,
"panic_strike" integer not null default 0,
"spoofing_attempts" integer not null default 0
);
END IF;
END
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT FROM pg_tables
WHERE schemaname = 'public'
AND tablename = 'panic_button_logs'
) THEN
create table "public"."panic_button_logs" (
"id" uuid not null default gen_random_uuid(),
"user_id" uuid not null,
"officer_id" uuid,
"incident_id" uuid not null,
"timestamp" timestamp(6) with time zone not null
);
END IF;
END
$$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT FROM pg_tables
WHERE schemaname = 'public'
AND tablename = 'patrol_units'
) THEN
create table "public"."patrol_units" (
"unit_id" character varying(20) not null,
"location_id" uuid not null,
"name" character varying(100) not null,
"type" character varying(50) not null,
"status" character varying(50) not null,
"radius" double precision not null,
"created_at" timestamp(6) with time zone not null default CURRENT_TIMESTAMP,
"id" character varying(100) not null
);
END IF;
END
$$;
alter table "public"."crimes" add column "avg_crime" double precision not null default 0;
alter table "public"."crimes" add column "crime_cleared" integer not null default 0;
alter table "public"."crimes" add column "source_type" character varying(100);
alter table "public"."crimes" alter column "year" drop not null;
alter table "public"."geographics" alter column "location" set data type gis.geography using "location"::gis.geography;
alter table "public"."location_logs" alter column "created_at" set default CURRENT_TIMESTAMP;
alter table "public"."location_logs" alter column "created_at" set data type timestamp(6) with time zone using "created_at"::timestamp(6) with time zone;
alter table "public"."location_logs" alter column "location" set data type gis.geography using "location"::gis.geography;
alter table "public"."location_logs" alter column "timestamp" set data type timestamp(6) with time zone using "timestamp"::timestamp(6) with time zone;
alter table "public"."location_logs" alter column "updated_at" set default CURRENT_TIMESTAMP;
alter table "public"."location_logs" alter column "updated_at" set data type timestamp(6) with time zone using "updated_at"::timestamp(6) with time zone;
alter table "public"."locations" add column "distance_to_unit" double precision;
-- First, drop any triggers on the locations table that might use the location column
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM pg_trigger
WHERE tgrelid = 'public.locations'::regclass
AND tgname = 'location_distance_to_unit_trigger'
) THEN
DROP TRIGGER location_distance_to_unit_trigger ON public.locations;
END IF;
END
$$;
-- Now we can safely alter the column type
alter table "public"."locations" alter column "location" set data type gis.geography using "location"::gis.geography;
alter table "public"."profiles" add column "nik" character varying(100) not null default ''::character varying;
-- Safely drop unit_id column if it exists
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'unit_statistics'
AND column_name = 'unit_id'
) THEN
ALTER TABLE "public"."unit_statistics" DROP COLUMN "unit_id";
END IF;
END
$$;
-- Safely add code_unit column if it does not exist
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'unit_statistics'
AND column_name = 'code_unit'
) THEN
ALTER TABLE "public"."unit_statistics" ADD COLUMN "code_unit" character varying(20) NOT NULL;
END IF;
END
$$;
alter table "public"."units" drop column "id";
alter table "public"."units" add column "city_id" character varying(20) not null;
alter table "public"."units" add column "phone" character varying(20);
alter table "public"."units" alter column "district_id" drop not null;
alter table "public"."units" alter column "location" set data type gis.geography using "location"::gis.geography;
alter table "public"."users" add column "banned_reason" character varying(255);
alter table "public"."users" add column "is_banned" boolean not null default false;
alter table "public"."users" add column "panic_strike" integer not null default 0;
alter table "public"."users" add column "spoofing_attempts" integer not null default 0;
CREATE UNIQUE INDEX _prisma_migrations_pkey ON public._prisma_migrations USING btree (id);
CREATE UNIQUE INDEX evidence_id_key ON public.evidence USING btree (id);
CREATE UNIQUE INDEX evidence_pkey ON public.evidence USING btree (id);
CREATE INDEX idx_evidence_incident_id ON public.evidence USING btree (incident_id);
CREATE INDEX idx_officers_name ON public.officers USING btree (name);
CREATE INDEX idx_officers_nrp ON public.officers USING btree (nrp);
CREATE INDEX idx_officers_position ON public.officers USING btree ("position");
CREATE INDEX idx_officers_rank ON public.officers USING btree (rank);
CREATE INDEX idx_officers_unit_id ON public.officers USING btree (unit_id);
CREATE INDEX idx_panic_buttons_user_id ON public.panic_button_logs USING btree (user_id);
CREATE INDEX idx_patrol_units_location_id ON public.patrol_units USING btree (location_id);
CREATE INDEX idx_patrol_units_name ON public.patrol_units USING btree (name);
CREATE INDEX idx_patrol_units_status ON public.patrol_units USING btree (status);
CREATE INDEX idx_patrol_units_type ON public.patrol_units USING btree (type);
CREATE INDEX idx_patrol_units_unit_id ON public.patrol_units USING btree (unit_id);
CREATE INDEX idx_profiles_nik ON public.profiles USING btree (nik);
CREATE UNIQUE INDEX officers_nrp_key ON public.officers USING btree (nrp);
CREATE UNIQUE INDEX officers_pkey ON public.officers USING btree (id);
CREATE UNIQUE INDEX panic_button_logs_pkey ON public.panic_button_logs USING btree (id);
CREATE UNIQUE INDEX patrol_units_id_key ON public.patrol_units USING btree (id);
CREATE UNIQUE INDEX patrol_units_pkey ON public.patrol_units USING btree (id);
CREATE UNIQUE INDEX profiles_nik_key ON public.profiles USING btree (nik);
CREATE UNIQUE INDEX unit_statistics_code_unit_month_year_key ON public.unit_statistics USING btree (code_unit, month, year);
CREATE UNIQUE INDEX units_pkey ON public.units USING btree (code_unit);
alter table "public"."_prisma_migrations" add constraint "_prisma_migrations_pkey" PRIMARY KEY using index "_prisma_migrations_pkey";
alter table "public"."evidence" add constraint "evidence_pkey" PRIMARY KEY using index "evidence_pkey";
alter table "public"."officers" add constraint "officers_pkey" PRIMARY KEY using index "officers_pkey";
alter table "public"."panic_button_logs" add constraint "panic_button_logs_pkey" PRIMARY KEY using index "panic_button_logs_pkey";
alter table "public"."patrol_units" add constraint "patrol_units_pkey" PRIMARY KEY using index "patrol_units_pkey";
alter table "public"."units" add constraint "units_pkey" PRIMARY KEY using index "units_pkey";
alter table "public"."evidence" add constraint "evidence_incident_id_fkey" FOREIGN KEY (incident_id) REFERENCES incident_logs(id) ON UPDATE CASCADE ON DELETE CASCADE not valid;
alter table "public"."evidence" validate constraint "evidence_incident_id_fkey";
alter table "public"."officers" add constraint "officers_patrol_unit_id_fkey" FOREIGN KEY (patrol_unit_id) REFERENCES patrol_units(id) ON UPDATE CASCADE ON DELETE RESTRICT not valid;
alter table "public"."officers" validate constraint "officers_patrol_unit_id_fkey";
alter table "public"."officers" add constraint "officers_role_id_fkey" FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE not valid;
alter table "public"."officers" validate constraint "officers_role_id_fkey";
alter table "public"."officers" add constraint "officers_unit_id_fkey" FOREIGN KEY (unit_id) REFERENCES units(code_unit) ON DELETE CASCADE not valid;
alter table "public"."officers" validate constraint "officers_unit_id_fkey";
alter table "public"."panic_button_logs" add constraint "panic_button_logs_incident_id_fkey" FOREIGN KEY (incident_id) REFERENCES incident_logs(id) ON DELETE CASCADE not valid;
alter table "public"."panic_button_logs" validate constraint "panic_button_logs_incident_id_fkey";
alter table "public"."panic_button_logs" add constraint "panic_button_logs_officer_id_fkey" FOREIGN KEY (officer_id) REFERENCES officers(id) ON DELETE CASCADE not valid;
alter table "public"."panic_button_logs" validate constraint "panic_button_logs_officer_id_fkey";
alter table "public"."panic_button_logs" add constraint "panic_button_logs_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE not valid;
alter table "public"."panic_button_logs" validate constraint "panic_button_logs_user_id_fkey";
alter table "public"."patrol_units" add constraint "patrol_units_location_id_fkey" FOREIGN KEY (location_id) REFERENCES locations(id) ON DELETE CASCADE not valid;
alter table "public"."patrol_units" validate constraint "patrol_units_location_id_fkey";
alter table "public"."patrol_units" add constraint "patrol_units_unit_id_fkey" FOREIGN KEY (unit_id) REFERENCES units(code_unit) ON DELETE CASCADE not valid;
alter table "public"."patrol_units" validate constraint "patrol_units_unit_id_fkey";
alter table "public"."unit_statistics" add constraint "unit_statistics_code_unit_fkey" FOREIGN KEY (code_unit) REFERENCES units(code_unit) ON DELETE CASCADE not valid;
alter table "public"."unit_statistics" validate constraint "unit_statistics_code_unit_fkey";
alter table "public"."units" add constraint "units_city_id_fkey" FOREIGN KEY (city_id) REFERENCES cities(id) ON DELETE CASCADE not valid;
alter table "public"."units" validate constraint "units_city_id_fkey";
set check_function_bodies = off;
CREATE OR REPLACE FUNCTION public.delete_user()
RETURNS void
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path TO ''
AS $function$
begin
delete from auth.users where id = (select auth.uid());
end;
$function$
;
CREATE OR REPLACE FUNCTION public.generate_username(email text)
RETURNS text
LANGUAGE plpgsql
SECURITY DEFINER
AS $function$
DECLARE
result_username TEXT;
username_base TEXT;
random_number INTEGER;
username_exists BOOLEAN;
BEGIN
-- Extract the part before @ from email
username_base := split_part(email, '@', 1);
-- Remove any special characters and replace with underscore
username_base := regexp_replace(username_base, '[^a-zA-Z0-9]', '_', 'g');
-- Generate a random number between 100 and 9999
random_number := floor(random() * 9900 + 100)::integer;
-- Combine the base and random number
result_username := username_base || random_number;
-- Check if username already exists
LOOP
SELECT EXISTS(SELECT 1 FROM public.profiles WHERE username = result_username) INTO username_exists;
IF NOT username_exists THEN
EXIT;
END IF;
-- Generate a different random number
random_number := floor(random() * 9900 + 100)::integer;
result_username := username_base || random_number;
END LOOP;
RETURN result_username;
END;
$function$
;
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
AS $function$
DECLARE
role_id UUID;
officer_role_id UUID;
is_officer BOOLEAN;
officer_data JSONB;
unit_id VARCHAR;
BEGIN
-- Check if the user is registering as an officer
is_officer := FALSE;
-- Check user_metadata for officer flag
IF NEW.raw_user_meta_data ? 'is_officer' THEN
is_officer := (NEW.raw_user_meta_data->>'is_officer')::boolean;
END IF;
IF is_officer THEN
-- Get officer role ID
SELECT id INTO officer_role_id FROM public.roles WHERE name = 'officer' LIMIT 1;
IF officer_role_id IS NULL THEN
RAISE EXCEPTION 'Officer role not found';
END IF;
-- Extract officer data from metadata
officer_data := NEW.raw_user_meta_data->'officer_data';
-- Get unit ID from metadata
unit_id := officer_data->>'unit_id';
IF unit_id IS NULL THEN
RAISE EXCEPTION 'Unit ID is required for officer registration';
END IF;
-- Insert into officers table
INSERT INTO public.officers (
id,
unit_id,
role_id,
nrp,
name,
rank,
position,
phone,
email,
created_at,
updated_at
) VALUES (
NEW.id,
unit_id,
officer_role_id,
officer_data->>'nrp',
COALESCE(officer_data->>'name', NEW.email),
officer_data->>'rank',
officer_data->>'position',
COALESCE(NEW.phone, officer_data->>'phone'),
NEW.email,
NEW.created_at,
NEW.updated_at
);
-- Return early since we've handled the officer case
RETURN NEW;
ELSE
-- Standard user registration - Get viewer role ID
SELECT id INTO role_id FROM public.roles WHERE name = 'viewer' LIMIT 1;
IF role_id IS NULL THEN
RAISE EXCEPTION 'Viewer role not found';
END IF;
-- Insert into users table
INSERT INTO public.users (
id,
roles_id,
email,
phone,
encrypted_password,
invited_at,
confirmed_at,
email_confirmed_at,
recovery_sent_at,
last_sign_in_at,
app_metadata,
user_metadata,
created_at,
updated_at,
banned_until,
is_anonymous
) VALUES (
NEW.id,
role_id,
NEW.email,
NEW.phone,
NEW.encrypted_password,
NEW.invited_at,
NEW.confirmed_at,
NEW.email_confirmed_at,
NEW.recovery_sent_at,
NEW.last_sign_in_at,
NEW.raw_app_meta_data,
NEW.raw_user_meta_data,
NEW.created_at,
NEW.updated_at,
NEW.banned_until,
NEW.is_anonymous
);
-- Insert into profiles table
INSERT INTO public.profiles (
id,
user_id,
avatar,
username,
first_name,
last_name,
bio,
address,
birth_date
) VALUES (
gen_random_uuid(),
NEW.id,
NULL,
public.generate_username(NEW.email),
NULL,
NULL,
NULL,
NULL,
NULL
);
RETURN NEW;
END IF;
END;
$function$
;
CREATE OR REPLACE FUNCTION public.handle_user_delete()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
AS $function$
DECLARE
is_officer BOOLEAN;
BEGIN
-- Check if the user is an officer
is_officer := EXISTS (SELECT 1 FROM public.officers WHERE id = OLD.id);
IF is_officer THEN
-- Delete officer record
DELETE FROM public.officers WHERE id = OLD.id;
ELSE
-- Delete standard user data
DELETE FROM public.profiles WHERE user_id = OLD.id;
DELETE FROM public.users WHERE id = OLD.id;
END IF;
RETURN OLD;
END;
$function$
;
CREATE OR REPLACE FUNCTION public.handle_user_type_change()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
AS $function$
DECLARE
is_officer_before BOOLEAN;
is_officer_after BOOLEAN;
officer_role_id UUID;
viewer_role_id UUID;
officer_data JSONB;
unit_id VARCHAR;
BEGIN
-- Determine officer status before and after update
is_officer_before := EXISTS (SELECT 1 FROM public.officers WHERE id = NEW.id);
-- Check if user_metadata indicates officer status after update
is_officer_after := FALSE;
IF NEW.raw_user_meta_data ? 'is_officer' THEN
is_officer_after := (NEW.raw_user_meta_data->>'is_officer')::boolean;
END IF;
-- If status changed from regular user to officer
IF NOT is_officer_before AND is_officer_after THEN
-- Get officer role ID
SELECT id INTO officer_role_id FROM public.roles WHERE name = 'officer' LIMIT 1;
IF officer_role_id IS NULL THEN
RAISE EXCEPTION 'Officer role not found';
END IF;
-- Extract officer data from metadata
officer_data := NEW.raw_user_meta_data->'officer_data';
-- Get unit ID from metadata
unit_id := officer_data->>'unit_id';
IF unit_id IS NULL THEN
RAISE EXCEPTION 'Unit ID is required for officer registration';
END IF;
-- Insert into officers table
INSERT INTO public.officers (
id,
unit_id,
role_id,
nrp,
name,
rank,
position,
phone,
email,
created_at,
updated_at
) VALUES (
NEW.id,
unit_id,
officer_role_id,
officer_data->>'nrp',
COALESCE(officer_data->>'name', NEW.email),
officer_data->>'rank',
officer_data->>'position',
COALESCE(NEW.phone, officer_data->>'phone'),
NEW.email,
NEW.created_at,
NEW.updated_at
);
-- Delete regular user data
DELETE FROM public.profiles WHERE user_id = NEW.id;
DELETE FROM public.users WHERE id = NEW.id;
-- If status changed from officer to regular user
ELSIF is_officer_before AND NOT is_officer_after THEN
-- Get viewer role ID
SELECT id INTO viewer_role_id FROM public.roles WHERE name = 'viewer' LIMIT 1;
IF viewer_role_id IS NULL THEN
RAISE EXCEPTION 'Viewer role not found';
END IF;
-- Insert into users table
INSERT INTO public.users (
id,
roles_id,
email,
phone,
encrypted_password,
invited_at,
confirmed_at,
email_confirmed_at,
recovery_sent_at,
last_sign_in_at,
app_metadata,
user_metadata,
created_at,
updated_at,
banned_until,
is_anonymous
) VALUES (
NEW.id,
viewer_role_id,
NEW.email,
NEW.phone,
NEW.encrypted_password,
NEW.invited_at,
NEW.confirmed_at,
NEW.email_confirmed_at,
NEW.recovery_sent_at,
NEW.last_sign_in_at,
NEW.raw_app_meta_data,
NEW.raw_user_meta_data,
NEW.created_at,
NEW.updated_at,
NEW.banned_until,
NEW.is_anonymous
);
-- Insert into profiles table
INSERT INTO public.profiles (
id,
user_id,
avatar,
username,
first_name,
last_name,
bio,
address,
birth_date
) VALUES (
gen_random_uuid(),
NEW.id,
NULL,
public.generate_username(NEW.email),
NULL,
NULL,
NULL,
NULL,
NULL
);
-- Delete officer record
DELETE FROM public.officers WHERE id = NEW.id;
END IF;
RETURN NEW;
END;
$function$
;
CREATE OR REPLACE FUNCTION public.handle_user_update()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
AS $function$
DECLARE
is_officer BOOLEAN;
officer_data JSONB;
BEGIN
-- Check if the user is an officer
is_officer := EXISTS (SELECT 1 FROM public.officers WHERE id = NEW.id);
-- Also check if user_metadata indicates officer status (for cases where metadata was updated)
IF NOT is_officer AND NEW.raw_user_meta_data ? 'is_officer' THEN
is_officer := (NEW.raw_user_meta_data->>'is_officer')::boolean;
END IF;
IF is_officer THEN
-- Extract officer data from metadata if it exists
IF NEW.raw_user_meta_data ? 'officer_data' THEN
officer_data := NEW.raw_user_meta_data->'officer_data';
-- Update officer record
UPDATE public.officers
SET
nrp = COALESCE(officer_data->>'nrp', nrp),
name = COALESCE(officer_data->>'name', name),
rank = COALESCE(officer_data->>'rank', rank),
position = COALESCE(officer_data->>'position', position),
phone = COALESCE(NEW.phone, officer_data->>'phone', phone),
email = COALESCE(NEW.email, email),
updated_at = NOW()
WHERE id = NEW.id;
ELSE
-- Basic update with available auth data
UPDATE public.officers
SET
phone = COALESCE(NEW.phone, phone),
email = COALESCE(NEW.email, email),
updated_at = NOW()
WHERE id = NEW.id;
END IF;
ELSE
-- Standard user update
UPDATE public.users
SET
email = COALESCE(NEW.email, email),
phone = COALESCE(NEW.phone, phone),
encrypted_password = COALESCE(NEW.encrypted_password, encrypted_password),
invited_at = COALESCE(NEW.invited_at, invited_at),
confirmed_at = COALESCE(NEW.confirmed_at, confirmed_at),
email_confirmed_at = COALESCE(NEW.email_confirmed_at, email_confirmed_at),
recovery_sent_at = COALESCE(NEW.recovery_sent_at, recovery_sent_at),
last_sign_in_at = COALESCE(NEW.last_sign_in_at, last_sign_in_at),
app_metadata = COALESCE(NEW.raw_app_meta_data, app_metadata),
user_metadata = COALESCE(NEW.raw_user_meta_data, user_metadata),
created_at = COALESCE(NEW.created_at, created_at),
updated_at = NOW(),
banned_until = CASE
WHEN NEW.banned_until IS NULL THEN NULL
ELSE COALESCE(NEW.banned_until, banned_until)
END,
is_anonymous = COALESCE(NEW.is_anonymous, is_anonymous)
WHERE id = NEW.id;
-- Create profile if it doesn't exist
INSERT INTO public.profiles (id, user_id, username)
SELECT gen_random_uuid(), NEW.id, public.generate_username(NEW.email)
WHERE NOT EXISTS (
SELECT 1 FROM public.profiles WHERE user_id = NEW.id
)
ON CONFLICT (user_id) DO NOTHING;
END IF;
RETURN NEW;
END;
$function$
;
CREATE OR REPLACE FUNCTION public.nearby_units(lat double precision, lon double precision, max_results integer DEFAULT 5)
RETURNS TABLE(code_unit character varying, name text, type text, address text, district_id character varying, lat_unit double precision, lon_unit double precision, distance_km double precision)
LANGUAGE sql
AS $function$
select
u.code_unit,
u.name,
u.type,
u.address,
u.district_id,
gis.ST_Y(u.location::gis.geometry) as lat_unit,
gis.ST_X(u.location::gis.geometry) as lon_unit,
gis.ST_Distance(
u.location::gis.geography,
gis.ST_SetSRID(gis.ST_MakePoint(lon, lat), 4326)::gis.geography
) / 1000 as distance_km
from units u
order by gis.ST_Distance(
u.location::gis.geography,
gis.ST_SetSRID(gis.ST_MakePoint(lon, lat), 4326)::gis.geography
)
limit max_results
$function$
;
CREATE OR REPLACE FUNCTION public.update_location_distance_to_unit()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
loc_lat FLOAT;
loc_lng FLOAT;
unit_lat FLOAT;
unit_lng FLOAT;
loc_point gis.GEOGRAPHY;
unit_point gis.GEOGRAPHY;
BEGIN
-- Ambil lat/lng dari location yang baru
SELECT gis.ST_Y(NEW.location::gis.geometry), gis.ST_X(NEW.location::gis.geometry)
INTO loc_lat, loc_lng;
-- Ambil lat/lng dari unit di distrik yang sama
SELECT gis.ST_Y(u.location::gis.geometry), gis.ST_X(u.location::gis.geometry)
INTO unit_lat, unit_lng
FROM units u
WHERE u.district_id = NEW.district_id
LIMIT 1;
-- Jika tidak ada unit di distrik yang sama, kembalikan NEW tanpa perubahan
IF unit_lat IS NULL OR unit_lng IS NULL THEN
RETURN NEW;
END IF;
-- Buat point geography dari lat/lng
loc_point := gis.ST_SetSRID(gis.ST_MakePoint(loc_lng, loc_lat), 4326)::gis.geography;
unit_point := gis.ST_SetSRID(gis.ST_MakePoint(unit_lng, unit_lat), 4326)::gis.geography;
-- Update jaraknya ke kolom distance_to_unit
NEW.distance_to_unit := gis.ST_Distance(loc_point, unit_point) / 1000;
RETURN NEW;
END;
$function$
;
grant delete on table "public"."_prisma_migrations" to "anon";
grant insert on table "public"."_prisma_migrations" to "anon";
grant references on table "public"."_prisma_migrations" to "anon";
grant select on table "public"."_prisma_migrations" to "anon";
grant trigger on table "public"."_prisma_migrations" to "anon";
grant truncate on table "public"."_prisma_migrations" to "anon";
grant update on table "public"."_prisma_migrations" to "anon";
grant delete on table "public"."_prisma_migrations" to "authenticated";
grant insert on table "public"."_prisma_migrations" to "authenticated";
grant references on table "public"."_prisma_migrations" to "authenticated";
grant select on table "public"."_prisma_migrations" to "authenticated";
grant trigger on table "public"."_prisma_migrations" to "authenticated";
grant truncate on table "public"."_prisma_migrations" to "authenticated";
grant update on table "public"."_prisma_migrations" to "authenticated";
grant delete on table "public"."_prisma_migrations" to "prisma";
grant insert on table "public"."_prisma_migrations" to "prisma";
grant references on table "public"."_prisma_migrations" to "prisma";
grant select on table "public"."_prisma_migrations" to "prisma";
grant trigger on table "public"."_prisma_migrations" to "prisma";
grant truncate on table "public"."_prisma_migrations" to "prisma";
grant update on table "public"."_prisma_migrations" to "prisma";
grant delete on table "public"."_prisma_migrations" to "service_role";
grant insert on table "public"."_prisma_migrations" to "service_role";
grant references on table "public"."_prisma_migrations" to "service_role";
grant select on table "public"."_prisma_migrations" to "service_role";
grant trigger on table "public"."_prisma_migrations" to "service_role";
grant truncate on table "public"."_prisma_migrations" to "service_role";
grant update on table "public"."_prisma_migrations" to "service_role";
grant delete on table "public"."evidence" to "anon";
grant insert on table "public"."evidence" to "anon";
grant references on table "public"."evidence" to "anon";
grant select on table "public"."evidence" to "anon";
grant trigger on table "public"."evidence" to "anon";
grant truncate on table "public"."evidence" to "anon";
grant update on table "public"."evidence" to "anon";
grant delete on table "public"."evidence" to "authenticated";
grant insert on table "public"."evidence" to "authenticated";
grant references on table "public"."evidence" to "authenticated";
grant select on table "public"."evidence" to "authenticated";
grant trigger on table "public"."evidence" to "authenticated";
grant truncate on table "public"."evidence" to "authenticated";
grant update on table "public"."evidence" to "authenticated";
grant delete on table "public"."evidence" to "prisma";
grant insert on table "public"."evidence" to "prisma";
grant references on table "public"."evidence" to "prisma";
grant select on table "public"."evidence" to "prisma";
grant trigger on table "public"."evidence" to "prisma";
grant truncate on table "public"."evidence" to "prisma";
grant update on table "public"."evidence" to "prisma";
grant delete on table "public"."evidence" to "service_role";
grant insert on table "public"."evidence" to "service_role";
grant references on table "public"."evidence" to "service_role";
grant select on table "public"."evidence" to "service_role";
grant trigger on table "public"."evidence" to "service_role";
grant truncate on table "public"."evidence" to "service_role";
grant update on table "public"."evidence" to "service_role";
grant delete on table "public"."officers" to "anon";
grant insert on table "public"."officers" to "anon";
grant references on table "public"."officers" to "anon";
grant select on table "public"."officers" to "anon";
grant trigger on table "public"."officers" to "anon";
grant truncate on table "public"."officers" to "anon";
grant update on table "public"."officers" to "anon";
grant delete on table "public"."officers" to "authenticated";
grant insert on table "public"."officers" to "authenticated";
grant references on table "public"."officers" to "authenticated";
grant select on table "public"."officers" to "authenticated";
grant trigger on table "public"."officers" to "authenticated";
grant truncate on table "public"."officers" to "authenticated";
grant update on table "public"."officers" to "authenticated";
grant delete on table "public"."officers" to "prisma";
grant insert on table "public"."officers" to "prisma";
grant references on table "public"."officers" to "prisma";
grant select on table "public"."officers" to "prisma";
grant trigger on table "public"."officers" to "prisma";
grant truncate on table "public"."officers" to "prisma";
grant update on table "public"."officers" to "prisma";
grant delete on table "public"."officers" to "service_role";
grant insert on table "public"."officers" to "service_role";
grant references on table "public"."officers" to "service_role";
grant select on table "public"."officers" to "service_role";
grant trigger on table "public"."officers" to "service_role";
grant truncate on table "public"."officers" to "service_role";
grant update on table "public"."officers" to "service_role";
grant delete on table "public"."panic_button_logs" to "anon";
grant insert on table "public"."panic_button_logs" to "anon";
grant references on table "public"."panic_button_logs" to "anon";
grant select on table "public"."panic_button_logs" to "anon";
grant trigger on table "public"."panic_button_logs" to "anon";
grant truncate on table "public"."panic_button_logs" to "anon";
grant update on table "public"."panic_button_logs" to "anon";
grant delete on table "public"."panic_button_logs" to "authenticated";
grant insert on table "public"."panic_button_logs" to "authenticated";
grant references on table "public"."panic_button_logs" to "authenticated";
grant select on table "public"."panic_button_logs" to "authenticated";
grant trigger on table "public"."panic_button_logs" to "authenticated";
grant truncate on table "public"."panic_button_logs" to "authenticated";
grant update on table "public"."panic_button_logs" to "authenticated";
grant delete on table "public"."panic_button_logs" to "prisma";
grant insert on table "public"."panic_button_logs" to "prisma";
grant references on table "public"."panic_button_logs" to "prisma";
grant select on table "public"."panic_button_logs" to "prisma";
grant trigger on table "public"."panic_button_logs" to "prisma";
grant truncate on table "public"."panic_button_logs" to "prisma";
grant update on table "public"."panic_button_logs" to "prisma";
grant delete on table "public"."patrol_units" to "anon";
grant insert on table "public"."patrol_units" to "anon";
grant references on table "public"."patrol_units" to "anon";
grant select on table "public"."patrol_units" to "anon";
grant trigger on table "public"."patrol_units" to "anon";
grant truncate on table "public"."patrol_units" to "anon";
grant update on table "public"."patrol_units" to "anon";
grant delete on table "public"."patrol_units" to "authenticated";
grant insert on table "public"."patrol_units" to "authenticated";
grant references on table "public"."patrol_units" to "authenticated";
grant select on table "public"."patrol_units" to "authenticated";
grant trigger on table "public"."patrol_units" to "authenticated";
grant truncate on table "public"."patrol_units" to "authenticated";
grant update on table "public"."patrol_units" to "authenticated";
grant delete on table "public"."patrol_units" to "prisma";
grant insert on table "public"."patrol_units" to "prisma";
grant references on table "public"."patrol_units" to "prisma";
grant select on table "public"."patrol_units" to "prisma";
grant trigger on table "public"."patrol_units" to "prisma";
grant truncate on table "public"."patrol_units" to "prisma";
grant update on table "public"."patrol_units" to "prisma";
grant delete on table "public"."patrol_units" to "service_role";
grant insert on table "public"."patrol_units" to "service_role";
grant references on table "public"."patrol_units" to "service_role";
grant select on table "public"."patrol_units" to "service_role";
grant trigger on table "public"."patrol_units" to "service_role";
grant truncate on table "public"."patrol_units" to "service_role";
grant update on table "public"."patrol_units" to "service_role";