A test_agent.sh & a README.rst
deleted: .app.py.swp modified: .gitignore new file: README.rst modified: app.py new file: initialize_db.py new file: test_agent.sh
This commit is contained in:
parent
af2b5acc21
commit
596dbb923d
6 changed files with 547 additions and 80 deletions
303
README.rst
Normal file
303
README.rst
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
===========================================
|
||||
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 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 <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 & log 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 all users out.
|
||||
|
||||
5. **Access**
|
||||
|
||||
Point your browser to `http://localhost:6544` or `http://<HOST>:<PORT>` according to your environment variables.
|
||||
|
||||
|
||||
Example: Agent Workflow Script
|
||||
==============================
|
||||
|
||||
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!
|
||||
Loading…
Add table
Add a link
Reference in a new issue