Create wiki page 'Database Schema'

2026-05-15 15:00:37 +00:00
parent c14cc4d8b4
commit 87e81d3a5f

123
Database-Schema.md Normal file

@@ -0,0 +1,123 @@
# Database Schema
PostgreSQL. UUIDs as primary keys to prevent enumeration and ease future federation. All timestamps include timezone (`TIMESTAMPTZ`).
## ERD (simplified)
```
users ──< thoughts ──< thought_tags >── tags
├──< follows
├──< top_friends
└──< api_keys
```
## Tables
### `users`
Stores user account and profile data.
| Column | Type | Constraints | Description |
|---|---|---|---|
| `id` | UUID | PK, DEFAULT gen_random_uuid() | Unique user identifier |
| `username` | VARCHAR(32) | NOT NULL, UNIQUE | User handle |
| `email` | VARCHAR(255) | NOT NULL, UNIQUE | Email address |
| `password_hash` | TEXT | NOT NULL | Argon2 or bcrypt hash |
| `display_name` | VARCHAR(50) | NULL | Public display name |
| `bio` | VARCHAR(160) | NULL | Public biography |
| `avatar_url` | TEXT | NULL | URL to avatar image |
| `header_url` | TEXT | NULL | URL to header/banner image |
| `custom_css` | TEXT | NULL | Custom profile CSS (sanitized) |
| `created_at` | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | Account creation time |
| `updated_at` | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | Last profile update |
---
### `thoughts`
Stores the content of each post.
| Column | Type | Constraints | Description |
|---|---|---|---|
| `id` | UUID | PK, DEFAULT gen_random_uuid() | Unique thought identifier |
| `user_id` | UUID | NOT NULL, FK → users(id) | Author |
| `content` | VARCHAR(128) | NOT NULL | Post text |
| `reply_to` | UUID | NULL, FK → thoughts(id) | Parent thought (if reply) |
| `visibility` | ENUM | NOT NULL, DEFAULT 'public' | `public`, `followers`, `private` |
| `created_at` | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | Post timestamp |
---
### `follows`
Join table for the follower/following relationship.
| Column | Type | Constraints | Description |
|---|---|---|---|
| `follower_id` | UUID | NOT NULL, FK → users(id) | User who follows |
| `following_id` | UUID | NOT NULL, FK → users(id) | User being followed |
| | | PK (follower_id, following_id) | Prevents duplicate follows |
---
### `top_friends`
Ordered "Top Friends" list per user (up to 8 entries).
| Column | Type | Constraints | Description |
|---|---|---|---|
| `user_id` | UUID | NOT NULL, FK → users(id) | Owner of the list |
| `friend_id` | UUID | NOT NULL, FK → users(id) | Friend being displayed |
| `position` | SMALLINT | NOT NULL | Display order (18) |
| | | PK (user_id, friend_id) | No duplicates |
| | | UNIQUE (user_id, position) | No duplicate positions |
---
### `tags`
Unique hashtag names.
| Column | Type | Constraints | Description |
|---|---|---|---|
| `id` | SERIAL | PK | Tag identifier |
| `name` | VARCHAR(50) | NOT NULL, UNIQUE | Tag name (e.g. `welcome`) |
---
### `thought_tags`
Many-to-many join between thoughts and tags.
| Column | Type | Constraints | Description |
|---|---|---|---|
| `thought_id` | UUID | NOT NULL, FK → thoughts(id) | The thought |
| `tag_id` | INTEGER | NOT NULL, FK → tags(id) | The tag |
| | | PK (thought_id, tag_id) | No duplicate tags per post |
---
### `api_keys`
Hashed API keys for third-party access.
| Column | Type | Constraints | Description |
|---|---|---|---|
| `id` | UUID | PK, DEFAULT gen_random_uuid() | Key identifier |
| `user_id` | UUID | NOT NULL, FK → users(id) | Owning user |
| `key_hash` | TEXT | NOT NULL, UNIQUE | Hashed key value |
| `name` | VARCHAR(50) | NOT NULL | User-provided label |
| `created_at` | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | Creation timestamp |
## Migrations
Migrations are managed by **SeaORM** in the `thoughts-backend/migration/` crate.
```bash
# Run pending migrations
cd thoughts-backend
cargo run -p migration
```
Migration files follow the naming pattern `m{YYYYMMDD}_{HHMMSS}_{description}.rs`.