aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Dockerfile2
-rw-r--r--README.md30
-rw-r--r--src/app.py22
-rw-r--r--src/requirements.txt6
4 files changed, 45 insertions, 15 deletions
diff --git a/Dockerfile b/Dockerfile
index 7a213a7..cb2b9df 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM python:3.13-slim
+FROM python:3.12-slim
# enable contrib (fonts-ibm-plex) and non-free (fonts-ubuntu) components
RUN sed -i 's/^Components: main$/Components: main contrib non-free/' /etc/apt/sources.list.d/debian.sources
diff --git a/README.md b/README.md
index 140f679..73e02b1 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
> suckless's sent tool ported to the very sucky web world
A web-based reimplementation of [suckless sent](https://tools.suckless.org/sent/)
-using pure PHP and vanilla JavaScript.
+using Python and vanilla JavaScript.
<img width="1280" height="800" alt="sent0" src="https://github.com/user-attachments/assets/0c503bd4-3609-4e36-ae23-77b7f0711736" />
<br><br>
@@ -17,9 +17,8 @@ using pure PHP and vanilla JavaScript.
(same as sent)
- **mouse navigation** — left-click right half = next, left half = prev, scroll
wheel
-- **image upload** — upload images and insert `@filename` references
+- **image upload** — upload images and insert `@filename` references (50 MB cap)
- **export** — download as `.sent` file for local sent, or export `.pdf` for portability
-
## usage
### docker compose (recommended)
@@ -37,6 +36,26 @@ docker build -t sent-web .
docker run -d -p 3000:3000 --init --name sent-web sent-web
```
+### local python run (without docker)
+
+Requirements:
+
+- Python `3.12+`
+- `fontconfig` (`fc-list` must be available)
+- `libmagic` runtime (`libmagic1` on Ubuntu)
+
+Setup:
+
+```sh
+cd src
+python3.12 -m venv .venv
+. .venv/bin/activate
+pip install -r requirements.txt
+gunicorn --bind 0.0.0.0:3000 --workers 2 app:app
+```
+
+Then open [http://localhost:3000](http://localhost:3000).
+
### presentation shortcuts
| key | action |
@@ -65,11 +84,12 @@ with multiple lines
## technology
-- **PHP 8.3** — no framework, just `.php` files
+- **Python 3.12+** — Flask backend
- **vanilla JavaScript** — no npm, no webpack, no react
- **[noir.css](https://github.com/kj-sh604/noir.css)** — classless CSS
-- **Apache** — serves it all
+- **Gunicorn** — production WSGI server
- **fontconfig** — `fc-list` for font enumeration
+- **python-magic + libmagic** — content-based upload type checks
- **Docker** — containerized with fonts pre-installed
## license
diff --git a/src/app.py b/src/app.py
index dce80a6..7d75f2b 100644
--- a/src/app.py
+++ b/src/app.py
@@ -1,5 +1,4 @@
#!/usr/bin/env python3
-# sent-web — app.py
import base64
import os
@@ -12,6 +11,7 @@ import magic
from flask import Flask, Response, jsonify, request, send_file, send_from_directory
app = Flask(__name__, static_folder=None)
+app.config["MAX_CONTENT_LENGTH"] = 50 * 1024 * 1024 # 50 MB upload cap
UPLOAD_DIR = Path(__file__).parent / "uploads"
UPLOAD_DIR.mkdir(mode=0o755, exist_ok=True)
@@ -84,11 +84,21 @@ def upload():
@app.route("/fonts")
def fonts():
- result = subprocess.run(
- ["fc-list", "--format=%{family}|%{style}|%{file}\n"],
- capture_output=True,
- text=True,
- )
+ try:
+ result = subprocess.run(
+ ["fc-list", "--format=%{family}|%{style}|%{file}\n"],
+ capture_output=True,
+ text=True,
+ shell=False,
+ timeout=10,
+ check=False,
+ )
+ except (FileNotFoundError, subprocess.TimeoutExpired):
+ return jsonify([])
+
+ if result.returncode != 0:
+ return jsonify([])
+
if not result.stdout.strip():
return jsonify([])
diff --git a/src/requirements.txt b/src/requirements.txt
index c9d8662..dfce493 100644
--- a/src/requirements.txt
+++ b/src/requirements.txt
@@ -1,3 +1,3 @@
-flask>=3.0,<4
-gunicorn>=22,<25
-python-magic>=0.4.27
+flask>=3.0,<3.2
+gunicorn>=22,<24
+python-magic>=0.4.27,<0.5 \ No newline at end of file