Files
webshop/shop/supabase/migrations/20260624000000_integrity_functions.sql
DanielS 5f65f890ee
All checks were successful
Staging Build / build (push) Successful in 3m37s
feat: link end customers directly to companies
2026-07-04 15:17:04 +02:00

196 lines
7.6 KiB
PL/PgSQL

-- CREATE FUNCTION public.check_database_integrity
CREATE OR REPLACE FUNCTION public.check_database_integrity()
RETURNS jsonb
SECURITY DEFINER
AS $$
DECLARE
profiles_exist boolean;
end_customers_exist boolean;
pos_modules_exist boolean;
licenses_exist boolean;
profiles_count bigint := 0;
end_customers_count bigint := 0;
pos_modules_count bigint := 0;
licenses_count bigint := 0;
fk_errors_count bigint := 0;
result jsonb;
BEGIN
-- Check table existence
SELECT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'profiles') INTO profiles_exist;
SELECT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'end_customers') INTO end_customers_exist;
SELECT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'pos_modules') INTO pos_modules_exist;
SELECT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'licenses') INTO licenses_exist;
-- Count rows if table exists
IF profiles_exist THEN
EXECUTE 'SELECT count(*) FROM public.profiles' INTO profiles_count;
END IF;
IF end_customers_exist THEN
EXECUTE 'SELECT count(*) FROM public.end_customers' INTO end_customers_count;
END IF;
IF pos_modules_exist THEN
EXECUTE 'SELECT count(*) FROM public.pos_modules' INTO pos_modules_count;
END IF;
IF licenses_exist THEN
EXECUTE 'SELECT count(*) FROM public.licenses' INTO licenses_count;
END IF;
-- Count foreign key errors (orphaned licenses)
IF licenses_exist AND end_customers_exist THEN
EXECUTE '
SELECT count(*)
FROM public.licenses l
LEFT JOIN public.end_customers c ON l.end_customer_id = c.id
WHERE c.id IS NULL AND l.end_customer_id IS NOT NULL
' INTO fk_errors_count;
END IF;
-- Build JSON result
result := jsonb_build_object(
'tables', jsonb_build_object(
'profiles', jsonb_build_object('exists', profiles_exist, 'count', profiles_count),
'end_customers', jsonb_build_object('exists', end_customers_exist, 'count', end_customers_count),
'pos_modules', jsonb_build_object('exists', pos_modules_exist, 'count', pos_modules_count),
'licenses', jsonb_build_object('exists', licenses_exist, 'count', licenses_count)
),
'errors', jsonb_build_object(
'foreign_keys', fk_errors_count
)
);
RETURN result;
END;
$$ LANGUAGE plpgsql;
-- CREATE FUNCTION public.repair_database_schema
CREATE OR REPLACE FUNCTION public.repair_database_schema()
RETURNS jsonb
SECURITY DEFINER
AS $$
DECLARE
repaired_tables text[] := '{}';
result jsonb;
BEGIN
-- 1. profiles table (should exist, but check just in case)
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'profiles') THEN
CREATE TABLE public.profiles (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
first_name TEXT,
last_name TEXT,
company_name TEXT,
vat_id TEXT,
address TEXT,
city TEXT,
zip_code TEXT,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL
);
repaired_tables := array_append(repaired_tables, 'profiles');
END IF;
-- 2. end_customers table
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'end_customers') THEN
CREATE TABLE public.end_customers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
partner_id UUID NOT NULL REFERENCES public.companies(id) ON DELETE CASCADE,
company_name TEXT NOT NULL,
vat_id TEXT,
first_name TEXT,
last_name TEXT,
street TEXT,
zip TEXT,
city TEXT,
is_anonymized BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
repaired_tables := array_append(repaired_tables, 'end_customers');
END IF;
-- 3. pos_modules table
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'pos_modules') THEN
CREATE TABLE public.pos_modules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
price NUMERIC(10, 2) NOT NULL DEFAULT 0.00,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
repaired_tables := array_append(repaired_tables, 'pos_modules');
END IF;
-- 4. licenses table
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = 'licenses') THEN
CREATE TABLE public.licenses (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
end_customer_id UUID REFERENCES public.end_customers(id) ON DELETE CASCADE,
pos_module_id UUID REFERENCES public.pos_modules(id) ON DELETE CASCADE,
license_key TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
repaired_tables := array_append(repaired_tables, 'licenses');
END IF;
-- Enable RLS on all tables
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.end_customers ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.pos_modules ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.licenses ENABLE ROW LEVEL SECURITY;
-- Recreate RLS Policies
-- profiles Policies
DROP POLICY IF EXISTS "Users can view their own profile" ON public.profiles;
CREATE POLICY "Users can view their own profile" ON public.profiles FOR SELECT USING (auth.uid() = id);
DROP POLICY IF EXISTS "Users can update their own profile" ON public.profiles;
CREATE POLICY "Users can update their own profile" ON public.profiles FOR UPDATE USING (auth.uid() = id);
DROP POLICY IF EXISTS "Users can insert their own profile" ON public.profiles;
CREATE POLICY "Users can insert their own profile" ON public.profiles FOR INSERT WITH CHECK (auth.uid() = id);
DROP POLICY IF EXISTS "Admins can view all profiles" ON public.profiles;
CREATE POLICY "Admins can view all profiles" ON public.profiles FOR SELECT USING (auth.role() = 'authenticated');
-- end_customers Policies
DROP POLICY IF EXISTS "Partner sehen nur eigene Endkunden" ON public.end_customers;
CREATE POLICY "Partner sehen nur eigene Endkunden" ON public.end_customers FOR ALL USING (partner_id = (SELECT company_id FROM public.users WHERE id = auth.uid())) WITH CHECK (partner_id = (SELECT company_id FROM public.users WHERE id = auth.uid()));
-- pos_modules Policies
DROP POLICY IF EXISTS "Allow authenticated read on pos_modules" ON public.pos_modules;
CREATE POLICY "Allow authenticated read on pos_modules" ON public.pos_modules FOR SELECT USING (auth.role() = 'authenticated');
-- licenses Policies
DROP POLICY IF EXISTS "Partner sehen Lizenzen eigener Kunden" ON public.licenses;
CREATE POLICY "Partner sehen Lizenzen eigener Kunden" ON public.licenses FOR SELECT USING (
EXISTS (
SELECT 1 FROM public.end_customers c
WHERE c.id = licenses.end_customer_id AND c.partner_id = (SELECT company_id FROM public.users WHERE id = auth.uid())
)
);
DROP POLICY IF EXISTS "Partner erstellen Lizenzen eigener Kunden" ON public.licenses;
CREATE POLICY "Partner erstellen Lizenzen eigener Kunden" ON public.licenses FOR INSERT WITH CHECK (
EXISTS (
SELECT 1 FROM public.end_customers c
WHERE c.id = licenses.end_customer_id AND c.partner_id = (SELECT company_id FROM public.users WHERE id = auth.uid())
)
);
result := jsonb_build_object(
'repaired', to_jsonb(repaired_tables),
'success', true
);
RETURN result;
END;
$$ LANGUAGE plpgsql;
-- CREATE FUNCTION public.optimize_database_indices
CREATE OR REPLACE FUNCTION public.optimize_database_indices()
RETURNS jsonb
SECURITY DEFINER
AS $$
BEGIN
REINDEX SCHEMA public;
RETURN jsonb_build_object('success', true, 'message', 'Schema public successfully reindexed.');
END;
$$ LANGUAGE plpgsql;