153 lines
5.7 KiB
Markdown
153 lines
5.7 KiB
Markdown
# RSS Reader
|
|
|
|
A self-hosted RSS reader: a Rust/actix-web + Diesel/PostgreSQL backend with a Vue 3 single-page-app frontend. Sync feeds, read articles, and mark them as read — from your desktop or your phone.
|
|
|
|
## Stack
|
|
|
|
- **Backend**: Rust, actix-web, Diesel ORM, PostgreSQL, JWT auth
|
|
- **Frontend**: Vue 3, Vite, axios
|
|
- **Database**: PostgreSQL 15
|
|
|
|
---
|
|
|
|
## Development setup
|
|
|
|
### Prerequisites
|
|
|
|
- [Rust](https://rustup.rs/) (stable toolchain)
|
|
- [Node.js](https://nodejs.org/) 20+ and npm
|
|
- PostgreSQL (locally, or via Docker — see below)
|
|
- The Diesel CLI: `cargo install diesel_cli --no-default-features --features postgres`
|
|
(requires `libpq`; on Debian/Ubuntu: `sudo apt install libpq-dev`)
|
|
|
|
### 1. Configure environment variables
|
|
|
|
Copy the example file and fill in your own values:
|
|
|
|
```sh
|
|
cp .env.example .env
|
|
```
|
|
|
|
`.env` is read by both the backend (via `dotenv`) and the `diesel` CLI, and is gitignored — never commit it.
|
|
|
|
| Variable | Purpose |
|
|
|---|---|
|
|
| `DATABASE_URL` | Postgres connection string, e.g. `postgres://admin:changeme@localhost/rss` |
|
|
| `JWT_SECRET` | Secret used to sign/verify auth tokens — use a long random string |
|
|
| `FRONTEND_ORIGIN` | Origin allowed by CORS, e.g. `http://localhost:5173` for the Vite dev server |
|
|
| `RUST_LOG` | Log level for `env_logger`, e.g. `info` |
|
|
| `POSTGRES_PASSWORD` | Password for the `admin` Postgres user (used by `docker-compose.yml`) |
|
|
|
|
### 2. Start PostgreSQL
|
|
|
|
The simplest way during development is to run just the database container:
|
|
|
|
```sh
|
|
docker compose up postgres -d
|
|
```
|
|
|
|
This starts Postgres on `localhost:5432` with the credentials from `.env`. Alternatively, point `DATABASE_URL` at any Postgres instance you already have running.
|
|
|
|
### 3. Run database migrations
|
|
|
|
Migrations are embedded in the backend binary and also run automatically on startup, but you can apply them manually with the Diesel CLI (useful when generating new migrations during development):
|
|
|
|
```sh
|
|
diesel setup # creates the database and runs existing migrations
|
|
diesel migration run # applies any pending migrations
|
|
diesel migration generate <name> # scaffolds a new up.sql/down.sql pair
|
|
```
|
|
|
|
### 4. Run the backend
|
|
|
|
```sh
|
|
cargo run
|
|
```
|
|
|
|
The API listens on `http://0.0.0.0:8001`. Backend logs respect `RUST_LOG`.
|
|
|
|
### 5. Run the frontend
|
|
|
|
```sh
|
|
cd vue
|
|
npm install
|
|
npm run dev -- --host
|
|
```
|
|
|
|
The Vite dev server runs on `http://localhost:5173` (the `--host` flag also exposes it on your LAN so you can test from a phone) and proxies `/api` requests to `http://localhost:8001` (configured in `vue/vite.config.js`).
|
|
|
|
### 6. Try it out
|
|
|
|
Create a user, then log in through the UI at `http://localhost:5173`:
|
|
|
|
```sh
|
|
curl -X POST -H "Content-Type: application/json" \
|
|
-d '{"name": "mace", "email": "you@example.com", "password": "secret"}' \
|
|
http://localhost:8001/api/v1/user/create
|
|
```
|
|
|
|
### Useful commands during development
|
|
|
|
```sh
|
|
# Backend
|
|
cargo build # compile
|
|
cargo test # run the test suite (needs a reachable Postgres + DATABASE_URL)
|
|
cargo clippy # lint
|
|
cargo fmt # format
|
|
```
|
|
|
|
```sh
|
|
# Frontend (run from vue/)
|
|
npm run test # run Vitest component tests
|
|
npm run lint # eslint
|
|
npm run format # prettier
|
|
```
|
|
|
|
```sh
|
|
# Inspect the dockerized database directly
|
|
docker exec -it rss-postgres psql -d rss -U admin
|
|
```
|
|
|
|
---
|
|
|
|
## Production setup (Docker)
|
|
|
|
The whole stack — Postgres, backend, and frontend — runs via Docker Compose. The frontend is built as a static Vue bundle and served by nginx, which also reverse-proxies `/api/` to the backend container.
|
|
|
|
### 1. Configure environment variables
|
|
|
|
Same as in development — create a root-level `.env` from `.env.example` and fill in **strong, unique** values for `POSTGRES_PASSWORD` and `JWT_SECRET`. Set `FRONTEND_ORIGIN` to the URL the frontend will actually be served from (e.g. `http://<host-ip>:8080` or your domain), since the backend's CORS policy only allows that origin.
|
|
|
|
### 2. Build and start everything
|
|
|
|
```sh
|
|
docker compose up --build -d
|
|
```
|
|
|
|
This builds three images and starts them on a shared network:
|
|
|
|
- **`postgres`** — PostgreSQL 15, data persisted in the `postgres_data` volume, reachable on `localhost:5432`
|
|
- **`backend`** — multi-stage build (compiles the Rust binary in a `rust:slim` builder, runs it in a slim `debian` runtime image); runs embedded Diesel migrations automatically on startup; listens on `0.0.0.0:8001`
|
|
- **`frontend`** — multi-stage build (compiles the Vue app with `node:20-alpine`, serves the static bundle with `nginx:alpine`); listens on `0.0.0.0:8080` and proxies `/api/` to the `backend` service over Docker's internal network
|
|
|
|
### 3. Use it
|
|
|
|
- From the host machine: `http://localhost:8080`
|
|
- From another device on the same network (e.g. a phone): `http://<host-LAN-IP>:8080`
|
|
|
|
### Operating the stack
|
|
|
|
```sh
|
|
docker compose ps # check container status
|
|
docker compose logs -f backend # follow backend logs
|
|
docker compose down # stop everything (keeps the postgres_data volume)
|
|
docker compose down -v # stop and wipe all data — careful!
|
|
docker compose up --build -d # rebuild after pulling code changes
|
|
```
|
|
|
|
### Notes
|
|
|
|
- Migrations run automatically at backend startup — no manual `diesel` step needed in production.
|
|
- Secrets (`POSTGRES_PASSWORD`, `JWT_SECRET`, `FRONTEND_ORIGIN`) come from the root `.env`, which is gitignored and interpolated into `docker-compose.yml` via `${VAR}` — never commit real secrets.
|
|
- If you need the app reachable from multiple origins (e.g. `localhost:8080` on desktop and `<lan-ip>:8080` on a phone), the current single-origin CORS check (`FRONTEND_ORIGIN`) won't allow both — pick the one you'll actually use, or relax the CORS policy in `src/main.rs` for a self-hosted, trusted-network setup.
|