Allow changing expiration date

This commit is contained in:
Mia Herkt 2022-11-30 02:16:19 +01:00
parent afe2329bf5
commit e168534258
Signed by: mia
GPG Key ID: 72E154B8622EC191
2 changed files with 48 additions and 33 deletions

View File

@ -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)

View File

@ -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 }}