Find a file
Russell Ballestrini 7a2a2133cf openapi for the win!
modified:   README.rst
	modified:   app.py
	new file:   openapi.yaml
2025-01-07 09:28:41 -05:00
templates download and view buttons 2024-12-31 08:41:52 -05:00
.gitignore A test_agent.sh & a README.rst 2025-01-06 19:56:48 -05:00
app.py openapi for the win! 2025-01-07 09:28:41 -05:00
initialize_db.py A test_agent.sh & a README.rst 2025-01-06 19:56:48 -05:00
openapi.yaml openapi for the win! 2025-01-07 09:28:41 -05:00
README.rst openapi for the win! 2025-01-07 09:28:41 -05:00
requirements.txt download and view buttons 2024-12-31 08:41:52 -05:00
test_agent.sh example agent 2025-01-06 21:23:58 -05:00

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

===========================================
pyrafiles - A Sophisticated Pyramid Project
===========================================

``pyrafiles`` is a **public domain** application built on the `Pyramid <https://trypyramid.com>`_ 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 users 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 <https://git.unturf.com/engineering/unturf/upload.unturf.com>`_

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://<HOST>:<PORT>` 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=<seconds> $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 <https://git.unturf.com/engineering/unturf/upload.unturf.com>`_.
- 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!