=========================================== pyrafiles - A Sophisticated Pyramid Project =========================================== ``pyrafiles`` is a **public domain** application built on the `Pyramid `_ framework. It demonstrates a flexible, multi-database design where each verified user maintains their own SQLite database for media uploads (images, audio, video). The main database (``main.db``) stores system- and user-level data, while each user’s personal DB file handles their specific uploads. Key Features ============ - **Passwordless Login** using email verification codes. - **Guest Sessions** for unverified visitors. - **Per-user SQLite** databases for media uploads (limit ~30MB each). - **Public/Private** media visibility controls. - **Admin Tools** (import user records, manage site settings). - **Agent-friendly** architecture: easily scriptable endpoints for login, verification, media uploads, etc. - **Configurable** via environment variables (including secret keys). Git Repository ============== The project is maintained at: - `pyrafiles Git Repo `_ Since this is public domain, you can adapt and redistribute it freely. Configuration via Environment ============================= `pyrafiles` fetches settings from environment variables with sensible defaults: - ``PYRAFILES_SECRET`` The secret key for session signing. If missing, ``pyrafiles`` automatically generates a **random 64-character** string at runtime & will log out all users out. - ``PYRAFILES_DB_URL`` Connection string for the main database. Default: ``sqlite:///main.db``. - ``PYRAFILES_HOST`` and ``PYRAFILES_PORT`` The host and port to serve on. Defaults: ``0.0.0.0`` (host), ``6544`` (port). - ``PYRAFILES_SMTP_HOST`` and ``PYRAFILES_SMTP_PORT`` SMTP server details. Defaults: ``localhost:25``. - Any other environment variables you wish to incorporate can be accessed in the code. Local Setup =========== 1. **Clone the Project** .. code-block:: bash git clone https://git.unturf.com/engineering/unturf/upload.unturf.com.git cd upload.unturf.com 2. **Create a Virtual Environment** .. code-block:: bash python -m venv venv source venv/bin/activate # Linux/Mac # or venv\Scripts\activate.bat # Windows 3. **Install Dependencies** .. code-block:: bash pip install -r requirements.txt 4. **Run** Create the database if this is the first time running the applicaiton. .. code-block:: bash python initialize_db.py Otherwise: .. code-block:: bash # Optionally set PYRAFILES_SECRET if you want a custom secret. export PYRAFILES_SECRET="YOUR_OWN_LONG_RANDOM_STRING" python main.py If ``PYRAFILES_SECRET`` is **not** set, the app automatically generates a 64-char secret at runtime & log out all users. 5. **Access** Point your browser to `http://localhost:6544` or `http://:` according to your environment variables. Example: Agent Workflow Script ============================== First of all, everything is documented as OpenAPI for agentic flows. * http://localhost:6544/docs * http://localhost:6544/openapi.yaml Below is a sample Bash script showing how an **agent** might: 1. Start the session (to get a cookie). 2. Log in with an email. 3. Prompt the user for a verification code. 4. Complete the verification. 5. Upload a file. .. code-block:: bash #!/usr/bin/env bash # # Usage: # MAX_COOKIE_AGE=1800 ./agent_upload.sh /path/to/somefile.jpg # # Description: # Upload a file to the pyrafiles server as an agent, making it public. # - If cookies.txt is newer than MAX_COOKIE_AGE seconds (default 31536000, ~1 year), # we skip re-login. # - Otherwise, we prompt for OTP again. # # Notes: # 1. If not set, MAX_COOKIE_AGE defaults to 31536000 (one year). # 2. We assume the server runs on http://localhost:6544. Adjust BASE_URL if needed. BASE_URL="${BASE_URL:-http://localhost:6544}" EMAIL="${EMAIL:-agent@example.com}" MEDIA_FILE="$1" # Default to 1 year in seconds if not provided: MAX_COOKIE_AGE="${MAX_COOKIE_AGE:-31536000}" if [[ -z "$MEDIA_FILE" ]]; then echo "Usage: MAX_COOKIE_AGE= $0 /path/to/mediafile" exit 1 fi # Check if cookies.txt is 'fresh enough': COOKIE_FRESH=0 if [[ -f cookies.txt ]]; then # 'stat -c %Y' returns the file's mod time on Linux; 'stat -f %m' on macOS/BSD MOD_TIME=$(stat -c %Y cookies.txt 2>/dev/null || stat -f %m cookies.txt 2>/dev/null) NOW=$(date +%s) AGE=$((NOW - MOD_TIME)) if (( AGE < MAX_COOKIE_AGE )); then COOKIE_FRESH=1 fi fi if [[ "$COOKIE_FRESH" -eq 1 ]]; then echo "Reusing existing cookies (file age < $MAX_COOKIE_AGE seconds)." echo "Skipping OTP prompt." else echo "Starting new session (cookie missing or stale)..." curl -s -c cookies.txt -b cookies.txt "$BASE_URL/" >/dev/null echo "Logging in with email: $EMAIL" curl -s -c cookies.txt -b cookies.txt \ -X POST \ -F "email=$EMAIL" \ "$BASE_URL/auth/login" echo "" echo "Check your console or email for the 6-digit verification code." read -p "Enter the 6-digit code: " VERIFICATION_CODE echo "Verifying code..." curl -s -c cookies.txt -b cookies.txt \ -X POST \ -F "code=$VERIFICATION_CODE" \ "$BASE_URL/auth/verify" echo "Cookies refreshed." fi echo "" echo "Uploading media: $MEDIA_FILE (public)" curl -s -c cookies.txt -b cookies.txt \ -X POST \ -F "media_file=@$MEDIA_FILE" \ -F "is_public=on" \ "$BASE_URL/media/upload" echo "" echo "Upload complete. File is public." Dockerfile with Caddy + uWSGI ============================= Below is an example Dockerfile that: - Uses **uWSGI** to run ``pyrafiles``. - Uses **Caddy** as a reverse proxy (and optionally HTTPS if configured). - Defines a **volume** for databases (so they are stored on the host). .. code-block:: dockerfile ########################################################### # Stage 1: Build the Python environment ########################################################### FROM python:3.9-slim AS builder WORKDIR /app COPY . /app # Install Python dependencies RUN pip install --no-cache-dir -r requirements.txt ########################################################### # Stage 2: Final image with Caddy + uWSGI + Python ########################################################### FROM caddy:2-alpine # Install Python, pip, and uWSGI from Alpine RUN apk add --no-cache python3 py3-pip uwsgi-python3 WORKDIR /app # Copy the app from the builder COPY --from=builder /app /app # Copy Caddyfile for the reverse proxy # (Assuming you have a Caddyfile in your repo root) COPY Caddyfile /etc/caddy/Caddyfile # Copy uWSGI config if desired # For example (assume you created uwsgi.ini in your repo): # [uwsgi] # module = main:app # master = true # processes = 4 # socket = 127.0.0.1:8080 # vacuum = true # die-on-term = true COPY uwsgi.ini /app/uwsgi.ini # Environment variables (optional overrides). # If PYRAFILES_SECRET is empty, the app itself generates a random 64-char secret & logs all users out. ENV PYRAFILES_SECRET="" ENV PYRAFILES_DB_URL="sqlite:///data/main.db" ENV PYRAFILES_HOST="0.0.0.0" ENV PYRAFILES_PORT="6544" # Expose HTTP and HTTPS EXPOSE 80 EXPOSE 443 # Define volume so host can persist user DBs outside the container # We'll store DB files in /data VOLUME ["/data"] # Command starts uWSGI and then starts Caddy # The app itself checks for PYRAFILES_SECRET and auto-generates one if missing. CMD ["/bin/sh", "-c", "\ uwsgi --ini /app/uwsgi.ini & \ caddy run --config /etc/caddy/Caddyfile \ "] .. note:: - We use ``/data`` as the volume. By default, the environment variable ``PYRAFILES_DB_URL`` is set to ``sqlite:///data/main.db``, so the main DB (and any user DB files) go inside ``/data``. - For user DB files, your app can also interpret an environment variable (like ``PYRAFILES_DB_BASEPATH=/data``) if you want to make that path configurable in the code. - Make sure to **mount** a volume at ``/data`` when you run the container, e.g.: .. code-block:: bash docker run -d \ -p 80:80 -p 443:443 \ -v /my/local/dbfolder:/data \ --name pyrafiles \ pyrafiles-image:latest - With that, any user database is stored in ``/my/local/dbfolder`` on the host. Tips ==== - For **production**, you likely want to set up a real SMTP server or third-party service (e.g. Mailgun, ImprovMV) and configure it via environment variables (``PYRAFILES_SMTP_HOST``, etc.). - Ensure you mount a volume for persistent SQLite files if you want to avoid data loss when containers are removed or replaced. - Because the project is **public domain**, you can adapt it without restriction, removing features, adding custom logic, etc. License and Public Domain ========================= This project is in the public domain. You are free to use, adapt, and redistribute it without attribution or additional licensing. If you find `pyrafiles` helpful, feel free to contribute back or share your enhancements! Support and Contact =================== - Issues: Please open tickets at the `git.unturf.com project page `_. - For general inquiries, you can reach out to the maintainers directly. We hope `pyrafiles` helps you get up and running quickly with a flexible media-sharing and multi-DB infrastructure! If you register an account, let me know and I will bless you as an developer to contribute. Enjoy and happy building!