# rustification-server REST API backend for Rustification, a chat application. Built with Axum, SQLx, and PostgreSQL. ## Tech stack | Layer | Technology | |-------|-----------| | HTTP framework | [Axum](https://github.com/tokio-rs/axum) 0.8 | | Database | PostgreSQL 17 via [SQLx](https://github.com/launchbadge/sqlx) 0.9 | | Auth | JWT (jsonwebtoken) + Argon2 password hashing | | Runtime | Tokio | | Migrations | SQLx built-in | ## Features - **Auth** — register, login, logout, JWT refresh via bearer tokens - **Workspaces** — create and manage workspaces with owner/admin/member roles and invite tokens - **Conversations** — DMs, group chats, and channels; public or private - **Messages** — send, edit, soft-delete; per-conversation history - **Attachments** — link files to messages (URL-based) - **Reactions** — emoji reactions per message - Soft deletes on users, workspaces, conversations, and messages (excluded from all list/get responses) ## Prerequisites - Rust (edition 2024) - PostgreSQL 17, **or** Docker + Docker Compose ## Getting started ### 1. Configure environment ```bash cp .env.example .env # Edit .env — set POSTGRES_*, DATABASE_URL, API_URL, API_PORT, JWT_SECRET ``` ### 2a. Run with Docker Compose (recommended) Starts PostgreSQL and the server together: ```bash docker compose up --build ``` The server will be available at `http://localhost:3000`. Migrations run automatically on startup — no manual step needed. ### 2b. Run locally Requires a running PostgreSQL instance matching your `DATABASE_URL`. ```bash cargo run ``` Migrations are embedded in the binary and run automatically when the server starts. ## Environment variables | Variable | Description | Example | |----------|-------------|---------| | `DATABASE_URL` | Postgres connection string | `postgres://admin:password@localhost:5432/rustification` | | `POSTGRES_DB` | Database name (used by Docker) | `rustification` | | `POSTGRES_USER` | Postgres user (used by Docker) | `admin` | | `POSTGRES_PASSWORD` | Postgres password (used by Docker) | `password` | | `API_URL` | Bind address | `0.0.0.0` | | `API_PORT` | Bind port | `3000` | | `JWT_SECRET` | Secret for signing JWTs | any random string | ## API Base URL: `http://localhost:3000/api/v1` All routes except `/auth/*` require a `Authorization: Bearer ` header. ### Auth | Method | Path | Description | |--------|------|-------------| | POST | `/auth/register` | Register a new user | | POST | `/auth/login` | Login, returns JWT | | POST | `/auth/refresh` | Refresh access token | | POST | `/auth/logout` | Logout (invalidates refresh token) | ### Users | Method | Path | Description | |--------|------|-------------| | GET | `/users` | List all users | | POST | `/users` | Create a user | | GET | `/users/{id}` | Get a user | | PUT | `/users/{id}` | Update display_name / avatar_url / bio | | DELETE | `/users/{id}` | Soft-delete a user | ### Workspaces | Method | Path | Description | |--------|------|-------------| | GET | `/workspaces` | List workspaces | | POST | `/workspaces` | Create a workspace | | GET | `/workspaces/{id}` | Get a workspace | | PUT | `/workspaces/{id}` | Update name / slug | | DELETE | `/workspaces/{id}` | Soft-delete | | GET | `/workspaces/{id}/members` | List members | | POST | `/workspaces/{id}/members` | Add member (`role`: owner \| admin \| member) | | PUT | `/workspaces/{workspace_id}/members/{user_id}` | Update role / display name | | DELETE | `/workspaces/{workspace_id}/members/{user_id}` | Remove member | | GET | `/workspaces/{id}/invitations` | List invite tokens | | POST | `/workspaces/{id}/invitations` | Create invite token | | DELETE | `/invitations/{id}` | Delete invite token | ### Conversations | Method | Path | Description | |--------|------|-------------| | GET | `/conversations` | List conversations (filter: `?workspace_id=uuid`) | | POST | `/conversations` | Create (`kind`: dm \| group \| channel) | | GET | `/conversations/{id}` | Get a conversation | | PUT | `/conversations/{id}` | Update name / is_public | | DELETE | `/conversations/{id}` | Soft-delete | | GET | `/conversations/{id}/members` | List members | | POST | `/conversations/{id}/members` | Add member | | DELETE | `/conversations/{conversation_id}/members/{user_id}` | Remove member | ### Messages | Method | Path | Description | |--------|------|-------------| | GET | `/conversations/{id}/messages` | List messages (chronological) | | POST | `/messages` | Send a message | | GET | `/messages/{id}` | Get a message | | PUT | `/messages/{id}` | Edit content | | DELETE | `/messages/{id}` | Soft-delete | ### Attachments & Reactions | Method | Path | Description | |--------|------|-------------| | GET | `/messages/{id}/attachments` | List attachments | | POST | `/attachments` | Create attachment | | DELETE | `/attachments/{id}` | Delete attachment | | GET | `/messages/{id}/reactions` | List reactions | | POST | `/reactions` | Add reaction | | DELETE | `/reactions/{id}` | Remove reaction | Full request/response shapes are documented in [`docs/api.md`](docs/api.md). ### Response codes | Status | Meaning | |--------|---------| | 200 | OK | | 201 | Created | | 204 | Deleted (no body) | | 404 | Not found | | 500 | Internal server error | ## Project structure ``` src/ ├── main.rs # Server setup, env, DB pool ├── route.rs # Route definitions (public + protected) ├── middleware.rs # JWT auth middleware ├── model.rs # SQLx types and response structs ├── schema.rs # Request body structs └── handlers/ # One file per resource ├── auth.rs ├── auth_token.rs ├── user.rs ├── workspace.rs ├── workspace_member.rs ├── workspace_invitation.rs ├── conversation.rs ├── conversation_member.rs ├── message.rs ├── attachment.rs └── reaction.rs migrations/ # SQLx migration files ```