Merge pull request #80 from bugsink/sourcemaps-spike

Sourcemaps: first version
This commit is contained in:
Klaas van Schelven
2025-04-14 11:08:19 +02:00
committed by GitHub
30 changed files with 966 additions and 7 deletions

View File

@@ -0,0 +1,10 @@
from django.contrib import admin
from .models import AuthToken
@admin.register(AuthToken)
class AuthTokenAdmin(admin.ModelAdmin):
list_display = ("token", "created_at")
list_filter = ("created_at",)
ordering = ("-created_at",)

View File

@@ -0,0 +1,11 @@
from django.core.management.base import BaseCommand
from bsmain.models import AuthToken
class Command(BaseCommand):
help = "Creates an auth_token and prints it on screen"""
def handle(self, *args, **options):
auth_token = AuthToken.objects.create()
print(auth_token.token)

View File

@@ -0,0 +1,44 @@
# Generated by Django 4.2.19 on 2025-04-11 11:33
import bsmain.models
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="AuthToken",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"token",
models.CharField(
default=bsmain.models.generate_token,
max_length=40,
unique=True,
validators=[
django.core.validators.RegexValidator(
message="Token must be a 40-character hexadecimal string.",
regex="^[a-f0-9]{40}$",
)
],
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
],
),
]

View File

View File

@@ -0,0 +1,20 @@
import secrets
from django.db import models
from django.core.validators import RegexValidator
def generate_token():
# nchars = nbytes * 2
return secrets.token_hex(nbytes=20)
class AuthToken(models.Model):
"""Global (Bugsink-wide) token for authentication."""
token = models.CharField(max_length=40, unique=True, default=generate_token, validators=[
RegexValidator(regex=r'^[a-f0-9]{40}$', message='Token must be a 40-character hexadecimal string.'),
])
created_at = models.DateTimeField(auto_now_add=True, editable=False)
def __str__(self):
return f"AuthToken(token={self.token})"

View File

@@ -0,0 +1,80 @@
{% extends "base.html" %}
{% load static %}
{% block title %}Auth Tokens · {{ site_title }}{% endblock %}
{% block content %}
<div class="flex items-center justify-center">
<div class="m-4 max-w-4xl flex-auto">
{% if messages %}
<ul class="mb-4">
{% for message in messages %}
{# if we introduce different levels we can use{% message.level == DEFAULT_MESSAGE_LEVELS.SUCCESS %} #}
<li class="bg-cyan-50 border-2 border-cyan-800 p-4 rounded-lg">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
<div class="flex">
<h1 class="text-4xl mt-4 font-bold">Auth Tokens</h1>
<div class="ml-auto mt-6">
<form action="{% url "auth_token_create" %}" method="post">
{% csrf_token %} {# margins display slightly different from the <a href version that I have for e.g. project memembers, but I don't care _that_ much #}
<button class="font-bold text-slate-800 border-slate-500 pl-4 pr-4 pb-2 pt-2 ml-1 border-2 bg-cyan-200 hover:bg-cyan-400 active:ring rounded-md">Add Token</button>
</form>
</div>
</div>
<div>
<form action="." method="post">
{% csrf_token %}
<table class="w-full mt-8">
<tbody>
<thead>
<tr class="bg-slate-200">
<th class="w-full p-4 text-left text-xl" colspan="2">Auth Tokens</th>
</tr>
{% for auth_token in auth_tokens %}
<tr class="bg-white border-slate-200 border-b-2">
<td class="w-full p-4">
<div>
{{ auth_token.token }}
</div>
</td>
<td class="p-4">
<div class="flex justify-end">
<button name="action" value="delete:{{ auth_token.id }}" class="font-bold text-slate-500 border-slate-300 pl-4 pr-4 pb-2 pt-2 ml-2 border-2 hover:bg-slate-200 active:ring rounded-md">Delete</button>
</div>
</td>
</tr>
{% empty %}
<tr class="bg-white border-slate-200 border-b-2">
<td class="w-full p-4">
<div>
No Auth Tokens.
</div>
</td>
<td class="p-4">
</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
</div>
</div>
{% endblock %}

9
bsmain/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import path
from .views import auth_token_list, auth_token_create
urlpatterns = [
path('auth_tokens/', auth_token_list, name='auth_token_list'),
path('auth_tokens/create/', auth_token_create, name='auth_token_create'),
]

View File

@@ -0,0 +1,39 @@
from django.shortcuts import render, redirect
from django.http import Http404
from django.contrib import messages
from django.contrib.auth.decorators import user_passes_test
from bugsink.decorators import atomic_for_request_method
from .models import AuthToken
@atomic_for_request_method
@user_passes_test(lambda u: u.is_superuser)
def auth_token_list(request):
auth_tokens = AuthToken.objects.all()
if request.method == 'POST':
# DIT KOMT ZO WEL
full_action_str = request.POST.get('action')
action, pk = full_action_str.split(":", 1)
if action == "delete":
AuthToken.objects.get(pk=pk).delete()
messages.success(request, 'Token deleted')
return redirect('auth_token_list')
return render(request, 'bsmain/auth_token_list.html', {
'auth_tokens': auth_tokens,
})
@atomic_for_request_method
@user_passes_test(lambda u: u.is_superuser)
def auth_token_create(request):
if request.method != 'POST':
raise Http404("Invalid request method")
AuthToken.objects.create()
return redirect("auth_token_list")