Files
hyzendust.github.io/SERVER_SETUP.md
2026-06-04 12:05:17 +05:30

5.0 KiB

Server Setup: PostgreSQL + PHP Auth for freedoms4

Overview

/var/www/freedoms4/          ← Hugo's published docs/ folder (static files)
/var/www/freedoms4/api/      ← PHP backend (auth.php lives here)

Nginx serves the static Hugo site and passes /api/ requests to PHP-FPM.


1 · Install PostgreSQL

sudo apt update
sudo apt install -y postgresql postgresql-contrib
sudo systemctl enable --now postgresql

2 · Create the database and user

sudo -u postgres psql

Inside the psql shell:

CREATE USER freedoms4_user WITH PASSWORD 'CHANGE_THIS_PASSWORD';
CREATE DATABASE freedoms4 OWNER freedoms4_user;
\c freedoms4

CREATE TABLE users (
    id            BIGSERIAL     PRIMARY KEY,
    username      VARCHAR(32)   NOT NULL UNIQUE,
    email         VARCHAR(254)  NOT NULL UNIQUE,
    password_hash VARCHAR(255)  NOT NULL,
    created_at    TIMESTAMPTZ   NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_users_username ON users (username);
CREATE INDEX idx_users_email    ON users (email);

-- Optional: allow only this user to access the table
REVOKE ALL ON TABLE users FROM PUBLIC;
GRANT SELECT, INSERT ON TABLE users TO freedoms4_user;
GRANT USAGE, SELECT ON SEQUENCE users_id_seq TO freedoms4_user;

\q

Important: use the same password you set in DB_PASS inside auth.php.


3 · Install the PHP PostgreSQL extension

# Find your PHP version first:
php -v

# Install the pgsql extension (replace 8.x with your version, e.g. 8.3):
sudo apt install -y php8.3-pgsql

# Restart PHP-FPM (replace 8.3 with your version):
sudo systemctl restart php8.3-fpm

Verify it loaded:

php -m | grep pgsql   # should print: pgsql

4 · Deploy the PHP file

sudo mkdir -p /var/www/freedoms4/api
sudo cp /path/to/auth.php /var/www/freedoms4/api/auth.php
sudo chown -R www-data:www-data /var/www/freedoms4/api
sudo chmod 640 /var/www/freedoms4/api/auth.php

Edit the config constants at the top of auth.php:

define('DB_PASS', 'CHANGE_THIS_PASSWORD');  // ← your actual password

5 · Configure Nginx

Open your site config (e.g. /etc/nginx/sites-available/freedoms4):

server {
    listen 443 ssl http2;
    server_name freedoms4.org www.freedoms4.org;

    root /var/www/freedoms4;
    index index.html;

    # ── Static Hugo files ───────────────────────────────────────────────
    location / {
        try_files $uri $uri/ $uri/index.html =404;
    }

    # ── PHP API ─────────────────────────────────────────────────────────
    location /api/ {
        # Only allow POST (OPTIONS for CORS preflight)
        limit_except POST OPTIONS {
            deny all;
        }

        # Pass to PHP-FPM (adjust socket path to match your PHP version)
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO       $fastcgi_path_info;
    }

    # ── Block direct access to .php files outside /api/ ─────────────────
    location ~* \.php$ {
        deny all;
    }

    # SSL certs (already configured, adjust paths if needed)
    ssl_certificate     /etc/letsencrypt/live/freedoms4.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/freedoms4.org/privkey.pem;
}

Test and reload:

sudo nginx -t
sudo systemctl reload nginx

6 · Deploy the Hugo frontend changes

In your local Hugo project, apply the three file changes from the delivery package, then rebuild and sync:

# From inside the freedoms4 project directory:
hugo --minify

# Sync to server (adjust user/host):
rsync -avz --delete docs/ user@your-vps:/var/www/freedoms4/

Or if you use git+CI, commit and push; your pipeline handles the rest.


7 · Test

# Sign up
curl -s -X POST https://freedoms4.org/api/auth.php \
  -H 'Content-Type: application/json' \
  -d '{"action":"signup","username":"testuser","email":"test@example.com","password":"hunter2hunter2"}' | jq .

# Log in
curl -s -X POST https://freedoms4.org/api/auth.php \
  -H 'Content-Type: application/json' \
  -d '{"action":"login","username":"testuser","password":"hunter2hunter2"}' | jq .

Both should return {"success":true, ...}.


Security notes

  • All passwords are stored as bcrypt hashes (cost 12). Plain-text passwords are never written to disk or logs.
  • Session cookies are HttpOnly, Secure, and SameSite=Strict.
  • A simple per-IP rate limit (20 requests per 15 min) is enforced server-side via PHP sessions.
  • For production, consider adding fail2ban rules on your Nginx access log to block repeated 429s at the firewall level.
  • Keep SESSION_SECURE = true (requires HTTPS, which you already have).