getting there

This commit is contained in:
Q
2023-08-19 19:42:31 +03:00
parent bdacb80c43
commit cfee5287d5
9 changed files with 332 additions and 254 deletions

View File

@@ -3,31 +3,39 @@
A mini file sharing website. A mini file sharing website.
# installation
- `bash create_config.sh`
- `docker-compose up --build`
# configuration # configuration
- configure service with data/config.json Configure service with .env:
- Change your `app_secret_key` !!
- Change your `access_token` !! ```
- Change your `public_url` EXPOSE=0.0.0.0:8136 # IP/Port to bind
- Change your `timezone` UID=1000 # files will be written as user
- `uid` = user id for new files GID=1000 # files will be writter as group
- `workers` = parallel processes (i.e. one upload reserves a process) TZ=Europe/Helsinki # your timezone
- `timeout` = timeout for processes, single upload might take a long time! WORKERS=4 # number of concurrent processes
- configure bind host and port in .env 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: - proxy with nginx, match bind port, body size and timeout to your needs:
``` ```
location /flees/ { location /mfl/ {
proxy_pass http://localhost:8136/; proxy_pass http://localhost:8136/;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Scheme $scheme; 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_max_body_size 8G;
client_body_timeout 3600s; client_body_timeout 3600s;
} }
``` ```
# Running
- `docker-compose up --build`

View File

@@ -14,13 +14,13 @@ RUN apt-get update -yqq \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
ARG UID ARG UUID
ARG GID ARG UGID
ARG TZ ARG TZ
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN groupadd -g $GID user && \ RUN groupadd -g $UGID user && \
useradd -u $UID -g $GID -ms /bin/bash user && \ useradd -u $UUID -g $UGID -ms /bin/bash user && \
mkdir -p /opt/venv && chown $UID:$GID /opt/venv mkdir -p /opt/venv && chown $UUID:$UGID /opt/venv
COPY ./requirements.txt /requirements.txt COPY ./requirements.txt /requirements.txt
COPY docker-builder.sh / COPY docker-builder.sh /
USER user USER user
@@ -28,7 +28,7 @@ USER user
RUN bash /docker-builder.sh RUN bash /docker-builder.sh
COPY ./ /app COPY ./ /app
USER root USER root
RUN chown -R $UID:$GID /app RUN chown -R $UUID:$UGID /app
USER user USER user
WORKDIR /app WORKDIR /app

View File

@@ -1,37 +1,33 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os, sys, time import os
import json import sys
from datetime import datetime import time
from flask import ( from flask import (
Flask, Flask,
render_template, render_template,
jsonify, jsonify,
current_app,
Response,
redirect,
url_for,
request, request,
g,
session,
send_file,
send_from_directory, send_from_directory,
abort,
) )
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from revprox import ReverseProxied from revprox import ReverseProxied
from utils import ( from utils.misc import (
random_token, random_token,
)
from utils.files import (
db_store_file, db_store_file,
file_details, file_details,
file_list, file_list,
file_full_path,
file_full_url,
db_add_download, db_add_download,
db_get_file, db_get_file,
db_delete_file, db_delete_file,
db_maintenance, db_maintenance,
) )
__MINI_FLEES_VERSION__ = "20230818.0" __VERSION__ = "20230818.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()
@@ -42,11 +38,30 @@ app.wsgi_app = ReverseProxied(app.wsgi_app)
@app.route("/") @app.route("/")
def index(): def index():
"""Returns Nothing"""
return "", 200 return "", 200
@app.route("/upload", methods=["POST"]) @app.route("/upload", methods=["POST"])
def upload(): 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": if request.method == "POST":
file = request.files.get("file") file = request.files.get("file")
name = request.headers.get("Name", None) name = request.headers.get("Name", None)
@@ -68,13 +83,16 @@ def upload():
if file: if file:
safe_filename = secure_filename(name) safe_filename = secure_filename(name)
while True:
token = random_token() token = random_token()
folder = os.path.join(app.config["DATAFOLDER"], token) folder = os.path.join(app.config["DATAFOLDER"], token)
if not os.path.exists(folder):
break
os.mkdir(folder) os.mkdir(folder)
filename = os.path.join(folder, safe_filename) filename = file_full_path(token, safe_filename)
file.save(filename) file.save(filename)
db_store_file(token, safe_filename, expires, max_dl) 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 return "File uploaded\n%s\n" % (download_url,), 200
else: else:
return "Use the 'file' variable to upload\n", 400 return "Use the 'file' variable to upload\n", 400
@@ -82,6 +100,14 @@ def upload():
@app.route("/details/<token>/<name>", methods=["GET"]) @app.route("/details/<token>/<name>", methods=["GET"])
def details(token, name): 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", "") secret = request.headers.get("Secret", "")
if secret != app.config["ACCESS_TOKEN"]: if secret != app.config["ACCESS_TOKEN"]:
return "Error", 401 return "Error", 401
@@ -91,11 +117,14 @@ def details(token, name):
@app.route("/delete/<token>/<name>", methods=["GET"]) @app.route("/delete/<token>/<name>", methods=["GET"])
def delete_file(name, token): def delete_file(name, token):
"""
Delete a file from the system
"""
secret = request.headers.get("Secret", "") secret = request.headers.get("Secret", "")
if secret != app.config["ACCESS_TOKEN"]: if secret != app.config["ACCESS_TOKEN"]:
return "Error", 401 return "Error", 401
try: try:
os.remove(os.path.join(os.getenv("DATAFOLDER"), token, name)) os.remove(os.path.join(app.config["DATAFOLDER"], token, name))
except Exception: except Exception:
pass pass
db_delete_file(token, name) db_delete_file(token, name)
@@ -104,6 +133,9 @@ def delete_file(name, token):
@app.route("/ls", methods=["GET"]) @app.route("/ls", methods=["GET"])
def ls(): def ls():
"""
Lists all uploaded files
"""
secret = request.headers.get("Secret", "") secret = request.headers.get("Secret", "")
if secret != app.config["ACCESS_TOKEN"]: if secret != app.config["ACCESS_TOKEN"]:
return "Error", 401 return "Error", 401
@@ -112,6 +144,11 @@ def ls():
@app.route("/maintenance", methods=["GET"]) @app.route("/maintenance", methods=["GET"])
def maintenance(): def maintenance():
"""
Clears DB of expired entries.
Deletes folders without DB entry
"""
secret = request.headers.get("Secret", "") secret = request.headers.get("Secret", "")
if secret != app.config["ACCESS_TOKEN"]: if secret != app.config["ACCESS_TOKEN"]:
return "Error", 401 return "Error", 401
@@ -120,29 +157,30 @@ def maintenance():
@app.route("/dl/<token>/<name>", methods=["GET"]) @app.route("/dl/<token>/<name>", methods=["GET"])
def download(name, token): def download(name, token):
"""
Download a file
"""
return download_file(token, name) return download_file(token, name)
@app.route("/script/client", methods=["GET"]) @app.route("/script/mfl", methods=["GET"])
def script_client(): def script_mfl():
secret = request.headers.get("Secret", "")
if secret != app.config["ACCESS_TOKEN"]:
return "Error", 401
return render_template( return render_template(
"client.py", name=name, token=token, rooturl=request.url_root "mfl",
) token=app.config["ACCESS_TOKEN"],
rooturl=app.config["PUBLIC_URL"],
version=__VERSION__,
@app.route("/script/flip", methods=["GET"])
def script_flip():
return render_template(
"flip",
name=name,
token=token,
rooturl=request.url_root,
version=__FLEES_VERSION__,
) )
def download_file(token, name): 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): if not os.path.exists(full_path):
return "Error", 404 return "Error", 404
db_stat = db_get_file(token, name) db_stat = db_get_file(token, name)
@@ -150,13 +188,13 @@ def download_file(token, name):
added, expires, downloads, max_dl = db_stat added, expires, downloads, max_dl = db_stat
else: else:
return "Error", 404 return "Error", 404
if downloads >= max_dl: if downloads >= max_dl and max_dl > -1:
return "Expired", 401 return "Expired", 401
if expires < time.time(): if expires < time.time():
return "Expired", 401 return "Expired", 401
db_add_download(token, name) db_add_download(token, name)
return send_from_directory( 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
) )

View File

@@ -1,3 +1 @@
from .misc import *
from .files import *
from .crypt import *

View File

@@ -1,5 +0,0 @@
import secrets
def random_token():
return secrets.token_urlsafe(8)

View File

@@ -1,19 +1,14 @@
import os import os
from datetime import datetime from datetime import datetime
from flask import current_app as app from flask import current_app as app
import re
import sys
import json
import stat
import time import time
import sqlite3 import sqlite3
import shutil import shutil
from .misc import * from .misc import file_size_MB, file_size_human, file_date_human
from .crypt import *
def get_db(): def get_db():
db = sqlite3.connect(os.getenv("FLASK_DB"), timeout=5) db = sqlite3.connect(app.config["DB"], timeout=5)
c = db.cursor() c = db.cursor()
return db, c return db, c
@@ -49,7 +44,7 @@ def db_get_files():
""" """
SELECT token,name,added,expires,downloads,max_downloads SELECT token,name,added,expires,downloads,max_downloads
FROM files FROM files
WHERE expires > ? and downloads < max_downloads WHERE expires > ? and (downloads < max_downloads or max_downloads = -1)
""", """,
(time.time(),), (time.time(),),
) )
@@ -82,13 +77,14 @@ def db_add_download(token, name):
def db_maintenance(): def db_maintenance():
messages = [] messages = []
# === Delete DB entries where expiry or max DL is used up ===
db, c = get_db() db, c = get_db()
rows = db.execute( rows = db.execute(
""" """
select select
token, name token, name
from files from files
where expires < ? or downloads >= max_downloads where expires < ? or (downloads >= max_downloads and max_downloads != -1)
""", """,
(int(time.time()),), (int(time.time()),),
) )
@@ -101,7 +97,7 @@ def db_maintenance():
c.executemany("DELETE FROM files WHERE token = ?", deleted_tokens) c.executemany("DELETE FROM files WHERE token = ?", deleted_tokens)
if c.rowcount > 0: if c.rowcount > 0:
db.commit() db.commit()
# Delete files, if DB entry is gone
subdirs = next(os.walk(app.config["DATAFOLDER"]))[1] subdirs = next(os.walk(app.config["DATAFOLDER"]))[1]
db, c = get_db() db, c = get_db()
for d in subdirs: for d in subdirs:
@@ -111,13 +107,35 @@ def db_maintenance():
).fetchone() ).fetchone()
if not keep: if not keep:
try: try:
for fname in os.listdir(os.path.join(app.config["DATAFOLDER"],d)): for fname in os.listdir(os.path.join(app.config["DATAFOLDER"], d)):
os.remove(os.path.join(app.config["DATAFOLDER"],d,fname)) os.remove(os.path.join(app.config["DATAFOLDER"], d, fname))
messages.append(f"Deleting file {d}/{fname}") messages.append(f"Deleting file {d}/{fname}")
except Exception: except Exception:
pass 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}") 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) return "\n".join(messages)
@@ -133,7 +151,7 @@ def file_age(path):
def file_details(token, name): def file_details(token, name):
full_path = os.path.join(os.getenv("FLASK_DATAFOLDER"), token, name) full_path = file_full_path(token, name)
try: try:
s = os.stat(full_path) s = os.stat(full_path)
db_stat = db_get_file(token, name) 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), "hsize": file_size_human(s.st_size, HTML=False),
"added": file_date_human(added), "added": file_date_human(added),
"name": name, "name": name,
"url": f"{app.config['PUBLIC_URL']}/dl/{token}/{name}", "url": file_full_url(token, name),
"expires": file_date_human(expires), "expires": file_date_human(expires),
"downloaded": downloads, "downloaded": downloads,
"max-dl": max_dl, "max-dl": max_dl,
@@ -155,76 +173,23 @@ def file_details(token, name):
return {} 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(): def file_list():
files = list(db_get_files()) files = list(db_get_files())
details = [] details = []
details.append(" Added/Expiry DL/MaxDL URL") details.append(" Added/Expiry DL/MaxDL URL")
details.append("=" * 75) details.append("=" * 75)
for file in files: for file in files:
url = f"{app.config['PUBLIC_URL']}/dl/{file[0]}/{file[1]}" url = file_full_url(file[0], file[1])
details.append( added = file_date_human(file[2])
f"{file_date_human(file[2])}/{file_date_human(file[3])} {file[4]:4d}/{file[5]:4d} {url}" expiry = file_date_human(file[3])
) details.append(f"{added}/{expiry} {file[4]:4d}/{file[5]:4d} {url}")
return details 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,))

View File

@@ -1,5 +1,12 @@
from datetime import datetime 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): def file_date_human(num):
@@ -22,45 +29,3 @@ def file_size_human(num, HTML=True):
def file_size_MB(num): def file_size_MB(num):
return "{:,.2f}".format(num / (1024 * 1024)) 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

View File

@@ -6,8 +6,8 @@ services:
build: build:
context: code context: code
args: args:
UID: ${UID} UUID: ${UUID}
GID: ${GID} UGID: ${UGID}
TZ: ${TZ} TZ: ${TZ}
ports: ports:
- "${EXPOSE}:5000" - "${EXPOSE}:5000"

View File

@@ -65,7 +65,7 @@ _qCol() {
} }
cont() { _cont() {
set +x set +x
_qCol G _qCol G
echo Continue echo Continue
@@ -76,6 +76,13 @@ cont() {
_qCol z _qCol z
set -x set -x
} }
_title() {
_qCol G
echo "$1"
_qCol Y
echo =========================================
_qCol z
}
set -e set -e
@@ -86,82 +93,184 @@ SMALL="small file"
SMALLS="small_file" SMALLS="small_file"
IMAGE="image.jpg" IMAGE="image.jpg"
test -f "$BIG" || dd if=/dev/zero of="$BIG" bs=8192 count=40000 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 "$SMALL" ||dd if=/dev/urandom of="$SMALL" bs=4096 count=400
test -f "$IMAGE" || convert -size 640x480 xc:gray $IMAGE test -f "$IMAGE" || convert -size 640x480 xc:gray $IMAGE
set -x . ../.env
#~ cont
ROOTURL="http://localhost:8136" function t00-rebuild-docker() {
CWD=$PWD
cd ..
docker-compose up --build -d --force-recreate
cd "$CWD"
}
function t01-maintenance-begin() {
curl -fL -w "\n" \
-H "Secret: $FLASK_ACCESS_TOKEN" \
"$FLASK_PUBLIC_URL"/maintenance
}
function t02-upload-small-image() {
if false; then
pv "$IMAGE" | \ pv "$IMAGE" | \
curl -fL -w "\n" -F file="@-" -X POST \ curl -fL -w "\n" -F file="@-" -X POST \
-H "Name: $IMAGE" \ -H "Name: $IMAGE" \
-H "Max-Downloads: 4" \ -H "Max-Downloads: 4" \
-H "Expires-Days: 14" \ -H "Expires-Days: 1" \
-H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ -H "Secret: $FLASK_ACCESS_TOKEN" \
"$ROOTURL"/upload "$FLASK_PUBLIC_URL"/upload
fi }
if false; then function t03-upload-small-file() {
pv "$SMALL" | \ pv "$SMALL" | \
curl -fL -w "\n" -F file="@-" -X POST \ curl -fL -w "\n" -F file="@-" -X POST \
-H "Name: $SMALL" \ -H "Name: $SMALL" \
-H "Max-Downloads: 4000" \ -H "Secret: $FLASK_ACCESS_TOKEN" \
-H "Expires-Days: 14" \ "$FLASK_PUBLIC_URL"/upload
-H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \
"$ROOTURL"/upload
}
function t04-upload-large-file() {
pv "$BIG" | \ pv "$BIG" | \
curl -fL -w "\n" -F file="@-" -X POST \ curl -fL -w "\n" -F file="@-" -X POST \
-H "Name: $BIG" \ -H "Name: $BIG" \
-H "Max-Downloads: 4000" \ -H "Max-Downloads: 4" \
-H "Expires-Days: 14" \ -H "Expires-Days: 1" \
-H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ -H "Secret: $FLASK_ACCESS_TOKEN" \
"$ROOTURL"/upload "$FLASK_PUBLIC_URL"/upload
fi }
sqlite3 ../data/flees.db "select * FROM files" function t05-check-db-manually() {
sqlite3 ../data/flees.db "select * FROM files"
}
function t06-list-files() {
if false; then
curl -fL -w "\n" \ curl -fL -w "\n" \
-H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ -H "Secret: $FLASK_ACCESS_TOKEN" \
"$ROOTURL"/details/OdD7X0aKOGM/big_file1.ext "$FLASK_PUBLIC_URL"/ls
}
fi 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, )
if false; then rm -f "$BIGS"
rm -f big_file1.ext wget "$download_url"
wget \ }
"$ROOTURL"/dl/OdD7X0aKOGM/big_file1.ext
fi
if false; then 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" \ curl -fL -w "\n" \
-H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ -H "Secret: $FLASK_ACCESS_TOKEN" \
"$ROOTURL"/delete/SKDMsQ3ifx8/image.jpg "$FLASK_PUBLIC_URL"/details/"$token_name"
fi }
function t09-file-delete() {
if true; then
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" \ curl -fL -w "\n" \
-H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ -H "Secret: $FLASK_ACCESS_TOKEN" \
"$ROOTURL"/ls "$FLASK_PUBLIC_URL"/delete/"$token_name"
fi }
if true; then function t09-unlimited-downloads() {
pv "$SMALL" | \ pv "$SMALL" | \
curl -fL -w "\n" -F file="@-" -X POST \ curl -fL -w "\n" -F file="@-" -X POST \
-H "Name: $SMALL" \ -H "Name: $SMALL" \
-H "Max-Downloads: 4000" \ -H "Max-Downloads: -1" \
-H "Expires-Days: -5" \ -H "Expires-Days: 1" \
-H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ -H "Secret: $FLASK_ACCESS_TOKEN" \
"$ROOTURL"/upload "$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: $FLASK_ACCESS_TOKEN" \
"$FLASK_PUBLIC_URL"/dl/"$token_name" > /dev/null
curl -fL -w "\n" \
-H "Secret: $FLASK_ACCESS_TOKEN" \
"$FLASK_PUBLIC_URL"/details/"$token_name"
done
}
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" \ curl -fL -w "\n" \
-H "Secret: dff789f0bbe8183d3254258b33a147d580c1131f39a698c56d3f640ac8415714" \ -H "Secret: $FLASK_ACCESS_TOKEN" \
"$ROOTURL"/maintenance "$FLASK_PUBLIC_URL"/maintenance
fi
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
}
_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