# 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 # 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://: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://: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 `: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.