update, and allow non-mount data

This commit is contained in:
q
2025-10-17 11:29:29 +03:00
parent fb5ce59582
commit 6bb6941287
7 changed files with 84 additions and 56 deletions

View File

@@ -1,36 +1,23 @@
FROM ubuntu:24.04 FROM debian:stable
COPY ./requirements.txt /requirements.txt
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
ENV TMPDIR=/tmp
RUN apt-get update -yqq \ RUN apt-get update -yqq \
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \
curl \ curl \
sqlite3 \ tzdata \
tzdata \ sqlite3 \
git \ python3-venv \
make \
python3-venv \
python3-pip \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/* \
&& mkdir -p /data \
&& python3 -m venv /opt/venv \
&& . /opt/venv/bin/activate \
&& pip3 install --no-cache -r /requirements.txt \
&& rm -rf /root/.cache
ARG UUID
ARG UGID
ARG TZ
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN id ubuntu && userdel ubuntu
RUN groupadd -g $UGID user && \
useradd -u $UUID -g $UGID -ms /bin/bash user && \
mkdir -p /opt/venv && chown $UUID:$UGID /opt/venv
COPY ./requirements.txt /requirements.txt
COPY docker-builder.sh /
USER user
RUN bash /docker-builder.sh
COPY ./ /app COPY ./ /app
USER root
RUN chown -R $UUID:$UGID /app
USER user
WORKDIR /app WORKDIR /app
RUN chmod 777 /app /data
CMD bash /app/entrypoint.sh CMD bash /app/entrypoint.sh

View File

@@ -20,6 +20,7 @@ from utils.files import (
db_add_download, db_add_download,
db_delete_file, db_delete_file,
db_get_file, db_get_file,
db_get_last_maintenance,
db_get_name, db_get_name,
db_maintenance, db_maintenance,
db_store_file, db_store_file,
@@ -28,6 +29,7 @@ from utils.files import (
file_full_url, file_full_url,
file_list, file_list,
file_list_simple, file_list_simple,
get_db,
invalidate_upload_token, invalidate_upload_token,
new_upload_token, new_upload_token,
validate_upload_token, validate_upload_token,
@@ -43,16 +45,17 @@ from werkzeug.utils import secure_filename
logging.basicConfig( logging.basicConfig(
level=logging.INFO, level=logging.INFO,
format=f"[%(asctime)s] [%(levelname)s] %(message)s", format="[%(asctime)s] [%(levelname)s] %(message)s",
) )
__VERSION__ = "20250328.0" __VERSION__ = "20251017.0"
app = Flask(__name__) app = Flask(__name__)
app.config.from_object(__name__) app.config.from_object(__name__)
app.config.from_prefixed_env() app.config.from_prefixed_env()
app.debug = True app.debug = True
app.secret_key = app.config["APP_SECRET_KEY"] app.secret_key = app.config["APP_SECRET_KEY"]
app.wsgi_app = ReverseProxied(app.wsgi_app) app.wsgi_app = ReverseProxied(app.wsgi_app)
app.config["KEY_LENGTH"] = int(app.config["KEY_LENGTH"])
@app.before_request @app.before_request
@@ -70,14 +73,24 @@ def log_the_status_code(response):
@app.route("/") @app.route("/")
def index(): def index():
"""Returns Index""" """Returns Index"""
return render_template( return render_template("index.html")
"index.html"
)
@app.route('/health.html', methods=["GET",]) # fmt: skip @app.route('/health', methods=["GET",]) # fmt: skip
def health(): def health():
return f"OK {request.url}", 200 try:
get_db()
except Exception:
return "DB Error", 500
try:
last_maintenance = db_get_last_maintenance()
if time.time() > last_maintenance + 86400: # Daily
return f"OK, {db_maintenance()}, {request.url}", 200
except Exception:
return "DB Maintenance error", 500
return f"OK, {request.url}", 200
@app.route("/upload", methods=["PUT", "POST"]) @app.route("/upload", methods=["PUT", "POST"])
@@ -144,7 +157,7 @@ def upload():
return "IP list contains unknown characters" return "IP list contains unknown characters"
while True: while True:
token = random_token() token = random_token(app.config["KEY_LENGTH"])
folder = os.path.join(app.config["DATAFOLDER"], token) folder = os.path.join(app.config["DATAFOLDER"], token)
if not os.path.exists(folder): if not os.path.exists(folder):
break break
@@ -196,6 +209,7 @@ def upload_token():
token = new_upload_token(expires) token = new_upload_token(expires)
return token, 200 return token, 200
@app.route("/details/<token>", methods=["GET"]) @app.route("/details/<token>", methods=["GET"])
@app.route("/details/<token>/<name>", methods=["GET"]) @app.route("/details/<token>/<name>", methods=["GET"])
def details(token, name=None): def details(token, name=None):
@@ -211,7 +225,10 @@ def details(token, name=None):
if secret != app.config["ACCESS_TOKEN"]: if secret != app.config["ACCESS_TOKEN"]:
return "Error", 401 return "Error", 401
if name is None: if name is None:
name = db_get_name(token)[0] try:
name = db_get_name(token)[0]
except TypeError:
return "No such file", 404
details = file_details(token, name) details = file_details(token, name)
if details["allowed_ip"] is not None: if details["allowed_ip"] is not None:
if not is_ip_allowed( if not is_ip_allowed(
@@ -223,7 +240,7 @@ def details(token, name=None):
@app.route("/delete/<token>", methods=["GET"]) @app.route("/delete/<token>", methods=["GET"])
@app.route("/delete/<token>/<name>", methods=["GET"]) @app.route("/delete/<token>/<name>", methods=["GET"])
def delete_file(token,name=None): def delete_file(token, name=None):
""" """
Delete a file from the system Delete a file from the system
""" """

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
PYTHON=python3
SQLITE=sqlite3 set -eu
export FLASK_DATAFOLDER="/data" export FLASK_DATAFOLDER="/data"
export FLASK_DB="/data/flees.db" export FLASK_DB="/data/flees.db"
@@ -9,21 +9,21 @@ export SERVER=gunicorn
export PID="flees.pid" export PID="flees.pid"
export WORKERS export WORKERS
export TIMEOUT export TIMEOUT
export TZ
if [[ $( stat -c %u /data ) -ne $( id -u ) ]]; then if [[ ! -w "$FLASK_DATAFOLDER" ]]; then
echo User id and /data folder owner do not match echo Cannot write to $FLASK_DATAFOLDER
printf 'UID: %s\nFolder: %s\n' $( id -u ) $( stat -c %u /data ) exit 1
exit 1
fi fi
set -eu
. /opt/venv/bin/activate . /opt/venv/bin/activate
sh ./init_db.sh "$FLASK_DB" sh ./init_db.sh "$FLASK_DB"
echo "Dowload script: curl -H 'Secret: [FLASK_ACCESS_TOKEN]' http://${FLASK_PUBLIC_URL}/script/mfl"
exec "$SERVER" \ exec "$SERVER" \
-w $WORKERS \ -w "$WORKERS" \
--timeout $TIMEOUT \ --worker-tmp-dir "$TMPDIR" \
--timeout "$TIMEOUT" \
--pid="$PID" \ --pid="$PID" \
-b 0.0.0.0:$INTERNAL_PORT \ -b 0.0.0.0:"$INTERNAL_PORT" \
'app:app' \ 'app:app' \
2>&1 | tee -a /data/flees.log 2>&1 | tee -a /data/flees.log

View File

@@ -2,6 +2,7 @@
cat <<'EOF' | sqlite3 "$1" cat <<'EOF' | sqlite3 "$1"
CREATE TABLE IF NOT EXISTS files ( CREATE TABLE IF NOT EXISTS files (
token text PRIMARY KEY, token text PRIMARY KEY,
name text NOT NULL, name text NOT NULL,
@@ -13,12 +14,18 @@ CREATE TABLE IF NOT EXISTS files (
allowed_ip text, allowed_ip text,
hidden boolean hidden boolean
); );
EOF
cat <<'EOF' | sqlite3 "$1"
CREATE TABLE IF NOT EXISTS upload_tokens ( CREATE TABLE IF NOT EXISTS upload_tokens (
token text PRIMARY KEY, token text PRIMARY KEY,
added integer NOT NULL, added integer NOT NULL,
expires integer NOT NULL expires integer NOT NULL
); );
CREATE TABLE IF NOT EXISTS tasks (
key text PRIMARY KEY,
value integer NOT NULL
);
INSERT OR IGNORE INTO tasks(key,value) VALUES('maintenance',0);
EOF EOF

View File

@@ -51,7 +51,6 @@ def db_get_name(token):
).fetchone() ).fetchone()
def db_get_files(): def db_get_files():
db, c = get_db() db, c = get_db()
return db.execute( return db.execute(
@@ -92,6 +91,16 @@ def db_add_download(token, name):
return return
def db_get_last_maintenance():
db, c = get_db()
return db.execute(
"""
SELECT value
FROM tasks WHERE key = 'maintenance'
"""
).fetchone()[0]
def db_maintenance(): def db_maintenance():
messages = [] messages = []
# === Delete DB entries where expiry or max DL is used up === # === Delete DB entries where expiry or max DL is used up ===
@@ -174,6 +183,9 @@ def db_maintenance():
if c.rowcount > 0: if c.rowcount > 0:
db.commit() db.commit()
db, c = get_db()
c.execute("UPDATE tasks SET value=? WHERE key='maintenance'", (int(time.time()),))
db.commit()
messages.append("Maintenance done.") messages.append("Maintenance done.")
return "\n".join(messages) return "\n".join(messages)

View File

@@ -3,15 +3,18 @@ services:
miniflees: miniflees:
build: build:
context: code context: code
args:
UUID: ${UUID}
UGID: ${UGID}
TZ: ${TZ}
ports: ports:
- "${EXPOSE}:5000" - "${EXPOSE}:5000"
volumes: volumes:
- ./data/:/data/ - $HOME/tmp/data/:/data/
restart: unless-stopped restart: unless-stopped
user: "${UUID}:${UGID}"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
interval: 3600s
timeout: 10s
retries: 3
start_period: 40s
environment: environment:
WORKERS: WORKERS:
TIMEOUT: TIMEOUT:
@@ -20,6 +23,7 @@ services:
FLASK_PUBLIC_URL: FLASK_PUBLIC_URL:
FLASK_DEFAULT_EXPIRE: FLASK_DEFAULT_EXPIRE:
FLASK_DEFAULT_MAX_DL: FLASK_DEFAULT_MAX_DL:
FLASK_KEY_LENGTH:
TZ: TZ:

View File

@@ -9,3 +9,4 @@ FLASK_ACCESS_TOKEN=dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415
FLASK_PUBLIC_URL=http://localhost:8136 FLASK_PUBLIC_URL=http://localhost:8136
FLASK_DEFAULT_EXPIRE=2592000 FLASK_DEFAULT_EXPIRE=2592000
FLASK_DEFAULT_MAX_DL=9999 FLASK_DEFAULT_MAX_DL=9999
FLASK_KEY_LENGTH=8