Cron Jobs & Automatische Taken

Supabase Edge Functions voeren geplande taken uit. Server-side logic voor data-handling.

1. Dagelijkse Openingsdatum Check (07:00)

Bestand: supabase/functions/dagelijkse-check/index.ts

Wat doet het:

  1. Zoekt cirkels waarvan opening_datum = vandaag
  2. Stelt verwijder_op = opening_datum + 30 dagen in voor alle leden
  3. Stuurt WhatsApp + Email berichten
  4. Markeert cirkel als opening_verzonden = true
  5. Logt audit entry

Pseudocode:

Deno.serve(async () => {
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL'),
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')
  );

  // Vind cirkels met opening = vandaag
  const { data: teOpenenCirkels } = await supabase
    .from('cirkels')
    .select('id, naam')
    .eq('opening_datum', today())
    .eq('opening_verzonden', false);

  for (const cirkel of teOpenenCirkels ?? []) {
    try {
      // 1. Stel verwijderdatum in (opening + 30d)
      const verwijderDatum = addDays(new Date(), 30);
      await supabase.rpc('stel_verwijderdatum_in', {
        p_cirkel_id: cirkel.id,
        p_verwijder_op: verwijderDatum
      });

      // 2. Stuur berichten
      await stuurWhatsAppBerichten(cirkel.id);
      await stuurEmailBerichten(cirkel.id);

      // 3. Markeer als verzonden
      await supabase
        .from('cirkels')
        .update({ opening_verzonden: true })
        .eq('id', cirkel.id);

      // 4. Log
      await supabase.from('verwijderlogs').insert({
        cirkel_id: cirkel.id,
        actie: 'opening_verzonden',
        details: { cirkel_naam: cirkel.naam }
      });

    } catch (err) {
      console.error(`Fout bij openen ${cirkel.id}:`, err);
    }
  }

  return new Response('OK', { status: 200 });
});

2. Wekelijkse Schrijf-Herinnering (7 Dagen Voor Opening)

Bestand: supabase/functions/schrijf-herinnering/index.ts

Wat doet het:

  • Zoekt cirkels met opening_datum = vandaag + 7 dagen
  • Stuurt WhatsApp herinnering: "Heb je al geschreven?"
  • Optioneel: Email reminder

Message:

āœļø *Heb je al een briefje geschreven?*

Over 7 dagen sluit de liefdevolle blik van cirkel "X".

Heb je al een notitie achtergelaten? Je hebt nog even de tijd.

šŸ‘‰ liefdevolleblik.nl/cirkel/{id}/schrijf

3. Data Vernietiging (03:00)

Bestand: supabase/functions/verwijder-verlopen/index.ts

Wat doet het:

  1. Zoekt profielen met verwijder_op <= vandaag
  2. Verwijdert in juiste volgorde (foreign keys!)
  3. Geanonimiseerd audit log

Volgorde Belangrijk:

1. Notities (schrijver EN ontvanger)
   ↓
2. Lidmaatschappen
   ↓
3. Profielen
   ↓
4. Auth accounts (Supabase)
   ↓
5. Audit log (hash van email)

Pseudocode:

Deno.serve(async () => {
  const supabase = createClient(/* service role */);

  // Vind profielen die moeten weg
  const { data: teVerwijderen } = await supabase
    .from('profielen')
    .select('id, email, voornaam')
    .lte('verwijder_op', today())
    .not('verwijder_op', 'is', null);

  for (const profiel of teVerwijderen ?? []) {
    try {
      // 1. Verwijder notities
      await supabase.from('notities').delete()
        .or(`schrijver_id.eq.${profiel.id},ontvanger_id.eq.${profiel.id}`);

      // 2. Verwijder lidmaatschappen
      await supabase.from('lidmaatschappen').delete()
        .eq('gebruiker_id', profiel.id);

      // 3. Verwijder profiel
      await supabase.from('profielen').delete()
        .eq('id', profiel.id);

      // 4. Verwijder auth account
      await supabase.auth.admin.deleteUser(profiel.id);

      // 5. Geanonimiseerd audit log
      await supabase.from('verwijderlogs').insert({
        actie: 'account_vernietigd',
        details: {
          email_hash: await sha256(profiel.email),
          voornaam_initials: profiel.voornaam[0] + '***'
        }
      });

    } catch (err) {
      console.error(`Verwijdering ${profiel.id} failed:`, err);
    }
  }

  // Verwijder lege cirkels
  await supabase.rpc('verwijder_lege_cirkels');

  return new Response('OK');
});

Planning in Supabase

Zet deze SQL-queries in je Supabase SQL editor:

-- Dagelijkse openings-check (07:00 UTC)
SELECT cron.schedule(
  'dagelijkse-opening-check',
  '0 7 * * *',
  $$ SELECT net.http_post(
    url := 'https://<project>.supabase.co/functions/v1/dagelijkse-check',
    headers := '{"Authorization": "Bearer <anon-key>"}'::jsonb
  ) $$
);

-- Wekelijkse schrijf-herinnering (maandag 17:00)
SELECT cron.schedule(
  'schrijf-herinnering',
  '0 17 * * 1',
  $$ SELECT net.http_post(
    url := 'https://<project>.supabase.co/functions/v1/schrijf-herinnering',
    headers := '{"Authorization": "Bearer <anon-key>"}'::jsonb
  ) $$
);

-- Dagelijkse data-vernietiging (03:00 UTC)
SELECT cron.schedule(
  'data-vernietiger',
  '0 3 * * *',
  $$ SELECT net.http_post(
    url := 'https://<project>.supabase.co/functions/v1/verwijder-verlopen',
    headers := '{"Authorization": "Bearer <anon-key>"}'::jsonb
  ) $$
);

Tijdzonesnotitie

Supabase cron werkt in UTC. Zet tijden om naar UTC (NL is UTC+1 winter, UTC+2 zomer).

NL Tijd UTC (Winter) UTC (Zomer)
07:00 06:00 05:00
17:00 16:00 15:00
03:00 02:00 01:00

Monitorering

Logs Bekijken

# Supabase Dashboard → Edge Functions → Logs
# Of via CLI:
supabase functions list
supabase functions logs dagelijkse-check

Testen

# Trigger functie manueel:
curl -X POST https://<project>.supabase.co/functions/v1/dagelijkse-check \
  -H "Authorization: Bearer <anon-key>"

Foutafhandeling

Alle edge functions hebben try-catch:

  • Error: Gelogged in Supabase logs
  • Continue: Andere items verwerken
  • Alert: Optional: Stuur e-mail op critieke fout

Zie Ook