diff --git a/README.md b/README.md new file mode 100644 index 0000000..f018d4a --- /dev/null +++ b/README.md @@ -0,0 +1,186 @@ +# 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`. + +### 2b. Run locally + +Requires a running PostgreSQL instance matching your `DATABASE_URL`. + +```bash +# Install sqlx-cli if needed +cargo install sqlx-cli --no-default-features --features rustls,postgres + +# Run migrations +sqlx migrate run + +# Start the server +cargo run +``` + +## 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 +```