upvoters/README.md

4.8 KiB

upvoters ☝️

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 which had this really cool anonymous upvote system. I wanted something similar and decided to implement it myself. The goal is maximum simplicity.

Table of Contents

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)
ALLOWED_ORIGINS Comma-separated list of allowed CORS origins (e.g. localhost:1313,example.com)
PORT Port to listen on (default: 3000)

Running locally

With Docker Compose

Builds and starts both the database and app:

docker compose up --build

The server will be available at http://localhost:3000.

Without Docker

Start the database:

docker compose up -d db

Run the server:

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

{
  "vote_count": 42,
  "voted": true
}

Running tests

Tests require a running PostgreSQL instance at postgres://uprs:password123@localhost:5432/uprs.

cargo test

Actual deployment

Here are the instructions for how I deployed this service. You might be able to just run it through the docker-compose on a VPS but I already had a postgres server set up:

  1. Install dependencies (rust/postgres) (For debian server).
sudo apt update
sudo apt install -y postgresql postgresql-contrib
  1. Create postgres user to manage postgres and upvoters user to run the app:
# Create the system user that will run the app
sudo useradd --system --home /opt/upvoters --create-home upvoters

# Create the PostgreSQL role with peer auth (no password needed)
sudo -u postgres psql -c "CREATE USER upvoters;"
sudo -u postgres psql -c "CREATE DATABASE upvoters OWNER upvoters;"

# Enable peer auth for the upvoters role (append to pg_hba.conf)
echo "local   upvoters   upvoters   peer" | sudo tee -a /etc/postgresql/16/main/pg_hba.conf
sudo systemctl reload postgresql
  1. Create the votes table as the upvoters user
sudo -u upvoters psql upvoters
CREATE EXTENSION IF NOT EXISTS pgcrypto;

CREATE TABLE IF NOT EXISTS votes (
    slug text not null,
    voter_id uuid not null,
    created_at timestamptz NOT NULL DEFAULT timezone('utc'::text, now()),
    primary key (slug, voter_id)
);
  1. Download the binary (You can also install rust and build yourself if you prefer). Replace v0.2.0 with the latest release tag.
sudo curl https://forge.alexselimov.com/aselimov/upvoters/releases/download/v0.2.0/upvoters-linux-x86_64 --output /opt/upvoters/upvoters
sudo chmod +x /opt/upvoters/upvoters
sudo chown upvoters /opt/upvoters/upvoters
  1. Create a systemd service at /etc/systemd/system/upvoters.service
[Unit]
Description=upvoters rust backend
After=network.target postgresql.service

[Service]
User=upvoters
Group=upvoters
WorkingDirectory=/opt/upvoters
ExecStart=/opt/upvoters/upvoters
Restart=on-failure
# The below connection string works if you have peer auth enabled and have postgresql running on 
# a socket
Environment='POSTGRES_CONNECTION_STRING=postgresql:///upvoters?host=/var/run/postgresql'
Environment='ALLOWED_ORIGINS=https://example.com'
# Specify a PORT if the default 3000 is already taken
Environment='PORT=3000'

[Install]
WantedBy=multi-user.target
  1. Enable and start the service
sudo systemctl enable --now upvoters
  1. Test from VPS using curl
curl https://example.com/api/posts/tests/votes

License

MIT — see LICENSE.