### Task 5: API posts — ownership e assegnazione autore **Files:** - Modify: `src/pages/api/admin/posts/index.ts` - Modify: `src/pages/api/admin/posts/[id].ts` - Test: `tests/posts-ownership.test.ts` (nuovo, a livello logico via funzione estratta) **Interfaces:** - Consumes: `createPost`, `getPostById`, `updatePost`, `deletePost` (Task 4), `locals.user` dal middleware. - Produces: helper `canModifyPost(role: string, userId: number, post: { author_id: number | null }): boolean` in `src/lib/posts.ts`. - [ ] **Step 1: Scrivi il test dell'ownership (livello logico)** Crea `tests/posts-ownership.test.ts`: ```typescript import { describe, it, expect } from 'vitest'; import { canModifyPost } from '../src/lib/posts'; describe('canModifyPost', () => { const own = { author_id: 7 }; const other = { author_id: 8 }; const orphan = { author_id: null }; it('admin e superuser modificano qualsiasi post', () => { for (const role of ['admin', 'superuser']) { expect(canModifyPost(role, 7, other)).toBe(true); expect(canModifyPost(role, 7, orphan)).toBe(true); } }); it('user modifica solo i propri', () => { expect(canModifyPost('user', 7, own)).toBe(true); expect(canModifyPost('user', 7, other)).toBe(false); expect(canModifyPost('user', 7, orphan)).toBe(false); }); }); ``` - [ ] **Step 2: Esegui e verifica il fallimento** Run: `npm test -- tests/posts-ownership.test.ts` Expected: FAIL — `canModifyPost` non esiste. - [ ] **Step 3: Aggiungi `canModifyPost` in `posts.ts`** ```typescript export function canModifyPost(role: string, userId: number, post: { author_id: number | null }): boolean { if (role === 'admin' || role === 'superuser') return true; return post.author_id === userId; } ``` - [ ] **Step 4: Esegui e verifica il successo** Run: `npm test -- tests/posts-ownership.test.ts` Expected: PASS. - [ ] **Step 5: Applica l'autore nel POST** In `src/pages/api/admin/posts/index.ts`, cambia la firma per leggere `locals` e passa `author_id`: ```typescript export const POST: APIRoute = async ({ request, locals }) => { let data: Record; try { data = await request.json(); } catch { return json(400, { error: 'Dati non validi.' }); } const parsed = parsePostBody(data); if ('error' in parsed) return json(400, { error: parsed.error }); try { const id = createPost(getDb(), { ...parsed.value, author_id: locals.user!.id }); return json(201, { id, slug: parsed.value.slug }); } catch (err: any) { if (String(err?.message).includes('UNIQUE')) return json(400, { error: 'Slug già esistente.' }); throw err; } }; ``` - [ ] **Step 6: Applica l'ownership su PUT e DELETE** In `src/pages/api/admin/posts/[id].ts`, aggiorna gli import e i due handler: ```typescript import { getPostById, updatePost, deletePost, canModifyPost } from '../../../../lib/posts'; export const PUT: APIRoute = async ({ params, request, locals }) => { const id = Number(params.id); const existing = getPostById(getDb(), id); if (!existing) return json(404, { error: 'Articolo non trovato.' }); if (!canModifyPost(locals.user!.role, locals.user!.id, existing)) return json(403, { error: 'Non puoi modificare questo articolo.' }); let data: Record; try { data = await request.json(); } catch { return json(400, { error: 'Dati non validi.' }); } const parsed = parsePostBody(data); if ('error' in parsed) return json(400, { error: parsed.error }); try { updatePost(getDb(), id, parsed.value); return json(200, { ok: true }); } catch (err: any) { if (String(err?.message).includes('UNIQUE')) return json(400, { error: 'Slug già esistente.' }); throw err; } }; export const DELETE: APIRoute = ({ params, locals }) => { const id = Number(params.id); const existing = getPostById(getDb(), id); if (!existing) return json(404, { error: 'Articolo non trovato.' }); if (!canModifyPost(locals.user!.role, locals.user!.id, existing)) return json(403, { error: 'Non puoi eliminare questo articolo.' }); deletePost(getDb(), id); return json(200, { ok: true }); }; ``` - [ ] **Step 7: Verifica build + test** Run: `npm run build && npm test` Expected: build OK, tutti i test verdi. - [ ] **Step 8: Commit** ```bash git add src/pages/api/admin/posts/index.ts src/pages/api/admin/posts/[id].ts src/lib/posts.ts tests/posts-ownership.test.ts git commit -m "feat(posts-api): assegna autore in POST, ownership su PUT/DELETE" ``` ---