forked from mia/0x0
Allow changing expiration date
This commit is contained in:
parent
afe2329bf5
commit
e168534258
2 changed files with 48 additions and 33 deletions
71
fhost.py
71
fhost.py
|
@ -158,6 +158,34 @@ class File(db.Model):
|
||||||
self.removed = permanent
|
self.removed = permanent
|
||||||
self.getpath().unlink(missing_ok=True)
|
self.getpath().unlink(missing_ok=True)
|
||||||
|
|
||||||
|
# Returns the epoch millisecond that a file should expire
|
||||||
|
#
|
||||||
|
# Uses the expiration time provided by the user (requested_expiration)
|
||||||
|
# upper-bounded by an algorithm that computes the size based on the size of the
|
||||||
|
# file.
|
||||||
|
#
|
||||||
|
# That is, all files are assigned a computed expiration, which can voluntarily
|
||||||
|
# shortened by the user either by providing a timestamp in epoch millis or a
|
||||||
|
# duration in hours.
|
||||||
|
def get_expiration(requested_expiration, size) -> int:
|
||||||
|
current_epoch_millis = time.time() * 1000;
|
||||||
|
|
||||||
|
# Maximum lifetime of the file in milliseconds
|
||||||
|
this_files_max_lifespan = get_max_lifespan(size);
|
||||||
|
|
||||||
|
# The latest allowed expiration date for this file, in epoch millis
|
||||||
|
this_files_max_expiration = this_files_max_lifespan + 1000 * time.time();
|
||||||
|
|
||||||
|
if requested_expiration is None:
|
||||||
|
return this_files_max_expiration
|
||||||
|
elif requested_expiration < 1650460320000:
|
||||||
|
# Treat the requested expiration time as a duration in hours
|
||||||
|
requested_expiration_ms = requested_expiration * 60 * 60 * 1000
|
||||||
|
return min(this_files_max_expiration, current_epoch_millis + requested_expiration_ms)
|
||||||
|
else:
|
||||||
|
# Treat the requested expiration time as a timestamp in epoch millis
|
||||||
|
return min(this_files_max_expiration, requested_expiration)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
requested_expiration can be:
|
requested_expiration can be:
|
||||||
- None, to use the longest allowed file lifespan
|
- None, to use the longest allowed file lifespan
|
||||||
|
@ -203,33 +231,7 @@ class File(db.Model):
|
||||||
|
|
||||||
return ext[:app.config["FHOST_MAX_EXT_LENGTH"]] or ".bin"
|
return ext[:app.config["FHOST_MAX_EXT_LENGTH"]] or ".bin"
|
||||||
|
|
||||||
# Returns the epoch millisecond that this file should expire
|
expiration = File.get_expiration(requested_expiration, len(data))
|
||||||
#
|
|
||||||
# Uses the expiration time provided by the user (requested_expiration)
|
|
||||||
# upper-bounded by an algorithm that computes the size based on the size of the
|
|
||||||
# file.
|
|
||||||
#
|
|
||||||
# That is, all files are assigned a computed expiration, which can voluntarily
|
|
||||||
# shortened by the user either by providing a timestamp in epoch millis or a
|
|
||||||
# duration in hours.
|
|
||||||
def get_expiration() -> int:
|
|
||||||
current_epoch_millis = time.time() * 1000;
|
|
||||||
|
|
||||||
# Maximum lifetime of the file in milliseconds
|
|
||||||
this_files_max_lifespan = get_max_lifespan(len(data));
|
|
||||||
|
|
||||||
# The latest allowed expiration date for this file, in epoch millis
|
|
||||||
this_files_max_expiration = this_files_max_lifespan + 1000 * time.time();
|
|
||||||
|
|
||||||
if requested_expiration is None:
|
|
||||||
return this_files_max_expiration
|
|
||||||
elif requested_expiration < 1650460320000:
|
|
||||||
# Treat the requested expiration time as a duration in hours
|
|
||||||
requested_expiration_ms = requested_expiration * 60 * 60 * 1000
|
|
||||||
return min(this_files_max_expiration, current_epoch_millis + requested_expiration_ms)
|
|
||||||
else:
|
|
||||||
# Treat the requested expiration time as a timestamp in epoch millis
|
|
||||||
return min(this_files_max_expiration, requested_expiration);
|
|
||||||
isnew = True
|
isnew = True
|
||||||
|
|
||||||
f = File.query.filter_by(sha256=digest).first()
|
f = File.query.filter_by(sha256=digest).first()
|
||||||
|
@ -240,18 +242,17 @@ class File(db.Model):
|
||||||
abort(451)
|
abort(451)
|
||||||
if f.expiration is None:
|
if f.expiration is None:
|
||||||
# The file has expired, so give it a new expiration date
|
# The file has expired, so give it a new expiration date
|
||||||
f.expiration = get_expiration()
|
f.expiration = expiration
|
||||||
|
|
||||||
# Also generate a new management token
|
# Also generate a new management token
|
||||||
f.mgmt_token = secrets.token_urlsafe()
|
f.mgmt_token = secrets.token_urlsafe()
|
||||||
else:
|
else:
|
||||||
# The file already exists, update the expiration if needed
|
# The file already exists, update the expiration if needed
|
||||||
f.expiration = max(f.expiration, get_expiration())
|
f.expiration = max(f.expiration, expiration)
|
||||||
isnew = False
|
isnew = False
|
||||||
else:
|
else:
|
||||||
mime = get_mime()
|
mime = get_mime()
|
||||||
ext = get_ext(mime)
|
ext = get_ext(mime)
|
||||||
expiration = get_expiration()
|
|
||||||
mgmt_token = secrets.token_urlsafe()
|
mgmt_token = secrets.token_urlsafe()
|
||||||
f = File(digest, ext, mime, addr, expiration, mgmt_token)
|
f = File(digest, ext, mime, addr, expiration, mgmt_token)
|
||||||
|
|
||||||
|
@ -386,6 +387,16 @@ def manage_file(f):
|
||||||
f.delete()
|
f.delete()
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return ""
|
return ""
|
||||||
|
if "expires" in request.form:
|
||||||
|
try:
|
||||||
|
requested_expiration = int(request.form["expires"])
|
||||||
|
except ValueError:
|
||||||
|
abort(400)
|
||||||
|
|
||||||
|
fsize = f.getpath().stat().st_size
|
||||||
|
f.expiration = File.get_expiration(requested_expiration, fsize)
|
||||||
|
db.session.commit()
|
||||||
|
return "", 202
|
||||||
|
|
||||||
abort(400)
|
abort(400)
|
||||||
|
|
||||||
|
|
|
@ -13,17 +13,21 @@ 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.
|
||||||
|
|
||||||
Files can be set to expire sooner by adding an "expires" parameter (in hours)
|
Files can be set to expire sooner by adding an "expires" parameter (in hours)
|
||||||
curl -F'file=@yourfile.png' -F'expires=24' {{ fhost_url }}
|
curl -F'file=@yourfile.png' -Fexpires=24 {{ fhost_url }}
|
||||||
OR by setting "expires" to a timestamp in epoch milliseconds
|
OR by setting "expires" to a timestamp in epoch milliseconds
|
||||||
curl -F'file=@yourfile.png' -F'expires=1681996320000' {{ fhost_url }}
|
curl -F'file=@yourfile.png' -Fexpires=1681996320000 {{ fhost_url }}
|
||||||
|
|
||||||
Expired files won't be removed immediately, but will be removed as part of
|
Expired files won't be removed immediately, but will be removed as part of
|
||||||
the next purge.
|
the next purge.
|
||||||
|
|
||||||
Whenever a file that does not already exist or has expired is uploaded,
|
Whenever a file that does not already exist or has expired is uploaded,
|
||||||
the HTTP response header includes an X-Token field. You can use this
|
the HTTP response header includes an X-Token field. You can use this
|
||||||
to delete the file immediately:
|
to perform management operations on the file.
|
||||||
|
|
||||||
|
To delete the file immediately:
|
||||||
curl -Ftoken=token_here -Fdelete= {{ fhost_url }}/abc.txt
|
curl -Ftoken=token_here -Fdelete= {{ fhost_url }}/abc.txt
|
||||||
|
To change the expiration date (see above):
|
||||||
|
curl -Ftoken=token_here -Fexpires=3 {{ fhost_url }}/abc.txt
|
||||||
|
|
||||||
{% set max_size = config["MAX_CONTENT_LENGTH"]|filesizeformat(True) %}
|
{% set max_size = config["MAX_CONTENT_LENGTH"]|filesizeformat(True) %}
|
||||||
Maximum file size: {{ max_size }}
|
Maximum file size: {{ max_size }}
|
||||||
|
|
Loading…
Reference in a new issue