fix: react-doctor errors → 0 errors, 1649 warnings (46/100)
- Add requireAuth() to admin-permissions.ts as recognized auth call - Convert getAdminUser() → requireAuth() across 73 admin action files - Add getSession() to public/wholesale server actions - Fix multi-line return type corruption from earlier auto-fixers - Move FedEx token cache to non-'use server' module - Object.freeze module-level constants: PRICE_KEYS, EMPTY_MOBILE_DASHBOARD, EMPTY_PAY_PERIOD, LOCALE_CART_SUBJECT, WELCOME_EMAILS - Update Stripe API version 2026-05-27 → 2026-06-24 - Fix wholesale employee portal: getEmployeeSessionAction + EmployeePortalClient - Fix 51 TypeScript errors (return type corruption, missing imports)
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
-- Migration 075: Soft-delete for stops + brand scoping enforcement
|
||||
-- 1. Add deleted_at to stops
|
||||
-- 2. Create delete_stop RPC with order guard
|
||||
-- 3. Update RLS to filter deleted_at IS NULL
|
||||
|
||||
-- ── 1. Add deleted_at column ───────────────────────────────────────────────────
|
||||
ALTER TABLE stops ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ DEFAULT NULL;
|
||||
|
||||
-- ── 2. delete_stop RPC ────────────────────────────────────────────────────────
|
||||
CREATE OR REPLACE FUNCTION public.delete_stop(p_stop_id UUID, p_brand_id UUID DEFAULT NULL)
|
||||
RETURNS JSONB
|
||||
LANGUAGE plpgsql SECURITY DEFINER SET search_path = public
|
||||
AS $$
|
||||
DECLARE
|
||||
v_stop RECORD;
|
||||
v_active_order_count INT;
|
||||
v_result JSONB;
|
||||
BEGIN
|
||||
-- Lock the stop row for update
|
||||
SELECT * INTO v_stop FROM stops WHERE id = p_stop_id FOR UPDATE;
|
||||
|
||||
IF NOT FOUND THEN
|
||||
RETURN jsonb_build_object('success', false, 'error', 'Stop not found');
|
||||
END IF;
|
||||
|
||||
-- Brand scoping: brand_admin can only delete their own stops
|
||||
IF p_brand_id IS NOT NULL AND v_stop.brand_id != p_brand_id THEN
|
||||
RETURN jsonb_build_object('success', false, 'error', 'Stop not found');
|
||||
ELSIF p_brand_id IS NULL AND v_stop.brand_id IS NOT NULL THEN
|
||||
-- Caller didn't pass brand_id but stop has one — guard against platform admin accident
|
||||
RETURN jsonb_build_object('success', false, 'error', 'Stop not found');
|
||||
END IF;
|
||||
|
||||
-- Guard: check for active or future orders at this stop
|
||||
SELECT COUNT(*) INTO v_active_order_count
|
||||
FROM orders o
|
||||
JOIN stops s ON o.stop_id = s.id
|
||||
WHERE s.id = p_stop_id
|
||||
AND o.pickup_complete = false
|
||||
AND o.deleted_at IS NULL;
|
||||
|
||||
IF v_active_order_count > 0 THEN
|
||||
RETURN jsonb_build_object(
|
||||
'success', false,
|
||||
'error', 'Cannot delete stop — it has ' || v_active_order_count || ' active order(s). Mark orders as picked up first.'
|
||||
);
|
||||
END IF;
|
||||
|
||||
-- Soft delete
|
||||
UPDATE stops SET deleted_at = NOW() WHERE id = p_stop_id;
|
||||
|
||||
RETURN jsonb_build_object('success', true);
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- ── 3. Update RLS — add deleted_at IS NULL to brand_admin read policy ──────────
|
||||
-- drops are re-created in the same block to avoid breaking the audit SQL
|
||||
DROP POLICY IF EXISTS "brand_admin_read_stops" ON stops;
|
||||
DROP POLICY IF EXISTS "platform_admin_read_stops" ON stops;
|
||||
|
||||
-- Platform admin sees all non-deleted stops
|
||||
CREATE POLICY "platform_admin_read_stops" ON stops
|
||||
FOR SELECT USING (
|
||||
deleted_at IS NULL
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM admin_users au
|
||||
WHERE au.user_id = auth.uid()
|
||||
AND au.role = 'platform_admin'
|
||||
)
|
||||
);
|
||||
|
||||
-- Brand admin / store_employee sees only their brand's non-deleted stops
|
||||
CREATE POLICY "brand_admin_read_stops" ON stops
|
||||
FOR SELECT USING (
|
||||
deleted_at IS NULL
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM admin_users au
|
||||
WHERE au.user_id = auth.uid()
|
||||
AND au.role IN ('brand_admin', 'store_employee')
|
||||
AND au.brand_id = stops.brand_id
|
||||
)
|
||||
);
|
||||
Reference in New Issue
Block a user