fix: update lessons

This commit is contained in:
2026-06-03 16:58:25 +02:00
parent eb1ba2e28d
commit a852aa0d52
83 changed files with 16830 additions and 0 deletions

View File

@@ -0,0 +1,332 @@
# Les 18 — Huiswerk
## Multi-user Tasks app met RLS + social login + AUTH.md
**Vak:** AI-Assisted Development
**Deadline:** Voor eindopdracht-inlevering (4 weken)
**Inleveren:** GitHub repo + `AUTH.md` in root + Vercel productie URL
---
## Doel
Bouw je auth-demo uit naar productie-niveau: tasks per user met RLS, GitHub social login, en deploy. Eindopdracht-ready.
---
## Onderdeel A — Tasks tabel + RLS (verplicht)
### A1 — Schema
In Supabase SQL Editor:
```sql
create table tasks (
id bigserial primary key,
user_id uuid not null references auth.users(id) default auth.uid(),
text text not null,
done boolean default false,
created_at timestamp default now()
);
alter table tasks enable row level security;
create policy "select own tasks" on tasks
for select using (auth.uid() = user_id);
create policy "insert own tasks" on tasks
for insert with check (auth.uid() = user_id);
create policy "update own tasks" on tasks
for update using (auth.uid() = user_id);
create policy "delete own tasks" on tasks
for delete using (auth.uid() = user_id);
```
### A2 — Tasks UI
`app/page.tsx` — extend met tasks lijst + form:
```tsx
import { createClient } from "@/utils/supabase/server";
import { redirect } from "next/navigation";
import { revalidatePath } from "next/cache";
async function addTask(formData: FormData) {
"use server";
const supabase = await createClient();
await supabase.from("tasks").insert({ text: formData.get("text") as string });
revalidatePath("/");
}
async function toggleTask(id: number, done: boolean) {
"use server";
const supabase = await createClient();
await supabase.from("tasks").update({ done: !done }).eq("id", id);
revalidatePath("/");
}
async function deleteTask(id: number) {
"use server";
const supabase = await createClient();
await supabase.from("tasks").delete().eq("id", id);
revalidatePath("/");
}
export default async function Home() {
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) redirect("/login");
const { data: tasks } = await supabase
.from("tasks")
.select("*")
.order("created_at", { ascending: false });
return (
<main className="p-8 max-w-2xl mx-auto">
<h1 className="text-2xl mb-4">Tasks van {user.email}</h1>
<form action={addTask} className="flex gap-2 mb-6">
<input name="text" className="border p-2 flex-1" placeholder="Nieuwe taak..." />
<button className="bg-blue-600 text-white px-4 py-2 rounded">+</button>
</form>
<ul className="space-y-2">
{tasks?.map((t) => (
<li key={t.id} className="flex items-center gap-2 p-2 border rounded">
<form action={toggleTask.bind(null, t.id, t.done)}>
<button className={t.done ? "line-through" : ""}>{t.text}</button>
</form>
<form action={deleteTask.bind(null, t.id)} className="ml-auto">
<button className="text-red-500">×</button>
</form>
</li>
))}
</ul>
</main>
);
}
```
### Eisen
- [ ] `tasks` tabel met RLS aan
- [ ] 4 policies: select / insert / update / delete
- [ ] UI: add, toggle, delete tasks werkt
- [ ] In incognito met ander account: andere tasks zichtbaar (geen vermenging)
---
## Onderdeel B — Social login (GitHub) (verplicht)
### B1 — GitHub OAuth App
1. github.com → Settings → Developer settings → OAuth Apps → New
2. Application name: jouw app
3. Homepage URL: `http://localhost:3000` (dev) of `https://yourapp.vercel.app` (prod)
4. Callback URL: `https://your-project.supabase.co/auth/v1/callback`
5. Save, generate Client Secret
### B2 — Configure in Supabase
1. Dashboard → Auth → Providers → GitHub
2. Enable + plak Client ID + Secret
### B3 — Button toevoegen
In `app/login/page.tsx`:
```tsx
async function signInWithGitHub() {
const supabase = createClient();
await supabase.auth.signInWithOAuth({
provider: "github",
options: { redirectTo: `${location.origin}/auth/callback` },
});
}
// In render:
<button onClick={signInWithGitHub} className="border px-4 py-2 mt-3 rounded">
Of: Login met GitHub
</button>
```
### Eisen
- [ ] GitHub OAuth App aangemaakt
- [ ] Supabase configured
- [ ] Klik op GitHub button → redirect → ingelogd
- [ ] Tasks werken met GitHub-account
---
## Onderdeel C — Deploy naar Vercel (verplicht)
Combineert Les 14/15:
1. Push naar GitHub
2. Vercel: import repo
3. Add env vars: `NEXT_PUBLIC_SUPABASE_URL`, `NEXT_PUBLIC_SUPABASE_ANON_KEY`
4. Deploy
5. Update Supabase Auth → URL Configuration:
- **Site URL:** `https://yourapp.vercel.app`
- **Redirect URLs:** `https://yourapp.vercel.app/**`, `http://localhost:3000/**`
6. Update GitHub OAuth App callback URL to productie
### Eisen
- [ ] App live op Vercel
- [ ] Magic link werkt op productie URL
- [ ] GitHub login werkt op productie URL
- [ ] Tasks per user werken op productie
---
## Onderdeel D — `AUTH.md` documentatie (verplicht)
In repo-root.
### Sectie 1 — Auth methoden
Welke auth methoden ondersteund:
- Magic link (default)
- GitHub OAuth
- (eventuele extra's)
### Sectie 2 — RLS policies
Lijst je policies + waarom:
```markdown
**Table: tasks**
- `select own tasks` — users zien alleen eigen tasks
- `insert own tasks` — voorkomt inserten op andermans naam
- `update own tasks` — alleen eigen toggle
- `delete own tasks` — alleen eigen delete
```
### Sectie 3 — Test van multi-user isolation
Bewijs dat RLS werkt:
- Screenshot van Account A's tasks
- Screenshot van Account B's tasks (incognito)
- Bewijs: account A's tasks staan NIET in account B's lijst
### Sectie 4 — Deployment checklist
Wat je moest aanpassen voor productie:
- Site URL in Supabase
- Redirect URLs in Supabase
- GitHub OAuth callback URL
- Env vars in Vercel
### Sectie 5 — Eindopdracht-architectuur (~250 woorden)
Concrete plan voor je eigen eindopdracht:
- App naam + doel
- User-flows (signup, login, kernfeature)
- Welke tabellen + RLS policies
- AI feature(s) en hoe ze user-context gebruiken
- Externe APIs (uit Les 17)
- Tijd-plan (4-6 weken)
### Vorm
- Max 800 woorden
- Screenshots zijn waardevol
- Concrete plan, geen vaagheid
---
## Bonus (optioneel)
### Bonus 1 — Email + password naast magic link
Voeg klassieke signup/login toe. Combine met magic link.
### Bonus 2 — Profile pagina
`/profile` — user kan naam, avatar URL, etc. instellen. Aparte `profiles` tabel met RLS.
### Bonus 3 — Team feature
Multi-user binnen team. Aparte tabel `teams`, junction `team_members`. RLS policies die team-leden toelaten elkaars data te zien.
### Bonus 4 — AI integration met user context
Voeg een AI chat toe waar de user-tasks deel zijn van de context. AI weet wat jouw tasks zijn (door RLS), kan helpen prioriteren.
---
## Inleveren
1. **GitHub repo URL** in Brightspace
2. **Vercel productie URL** in Brightspace
3. **`AUTH.md`** in repo-root
4. **Test-account credentials** voor docent (email of GitHub)
5. **2-3 screenshots** van multi-user test
---
## Beoordeling
| Criterium | Punten |
|-----------|--------|
| A — Tasks + RLS werkt (4 policies) | 3 |
| B — GitHub OAuth werkt | 2 |
| C — Deployed op Vercel + auth werkt op prod | 2 |
| D — AUTH.md compleet (5 secties) | 2 |
| Multi-user isolation bewezen | 1 |
| **Totaal** | **10** |
Voldoende = 6+. Bonus telt mee.
---
## Tijd-indicatie
| Onderdeel | Tijd |
|-----------|------|
| A — Tasks + RLS | 50 min |
| B — GitHub OAuth | 25 min |
| C — Deploy | 20 min |
| D — AUTH.md | 35 min |
| **Totaal** | **~2 uur 10 min** |
---
## Veelvoorkomende valkuilen
| Probleem | Oplossing |
|----------|-----------|
| Tasks van andere users zichtbaar | RLS niet aan, of policy `using (true)` |
| Insert faalt met "row violates" | RLS check faalt — check `with check (auth.uid() = user_id)` + default |
| OAuth redirect 404 | Site URL niet bijgewerkt voor productie |
| Magic link werkt lokaal niet prod | Site URL in Supabase staat nog op localhost |
| Cookies werken niet | Middleware draait niet — check matcher en path |
| `auth.uid()` returnt null | User niet ingelogd — check session in Server Component |
---
## Tips
- **Test isolation in incognito** — alleen daar ben je écht "another user"
- **RLS error messages** kunnen vaag zijn — log policy + auth.uid() in Postgres function voor debug
- **Productie env vars** — vergeet niet je Vercel env vars te zetten
- **Eindopdracht-koppeling:** dit huiswerk is je startpunt — bouw eindopdracht hier op
- **Veel succes met de eindopdracht!**
---
## Voor je begint aan de eindopdracht
Als afsluiting:
1. Plan je eindopdracht concreet (Sectie 5 van AUTH.md is de basis)
2. Start een nieuwe repo van scratch óf bouw verder op deze
3. Push klein en vaak — niet één big commit aan eind
4. Vraag hulp tijdig — Slack, mail, office hours
5. Plan tijd in van min 8-10 uur/week voor 4-6 weken
**Bedankt voor jullie inzet deze 18 lessen. Veel succes met de eindopdracht!**