--- name: supabase-storage description: Supabase Storage fuer Datei-Upload und Download. Nutze diesen Skill fuer Bucket-Erstellung, File Upload, Download, Signed URLs und Storage Policies. Aktiviert bei Begriffen wie "Upload", "Download", "Datei", "File", "Bild hochladen", "Storage", "Bucket", "S3", "Bilder", "Avatar", "Dokumente". --- # Supabase Storage Skill Dieser Skill hilft bei allen Storage-Operationen mit Supabase. ## Bucket erstellen ```typescript // Server-side mit Admin Client const { data, error } = await supabaseAdmin.storage.createBucket('avatars', { public: true, fileSizeLimit: 1024 * 1024 * 2, // 2MB allowedMimeTypes: ['image/png', 'image/jpeg', 'image/webp'] }); ``` Oder via SQL: ```sql INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types) VALUES ( 'avatars', 'avatars', true, 2097152, ARRAY['image/png', 'image/jpeg', 'image/webp'] ); ``` ## File Upload ```typescript async function uploadFile(file: File, bucket: string, path: string) { const { data, error } = await supabase.storage .from(bucket) .upload(path, file, { cacheControl: '3600', upsert: false // true = ueberschreiben erlaubt }); if (error) throw error; // Public URL holen const { data: urlData } = supabase.storage .from(bucket) .getPublicUrl(path); return urlData.publicUrl; } ``` ## File Download ```typescript // Als Blob const { data, error } = await supabase.storage .from('documents') .download('path/to/file.pdf'); // Public URL (oeffentliche Buckets) const { data } = supabase.storage .from('avatars') .getPublicUrl('user123/avatar.jpg'); // Signed URL (private Buckets, zeitlich begrenzt) const { data, error } = await supabase.storage .from('private-docs') .createSignedUrl('secret.pdf', 3600); // 1 Stunde ``` ## File Management ```typescript // Liste Dateien const { data, error } = await supabase.storage .from('bucket') .list('folder/', { limit: 100, sortBy: { column: 'created_at', order: 'desc' } }); // Loeschen const { error } = await supabase.storage .from('bucket') .remove(['path/file1.jpg', 'path/file2.jpg']); // Verschieben const { error } = await supabase.storage .from('bucket') .move('old/path.jpg', 'new/path.jpg'); ``` ## Storage Policies (RLS) ```sql -- User koennen eigene Dateien hochladen CREATE POLICY "Users upload own files" ON storage.objects FOR INSERT TO authenticated WITH CHECK ( bucket_id = 'user-files' AND (storage.foldername(name))[1] = auth.uid()::text ); -- User koennen eigene Dateien lesen CREATE POLICY "Users read own files" ON storage.objects FOR SELECT TO authenticated USING ( bucket_id = 'user-files' AND (storage.foldername(name))[1] = auth.uid()::text ); -- User koennen eigene Dateien loeschen CREATE POLICY "Users delete own files" ON storage.objects FOR DELETE TO authenticated USING ( bucket_id = 'user-files' AND (storage.foldername(name))[1] = auth.uid()::text ); -- Oeffentlicher Lesezugriff CREATE POLICY "Public read" ON storage.objects FOR SELECT USING (bucket_id = 'public-assets'); ``` ## React Upload Component Siehe `templates/FileUpload.tsx` fuer eine fertige Component. ## Best Practices 1. Ordnerstruktur: `{user_id}/{type}/{filename}` 2. File Size Limits setzen 3. MIME Types einschraenken 4. Signed URLs fuer private Dateien 5. Unique Filenames (z.B. mit Timestamp)