From a2c678cdba0b2b75fb1ca86cb572552b44559c19 Mon Sep 17 00:00:00 2001 From: Alex Selimov Date: Thu, 19 Mar 2026 15:52:48 -0400 Subject: [PATCH] Rename package --- Cargo.lock | 2 +- Cargo.toml | 3 +- Dockerfile | 15 ++++++++ README.md | 87 ++++++++++++++++++++++++++++++++++++++++++++-- docker-compose.yml | 12 +++++++ src/main.rs | 4 +-- 6 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 Dockerfile diff --git a/Cargo.lock b/Cargo.lock index cf454fd..3210271 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1961,7 +1961,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] -name = "uprs" +name = "upvoters" version = "0.1.0" dependencies = [ "anyhow", diff --git a/Cargo.toml b/Cargo.toml index f0e1c53..70a8350 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ [package] -name = "uprs" +name = "upvoters" version = "0.1.0" edition = "2024" +license = "MIT" [dependencies] anyhow = "1" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fbd5f56 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM rust:1.88-slim AS builder + +WORKDIR /app + +COPY Cargo.toml Cargo.lock ./ +RUN mkdir src && echo "fn main() {}" > src/main.rs && cargo build --release && rm -rf src + +COPY src ./src +RUN touch src/main.rs && cargo build --release --locked + +FROM debian:bookworm-slim + +COPY --from=builder /app/target/release/upvoters /usr/local/bin/upvoters + +CMD ["upvoters"] diff --git a/README.md b/README.md index 1b07d58..c6ce19b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,86 @@ -# uprs +# upvoters ☝️ -Simple Rust backend for supporting anonymous like/upvote functionality in hugo sites. +upvoters is a basic anonymous voting system that can be added to a blog. +I recently reworked my personal blog with the Hugo Bear blog theme. +I came across the [creator's (Herman Martinus) blogging site](https://herman.bearblog.dev/) which had this really cool anonymous upvote system. +I wanted something similar and decided to implement it myself. +The goal is maximum simplicity. + +## Important Notes + +This service does NOT have any authentication/authorization, so you should NOT serve this to the public web. +I'm using this by just serving it on the same VPS as my blog. + + +## Requirements + +- Rust (stable) +- PostgreSQL 16 + +## Configuration + +The following environment variables are required: + +| Variable | Description | +|------------------------------|----------------------------------------------------------------------------------------| +| `POSTGRES_CONNECTION_STRING` | PostgreSQL connection string (e.g. `postgres://user:password@localhost:5432/upvoters`) | + +## Running locally + +### With Docker Compose + +Builds and starts both the database and app: + +```sh +docker compose up --build +``` + +The server will be available at `http://localhost:3000`. + +### Without Docker + +Start the database: + +```sh +docker compose up -d db +``` + +Run the server: + +```sh +POSTGRES_CONNECTION_STRING=postgres://uprs:password123@localhost:5432/uprs cargo run +``` + +The server listens on port `3000`. + +## API + +| Method | Endpoint | Description | +|---|---|---| +| `GET` | `/health` | Health check | +| `POST` | `/posts/{slug}/vote` | Upvote a post | +| `DELETE` | `/posts/{slug}/vote` | Remove a vote from a post | +| `GET` | `/posts/{slug}/votes` | Get vote count and whether the current user has voted | + +Voter identity is tracked via a `voter_id` cookie. One is set automatically on first request. + +### `GET /posts/{slug}/votes` response + +```json +{ + "vote_count": 42, + "voted": true +} +``` + +## Running tests + +Tests require a running PostgreSQL instance at `postgres://uprs:password123@localhost:5432/uprs`. + +```sh +cargo test +``` + +## License + +MIT — see [LICENSE](LICENSE). diff --git a/docker-compose.yml b/docker-compose.yml index 86f7a6f..5a55b89 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,5 +12,17 @@ services: - ./db/migrations:/docker-entrypoint-initdb.d # run initial schema ports: - "5432:5432" + + app: + build: . + container_name: uprs_app + restart: always + environment: + POSTGRES_CONNECTION_STRING: postgres://uprs:password123@db:5432/uprs + ports: + - "3000:3000" + depends_on: + - db + volumes: pgdata: diff --git a/src/main.rs b/src/main.rs index 6350267..0514583 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; -use uprs::state::AppState; +use upvoters::state::AppState; #[tokio::main] async fn main() { @@ -14,7 +14,7 @@ async fn main() { let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); tracing::info!("listening on {}", listener.local_addr().unwrap()); - axum::serve(listener, uprs::app(AppState::new().await)) + axum::serve(listener, upvoters::app(AppState::new().await)) .await .unwrap(); }