Compare commits
No commits in common. "a389c18e0eb8b34bfd970d18a5ae7b00f7f65d01" and "b5f0cfdf6f9d621fd83a054375e50e7a4c9e42de" have entirely different histories.
a389c18e0e
...
b5f0cfdf6f
7 changed files with 4 additions and 260 deletions
6
fhost.py
6
fhost.py
|
@ -330,9 +330,9 @@ def get(path):
|
||||||
|
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
@app.route("/", methods=["GET", "POST", "PUT"])
|
@app.route("/", methods=["GET", "POST"])
|
||||||
def fhost():
|
def fhost():
|
||||||
if request.method == "POST" or request.method == "PUT":
|
if request.method == "POST":
|
||||||
sf = None
|
sf = None
|
||||||
|
|
||||||
if "file" in request.files:
|
if "file" in request.files:
|
||||||
|
@ -344,7 +344,7 @@ def fhost():
|
||||||
|
|
||||||
abort(400)
|
abort(400)
|
||||||
else:
|
else:
|
||||||
return render_template("boop.html")
|
return render_template("index.html")
|
||||||
|
|
||||||
@app.route("/robots.txt")
|
@app.route("/robots.txt")
|
||||||
def robots():
|
def robots():
|
||||||
|
|
|
@ -8,4 +8,3 @@ Flask_SQLAlchemy
|
||||||
validators
|
validators
|
||||||
flask_migrate
|
flask_migrate
|
||||||
python_magic
|
python_magic
|
||||||
uwsgi
|
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
server {
|
|
||||||
listen 443 ssl http2;
|
|
||||||
listen [::]:443 ssl http2;
|
|
||||||
server_name boop.icu www.boop.icu;
|
|
||||||
|
|
||||||
ssl_certificate /etc/letsencrypt/live/boop.icu/fullchain.pem;
|
|
||||||
ssl_certificate_key /etc/letsencrypt/live/boop.icu/privkey.pem;
|
|
||||||
|
|
||||||
include common/acme.conf;
|
|
||||||
include common/statuscodes.conf;
|
|
||||||
include common/ssl.conf;
|
|
||||||
|
|
||||||
# Upload size limit
|
|
||||||
client_max_body_size 256M;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
include uwsgi_params;
|
|
||||||
uwsgi_pass unix:/opt/boop/boop.icu.sock;
|
|
||||||
uwsgi_param UWSGI_SCHEME https;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /css {
|
|
||||||
root /opt/boop/css;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /up {
|
|
||||||
root /opt/boop;
|
|
||||||
internal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
listen [::]:80;
|
|
||||||
server_name boop.icu www.boop.icu;
|
|
||||||
return 301 https://boop.icu$request_uri;
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=boop.icu instance
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
User=boop
|
|
||||||
Group=http
|
|
||||||
WorkingDirectory=/opt/boop
|
|
||||||
ExecStart=/opt/boop/.local/bin/uwsgi --ini /opt/boop/boop.icu.ini --enable-threads
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
|
@ -1,142 +0,0 @@
|
||||||
{% set title = config["SITE_NAME"] %}
|
|
||||||
{% set fhost_url = url_for("fhost", _external=True).rstrip("/") %}
|
|
||||||
{% set max_size = config["MAX_CONTENT_LENGTH"]|filesizeformat(True) %}
|
|
||||||
{% set not_allowed = config["FHOST_MIME_BLACKLIST"]|join(", ") %}
|
|
||||||
{% set half = ((config["MAX_CONTENT_LENGTH"]/2)|filesizeformat(True)).split(" ")[0].rjust(27) %}
|
|
||||||
{% set max = max_size.split(" ")[0].rjust(27) %}
|
|
||||||
{% set unit = max_size.split(" ")[1].rjust(54) %}
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
<style type="text/css">
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
body { width: 80%; }
|
|
||||||
pre { font-size: 12px; }
|
|
||||||
}
|
|
||||||
@media (min-width: 992px) {
|
|
||||||
body { width: 70%; }
|
|
||||||
}
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
body { width: 65%; }
|
|
||||||
}
|
|
||||||
::selection {
|
|
||||||
background: #99F2B9;
|
|
||||||
color: #0E0E0E;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
background: #0E0E0E;
|
|
||||||
color: #3EE77B;
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.43;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
form {
|
|
||||||
margin-block: 0em;
|
|
||||||
margin-block-end: 1em;
|
|
||||||
}
|
|
||||||
form input, hr, pre {
|
|
||||||
border: 1px solid #414141;
|
|
||||||
}
|
|
||||||
form input {
|
|
||||||
background: #0E0E0E;
|
|
||||||
color: #3EE77B;
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 16px;
|
|
||||||
height: 36px;
|
|
||||||
padding: 6px 12px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 48px;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
font-size: 32px;
|
|
||||||
}
|
|
||||||
h1, h3 {
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.2;
|
|
||||||
margin-top: 22px;
|
|
||||||
margin-bottom: 11px;
|
|
||||||
}
|
|
||||||
h1, h3, pre, a, a:visited {
|
|
||||||
color: #99F2B9;
|
|
||||||
}
|
|
||||||
p, pre {
|
|
||||||
margin: 0 0 11px;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
background: #282828;
|
|
||||||
padding: 10.5px;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
padding-right: 15px;
|
|
||||||
padding-left: 15px;
|
|
||||||
margin-right: auto;
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
|
|
||||||
<h1>{{ title }}</h1>
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
HTTP POST files here:
|
|
||||||
<pre>curl -F'file=@yourfile.png' {{ fhost_url }}</pre>
|
|
||||||
You can also POST remote URLs:
|
|
||||||
<pre>curl -F'url=http://example.com/image.jpg' {{ fhost_url }}</pre>
|
|
||||||
Or you can shorten URLs:
|
|
||||||
<pre>curl -F'shorten=http://example.com/some/long/url' {{ fhost_url }}</pre>
|
|
||||||
Alternatively, you can use PUT:
|
|
||||||
<pre>curl -X PUT -T 'yourfile.png' {{ fhost_url }}</pre>
|
|
||||||
|
|
||||||
<p>File URLs are valid for at least 30 days and up to a year (see below).<br/>
|
|
||||||
Shortened URLs do not expire.</p>
|
|
||||||
|
|
||||||
<p>Maximum file size: {{ max_size }}</p>
|
|
||||||
<p>Not allowed: {{ not_allowed }}</p>
|
|
||||||
|
|
||||||
<h3>FILE RETENTION PERIOD</h3>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
retention = min_age + (-max_age + min_age) * pow((file_size / max_size - 1), 3)
|
|
||||||
|
|
||||||
days
|
|
||||||
365 | \\
|
|
||||||
| \\
|
|
||||||
| \\
|
|
||||||
| \\
|
|
||||||
| \\
|
|
||||||
| \\
|
|
||||||
| ..
|
|
||||||
| \
|
|
||||||
197.5 | ----------..-------------------------------------------
|
|
||||||
| ..
|
|
||||||
| \
|
|
||||||
| ..
|
|
||||||
| ...
|
|
||||||
| ..
|
|
||||||
| ...
|
|
||||||
| ....
|
|
||||||
| ......
|
|
||||||
30 | ....................
|
|
||||||
0{{ half }}{{ max }}
|
|
||||||
{{ unit }}
|
|
||||||
</pre>
|
|
||||||
<h3>UPLOAD DIRECTLY</h3>
|
|
||||||
<hr>
|
|
||||||
<form action="{{ fhost_url }}" method="POST" enctype="multipart/form-data">
|
|
||||||
<label for="file">File:</label>
|
|
||||||
<input class="form-control" type="file" name="file"><br><br>
|
|
||||||
<input class="form-control" type="submit" value="Submit">
|
|
||||||
</form>
|
|
||||||
<p>Please report illegal content to <a href="mailto:fedi@criminallycute.fi?subject="boop.icu content">fedi@criminallycute.fi</a> with the subject "boop.icu content". Include a link to content to be removed.</p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -9,9 +9,6 @@ You can also POST remote URLs:
|
||||||
Or you can shorten URLs:
|
Or you can shorten URLs:
|
||||||
curl -F'shorten=http://example.com/some/long/url' {{ fhost_url }}
|
curl -F'shorten=http://example.com/some/long/url' {{ fhost_url }}
|
||||||
|
|
||||||
Alternatively, you can use PUT:
|
|
||||||
curl -X PUT -T 'yourfile.png' {{ fhost_url }}
|
|
||||||
|
|
||||||
File URLs are valid for at least 30 days and up to a year (see below).
|
File URLs are valid for at least 30 days and up to a year (see below).
|
||||||
Shortened URLs do not expire.
|
Shortened URLs do not expire.
|
||||||
{% set max_size = config["MAX_CONTENT_LENGTH"]|filesizeformat(True) %}
|
{% set max_size = config["MAX_CONTENT_LENGTH"]|filesizeformat(True) %}
|
||||||
|
|
|
@ -18,7 +18,7 @@ def client():
|
||||||
db_upgrade()
|
db_upgrade()
|
||||||
yield client
|
yield client
|
||||||
|
|
||||||
def test_client_post(client):
|
def test_client(client):
|
||||||
payloads = [
|
payloads = [
|
||||||
({ "file" : (BytesIO(b"hello"), "hello.txt") }, 200, b"https://localhost/E.txt\n"),
|
({ "file" : (BytesIO(b"hello"), "hello.txt") }, 200, b"https://localhost/E.txt\n"),
|
||||||
({ "file" : (BytesIO(b"hello"), "hello.ignorethis") }, 200, b"https://localhost/E.txt\n"),
|
({ "file" : (BytesIO(b"hello"), "hello.ignorethis") }, 200, b"https://localhost/E.txt\n"),
|
||||||
|
@ -79,64 +79,3 @@ def test_client_post(client):
|
||||||
rv = client.get(p)
|
rv = client.get(p)
|
||||||
assert rv.status_code == code
|
assert rv.status_code == code
|
||||||
|
|
||||||
def test_client_put(client):
|
|
||||||
payloads = [
|
|
||||||
({ "file" : (BytesIO(b"hello"), "hello.txt") }, 200, b"https://localhost/E.txt\n"),
|
|
||||||
({ "file" : (BytesIO(b"hello"), "hello.ignorethis") }, 200, b"https://localhost/E.txt\n"),
|
|
||||||
({ "file" : (BytesIO(b"bye"), "bye.truncatethis") }, 200, b"https://localhost/Q.truncate\n"),
|
|
||||||
({ "file" : (BytesIO(b"hi"), "hi.tar.gz") }, 200, b"https://localhost/h.tar.gz\n"),
|
|
||||||
({ "file" : (BytesIO(b"lea!"), "lea!") }, 200, b"https://localhost/d.txt\n"),
|
|
||||||
({ "file" : (BytesIO(b"why?"), "balls", "application/x-dosexec") }, 415, None),
|
|
||||||
({ "shorten" : "https://0x0.st" }, 200, b"https://localhost/E\n"),
|
|
||||||
({ "shorten" : "https://localhost" }, 400, None),
|
|
||||||
({}, 400, None),
|
|
||||||
]
|
|
||||||
|
|
||||||
for p, s, r in payloads:
|
|
||||||
rv = client.put("/", buffered=True,
|
|
||||||
content_type="multipart/form-data",
|
|
||||||
data=p)
|
|
||||||
assert rv.status_code == s
|
|
||||||
if r:
|
|
||||||
assert rv.data == r
|
|
||||||
|
|
||||||
f = File.query.get(2)
|
|
||||||
f.removed = True
|
|
||||||
db.session.add(f)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
rq = [
|
|
||||||
(200, [
|
|
||||||
"/",
|
|
||||||
"robots.txt",
|
|
||||||
"E.txt",
|
|
||||||
"E.txt/test",
|
|
||||||
"E.txt/test.py",
|
|
||||||
"d.txt",
|
|
||||||
"h.tar.gz",
|
|
||||||
]),
|
|
||||||
(302, [
|
|
||||||
"E",
|
|
||||||
"E/test",
|
|
||||||
"E/test.bin",
|
|
||||||
]),
|
|
||||||
(404, [
|
|
||||||
"test.bin",
|
|
||||||
"test.bin/test",
|
|
||||||
"test.bin/test.py",
|
|
||||||
"test",
|
|
||||||
"test/test",
|
|
||||||
"test.bin/test.py",
|
|
||||||
"E.bin",
|
|
||||||
]),
|
|
||||||
(451, [
|
|
||||||
"Q.truncate",
|
|
||||||
]),
|
|
||||||
]
|
|
||||||
|
|
||||||
for code, paths in rq:
|
|
||||||
for p in paths:
|
|
||||||
app.logger.info(f"GET {p}")
|
|
||||||
rv = client.get(p)
|
|
||||||
assert rv.status_code == code
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue