From 7adb2b4a2c69fbb05f576235a85afac8669885b5 Mon Sep 17 00:00:00 2001 From: Benjamin Howe Date: Mon, 24 Jun 2024 22:09:45 +0100 Subject: [PATCH] Add form admin UI (#46) * Return form submissions * Added form submission page * Add table CSS * Various tweaks * Add single submission view --- _assets/css/main.css | 64 ++++++++++ contact-urgent.md | 2 +- contact.md | 2 +- functions/api/{ => form}/contact-urgent.js | 2 +- functions/api/{ => form}/contact.js | 2 +- functions/secure/api/form.js | 27 +++++ functions/secure/api/form/[submission].js | 28 +++++ secure/form-submissions.md | 134 +++++++++++++++++++++ secure/index.md | 1 + 9 files changed, 258 insertions(+), 4 deletions(-) rename functions/api/{ => form}/contact-urgent.js (92%) rename functions/api/{ => form}/contact.js (89%) create mode 100644 functions/secure/api/form.js create mode 100644 functions/secure/api/form/[submission].js create mode 100644 secure/form-submissions.md diff --git a/_assets/css/main.css b/_assets/css/main.css index c6a3908..c7530a2 100644 --- a/_assets/css/main.css +++ b/_assets/css/main.css @@ -65,6 +65,24 @@ code, pre { font-family: monospace; } +table { + border: solid 1px #000; + border-collapse: collapse; + border-spacing: 0; +} + +thead th { + border: solid 1px #000; + font-weight: bold; + padding: 10px; + text-align: left; +} + +tbody td { + border: solid 1px #000; + padding: 10px; +} + /* header */ header, footer { @@ -163,7 +181,13 @@ label { /* professional experience (about page) */ +table#professional-experience { + border: none; +} + table#professional-experience td { + border: none; + padding: 0; vertical-align: top; } @@ -190,6 +214,46 @@ table#professional-experience tr + tr td { padding-top: 0.75rem; } +/* spinner used while waiting for fetch, see https://loading.io/css/ */ + +.lds-dual-ring { + color: #961c1c; + display: block; + margin: 0 auto; +} + +.lds-dual-ring, +.lds-dual-ring:after { + box-sizing: border-box; +} + +.lds-dual-ring { + display: inline-block; + width: 80px; + height: 80px; +} + +.lds-dual-ring:after { + content: " "; + display: block; + width: 64px; + height: 64px; + margin: 8px; + border-radius: 50%; + border: 6.4px solid currentColor; + border-color: currentColor transparent currentColor transparent; + animation: lds-dual-ring 1.2s linear infinite; +} + +@keyframes lds-dual-ring { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + /* footer */ #main { diff --git a/contact-urgent.md b/contact-urgent.md index 5c97d7a..9b012a9 100644 --- a/contact-urgent.md +++ b/contact-urgent.md @@ -5,7 +5,7 @@ title: Urgent Contact If you have an access token then you can send me an urgent message using this form: -
+
diff --git a/contact.md b/contact.md index f2cbcac..1492f52 100644 --- a/contact.md +++ b/contact.md @@ -5,7 +5,7 @@ title: Contact If you'd like to get in touch then you can contact me using the below form. - +
diff --git a/functions/api/contact-urgent.js b/functions/api/form/contact-urgent.js similarity index 92% rename from functions/api/contact-urgent.js rename to functions/api/form/contact-urgent.js index f51d6df..340e891 100644 --- a/functions/api/contact-urgent.js +++ b/functions/api/form/contact-urgent.js @@ -1,4 +1,4 @@ -import { handleForm } from "../../functions-src/forms.js" +import { handleForm } from "../../../functions-src/forms.js" export async function onRequest(context) { if (context.request.method !== "POST") { diff --git a/functions/api/contact.js b/functions/api/form/contact.js similarity index 89% rename from functions/api/contact.js rename to functions/api/form/contact.js index 15e87be..ec8c623 100644 --- a/functions/api/contact.js +++ b/functions/api/form/contact.js @@ -1,4 +1,4 @@ -import { handleForm } from "../../functions-src/forms.js" +import { handleForm } from "../../../functions-src/forms.js" export async function onRequest(context) { if (context.request.method !== "POST") { diff --git a/functions/secure/api/form.js b/functions/secure/api/form.js new file mode 100644 index 0000000..985b182 --- /dev/null +++ b/functions/secure/api/form.js @@ -0,0 +1,27 @@ +export async function onRequest(context) { + if (context.request.method !== "GET") { + return new Response("Invalid request method", { status: 405 }); + } + + const submissionsQuery = await context.env.DB_FORMS.prepare("SELECT submission_id, form_id, submitted_ts, spam_reasons, json_extract(headers, '$.cf-connecting-ip') as ip, json_extract(cf, '$.asn') as asn, json_extract(cf, '$.country') as country FROM submissions WHERE submitted_ts > ? ORDER BY submitted_ts DESC") + .bind( + new Date().subtractDays(30).toISOString(), + ) + .all(); + const rows = submissionsQuery + .results + .map((row) => { + row.spam_reasons = JSON.parse(row.spam_reasons); + row.asn = row.asn.toString(); + return row; + }); + + return Response.json(rows); +} + +// TODO: this is bad practice, consider replacing (see https://www.reddit.com/r/learnjavascript/comments/qgtut6/comment/hi8jg6w/) +Date.prototype.subtractDays = function(days) { + var date = new Date(this.valueOf()); + date.setDate(date.getDate() - days); + return date; +} diff --git a/functions/secure/api/form/[submission].js b/functions/secure/api/form/[submission].js new file mode 100644 index 0000000..cdc010c --- /dev/null +++ b/functions/secure/api/form/[submission].js @@ -0,0 +1,28 @@ +export async function onRequest(context) { + if (context.request.method !== "GET") { + return new Response("Invalid request method", { status: 405 }); + } + + const submissionsQuery = await context.env.DB_FORMS.prepare("SELECT * FROM submissions WHERE submission_id = ? ORDER BY submitted_ts DESC") + .bind( + context.params.submission, + ) + .all(); + const rows = submissionsQuery + .results + .map((row) => { + row.fields = JSON.parse(row.fields); + row.spam_reasons = JSON.parse(row.spam_reasons); + row.cf = JSON.parse(row.cf); + row.headers = JSON.parse(row.headers); + return row; + }); + + if (rows.length === 0) { + return new Response("Submission not found", { status: 404 }); + } else if (rows.length === 1) { + return Response.json(rows[0]); + } else { + return new Response(`Multiple submissions found for ID "${context.params.submission}"`, { status: 500 }); + } +} diff --git a/secure/form-submissions.md b/secure/form-submissions.md new file mode 100644 index 0000000..424618e --- /dev/null +++ b/secure/form-submissions.md @@ -0,0 +1,134 @@ +--- +layout: default +title: Form Submissions +--- + +
+
+ + diff --git a/secure/index.md b/secure/index.md index 547991e..8b40676 100644 --- a/secure/index.md +++ b/secure/index.md @@ -3,4 +3,5 @@ layout: default title: "Secure area: index" --- +- [(Contact) form submissions](form-submissions) - [Token generator (for urgent contact form)](token-generator)