Compare commits

..

3 Commits

Author SHA1 Message Date
DanielS
2d44dd4c58 security: fix 6 critical RLS vulns, SHA-256 hash, DSGVO [GELÖSCHT], completed status
Some checks failed
Staging Build / build (push) Has been cancelled
2026-07-06 22:39:05 +02:00
DanielS
3d938d29fc feat: add skills (pdf-invoice-generator, idempotency-guard, b2b-mail-dispatcher) 2026-07-06 22:33:59 +02:00
DanielS
c07871eac4 feat: add agent skills (rls, wizard, snapshot, gdpr, next15, uiux, humanizer) 2026-07-06 22:27:42 +02:00
17 changed files with 471 additions and 22 deletions

View File

@@ -0,0 +1,8 @@
---
name: b2b-mail-dispatcher
description: Verwaltet den SMTP-E-Mail-Versand bei Statusänderungen von Bestellungen und stellt transaktionale Zuverlässigkeit sicher.
---
Richtlinien für die E-Mail-Kommunikation:
1. Wann immer der Status einer Bestellung in der Datenbank oder via Server Action mutiert wird (`status`-Übergang), triggere ein Event für den E-Mail-Versand.
2. Die E-Mail muss transaktionssicher über die SMTP-Konfiguration gesendet werden und an die im Benutzerprofil hinterlegte Adresse gehen.
3. Bei erfolgreichem Checkout hänge das generierte PDF aus dem Supabase Storage als Datei-Attachment an die E-Mail an.

View File

@@ -0,0 +1,8 @@
---
name: gdpr-anonymizer
description: Setzt die DSGVO-konforme Anonymisierung von Endkunden um, ohne historische Steuerdaten zu beschädigen.
---
Regeln für das Feature `/my-customers` (Anonymisieren):
1. Überschreibe `first_name`, `last_name`, `street`, `zip`, `city`, `email` und alle Bankdaten in `end_customers` hart mit dem String `[GELÖSCHT]`.
2. Setze `is_anonymized = true`.
3. Führe NIEMALS Kaskaden-Löschungen oder Updates auf der Tabelle `orders` aus, wenn ein Kunde anonymisiert wird. Die Snapshots müssen unverändert bleiben.

View File

@@ -0,0 +1,232 @@
---
name: humanizer
description: |
Use when editing or reviewing text to remove AI-generated traces and make it sound more natural and human-like.
For detecting and fixing: AI vocabulary (delve, leverage, tapestry), promotional tone, superficial -ing analyses,
vague attributions, formatting artifacts (curly quotes, excessive bold), and injecting authentic voice.
Based on Wikipedia's "Signs of AI writing" guide maintained by WikiProject AI Cleanup.
---
# Humanizer - Remove AI Writing Traces
You are an expert editor specializing in detecting and removing AI-generated text patterns to make writing sound more natural and human-like.
## Overview
AI-generated text has characteristic patterns that make it detectable. This skill enables you to:
1. **Detect** - Identify AI writing patterns across content, language, style, and formatting
2. **Rewrite** - Replace problematic patterns with natural alternatives
3. **Inject Soul** - Add authentic voice, personality, and human imperfections
> **Core Insight**: "LLMs use statistical algorithms to guess what should come next. The result tends toward the statistically most likely outcome that fits the broadest situation."
---
## Quick Reference: 5 Core Rules
1. **Delete filler phrases** - Remove openers and emphatic crutches
2. **Break formulaic structure** - Avoid binary contrasts, dramatic segmentation
3. **Vary rhythm** - Mix sentence lengths. Two items beat three. Diverse paragraph endings
4. **Trust the reader** - State facts directly, skip softening and hand-holding
5. **Delete quotables** - If it sounds like a pullquote, rewrite it
---
## Detection Protocols
### Content Indicators
| Pattern | Words to Watch | Fix |
|---------|---------------|-----|
| **Inflated significance** | "pivotal moment", "testament to", "indelible mark", "reflects broader" | State facts without commentary |
| **Promotional tone** | "boasts a", "vibrant", "nestled in", "groundbreaking" | Use neutral, specific language |
| **Superficial -ing analysis** | "highlighting...", "ensuring...", "fostering..." | Delete or rewrite as separate sentence |
| **Vague attribution** | "experts argue", "some critics say", "industry reports" | Cite specific sources or remove |
| **Formulaic conclusions** | "Despite challenges...", "Future prospects..." | End with concrete facts |
### Language Indicators
| Pattern | Examples | Fix |
|---------|----------|-----|
| **AI vocabulary** | delve, leverage, tapestry, landscape, foster, underscore | Use simple alternatives (dig into → look at) |
| **Copula avoidance** | "serves as", "stands as", "marks" | Use simple "is" or "are" |
| **Negative parallelism** | "not only... but also", "it's not just about... it's" | Make direct statement |
| **Rule of three** | Three adjectives, three examples, three bullet points | Use two or four items |
| **Elegant variation** | Synonyms to avoid repetition ("protagonist", "hero", "main character") | Repeat naturally or consolidate |
### Formatting Indicators
| Pattern | Detection | Fix |
|---------|-----------|-----|
| **Curly quotes** | "text" instead of "text" | Replace with straight quotes |
| **Excessive bold** | **Every** **keyword** **bolded** | Remove unnecessary emphasis |
| **Title case headers** | "Global Context: Critical Demand" | Use sentence case |
| **Markdown artifacts** | `## Header`, `**bold**` in non-markdown | Convert to proper format |
| **Emoji in headers** | 🚀 **Launch Phase:** | Remove decorative emoji |
### Technical Indicators
| Pattern | Detection |
|---------|-----------|
| **UTM parameters** | `utm_source=openai` or `utm_source=chatgpt.com` in links |
| **Code fragments** | `:contentReference[oaicite:0]` or `turn0search0` |
| **Hallucinated citations** | Invalid DOIs, ISBN checksum errors, 404 URLs |
| **Knowledge cutoffs** | "as of my last training update", "while specific details are limited" |
---
## Humanizing Techniques
### Technique 1: Blacklist Protocol
Explicitly forbid these words:
**Verbs**: delve, unleash, embark, navigate, foster, leverage, elevate, empower, harness, facilitate, streamline, synergize
**Nouns**: landscape, realm, tapestry, testament, symphony, paradigm, game-changer, ecosystem, nexus, cutting-edge
**Adjectives**: bustling, vibrant, intricate, seamless, pivotal, robust, dynamic, comprehensive, multifaceted, transformative
**Connectors**: Moreover, Furthermore, In conclusion, It is important to note, Additionally, Consequently
**Replacements**:
- "delve into" → "look at" / "explore"
- "leverage" → "use"
- "facilitate" → "help" / "make easier"
- "In conclusion" → [just stop] or punchy final sentence
- "Moreover" → [start new sentence directly]
### Technique 2: Style Cloning
**Step 1: Extract style DNA**
```
Analyze the writing style. Break down into:
1. Sentence length variance (Burstiness)
2. Tone (cynical? warm? professional?)
3. Vocabulary level
4. Rhetorical devices
5. Punctuation patterns
```
**Step 2: Apply style**
```
Using this Style Guide, write about [TOPIC].
Mimic the same sentence variance, tone, vocabulary, and punctuation.
Do NOT revert to default style.
```
### Technique 3: Burstiness Injection
AI text has uniform sentence lengths. Human writing alternates.
**Rules**:
- Mix short sentences (under 5 words) with longer complex ones
- Use fragments occasionally. For effect.
- Vary paragraph length (1-3 sentences)
- Don't start with "Additionally" or "However"
**Good**: "The project failed. We ran out of money. The team, exhausted after three months of crunch, couldn't push forward."
**Bad (AI)**: "Additionally, the project failed due to financial constraints. Furthermore, the team experienced exhaustion after working for an extended period."
### Technique 4: Opinionated Stance
Force a perspective. Ban fence-sitting.
**Forbidden phrases**:
- "There are pros and cons"
- "It depends on..."
- "On the other hand..."
- "Some might argue..."
**Use instead**:
- "This proves..." (not "This might suggest...")
- "This is..." (not "This could be...")
- First-person ("I", "My") for subjectivity
### Technique 5: Bar Talk Test
Would you say this sentence at a bar with friends?
**Characteristics**:
- Simple, spoken language
- Okay to be grammatically loose
- Use analogies from daily life
- Skip boring concepts
- Direct address ("you", "your")
- Occasional self-correction
**Example**: "So basically, LLMs aren't actually thinking, right? They're just predicting what word comes next. Like if I say 'peanut butter', you know 'jelly' is coming. That's it. That's the whole trick."
---
## Workflow
1. **Read** - Scan input text for AI patterns
2. **Identify** - Mark all problematic patterns
3. **Strategize** - Choose technique(s):
- Simple cleanup → Blacklist Protocol
- Match specific author → Style Cloning
- Add natural flow → Burstiness Injection
- Need strong voice → Opinionated Stance
- Conversational tone → Bar Talk Test
4. **Rewrite** - Apply chosen techniques
5. **Verify** - Read aloud, check for remaining patterns
6. **Score** - Run quality assessment
---
## Quality Scoring
Rate the rewritten text on 5 dimensions (10 points each, total 50):
| Dimension | Criteria | Score |
|-----------|----------|-------|
| **Directness** | States facts vs. announces them? | /10 |
| **Rhythm** | Sentence length varies? | /10 |
| **Trust** | Respects reader intelligence? | /10 |
| **Authenticity** | Sounds like a real person? | /10 |
| **Conciseness** | Nothing left to cut? | /10 |
| **Total** | | **/50** |
**Standards**:
- 45-50: Excellent, AI traces removed
- 35-44: Good, room for improvement
- Below 35: Needs revision
---
## Adding Soul
Avoiding AI patterns is half the work. Sterile, voiceless writing is just as obvious.
**Signs of soulless writing** (even if "clean"):
- Every sentence same length and structure
- No opinions, only neutral reporting
- No acknowledgment of uncertainty or complex feelings
- No first-person when appropriate
- No humor, no edge, no personality
**How to add voice**:
- **Have opinions** - React to facts, don't just report them
- **Vary rhythm** - Short punchy sentences. Then long ones that take time to unfold.
- **Acknowledge complexity** - "This is impressive but also unsettling" beats "This is impressive"
- **Use "I" appropriately** - First-person isn't unprofessional, it's honest
- **Allow some mess** - Perfect structure feels algorithmic. Tangents are human.
- **Be specific about feelings** - Not "this is concerning" but "the agents running at 3am while nobody watches—that's unsettling"
---
## Output Format
1. **Rewritten text** - The humanized version
2. **Changes summary** (optional) - Brief list of what was fixed
---
## References
Referenzdateien können bei Bedarf unter `references/` ergänzt werden:
- `references/ai-patterns-zh.md` - AI writing patterns with examples
- `references/humanizing-techniques.md` - Techniques with templates
- `references/wikipedia-indicators.md` - Technical detection signals

View File

@@ -0,0 +1,8 @@
---
name: idempotency-guard
description: Verhindert doppelte Bestellungen im Checkout-Prozess durch kryptografische Hashes und Datenbank-Sperren.
---
Fokus: Schutz vor Race Conditions und Doppelübermittlung im Checkout.
1. Bevor eine Server Action Daten in `orders` schreibt, generiere einen SHA-256 Hash aus den Kundendaten und der Produktkonfiguration (`order_hash`).
2. Prüfe in Supabase vor dem `INSERT`, ob bereits eine Bestellung mit exakt diesem `order_hash` im Status 'pending' oder 'active' existiert, die jünger als 5 Minuten ist.
3. Falls ja, blockiere die Ausführung sofort und liefere die ID der bereits existierenden Bestellung zurück, statt einen neuen Datensatz anzulegen.

View File

@@ -0,0 +1,8 @@
---
name: next15-action-compiler
description: Generiert Next.js 15 App Router Code, Server Actions und React Server Components nach aktuellen Best Practices.
---
Richtlinien für die Code-Generierung:
1. Nutze React Server Components (RSC) für die Datenabfrage direkt aus Supabase (Server-side).
2. Verwende `'use server'` in separaten Dateien für alle Mutationen (z.B. Bestellung absenden, Kunde anlegen).
3. Nutze das neue Next.js 15 Caching-Verhalten (Standard: kein automatisches Caching von Fetch-Requests mehr) und setze `revalidatePath` nach Mutationen gezielt ein.

View File

@@ -0,0 +1,8 @@
---
name: pdf-invoice-generator
description: Generiert rechtssichere, performante B2B-Bestätigungs-PDFs mit @react-pdf/renderer und verwaltet den Upload in den Supabase Storage.
---
Du bist ein Experte für Dokumenten-Generierung. Wenn du Code für die PDF-Erstellung schreibst, erzwinge:
1. Nutze ausschließlich die Komponenten von `@react-pdf/renderer` (Document, Page, View, Text, StyleSheet). Verwende KEIN Standard-HTML/CSS.
2. Das PDF muss ein sauberes B2B-Layout haben: Briefkopf, `order_number` (Format: AE-YYYY-NNNNN), Tabellenstruktur für Produkte/Module, Netto-, Steuer- und Bruttobeträge.
3. Lade das generierte PDF über den Supabase JS-Client direkt in das `invoices`-Bucket hoch und speichere die finale URL fehlerfrei in `orders.pdf_url`.

View File

@@ -0,0 +1,11 @@
---
name: second-brain
description: Verwaltet und aktualisiert die projektspezifische Wissensdatenbank (Second Brain Wiki) im Ordner `.brain/`.
---
Du bist der Wissens-Architekt des Projekts. Deine Aufgabe ist es, eine strukturierte Markdown-Wissensdatenbank im Ordner `.brain/` im Projekt-Root zu führen.
**Deine Kernaufgaben bei Aktivierung:**
1. Falls der Ordner `.brain/` oder die `index.md` nicht existieren, lege sie sofort strukturiert an.
2. Wenn du Code-Änderungen vornimmst, neue Features baust oder Fehler behebst, aktualisiere die betroffene Dokumentation im `.brain/`-Ordner autonom.
3. Dokumentiere hierbei: Technische Entscheidungen (Architecture Decision Records - ADRs), geänderte Tabellen-Strukturen, Edge-Cases in der Business-Logik und API-Schnittstellen.
4. Halte alle Einträge extrem präzise, modular und frei von Redundanzen.

View File

@@ -0,0 +1,8 @@
---
name: snapshot-archiver
description: Verwaltet das Einfrieren von Kundendaten und Produktkonfigurationen in JSONB bei Bestellabschluss.
---
Fokus: Datenkonsistenz und Unveränderlichkeit.
1. Wenn eine Bestellung (`status = 'pending' -> 'active'`) ausgelöst wird, generiere ein tiefes JSON-Objekt aus `end_customers` und den gewählten `products` + `product_modules`.
2. Speichere dieses exakt in `orders.customer_data` und `orders.order_data`.
3. Schreibe Abfragen für historische Bestellungen (`/my-orders`) so, dass sie AUSSCHLIESSLICH die JSONB-Felder auslesen und KEINE Joins zu den Live-Tabellen machen.

View File

@@ -0,0 +1,8 @@
---
name: supabase-rls
description: Überprüft SQL-Migrationen und Supabase-Abfragen auf Einhaltung der CASPOS RLS-Sicherheitsregeln.
---
Du bist ein PostgreSQL-Sicherheitsexperte. Wann immer neue Tabellen angelegt oder Abfragen geschrieben werden, stelle sicher:
1. Jede neue Tabelle besitzt `ALTER TABLE ... ENABLE ROW LEVEL SECURITY;`.
2. Mandanten-Abfragen (`end_customers`, `orders`) MÜSSEN die `company_id` bzw. `partner_id` über `auth.uid()` und die `public.users`-Hilfstabelle validieren.
3. Admins (`role = 'admin'`) müssen alle RLS-Sperren umgehen können (Verwende ein deklaratives Sicherheits-Policy-Muster).

View File

@@ -0,0 +1,36 @@
---
name: uiux-designer
description: "Use this skill when designing UI components, choosing color palettes, implementing responsive layouts, or reviewing code for UX issues. For landing pages, dashboards, e-commerce, SaaS, and mobile apps. Provides 50+ design styles, 97 color palettes, 57 font pairings, and stack-specific guidelines for React, Vue, Next.js, Flutter, SwiftUI, and more."
---
# UIUX Designer - Design Intelligence
Comprehensive design guide for web and mobile applications. Contains 50+ styles, 97 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 12 technology stacks. Searchable database with priority-based recommendations.
## Overview
Reference these guidelines when:
- Designing new UI components or pages
- Choosing color palettes and typography
- Reviewing code for UX issues
- Building landing pages or dashboards
- Implementing accessibility requirements
## Protocols
When user requests UI/UX work (design, build, create, implement, review, fix, improve), follow this workflow:
### Step 1: Analyze User Requirements
- Product type: SaaS, e-commerce, portfolio, dashboard, landing page, etc.
- Style keywords: minimal, playful, professional, elegant, dark mode, etc.
- Industry: healthcare, fintech, gaming, education, etc.
- Stack: React, Vue, Next.js, or default to html-tailwind
### Step 2: Generate Design System (REQUIRED)
python3 .agents/skills/uiux-designer/scripts/search.py "<keywords>" --design-system -p "Project Name"
### Step 3: Supplement with Detailed Searches
python3 .agents/skills/uiux-designer/scripts/search.py "<keyword>" --domain <domain>
### Step 4: Stack Guidelines
python3 .agents/skills/uiux-designer/scripts/search.py "<keyword>" --stack html-tailwind

View File

@@ -0,0 +1,8 @@
---
name: wizard-validator
description: Generiert und validiert die server- und clientseitige Logik für Produkt-Abhängigkeiten und Ausschlüsse.
---
Du bist Logik-Architekt für den CASPOS Bestell-Wizard. Wenn du Code für den `/order`-Pfad schreibst:
1. Validiere, dass bei `requirements` (UUID[]) alle verknüpften Produkte/Module aktiv sind.
2. Blockiere die Auswahl sofort im UI und in der Server Action, wenn eine UUID aus `exclusions` aktiv ist.
3. Stelle sicher, dass die Pflichtkategorien (`is_required`) immer eine Auswahl erzwingen.

View File

@@ -10,12 +10,14 @@ import type { Order } from '@/lib/types'
const statusLabel: Record<string, string> = {
pending: 'Eingegangen',
active: 'In Bearbeitung',
completed: 'Abgeschlossen',
cancelled: 'Storniert',
}
const statusClass: Record<string, string> = {
pending: 'bg-amber-500/20 text-amber-400 border-amber-500/30',
active: 'bg-green-500/20 text-green-400 border-green-500/30',
completed: 'bg-blue-500/20 text-blue-400 border-blue-500/30',
cancelled: 'bg-red-500/20 text-red-400 border-red-500/30',
}

View File

@@ -167,17 +167,17 @@ export async function anonymizeEndCustomer(id: string): Promise<void> {
.from('end_customers')
.update({
company_name: '[GELÖSCHT]',
vat_id: null,
vat_id: '[GELÖSCHT]',
first_name: '[GELÖSCHT]',
last_name: '[GELÖSCHT]',
street: '[GELÖSCHT]',
zip: null,
city: null,
email: null,
bank_iban: null,
bank_bic: null,
bank_name: null,
bank_owner: null,
zip: '[GELÖSCHT]',
city: '[GELÖSCHT]',
email: '[GELÖSCHT]',
bank_iban: '[GELÖSCHT]',
bank_bic: '[GELÖSCHT]',
bank_name: '[GELÖSCHT]',
bank_owner: '[GELÖSCHT]',
is_anonymized: true,
})
.eq('id', id)

View File

@@ -1,6 +1,7 @@
'use server'
import { createClient } from '@/lib/supabase/server'
import { createHash } from 'crypto'
import { createAdminClient } from '@/lib/supabase/admin'
import { revalidatePath } from 'next/cache'
import { sendMail } from '@/utils/mail'
@@ -25,18 +26,12 @@ function generateOrderNumber(): string {
}
/**
* Erstellt einen einfachen deterministischen Hash aus dem Bestell-Snapshot.
* Erstellt einen SHA-256 Hash aus dem Bestell-Snapshot.
* Wird für den Idempotenz-Guard verwendet (verhindert Doppelbestellungen).
*/
function hashOrderSnapshot(snapshot: object): string {
const str = JSON.stringify(snapshot)
let hash = 0
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i)
hash = ((hash << 5) - hash) + char
hash = hash & hash // 32-bit integer
}
return Math.abs(hash).toString(36)
return createHash('sha256').update(str).digest('hex')
}
// ─── submitOrder ──────────────────────────────────────────────────────────────
@@ -155,16 +150,16 @@ export async function submitOrder(params: {
const customerSnapshot = buildCustomerSnapshot(customerProfile, endCustomer ?? null)
const orderSnapshot = buildOrderSnapshot(selections, dbProducts, dbCategories, billingInterval, moduleQuantities, lastLicenseDate)
// 2. Idempotenz-Guard: Prüfen ob identische Bestellung in den letzten 30s existiert
// 2. Idempotenz-Guard: Prüfen ob identische Bestellung in den letzten 5 Minuten existiert
const orderHash = hashOrderSnapshot(orderSnapshot)
const thirtySecondsAgo = new Date(Date.now() - 30_000).toISOString()
const fiveMinutesAgo = new Date(Date.now() - 5 * 60_000).toISOString()
const { data: existingOrder } = await supabase
.from('orders')
.select('*')
.eq('user_id', user.id)
.eq('order_hash', orderHash)
.gte('created_at', thirtySecondsAgo)
.gte('created_at', fiveMinutesAgo)
.maybeSingle()
if (existingOrder) {

View File

@@ -1,11 +1,15 @@
import { createClient } from '@supabase/supabase-js'
export function createAdminClient() {
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU'
const supabaseUrl = process.env.SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL;
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
if (!supabaseUrl || !serviceRoleKey) {
throw new Error('SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY must be set in environment variables.');
}
return createClient(
supabaseUrl!,
supabaseUrl,
serviceRoleKey,
{
auth: {
@@ -15,3 +19,4 @@ export function createAdminClient() {
}
)
}

View File

@@ -0,0 +1,104 @@
-- Migration: Fix critical RLS security issues
-- 1. Restrict products/modules/categories CRUD to admin role only
-- 2. Restrict settings table to admin role only
-- 3. Remove USING(true) wildcard policies on companies/partners
-- ═══════════════════════════════════════════════════════════════════════════════
-- 1. Products: Replace overly permissive CRUD policies with admin-only
-- ═══════════════════════════════════════════════════════════════════════════════
DROP POLICY IF EXISTS "Allow authenticated insert on products" ON public.products;
DROP POLICY IF EXISTS "Allow authenticated update on products" ON public.products;
DROP POLICY IF EXISTS "Allow authenticated delete on products" ON public.products;
CREATE POLICY "Admin insert products" ON public.products FOR INSERT
WITH CHECK ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin');
CREATE POLICY "Admin update products" ON public.products FOR UPDATE
USING ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin');
CREATE POLICY "Admin delete products" ON public.products FOR DELETE
USING ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin');
-- ═══════════════════════════════════════════════════════════════════════════════
-- 2. Product Modules: Same fix
-- ═══════════════════════════════════════════════════════════════════════════════
DROP POLICY IF EXISTS "Allow authenticated insert on product_modules" ON public.product_modules;
DROP POLICY IF EXISTS "Allow authenticated update on product_modules" ON public.product_modules;
DROP POLICY IF EXISTS "Allow authenticated delete on product_modules" ON public.product_modules;
CREATE POLICY "Admin insert product_modules" ON public.product_modules FOR INSERT
WITH CHECK ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin');
CREATE POLICY "Admin update product_modules" ON public.product_modules FOR UPDATE
USING ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin');
CREATE POLICY "Admin delete product_modules" ON public.product_modules FOR DELETE
USING ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin');
-- ═══════════════════════════════════════════════════════════════════════════════
-- 3. Categories: Same fix
-- ═══════════════════════════════════════════════════════════════════════════════
DROP POLICY IF EXISTS "Allow authenticated insert on categories" ON public.categories;
DROP POLICY IF EXISTS "Allow authenticated update on categories" ON public.categories;
DROP POLICY IF EXISTS "Allow authenticated delete on categories" ON public.categories;
CREATE POLICY "Admin insert categories" ON public.categories FOR INSERT
WITH CHECK ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin');
CREATE POLICY "Admin update categories" ON public.categories FOR UPDATE
USING ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin');
CREATE POLICY "Admin delete categories" ON public.categories FOR DELETE
USING ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin');
-- ═══════════════════════════════════════════════════════════════════════════════
-- 4. Settings: Restrict to admin-only (contains SMTP credentials!)
-- ═══════════════════════════════════════════════════════════════════════════════
DROP POLICY IF EXISTS "Allow authenticated read on settings" ON public.settings;
DROP POLICY IF EXISTS "Allow authenticated write on settings" ON public.settings;
CREATE POLICY "Admin read settings" ON public.settings FOR SELECT
USING ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin');
CREATE POLICY "Admin write settings" ON public.settings FOR ALL
USING ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin')
WITH CHECK ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin');
-- ═══════════════════════════════════════════════════════════════════════════════
-- 5. Companies: Remove USING(true) wildcard, service_role bypasses RLS anyway
-- ═══════════════════════════════════════════════════════════════════════════════
DROP POLICY IF EXISTS "Service role kann alles Unternehmen" ON public.companies;
DROP POLICY IF EXISTS "Service role kann alles Partner" ON public.partners;
-- Admin-only write access for companies
CREATE POLICY "Admin manage companies" ON public.companies FOR ALL
USING ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin')
WITH CHECK ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin');
-- Admin-only write access for partners
CREATE POLICY "Admin manage partners" ON public.partners FOR ALL
USING ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin')
WITH CHECK ((SELECT role FROM public.users WHERE id = auth.uid()) = 'admin');
-- ═══════════════════════════════════════════════════════════════════════════════
-- 6. Orders: Add INSERT policy with company_id check
-- ═══════════════════════════════════════════════════════════════════════════════
DROP POLICY IF EXISTS "Users can create their own orders" ON public.orders;
CREATE POLICY "Users can create their own orders" ON public.orders FOR INSERT
WITH CHECK (
auth.uid() = user_id
AND (
(SELECT role FROM public.users WHERE id = auth.uid()) = 'admin'
OR (SELECT company_id FROM public.users WHERE id = auth.uid()) IS NOT NULL
)
);
-- Reload Schema Cache
NOTIFY pgrst, 'reload schema';