update, and allow non-mount data
This commit is contained in:
@@ -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 \
|
||||||
git \
|
sqlite3 \
|
||||||
make \
|
|
||||||
python3-venv \
|
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
|
||||||
|
|||||||
33
code/app.py
33
code/app.py
@@ -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:
|
||||||
|
try:
|
||||||
name = db_get_name(token)[0]
|
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(
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user