From cfee5287d5344fe3c7ed8cedceca7ba079b43098 Mon Sep 17 00:00:00 2001 From: Q Date: Sat, 19 Aug 2023 19:42:31 +0300 Subject: [PATCH] getting there --- README.md | 40 ++++--- code/Dockerfile | 12 +-- code/app.py | 108 ++++++++++++------- code/utils/__init__.py | 4 +- code/utils/crypt.py | 5 - code/utils/files.py | 131 +++++++++-------------- code/utils/misc.py | 51 ++------- docker-compose.yaml | 4 +- test/run-tests.sh | 231 ++++++++++++++++++++++++++++++----------- 9 files changed, 332 insertions(+), 254 deletions(-) delete mode 100644 code/utils/crypt.py diff --git a/README.md b/README.md index f5898db..128f0f8 100644 --- a/README.md +++ b/README.md @@ -3,31 +3,39 @@ A mini file sharing website. -# installation - -- `bash create_config.sh` -- `docker-compose up --build` - # configuration -- configure service with data/config.json - - Change your `app_secret_key` !! - - Change your `access_token` !! - - Change your `public_url` - - Change your `timezone` - - `uid` = user id for new files - - `workers` = parallel processes (i.e. one upload reserves a process) - - `timeout` = timeout for processes, single upload might take a long time! -- configure bind host and port in .env +Configure service with .env: + +``` +EXPOSE=0.0.0.0:8136 # IP/Port to bind +UID=1000 # files will be written as user +GID=1000 # files will be writter as group +TZ=Europe/Helsinki # your timezone +WORKERS=4 # number of concurrent processes +FLASK_APP_SECRET_KEY=8a36bfea77d842386a2a0c7c3e044228363d # Key that encrypts cookies +FLASK_ACCESS_TOKEN=dff789f0bbe8183d3254258b33a147d5 # Key used in request headers to allow upload etc. +FLASK_PUBLIC_URL=http://localhost:8136 # Public url +FLASK_DEFAULT_EXPIRE=2592000 # Default seconds to expire files +FLASK_DEFAULT_MAX_DL=9999 # Default maximum download times +``` + - proxy with nginx, match bind port, body size and timeout to your needs: ``` -location /flees/ { +location /mfl/ { proxy_pass http://localhost:8136/; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Scheme $scheme; - proxy_set_header X-Script-Name /flees; + proxy_set_header X-Script-Name /mfl; client_max_body_size 8G; client_body_timeout 3600s; } ``` + +# Running + +- `docker-compose up --build` + + + diff --git a/code/Dockerfile b/code/Dockerfile index 56f40a8..f9a0fbb 100644 --- a/code/Dockerfile +++ b/code/Dockerfile @@ -14,13 +14,13 @@ RUN apt-get update -yqq \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -ARG UID -ARG GID +ARG UUID +ARG UGID ARG TZ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone -RUN groupadd -g $GID user && \ - useradd -u $UID -g $GID -ms /bin/bash user && \ - mkdir -p /opt/venv && chown $UID:$GID /opt/venv +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 @@ -28,7 +28,7 @@ USER user RUN bash /docker-builder.sh COPY ./ /app USER root -RUN chown -R $UID:$GID /app +RUN chown -R $UUID:$UGID /app USER user WORKDIR /app diff --git a/code/app.py b/code/app.py index e6ff4e8..22fb0d6 100644 --- a/code/app.py +++ b/code/app.py @@ -1,37 +1,33 @@ # -*- coding: utf-8 -*- -import os, sys, time -import json -from datetime import datetime +import os +import sys +import time from flask import ( Flask, render_template, jsonify, - current_app, - Response, - redirect, - url_for, request, - g, - session, - send_file, send_from_directory, - abort, ) from werkzeug.utils import secure_filename from revprox import ReverseProxied -from utils import ( +from utils.misc import ( random_token, +) +from utils.files import ( db_store_file, file_details, file_list, + file_full_path, + file_full_url, db_add_download, db_get_file, db_delete_file, db_maintenance, ) -__MINI_FLEES_VERSION__ = "20230818.0" +__VERSION__ = "20230818.0" app = Flask(__name__) app.config.from_object(__name__) app.config.from_prefixed_env() @@ -42,11 +38,30 @@ app.wsgi_app = ReverseProxied(app.wsgi_app) @app.route("/") def index(): + """Returns Nothing""" return "", 200 @app.route("/upload", methods=["POST"]) def upload(): + """ + Upload a file, example CURL: + + cat file | \ + curl -fL -w "\n" -F file="@-" -X POST \ + -H "Name: my.file.ext" \ + -H "Max-Downloads: 4000" \ + -H "Expires-Days: 14" \ + -H "Secret: dff789f0bbe8183d32542" \ + "$FLASK_PUBLIC_URL"/upload + + - Additionally, "Expires-Hours" can be used. + - Max-Dowloads: -1 means no upper limit + + + Returns the file download URL + """ + if request.method == "POST": file = request.files.get("file") name = request.headers.get("Name", None) @@ -68,13 +83,16 @@ def upload(): if file: safe_filename = secure_filename(name) - token = random_token() - folder = os.path.join(app.config["DATAFOLDER"], token) + while True: + token = random_token() + folder = os.path.join(app.config["DATAFOLDER"], token) + if not os.path.exists(folder): + break os.mkdir(folder) - filename = os.path.join(folder, safe_filename) + filename = file_full_path(token, safe_filename) file.save(filename) db_store_file(token, safe_filename, expires, max_dl) - download_url = f"{app.config['PUBLIC_URL']}/dl/{token}/{safe_filename}" + download_url = file_full_url(token, safe_filename) return "File uploaded\n%s\n" % (download_url,), 200 else: return "Use the 'file' variable to upload\n", 400 @@ -82,6 +100,14 @@ def upload(): @app.route("/details//", methods=["GET"]) def details(token, name): + """ + Get JSON of file details. Size, added date, download times, etc. + + curl -fL -w "\n" \ + -H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ + "$ROOTURL"/details/OdD7X0aKOGM/big_file1.ext + + """ secret = request.headers.get("Secret", "") if secret != app.config["ACCESS_TOKEN"]: return "Error", 401 @@ -91,11 +117,14 @@ def details(token, name): @app.route("/delete//", methods=["GET"]) def delete_file(name, token): + """ + Delete a file from the system + """ secret = request.headers.get("Secret", "") if secret != app.config["ACCESS_TOKEN"]: return "Error", 401 try: - os.remove(os.path.join(os.getenv("DATAFOLDER"), token, name)) + os.remove(os.path.join(app.config["DATAFOLDER"], token, name)) except Exception: pass db_delete_file(token, name) @@ -104,6 +133,9 @@ def delete_file(name, token): @app.route("/ls", methods=["GET"]) def ls(): + """ + Lists all uploaded files + """ secret = request.headers.get("Secret", "") if secret != app.config["ACCESS_TOKEN"]: return "Error", 401 @@ -112,6 +144,11 @@ def ls(): @app.route("/maintenance", methods=["GET"]) def maintenance(): + """ + Clears DB of expired entries. + Deletes folders without DB entry + """ + secret = request.headers.get("Secret", "") if secret != app.config["ACCESS_TOKEN"]: return "Error", 401 @@ -120,29 +157,30 @@ def maintenance(): @app.route("/dl//", methods=["GET"]) def download(name, token): + """ + Download a file + """ return download_file(token, name) -@app.route("/script/client", methods=["GET"]) -def script_client(): +@app.route("/script/mfl", methods=["GET"]) +def script_mfl(): + secret = request.headers.get("Secret", "") + if secret != app.config["ACCESS_TOKEN"]: + return "Error", 401 return render_template( - "client.py", name=name, token=token, rooturl=request.url_root - ) - - -@app.route("/script/flip", methods=["GET"]) -def script_flip(): - return render_template( - "flip", - name=name, - token=token, - rooturl=request.url_root, - version=__FLEES_VERSION__, + "mfl", + token=app.config["ACCESS_TOKEN"], + rooturl=app.config["PUBLIC_URL"], + version=__VERSION__, ) def download_file(token, name): - full_path = os.path.join(os.getenv("FLASK_DATAFOLDER"), token, name) + """ + check for file expiry, and send file if allowed + """ + full_path = os.path.join(app.config["DATAFOLDER"], token, name) if not os.path.exists(full_path): return "Error", 404 db_stat = db_get_file(token, name) @@ -150,13 +188,13 @@ def download_file(token, name): added, expires, downloads, max_dl = db_stat else: return "Error", 404 - if downloads >= max_dl: + if downloads >= max_dl and max_dl > -1: return "Expired", 401 if expires < time.time(): return "Expired", 401 db_add_download(token, name) return send_from_directory( - directory=os.path.join(os.getenv("FLASK_DATAFOLDER"), token), path=name + directory=os.path.join(app.config["DATAFOLDER"], token), path=name ) diff --git a/code/utils/__init__.py b/code/utils/__init__.py index e45bb4d..8b13789 100644 --- a/code/utils/__init__.py +++ b/code/utils/__init__.py @@ -1,3 +1 @@ -from .misc import * -from .files import * -from .crypt import * + diff --git a/code/utils/crypt.py b/code/utils/crypt.py deleted file mode 100644 index 609715f..0000000 --- a/code/utils/crypt.py +++ /dev/null @@ -1,5 +0,0 @@ -import secrets - - -def random_token(): - return secrets.token_urlsafe(8) diff --git a/code/utils/files.py b/code/utils/files.py index 7461cd4..2252c3b 100644 --- a/code/utils/files.py +++ b/code/utils/files.py @@ -1,19 +1,14 @@ import os from datetime import datetime from flask import current_app as app -import re -import sys -import json -import stat import time import sqlite3 import shutil -from .misc import * -from .crypt import * +from .misc import file_size_MB, file_size_human, file_date_human def get_db(): - db = sqlite3.connect(os.getenv("FLASK_DB"), timeout=5) + db = sqlite3.connect(app.config["DB"], timeout=5) c = db.cursor() return db, c @@ -49,8 +44,8 @@ def db_get_files(): """ SELECT token,name,added,expires,downloads,max_downloads FROM files - WHERE expires > ? and downloads < max_downloads - """, + WHERE expires > ? and (downloads < max_downloads or max_downloads = -1) + """, (time.time(),), ) @@ -82,14 +77,15 @@ def db_add_download(token, name): def db_maintenance(): messages = [] + # === Delete DB entries where expiry or max DL is used up === db, c = get_db() rows = db.execute( """ select token, name from files - where expires < ? or downloads >= max_downloads - """, + where expires < ? or (downloads >= max_downloads and max_downloads != -1) + """, (int(time.time()),), ) deleted_tokens = [] @@ -101,7 +97,7 @@ def db_maintenance(): c.executemany("DELETE FROM files WHERE token = ?", deleted_tokens) if c.rowcount > 0: db.commit() - + # Delete files, if DB entry is gone subdirs = next(os.walk(app.config["DATAFOLDER"]))[1] db, c = get_db() for d in subdirs: @@ -111,13 +107,35 @@ def db_maintenance(): ).fetchone() if not keep: try: - for fname in os.listdir(os.path.join(app.config["DATAFOLDER"],d)): - os.remove(os.path.join(app.config["DATAFOLDER"],d,fname)) + for fname in os.listdir(os.path.join(app.config["DATAFOLDER"], d)): + os.remove(os.path.join(app.config["DATAFOLDER"], d, fname)) messages.append(f"Deleting file {d}/{fname}") except Exception: pass - shutil.rmtree(os.path.join(app.config["DATAFOLDER"],d), ignore_errors=True) + shutil.rmtree(os.path.join(app.config["DATAFOLDER"], d), ignore_errors=True) messages.append(f"Deleting folder {d}") + + # Delete DB entries, if files have been deleted (probably manually) + db, c = get_db() + rows = db.execute( + """ + select + token, name + from files + """ + ) + deleted_tokens = [] + for row in rows: + full_path = file_full_path(row[0], row[1]) + if not os.path.exists(full_path): + deleted_tokens.append((row[0],)) + messages.append(f"Deleting DB {row[0]}/{row[1]} - files missing") + if len(deleted_tokens) > 0: + db, c = get_db() + c.executemany("DELETE FROM files WHERE token = ?", deleted_tokens) + if c.rowcount > 0: + db.commit() + messages.append("Maintenance done.") return "\n".join(messages) @@ -133,7 +151,7 @@ def file_age(path): def file_details(token, name): - full_path = os.path.join(os.getenv("FLASK_DATAFOLDER"), token, name) + full_path = file_full_path(token, name) try: s = os.stat(full_path) db_stat = db_get_file(token, name) @@ -146,7 +164,7 @@ def file_details(token, name): "hsize": file_size_human(s.st_size, HTML=False), "added": file_date_human(added), "name": name, - "url": f"{app.config['PUBLIC_URL']}/dl/{token}/{name}", + "url": file_full_url(token, name), "expires": file_date_human(expires), "downloaded": downloads, "max-dl": max_dl, @@ -155,76 +173,23 @@ def file_details(token, name): return {} +def file_full_path(token, name): + return os.path.join(app.config["DATAFOLDER"], token, name) + + +def file_full_url(token, name): + return f"{app.config['PUBLIC_URL']}/dl/{token}/{name}" + + def file_list(): files = list(db_get_files()) details = [] - details.append(" Added/Expiry DL/MaxDL URL") + details.append(" Added/Expiry DL/MaxDL URL") details.append("=" * 75) for file in files: - url = f"{app.config['PUBLIC_URL']}/dl/{file[0]}/{file[1]}" - details.append( - f"{file_date_human(file[2])}/{file_date_human(file[3])} {file[4]:4d}/{file[5]:4d} {url}" - ) + url = file_full_url(file[0], file[1]) + added = file_date_human(file[2]) + expiry = file_date_human(file[3]) + details.append(f"{added}/{expiry} {file[4]:4d}/{file[5]:4d} {url}") return details - - -def get_expiring_file(ehash): - connection = apsw.Connection(app.config["SQLITE_FILE"]) - cursor = connection.cursor() - - for row in cursor.execute( - "SELECT file, expires FROM expiring WHERE hash = ?", (ehash,) - ): - return row[0], row[1] - return None, None - - -def get_script_url(public_url, share, end_point, token="[TOKEN]"): - cmd = None - doc = None - if get_or_none("direct_links", share) and end_point == "download": - end_point = "direct" - url = "%s/script/%s/%s/%s" % (public_url, end_point, share["name"], token) - if end_point in ("download", "direct"): - cmd = "curl -s %s | bash /dev/stdin [-f]" % (url,) - doc = "Download all files in the share. -f to force overwrite existing files." - if end_point == "client": - cmd = "python <( curl -s %s )" % (url,) - doc = "Console client to download and upload files." - if end_point == "upload_split": - cmd = ( - "curl -s %s | python - [-s split_size_in_Mb] file_to_upload.ext [second.file.ext]" - % (url,) - ) - doc = "Upload files to the share. -s to set splitting size." - if end_point == "flip": - cmd = "curl -s %s > flip && ./flip" % (url,) - doc = "Use the share as a command line clipboard" - return {"cmd": cmd, "doc": doc} - - -def set_expiring_file(share, filename, expires): - connection = apsw.Connection(app.config["SQLITE_FILE"]) - cursor = connection.cursor() - - while True: - ehash = random_expiring_hash() - matches = len( - list(cursor.execute("SELECT file FROM expiring WHERE hash = ?", (ehash,))) - ) - if matches == 0: - break - - cursor.execute( - "INSERT INTO expiring (hash, file, expires) VALUES (?,?,?)", - (ehash, filename, expires), - ) - return "/".join((app.config["PUBLIC_URL"], "e", ehash, os.path.basename(filename))) - - -def remove_expiring_file(share, filename): - connection = apsw.Connection(app.config["SQLITE_FILE"]) - cursor = connection.cursor() - - cursor.execute("DELETE FROM expiring WHERE file = ?", (filename,)) diff --git a/code/utils/misc.py b/code/utils/misc.py index 40f06f9..2cc5062 100644 --- a/code/utils/misc.py +++ b/code/utils/misc.py @@ -1,5 +1,12 @@ from datetime import datetime -from flask import current_app as app +import secrets +import string + +VALID_TOKEN_CHARS = string.digits + string.ascii_letters + + +def random_token(): + return "".join(secrets.choice(VALID_TOKEN_CHARS) for i in range(8)) def file_date_human(num): @@ -22,45 +29,3 @@ def file_size_human(num, HTML=True): def file_size_MB(num): return "{:,.2f}".format(num / (1024 * 1024)) - - -def get_or_none(key, d, none=None): - if key in d: - return d[key] - else: - return none - - -def is_path_safe(path): - if path.startswith("."): - return False - if "/." in path: - return False - return True - - -def is_valid_url(url, qualifying=None): - min_attributes = ("scheme", "netloc") - qualifying = min_attributes if qualifying is None else qualifying - token = urlparse(url) - return all([getattr(token, qualifying_attr) for qualifying_attr in qualifying]) - - -def path2url(path): - return pathname2url(path) - - -def safe_name(s): - return safe_string(s, "-_") - - -def safe_path(s): - return safe_string(s, "-_/") - - -def safe_string(s, valid, no_repeat=False): - """return a safe string, replace non alnum characters with _ . all characters in valid are considered valid.""" - safe = "".join([c if c.isalnum() or c in valid else "_" for c in s]) - if no_repeat: - safe = re.sub(r"_+", "_", safe) - return safe diff --git a/docker-compose.yaml b/docker-compose.yaml index f617828..adb3001 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -6,8 +6,8 @@ services: build: context: code args: - UID: ${UID} - GID: ${GID} + UUID: ${UUID} + UGID: ${UGID} TZ: ${TZ} ports: - "${EXPOSE}:5000" diff --git a/test/run-tests.sh b/test/run-tests.sh index 86e7e4e..aa39c3d 100755 --- a/test/run-tests.sh +++ b/test/run-tests.sh @@ -65,7 +65,7 @@ _qCol() { } -cont() { +_cont() { set +x _qCol G echo Continue @@ -76,6 +76,13 @@ cont() { _qCol z set -x } +_title() { + _qCol G + echo "$1" + _qCol Y + echo ========================================= + _qCol z +} set -e @@ -86,82 +93,184 @@ SMALL="small file" SMALLS="small_file" IMAGE="image.jpg" + test -f "$BIG" || dd if=/dev/zero of="$BIG" bs=8192 count=40000 test -f "$SMALL" ||dd if=/dev/urandom of="$SMALL" bs=4096 count=400 test -f "$IMAGE" || convert -size 640x480 xc:gray $IMAGE -set -x -#~ cont -ROOTURL="http://localhost:8136" +. ../.env -if false; then - pv "$IMAGE" | \ - curl -fL -w "\n" -F file="@-" -X POST \ - -H "Name: $IMAGE" \ - -H "Max-Downloads: 4" \ - -H "Expires-Days: 14" \ - -H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ - "$ROOTURL"/upload -fi +function t00-rebuild-docker() { + CWD=$PWD + cd .. + docker-compose up --build -d --force-recreate + cd "$CWD" +} -if false; then - pv "$SMALL" | \ - curl -fL -w "\n" -F file="@-" -X POST \ - -H "Name: $SMALL" \ - -H "Max-Downloads: 4000" \ - -H "Expires-Days: 14" \ - -H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ - "$ROOTURL"/upload +function t01-maintenance-begin() { + curl -fL -w "\n" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/maintenance +} - pv "$BIG" | \ - curl -fL -w "\n" -F file="@-" -X POST \ - -H "Name: $BIG" \ - -H "Max-Downloads: 4000" \ - -H "Expires-Days: 14" \ - -H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ - "$ROOTURL"/upload -fi +function t02-upload-small-image() { -sqlite3 ../data/flees.db "select * FROM files" + pv "$IMAGE" | \ + curl -fL -w "\n" -F file="@-" -X POST \ + -H "Name: $IMAGE" \ + -H "Max-Downloads: 4" \ + -H "Expires-Days: 1" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/upload +} -if false; then - curl -fL -w "\n" \ - -H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ - "$ROOTURL"/details/OdD7X0aKOGM/big_file1.ext +function t03-upload-small-file() { + pv "$SMALL" | \ + curl -fL -w "\n" -F file="@-" -X POST \ + -H "Name: $SMALL" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/upload -fi +} -if false; then - rm -f big_file1.ext - wget \ - "$ROOTURL"/dl/OdD7X0aKOGM/big_file1.ext -fi +function t04-upload-large-file() { + pv "$BIG" | \ + curl -fL -w "\n" -F file="@-" -X POST \ + -H "Name: $BIG" \ + -H "Max-Downloads: 4" \ + -H "Expires-Days: 1" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/upload +} -if false; then +function t05-check-db-manually() { + sqlite3 ../data/flees.db "select * FROM files" +} + +function t06-list-files() { + + curl -fL -w "\n" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/ls +} + +function t07-file-download() { + download_url=$( curl -fL -w "\n" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/ls | grep "$BIGS" | tail -n 1 | sed s,.*http,http, ) + + rm -f "$BIGS" + wget "$download_url" +} + +function t08-file-details() { + download_url=$( curl -fL -w "\n" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/ls | grep "$BIGS" | tail -n 1 | sed s,.*http,http, ) + token_name=$( echo $download_url | sed s,.*/dl/,, ) + curl -fL -w "\n" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/details/"$token_name" +} +function t09-file-delete() { + + + download_url=$( curl -fL -w "\n" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/ls | grep "$BIGS" | tail -n 1 | sed s,.*http,http, ) + token_name=$( echo $download_url | sed s,.*/dl/,, ) + curl -fL -w "\n" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/delete/"$token_name" +} + +function t09-unlimited-downloads() { + pv "$SMALL" | \ + curl -fL -w "\n" -F file="@-" -X POST \ + -H "Name: $SMALL" \ + -H "Max-Downloads: -1" \ + -H "Expires-Days: 1" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/upload + download_url=$( curl -fL -w "\n" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/ls | grep "$SMALLS" | tail -n 1 | sed s,.*http,http, ) + token_name=$( echo $download_url | sed s,.*/dl/,, ) + for ((i=0;i<10;i++)); do curl -fL -w "\n" \ - -H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ - "$ROOTURL"/delete/SKDMsQ3ifx8/image.jpg -fi - -if true; then + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/dl/"$token_name" > /dev/null curl -fL -w "\n" \ - -H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ - "$ROOTURL"/ls -fi + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/details/"$token_name" + done +} -if true; then - pv "$SMALL" | \ - curl -fL -w "\n" -F file="@-" -X POST \ - -H "Name: $SMALL" \ - -H "Max-Downloads: 4000" \ - -H "Expires-Days: -5" \ - -H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ - "$ROOTURL"/upload +function t10-maintenance-post() { + + pv "$SMALL" | \ + curl -fL -w "\n" -F file="@-" -X POST \ + -H "Name: $SMALL" \ + -H "Max-Downloads: 4" \ + -H "Expires-Days: -5" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/upload + pv "$SMALL" | \ + curl -fL -w "\n" -F file="@-" -X POST \ + -H "Name: $SMALL" \ + -H "Max-Downloads: 0" \ + -H "Expires-Days: 3" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/upload + + curl -fL -w "\n" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/maintenance + + curl -fL -w "\n" \ + -H "Secret: $FLASK_ACCESS_TOKEN" \ + "$FLASK_PUBLIC_URL"/ls +} + +_getlist() { + declare -F | awk '{ print $3 }' | grep -v ^_ + echo exit +} + +_getnext() { + hitfound=0 + for line in $( _getlist ); do + if [[ $hitfound -eq 1 ]]; then + echo "$line" + return + fi + if [[ "$line" = "$1" ]]; then + hitfound=1 + fi + done +} - curl -fL -w "\n" \ - -H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ - "$ROOTURL"/maintenance -fi +_getchoice() { + _getlist | \ + smenu -m "Pick task" -n 20 -t 1 -a c:2,b m:3,b -s '/'$1 + +} + + +next_task=0 +while true; do + choice=$( _getchoice $next_task ) + if [[ -z "$choice" ]]; then break; fi + _title "$choice" + set -x + "$choice" + set +x + _title "" + #~ next_task=$(( next_task + 1 )) + next_task=$( _getnext "$choice" ) + +done +