- Rust 64.8%
- CSS 27.8%
- Mermaid 4.2%
- Dockerfile 3.2%
| .continue/rules | ||
| entity | ||
| migration | ||
| src | ||
| static | ||
| templates | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| Containerfile | ||
| README.md | ||
Poetry Blog - Books Built from Posts
A Rust-based web application built with SeaORM, Poem, and SQLite that allows users to create authors, write blog posts, and compile those posts into books. Think of it as a self-publishing platform where your writings become books.
📚 Project Overview
- Authors write individual blog posts
- Posts are published on a blog
- Books are created by selecting and ordering specific posts as their chapters/contents
Key Features
- ✅ Create authors with profiles and avatars
- ✅ Write and publish individual blog posts
- ✅ Compile posts into books (each book can have multiple posts as "contents")
- ✅ Maintain display order for posts within books
- ✅ Full CRUD operations for all entities
- ✅ Relational database with proper foreign key constraints
- ✅ Image upload support (stored as Blobs)
Database Schema
erDiagram
author {
bigint id PK
text first_name
text last_name
text middle_name
text bio
blob avatar
text email
custom display_name
text created_at
text updated_at
bigint rback_role_id FK
}
book_attribution {
bigint book_id PK,FK
bigint author_id PK,FK
text attribution
}
book_contents {
bigint book_id PK,FK
bigint post_id PK,FK
bigint display_order
}
books {
bigint id PK
text title
text author_id FK
text publish_date
text created_at
text blurb
blob image
text status
}
posts {
bigint id PK
bigint author_id FK
text post_status
text title
text content
text created_at
bigint updated_at
blob image
}
rbac_roles {
bigint id PK
text name
text description
}
author }o--|| rbac_roles : "rback_role_id"
author }o--o{ books : "[book_attribution]"
book_attribution }o--|| author : "author_id"
book_attribution }o--|| books : "book_id"
book_contents }o--|| books : "book_id"
book_contents }o--|| posts : "post_id"
books }o--|| author : "author_id"
books }o--o{ posts : "[book_contents]"
posts }o--|| author : "author_id"
The application uses SQLite with four main tables:
| Table | Description | Relationships |
|---|---|---|
author |
Stores author information | Has many books, has many posts |
books |
Individual books with metadata | Belongs to author, has many contents |
posts |
Individual blog posts | Belongs to author, in many books |
book_contents |
Join table linking books to posts | Foreign keys to both books & posts |
book_attribution |
Join table for author/editor roles | Foreign keys to author & books |
rbac_roles |
stores app auth roles for data access. | simple role lookup table |
🛠️ Tech Stack
- Language: Rust
- Web Framework: Poem (async HTTP framework)
- ORM: SeaORM (asynchronous Rust ORM)
- Database: SQLite (embedded, zero-configuration)
- Migrations: SeaORM Migrations crate
- Async Runtime: Tokio (via Poem)
📦 [Proposed] Project Structure
poetry-blog/
├── Cargo.toml
├── Cargo.lock
├── .env (environment configuration)
├── .env.example
├── README.md
├── README-books-posts.md
├── entity/ # SeaORM entities
│ ├── src/
│ │ ├── mod.rs
│ │ ├── author.rs # Author entity
│ │ ├── books.rs # Book entity
│ │ ├── posts.rs # Post entity
│ │ └── book_contents.rs # Join table entity
│ ├── entities/ # Generated entity structs
│ └── migrations/ # Entity migration scripts
├── migration/ # SeaORM migrations
│ ├── src/
│ │ ├── lib.rs # Migration library
│ │ ├── main.rs # Migration executable
│ │ ├── m20220101_000001_create_table.rs # Original migration
│ │ └── m20240101_000001_create_tables.rs # NEW: Corrected schema
│ └── migrations/ # Migration snapshots
├── src/ # Main application
│ ├── main.rs # Application entry point
│ ├── routes/ # Route definitions
│ │ ├── mod.rs
│ │ ├── posts.rs
│ │ ├── books.rs
│ │ └── pages.rs
│ ├── handlers/ # Route handlers
│ │ ├── mod.rs
│ │ ├── posts.rs
│ │ ├── books.rs
│ │ └── pages.rs
│ ├── db/ # Database utilities
│ │ └── mod.rs
│ ├── models/ # Data models (DTOs)
│ │ ├── author.rs
│ │ ├── book.rs
│ │ ├── post.rs
│ │ └── book_content.rs
│ └── config/ # Configuration
│ └── mod.rs
└── tests/ # Integration tests
└── integration_test.rs
🚀 Getting Started
Prerequisites
- Rust 1.70+ installed
- SQLite dev stuff (sqlite-dev)
Access the application:
Open your browser and navigate to: http://localhost:3000
🗄️ Database Setup
First 1. Migrate to create tables
The SeaORM migration system automatically creates the database schema when you run the application for the first time. The migration will:
- Create the
authortable - Create the
bookstable with foreign key to author - Create the
poststable with foreign key to author - Create the
book_contentsjoin table ... TODO!
Manual Migration (if needed)
TODO!
🌐 API Endpoints (Current Implementation)
Books
| Method | Endpoint | Description |
|---|---|---|
| POST | /books |
Create a new book |
| GET | /books |
List all books |
| GET | /books/:id |
Get a specific book |
| POST | /books/:id/image |
Upload book image |
Posts
| Method | Endpoint | Description |
|---|---|---|
| GET | / |
List all posts (home page) |
| GET | /:id |
Get a specific post |
| POST | /posts |
Create a new post |
Authors
| Method | Endpoint | Description |
|---|---|---|
| GET | /authors |
List all authors |
| POST | /authors |
Create a new author |
Development Tips
Adding New Routes
- Define your route in
src/routes/mod.rs - Create the handler in
src/handlers/[entity].rs - Create data models in
src/models/[entity].rs
Database Operations / Useful commands/code
Use SeaORM's entity methods for database operations:
// Example: Create a new author
use entity::author;
let author = author::Entity::insert(new_author)
.exec(db)
.await?;
Working with Relationships
// Load posts for a book
use entity::book_contents;
let contents = book_contents::Entity::find()
.belongs_to(books::Entity)
.and_related_to(books::Entity::find_by_id(book_id))
.all(db)
.await?;
📝 Notes for Developers
Important Schema Decisions
-
Author ID Type Consistency: Both
books.author_idandposts.author_idusei64to match theauthor.idtype. This ensures foreign key consistency. -
BookContents Join Table: The
book_contentstable:- Uses a composite primary key (
book_id,post_id) - Tracks
display_orderto maintain chapter/page sequencing - Uses
NoActionfor foreign key constraints (no cascading deletes) ... should change this?
- Uses a composite primary key (
-
Image Storage: All images are stored as
Blobfields in the database. For production, consider moving to file storage (S3, local disk, etc.). -
Cascade vs NoAction:
Cascadeon author relations: Deleting an author deletes their books/postsNoActionon book_contents: Prevents deleting referenced books/posts
🔮 Future Enhancements
- Add pagination to list endpoints
- Implement search/filter capabilities
- Add input validation with serde_json::value::Value
- Implement image upload to file system instead of DB Blobs
- Add API documentation with Swagger/OpenAPI
- Implement user authentication/authorization
- Add email notifications for new book publications
- Create an admin dashboard
📜 License
This project is licensed under the GPL 2.0 License - see the LICENSE file for details.