- Add migration 002_posts.sql with posts, tags, post_tags tables - Add Post/Tag/PostStats models with PostStatus enum - Add posts API with full CRUD: - create_post, update_post, delete_post (admin only) - get_post_by_slug, list_published_posts (public) - list_posts, get_post_stats (admin) - list_tags, get_posts_by_tag, search_posts (public) - Slug auto-generation with uniqueness check - Server-side markdown rendering with pulldown-cmark - Auto-summary extraction from markdown - Soft delete support
39 lines
1.2 KiB
SQL
39 lines
1.2 KiB
SQL
CREATE TYPE post_status AS ENUM ('draft', 'published');
|
|
|
|
CREATE TABLE posts (
|
|
id SERIAL PRIMARY KEY,
|
|
author_id INT NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
|
|
|
|
title VARCHAR(200) NOT NULL,
|
|
slug VARCHAR(200) NOT NULL,
|
|
summary VARCHAR(500),
|
|
|
|
content_md TEXT NOT NULL,
|
|
content_html TEXT,
|
|
|
|
status post_status NOT NULL DEFAULT 'draft',
|
|
published_at TIMESTAMPTZ,
|
|
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
deleted_at TIMESTAMPTZ,
|
|
|
|
CONSTRAINT posts_slug_unique UNIQUE (slug)
|
|
);
|
|
|
|
CREATE INDEX idx_posts_status_published ON posts(status, published_at DESC) WHERE deleted_at IS NULL;
|
|
CREATE INDEX idx_posts_slug ON posts(slug) WHERE deleted_at IS NULL;
|
|
|
|
CREATE TABLE tags (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(50) UNIQUE NOT NULL
|
|
);
|
|
|
|
CREATE TABLE post_tags (
|
|
post_id INT NOT NULL REFERENCES posts(id) ON DELETE CASCADE,
|
|
tag_id INT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
|
|
PRIMARY KEY (post_id, tag_id)
|
|
);
|
|
|
|
CREATE INDEX idx_post_tags_post ON post_tags(post_id);
|
|
CREATE INDEX idx_post_tags_tag ON post_tags(tag_id); |