Add ::/list/ endpoint, with a secret to find them.
This commit is contained in:
parent
e376fab285
commit
2b13d6cbb0
|
@ -12,12 +12,14 @@ class PasteAdmin(admin.ModelAdmin):
|
||||||
"paste_time",
|
"paste_time",
|
||||||
"access_time",
|
"access_time",
|
||||||
"viewcount",
|
"viewcount",
|
||||||
|
"auth",
|
||||||
)
|
)
|
||||||
fields = (
|
fields = (
|
||||||
(
|
(
|
||||||
"slug",
|
"slug",
|
||||||
"size",
|
"size",
|
||||||
),
|
),
|
||||||
|
"auth",
|
||||||
(
|
(
|
||||||
"paste_time",
|
"paste_time",
|
||||||
"access_time",
|
"access_time",
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 4.2 on 2023-04-25 09:33
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("paste", "0008_paste_access_time"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="paste",
|
||||||
|
name="auth",
|
||||||
|
field=models.CharField(default="", max_length=64),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,4 +1,5 @@
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
from hashlib import sha256
|
||||||
|
|
||||||
import shortuuid
|
import shortuuid
|
||||||
from django.core.validators import MaxLengthValidator
|
from django.core.validators import MaxLengthValidator
|
||||||
|
@ -10,9 +11,16 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from webtools import settings
|
from webtools import settings
|
||||||
|
|
||||||
|
|
||||||
|
class PasteQuerySet(models.QuerySet):
|
||||||
|
def by_secret(self, secret):
|
||||||
|
auth = sha256(secret.encode("UTF-8")).hexdigest()
|
||||||
|
return self.filter(auth=auth)
|
||||||
|
|
||||||
|
|
||||||
class Paste(models.Model):
|
class Paste(models.Model):
|
||||||
"""Paste object."""
|
"""Paste object."""
|
||||||
|
|
||||||
|
objects = PasteQuerySet.as_manager()
|
||||||
filename = models.CharField(max_length=255, default="")
|
filename = models.CharField(max_length=255, default="")
|
||||||
slug = models.SlugField(unique=True, editable=False)
|
slug = models.SlugField(unique=True, editable=False)
|
||||||
content = models.TextField(
|
content = models.TextField(
|
||||||
|
@ -23,6 +31,14 @@ class Paste(models.Model):
|
||||||
access_time = models.DateTimeField(auto_now=True)
|
access_time = models.DateTimeField(auto_now=True)
|
||||||
viewcount = models.IntegerField(default=0, editable=False)
|
viewcount = models.IntegerField(default=0, editable=False)
|
||||||
|
|
||||||
|
# auth stores a sha256 (hexdigest) of the Authentication header.
|
||||||
|
auth = models.CharField(max_length=64, default="")
|
||||||
|
|
||||||
|
def set_secret(self, secret=None):
|
||||||
|
if not secret:
|
||||||
|
return
|
||||||
|
self.auth = sha256(secret.encode("UTF-8")).hexdigest()
|
||||||
|
|
||||||
def compute_size(self):
|
def compute_size(self):
|
||||||
"""Computes size."""
|
"""Computes size."""
|
||||||
self.size = len(self.content)
|
self.size = len(self.content)
|
||||||
|
|
|
@ -7,5 +7,6 @@ from webtools import settings
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", views.index, name="index"),
|
path("", views.index, name="index"),
|
||||||
path("::/static/<path>", serve, {"document_root": settings.STATIC_ROOT}),
|
path("::/static/<path>", serve, {"document_root": settings.STATIC_ROOT}),
|
||||||
|
path("::/list/", views.list_view),
|
||||||
path("<path:slug>", views.show, name="short_paste"),
|
path("<path:slug>", views.show, name="short_paste"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from datetime import datetime
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from mimetypes import common_types, types_map
|
from mimetypes import common_types, types_map
|
||||||
|
|
||||||
|
@ -29,6 +30,21 @@ def get_files(request):
|
||||||
return {"": request}
|
return {"": request}
|
||||||
|
|
||||||
|
|
||||||
|
def pastes_as_table(request, pastes, headers=("URL", "size", "filename")):
|
||||||
|
def paste_attr(paste, attr):
|
||||||
|
if attr == "URL":
|
||||||
|
return request.build_absolute_uri(paste.get_absolute_url())
|
||||||
|
value = getattr(paste, attr)
|
||||||
|
if isinstance(value, datetime):
|
||||||
|
return value.isoformat(timespec="seconds")
|
||||||
|
return value
|
||||||
|
|
||||||
|
values = []
|
||||||
|
for paste in pastes:
|
||||||
|
values.append([paste_attr(paste, attr) for attr in headers])
|
||||||
|
return tabulate(values, headers=headers, tablefmt="github") + "\n"
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
def index(request):
|
def index(request):
|
||||||
"""Displays form."""
|
"""Displays form."""
|
||||||
|
@ -46,22 +62,32 @@ def index(request):
|
||||||
filename=filename.split("/")[-1],
|
filename=filename.split("/")[-1],
|
||||||
content=the_file.read().decode("UTF-8"),
|
content=the_file.read().decode("UTF-8"),
|
||||||
)
|
)
|
||||||
|
paste.set_secret(request.headers.get("Authorization"))
|
||||||
paste.compute_size()
|
paste.compute_size()
|
||||||
paste.save()
|
paste.save()
|
||||||
pastes.append(paste)
|
pastes.append(paste)
|
||||||
|
|
||||||
table = [
|
return HttpResponse(pastes_as_table(request, pastes), content_type="text/plain")
|
||||||
(
|
|
||||||
request.build_absolute_uri(paste.get_absolute_url()),
|
|
||||||
paste.size,
|
def list_view(request):
|
||||||
paste.filename,
|
secret = request.headers.get("Authorization")
|
||||||
)
|
pastes = []
|
||||||
for paste in pastes
|
if secret:
|
||||||
]
|
pastes = Paste.objects.by_secret(secret).order_by("paste_time")
|
||||||
return HttpResponse(
|
table = pastes_as_table(
|
||||||
tabulate(table, headers=("URL", "size", "filename"), tablefmt="github") + "\n",
|
request,
|
||||||
content_type="text/plain",
|
pastes,
|
||||||
|
headers=("filename", "size", "URL", "paste_time", "access_time"),
|
||||||
)
|
)
|
||||||
|
if "html" in request.headers["accept"]:
|
||||||
|
return HttpResponse(
|
||||||
|
loader.render_to_string(
|
||||||
|
"paste/show-markdown.html", {"highlighted": markdown_to_html(table)}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return HttpResponse(table, content_type="text/plain")
|
||||||
|
|
||||||
|
|
||||||
def show(request, slug):
|
def show(request, slug):
|
||||||
|
|
Loading…
Reference in New Issue