# Review package: db92fc9..6d1e260 ## Commits 6d1e260 feat(posts): author_id nel repository + listByAuthor + helper utenti ## Files changed src/lib/auth.ts | 10 ++++++++++ src/lib/posts.ts | 14 +++++++++----- tests/posts.test.ts | 27 ++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 6 deletions(-) ## Diff diff --git a/src/lib/auth.ts b/src/lib/auth.ts index cd10d1e..a5146c3 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -61,14 +61,24 @@ const RULES: [RegExp, Role[]][] = [ ]; export function canAccessAdminPath(role: string, pathname: string): boolean { if (role === 'admin') return true; for (const [re, roles] of RULES) { if (re.test(pathname)) return roles.includes(role as Role); } return true; // blog, upload, logout, showtags: tutti i loggati } +export function getUsernameById(db: Database.Database, id: number): string | null { + const row = db.prepare('SELECT username FROM users WHERE id = ?').get(id) as { username: string } | undefined; + return row?.username ?? null; +} + +export function listUsers(db: Database.Database): { id: number; username: string; role: string }[] { + return db.prepare('SELECT id, username, role FROM users ORDER BY username').all() as + { id: number; username: string; role: string }[]; +} + export function landingFor(role: string): string { if (role === 'superuser') return '/admin/content'; return '/admin'; } diff --git a/src/lib/posts.ts b/src/lib/posts.ts index dad9159..1c8e44a 100644 --- a/src/lib/posts.ts +++ b/src/lib/posts.ts @@ -1,29 +1,29 @@ import type Database from 'better-sqlite3'; export interface Post { id: number; title: string; slug: string; category: string; excerpt: string; - body_html: string; cover: string | null; draft: number; + body_html: string; cover: string | null; draft: number; author_id: number | null; published_at: string | null; created_at: string; updated_at: string; } export interface PostInput { title: string; slug: string; category: string; excerpt: string; - body_html: string; cover?: string | null; draft: boolean; + body_html: string; cover?: string | null; draft: boolean; author_id?: number | null; } export function createPost(db: Database.Database, input: PostInput): number { const res = db.prepare( - `INSERT INTO posts (title, slug, category, excerpt, body_html, cover, draft, published_at) - VALUES (@title, @slug, @category, @excerpt, @body_html, @cover, @draft, + `INSERT INTO posts (title, slug, category, excerpt, body_html, cover, draft, author_id, published_at) + VALUES (@title, @slug, @category, @excerpt, @body_html, @cover, @draft, @author_id, CASE WHEN @draft = 0 THEN datetime('now') ELSE NULL END)` - ).run({ ...input, cover: input.cover ?? null, draft: input.draft ? 1 : 0 }); + ).run({ ...input, cover: input.cover ?? null, author_id: input.author_id ?? null, draft: input.draft ? 1 : 0 }); return Number(res.lastInsertRowid); } export function updatePost(db: Database.Database, id: number, input: PostInput): void { db.prepare( `UPDATE posts SET title = @title, slug = @slug, category = @category, excerpt = @excerpt, body_html = @body_html, cover = @cover, draft = @draft, published_at = CASE WHEN @draft = 0 AND published_at IS NULL THEN datetime('now') ELSE published_at END, updated_at = datetime('now') WHERE id = @id` @@ -65,10 +65,14 @@ export function listRecent(db: Database.Database, limit = 4): Post[] { 'SELECT * FROM posts WHERE draft = 0 ORDER BY published_at DESC LIMIT ?' ).all(limit) as Post[]; } export function listArchiveMonths(db: Database.Database): { month: string; count: number }[] { return db.prepare( `SELECT strftime('%Y-%m', published_at) AS month, COUNT(*) AS count FROM posts WHERE draft = 0 GROUP BY month ORDER BY month DESC` ).all() as { month: string; count: number }[]; } + +export function listByAuthor(db: Database.Database, authorId: number): Post[] { + return db.prepare('SELECT * FROM posts WHERE author_id = ? ORDER BY updated_at DESC').all(authorId) as Post[]; +} diff --git a/tests/posts.test.ts b/tests/posts.test.ts index ad373fa..58734a7 100644 --- a/tests/posts.test.ts +++ b/tests/posts.test.ts @@ -1,17 +1,18 @@ import { describe, it, expect, beforeEach } from 'vitest'; import type Database from 'better-sqlite3'; import { createDb } from '../src/lib/db'; import { createPost, updatePost, deletePost, getPostById, getPublishedBySlug, - listPublished, listAllPosts, listRecent, listArchiveMonths, type PostInput, + listPublished, listAllPosts, listRecent, listArchiveMonths, listByAuthor, type PostInput, } from '../src/lib/posts'; +import { createUser, getUsernameById, listUsers } from '../src/lib/auth'; let db: Database.Database; beforeEach(() => { db = createDb(':memory:'); }); const base: PostInput = { title: 'Titolo', slug: 'titolo', category: 'Contenuti educativi', excerpt: 'estratto', body_html: '

corpo

', cover: null, draft: false, }; describe('posts repository', () => { @@ -65,10 +66,34 @@ describe('posts repository', () => { it('listRecent e archivio mesi', () => { createPost(db, base); expect(listRecent(db, 4)).toHaveLength(1); const months = listArchiveMonths(db); expect(months).toHaveLength(1); expect(months[0].count).toBe(1); expect(months[0].month).toMatch(/^\d{4}-\d{2}$/); }); }); + +describe('autore articoli', () => { + it('createPost salva author_id e listByAuthor filtra', () => { + const aliceId = createUser(db, 'alice', 'segreta123', 'user'); + const bobId = createUser(db, 'bob', 'segreta123', 'user'); + createPost(db, { ...base, slug: 'a1', author_id: aliceId }); + createPost(db, { ...base, slug: 'a2', author_id: aliceId }); + createPost(db, { ...base, slug: 'b1', author_id: bobId }); + expect(listByAuthor(db, aliceId).length).toBe(2); + expect(listByAuthor(db, bobId).length).toBe(1); + }); + + it('post senza author_id ha author_id null', () => { + const id = createPost(db, { ...base, slug: 'orfano' }); + expect(getPostById(db, id)!.author_id).toBeNull(); + }); + + it('getUsernameById e listUsers', () => { + const id = createUser(db, 'carla', 'segreta123', 'superuser'); + expect(getUsernameById(db, id)).toBe('carla'); + expect(getUsernameById(db, 99999)).toBeNull(); + expect(listUsers(db).some((u) => u.username === 'carla' && u.role === 'superuser')).toBe(true); + }); +});