diff options
| author | Dennis Fink | 2026-08-17 20:06:22 +0200 |
|---|---|---|
| committer | Dennis Fink | 2026-08-17 20:06:22 +0200 |
| commit | d4a03623544aac0ce911d8471ff4b033bc3255e1 (patch) | |
| tree | 0d707db5a9aed7442c28e0ac0de8898aab54c090 /webmentions_ssg | |
| parent | 85a73c811a3de333138978d8d450533caa668dc5 (diff) | |
| download | webmentions-ssg-d4a03623544aac0ce911d8471ff4b033bc3255e1.tar.gz webmentions-ssg-d4a03623544aac0ce911d8471ff4b033bc3255e1.zip | |
feat(sender): add manual source rescan action
Add an authenticated action to queue a source rescan from the sent
Webmentions overview.
Extract the reusable confirmation modal and its JavaScript so received
and sent admin actions share the same implementation.
Diffstat (limited to 'webmentions_ssg')
| -rw-r--r-- | webmentions_ssg/static/js/confirmation-modal.js | 26 | ||||
| -rw-r--r-- | webmentions_ssg/tasks/scanner.py | 9 | ||||
| -rw-r--r-- | webmentions_ssg/templates/includes/confirmation_modal.html | 27 | ||||
| -rw-r--r-- | webmentions_ssg/templates/received.html | 57 | ||||
| -rw-r--r-- | webmentions_ssg/templates/sent.html | 19 | ||||
| -rw-r--r-- | webmentions_ssg/views.py | 18 |
6 files changed, 97 insertions, 59 deletions
diff --git a/webmentions_ssg/static/js/confirmation-modal.js b/webmentions_ssg/static/js/confirmation-modal.js new file mode 100644 index 0000000..e278790 --- /dev/null +++ b/webmentions_ssg/static/js/confirmation-modal.js @@ -0,0 +1,26 @@ +document.addEventListener("DOMContentLoaded", () => { + const confirmationModal = document.getElementById("confirmation-modal"); + + if (confirmationModal === null) { + return; + } + + confirmationModal.addEventListener("show.bs.modal", (event) => { + const button = event.relatedTarget; + + const form = confirmationModal.querySelector("#confirmation-modal-form"); + const title = confirmationModal.querySelector("#confirmation-modal-title"); + const message = confirmationModal.querySelector( + "#confirmation-modal-message", + ); + const submit = form.querySelector("#submit"); + + form.action = button.dataset.action; + + title.textContent = button.dataset.title; + message.textContent = button.dataset.message; + + submit.value = button.dataset.confirmLabel; + submit.className = `btn ${button.dataset.confirmClass}`; + }); +}); diff --git a/webmentions_ssg/tasks/scanner.py b/webmentions_ssg/tasks/scanner.py index f80e5f4..ff75697 100644 --- a/webmentions_ssg/tasks/scanner.py +++ b/webmentions_ssg/tasks/scanner.py @@ -1,6 +1,6 @@ from collections.abc import Iterator from dataclasses import dataclass -from datetime import datetime, timezone +from datetime import UTC, datetime from fnmatch import fnmatchcase from itertools import chain from pathlib import Path @@ -327,7 +327,7 @@ def scan_sources() -> None: seen_paths: set[str] = set() scanned_count = 0 - scan_time = datetime.now(timezone.utc) + scan_time = datetime.now(UTC) for index_path in root.rglob("index.html"): relative_path = index_path.relative_to(root) @@ -395,3 +395,8 @@ def scan_sources() -> None: scanned_count, pending_count, ) + + +@huey.task() +def manual_scan_sources() -> None: + return scan_sources() diff --git a/webmentions_ssg/templates/includes/confirmation_modal.html b/webmentions_ssg/templates/includes/confirmation_modal.html new file mode 100644 index 0000000..ff9bd06 --- /dev/null +++ b/webmentions_ssg/templates/includes/confirmation_modal.html @@ -0,0 +1,27 @@ +<div class="modal fade" + id="confirmation-modal" + tabindex="-1" + aria-labelledby="confirmation-modal-title" + aria-hidden="true"> + <div class="modal-dialog modal-dialog-centered"> + <div class="modal-content"> + <div class="modal-header"> + <h1 class="modal-title fs-5" id="confirmation-modal-title"></h1> + <button type="button" + class="btn-close" + data-bs-dismiss="modal" + aria-label="Close"></button> + </div> + <div class="modal-body"> + <p id="confirmation-modal-message" class="mb-0"></p> + </div> + <div class="modal-footer"> + {{ render_form(action_form, + id="confirmation-modal-form", + button_style="primary", + form_group_classes="mb-0") }} + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> + </div> + </div> + </div> +</div> diff --git a/webmentions_ssg/templates/received.html b/webmentions_ssg/templates/received.html index a44b706..525e811 100644 --- a/webmentions_ssg/templates/received.html +++ b/webmentions_ssg/templates/received.html @@ -84,65 +84,12 @@ {% if webmentions.pages > 1 %} <div class="mt-3">{{ render_pagination(webmentions) }}</div> {% endif %} - <div class="modal fade" - id="confirmation-modal" - tabindex="-1" - aria-labelledby="confirmation-modal-title" - aria-hidden="true"> - <div class="modal-dialog modal-dialog-centered"> - <div class="modal-content"> - <div class="modal-header"> - <h1 class="modal-title fs-5" id="confirmation-modal-title"></h1> - <button type="button" - class="btn-close" - data-bs-dismiss="modal" - aria-label="Close"></button> - </div> - <div class="modal-body"> - <p id="confirmation-modal-message" class="mb-0"></p> - </div> - <div class="modal-footer"> - {{ render_form(action_form, - id="confirmation-modal-form", - button_style="primary", - form_group_classes="mb-0") }} - <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> - </div> - </div> - </div> - </div> + {% include "includes/confirmation_modal.html" %} {% else %} <p class="text-body-secondary">No Webmentions have been received yet.</p> {% endif %} {% endblock %} {% block scripts %} {{ super() }} - <script> - const confirmationModal = document.getElementById( - "confirmation-modal" - ); - - confirmationModal.addEventListener("show.bs.modal", (event) => { - const button = event.relatedTarget; - - const form = confirmationModal.querySelector( - "#confirmation-modal-form" - ); - const title = confirmationModal.querySelector( - "#confirmation-modal-title" - ); - const message = confirmationModal.querySelector( - "#confirmation-modal-message" - ); - const submit = form.querySelector("#submit"); - - form.action = button.dataset.action; - - title.textContent = button.dataset.title; - message.textContent = button.dataset.message; - - submit.value = button.dataset.confirmLabel; - submit.className = `btn ${button.dataset.confirmClass}`; - }); - </script> + <script src="{{ url_for('static', filename='js/confirmation-modal.js') }}"></script> {% endblock %} diff --git a/webmentions_ssg/templates/sent.html b/webmentions_ssg/templates/sent.html index 4993bef..bc9e021 100644 --- a/webmentions_ssg/templates/sent.html +++ b/webmentions_ssg/templates/sent.html @@ -1,9 +1,21 @@ {% extends "base.html" %} +{% from "bootstrap5/form.html" import render_form %} {% from "bootstrap5/pagination.html" import render_pagination %} {% block content %} <div class="d-flex justify-content-between align-items-center mb-3"> <h1 class="mb-0">Sources</h1> - <span class="text-body-secondary">{{ sources.total }} sources</span> + <div class="d-flex flex-column justify-content-between"> + <span class="text-body-secondary mb-1">{{ sources.total }} sources</span> + <button type="button" + class="btn btn-sm btn-primary" + data-bs-toggle="modal" + data-bs-target="#confirmation-modal" + data-action="{{ url_for("root.rescan_sent_sources") }}" + data-title="Rescan sources?" + data-message="All sources will be scanned again for changes and webmentions." + data-confirm-label="Rescan" + data-confirm-class="btn-primary">Rescan</button> + </div> </div> {% if sources.items %} <div class="table-responsive"> @@ -60,7 +72,12 @@ {% if sources.pages > 1 %} <div class="mt-3">{{ render_pagination(sources) }}</div> {% endif %} + {% include "includes/confirmation_modal.html" %} {% else %} <p class="text-body-secondary">No Webmention sources have been discovered yet.</p> {% endif %} {% endblock %} +{% block scripts %} + {{ super() }} + <script src="{{ url_for('static', filename='js/confirmation-modal.js') }}"></script> +{% endblock %} diff --git a/webmentions_ssg/views.py b/webmentions_ssg/views.py index eeea765..13e5086 100644 --- a/webmentions_ssg/views.py +++ b/webmentions_ssg/views.py @@ -22,6 +22,7 @@ from . import CSRF, forms from . import DATABASE as db from .models import ReceivedWebmention, Source, User from .tasks.receiver import verify_webmention +from .tasks.scanner import manual_scan_sources root_page = Blueprint("root", __name__) @@ -138,7 +139,22 @@ def sent() -> ResponseReturnValue: .order_by(Source.last_seen_at.desc()), per_page=25, ) - return render_template("sent.html", title="Sent Webmentions", sources=sources) + return render_template( + "sent.html", + title="Sent Webmentions", + sources=sources, + action_form=forms.AdminActionForm(), + ) + + +@root_page.post("/sent/rescan") +@login_required +def rescan_sent_sources(): + form = forms.AdminActionForm() + if form.validate_on_submit(): + manual_scan_sources() + flash("Source rescan queued.", "success") + return redirect(url_for("root.sent")) @root_page.route("/sent/<uuid:identifier>") |
