From 6bb01f0bf17c82dcfbbb4a962920ecdd8805ce3f Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 3 Feb 2025 17:35:34 +0100 Subject: [PATCH 1/3] feat(frontend): show audit log entry for closed reports --- .../Moderation/ReportsController.cs | 4 ++ Foxnouns.Backend/Dto/Moderation.cs | 4 +- .../src/lib/api/models/moderation.ts | 1 + .../admin/ClosedReportAuditLog.svelte | 37 +++++++++++++++++ .../routes/admin/reports/[id]/+page.server.ts | 7 +++- .../routes/admin/reports/[id]/+page.svelte | 40 ++++++++++++++----- 6 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 Foxnouns.Frontend/src/lib/components/admin/ClosedReportAuditLog.svelte diff --git a/Foxnouns.Backend/Controllers/Moderation/ReportsController.cs b/Foxnouns.Backend/Controllers/Moderation/ReportsController.cs index 2bd2b2d..c5472b3 100644 --- a/Foxnouns.Backend/Controllers/Moderation/ReportsController.cs +++ b/Foxnouns.Backend/Controllers/Moderation/ReportsController.cs @@ -229,6 +229,7 @@ public class ReportsController( .Reports.Include(r => r.Reporter) .Include(r => r.TargetUser) .Include(r => r.TargetMember) + .Include(r => r.AuditLogEntry) .FirstOrDefaultAsync(r => r.Id == id, ct); if (report == null) throw new ApiError.NotFound("No report with that ID found."); @@ -243,6 +244,9 @@ public class ReportsController( ), Member: report.TargetMember != null ? memberRenderer.RenderMember(report.TargetMember) + : null, + AuditLogEntry: report.AuditLogEntry != null + ? moderationRenderer.RenderAuditLogEntry(report.AuditLogEntry) : null ) ); diff --git a/Foxnouns.Backend/Dto/Moderation.cs b/Foxnouns.Backend/Dto/Moderation.cs index 3792e31..26fd0aa 100644 --- a/Foxnouns.Backend/Dto/Moderation.cs +++ b/Foxnouns.Backend/Dto/Moderation.cs @@ -39,7 +39,9 @@ public record ReportResponse( public record ReportDetailResponse( ReportResponse Report, UserResponse User, - [property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] MemberResponse? Member + [property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] MemberResponse? Member, + [property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + AuditLogResponse? AuditLogEntry ); public record AuditLogResponse( diff --git a/Foxnouns.Frontend/src/lib/api/models/moderation.ts b/Foxnouns.Frontend/src/lib/api/models/moderation.ts index edd0865..eee9382 100644 --- a/Foxnouns.Frontend/src/lib/api/models/moderation.ts +++ b/Foxnouns.Frontend/src/lib/api/models/moderation.ts @@ -75,6 +75,7 @@ export type ReportDetails = { report: Report; user: User; member?: Member; + audit_log_entry?: AuditLogEntry; }; export type QueriedUser = { diff --git a/Foxnouns.Frontend/src/lib/components/admin/ClosedReportAuditLog.svelte b/Foxnouns.Frontend/src/lib/components/admin/ClosedReportAuditLog.svelte new file mode 100644 index 0000000..7f7f3b0 --- /dev/null +++ b/Foxnouns.Frontend/src/lib/components/admin/ClosedReportAuditLog.svelte @@ -0,0 +1,37 @@ + + +
+

+ Closed by at + {idTimestamp(entry.id).toLocaleString(DateTime.DATETIME_MED)} +

+

+ {#if entry.type === AuditLogEntryType.IgnoreReport} + Report was ignored + {:else if entry.type === AuditLogEntryType.WarnUser || entry.type === AuditLogEntryType.WarnUserAndClearProfile} + User was warned + {#if entry.cleared_fields && entry.cleared_fields.length > 0} +
Cleared fields: {entry.cleared_fields.join(", ")} + {/if} + {:else if entry.type === AuditLogEntryType.SuspendUser} + User was suspended + {/if} +

+

Reason

+

+ {#if entry.reason} + {@html renderMarkdown(entry.reason)} + {:else} + (no reason given) + {/if} +

+
diff --git a/Foxnouns.Frontend/src/routes/admin/reports/[id]/+page.server.ts b/Foxnouns.Frontend/src/routes/admin/reports/[id]/+page.server.ts index a9a2d44..214049a 100644 --- a/Foxnouns.Frontend/src/routes/admin/reports/[id]/+page.server.ts +++ b/Foxnouns.Frontend/src/routes/admin/reports/[id]/+page.server.ts @@ -7,7 +7,12 @@ export const load = async ({ params, fetch, cookies }) => { fetch, cookies, }); - return { report: resp.report, user: resp.user, member: resp.member }; + return { + report: resp.report, + user: resp.user, + member: resp.member, + auditLogEntry: resp.audit_log_entry, + }; }; export const actions = createModactions(); diff --git a/Foxnouns.Frontend/src/routes/admin/reports/[id]/+page.svelte b/Foxnouns.Frontend/src/routes/admin/reports/[id]/+page.svelte index 17bff1d..2e953d5 100644 --- a/Foxnouns.Frontend/src/routes/admin/reports/[id]/+page.svelte +++ b/Foxnouns.Frontend/src/routes/admin/reports/[id]/+page.svelte @@ -2,6 +2,7 @@ import type { Member } from "$api/models/member"; import type { User } from "$api/models/user"; import ActionForm from "$components/admin/ActionForm.svelte"; + import ClosedReportAuditLog from "$components/admin/ClosedReportAuditLog.svelte"; import PartialProfileCard from "$components/admin/PartialProfileCard.svelte"; import ProfileHeader from "$components/profile/ProfileHeader.svelte"; import MemberCard from "$components/profile/user/MemberCard.svelte"; @@ -10,13 +11,21 @@ type Props = { data: PageData; form: ActionData }; let { data, form }: Props = $props(); - let { report, user, member } = $derived(data); + let { report, user, auditLogEntry } = $derived(data); Report on @{user.username} • pronouns.cc +{#if report.status === "CLOSED"} +
+ This report has already been handled. See audit log entry +
+{/if} +

Target user

@@ -55,15 +64,26 @@
-
-

Take action

- -
+{#if report.status === "OPEN"} +
+

Take action

+ +
+{:else if report.status === "CLOSED" && auditLogEntry} + +{:else} +
+

Closed by an unknown moderator

+

+ This should not happen! +

+
+{/if} {#if report.snapshot}

Profile at time of report

From 32e0c09d06306089887639116410ff043b599054 Mon Sep 17 00:00:00 2001 From: sam Date: Fri, 7 Feb 2025 20:33:59 +0100 Subject: [PATCH 2/3] fix(backend): add thousands separators to footer --- Foxnouns.Frontend/src/lib/components/Footer.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Foxnouns.Frontend/src/lib/components/Footer.svelte b/Foxnouns.Frontend/src/lib/components/Footer.svelte index f3315f2..6fd6564 100644 --- a/Foxnouns.Frontend/src/lib/components/Footer.svelte +++ b/Foxnouns.Frontend/src/lib/components/Footer.svelte @@ -23,8 +23,8 @@
    -
  • {meta.users.total} users
  • -
  • {meta.members} members
  • +
  • {meta.users.total.toLocaleString()} users
  • +
  • {meta.members.toLocaleString()} members
From 74800b46ef0ae0069d68ae02386ee979f7efa9eb Mon Sep 17 00:00:00 2001 From: sam Date: Fri, 7 Feb 2025 20:57:27 +0100 Subject: [PATCH 3/3] feat(frontend): don't break signup pages on reload --- .../Authentication/FediverseAuthController.cs | 3 +-- Foxnouns.Frontend/src/lib/actions/callback.ts | 25 ++++++++++++++----- .../src/lib/i18n/locales/en.json | 2 +- .../auth/callback/discord/+page.server.ts | 5 ---- .../routes/auth/callback/discord/+page.svelte | 17 +++---------- .../auth/callback/google/+page.server.ts | 5 ---- .../routes/auth/callback/google/+page.svelte | 17 +++---------- .../mastodon/[instance]/+page.server.ts | 5 ---- .../callback/mastodon/[instance]/+page.svelte | 19 ++++---------- .../register/[ticket]/+page.server.ts} | 25 ++++++++++++++----- .../callback/register/[ticket]/+page.svelte | 22 ++++++++++++++++ .../auth/callback/tumblr/+page.server.ts | 5 ---- .../routes/auth/callback/tumblr/+page.svelte | 17 +++---------- 13 files changed, 79 insertions(+), 88 deletions(-) rename Foxnouns.Frontend/src/{lib/actions/register.ts => routes/auth/callback/register/[ticket]/+page.server.ts} (62%) create mode 100644 Foxnouns.Frontend/src/routes/auth/callback/register/[ticket]/+page.svelte diff --git a/Foxnouns.Backend/Controllers/Authentication/FediverseAuthController.cs b/Foxnouns.Backend/Controllers/Authentication/FediverseAuthController.cs index edc7b6a..1ae2ef9 100644 --- a/Foxnouns.Backend/Controllers/Authentication/FediverseAuthController.cs +++ b/Foxnouns.Backend/Controllers/Authentication/FediverseAuthController.cs @@ -94,8 +94,7 @@ public class FediverseAuthController( public async Task RegisterAsync([FromBody] OauthRegisterRequest req) { FediverseTicketData? ticketData = await keyCacheService.GetKeyAsync( - $"fediverse:{req.Ticket}", - true + $"fediverse:{req.Ticket}" ); if (ticketData == null) throw new ApiError.BadRequest("Invalid ticket", "ticket", req.Ticket); diff --git a/Foxnouns.Frontend/src/lib/actions/callback.ts b/Foxnouns.Frontend/src/lib/actions/callback.ts index 3df1070..5c525ab 100644 --- a/Foxnouns.Frontend/src/lib/actions/callback.ts +++ b/Foxnouns.Frontend/src/lib/actions/callback.ts @@ -4,10 +4,12 @@ import type { AddAccountResponse, CallbackResponse } from "$api/models"; import { setToken } from "$lib"; import log from "$lib/log"; import { isRedirect, redirect, type ServerLoadEvent } from "@sveltejs/kit"; +import type { TicketData } from "../../routes/auth/callback/register/[ticket]/+page.server"; export default function createCallbackLoader( callbackType: string, bodyFn?: (event: ServerLoadEvent) => Promise, + returnData?: boolean, ) { return async (event: ServerLoadEvent) => { const { parent, fetch, cookies } = event; @@ -53,12 +55,23 @@ export default function createCallbackLoader( redirect(303, `/@${resp.user!.username}`); } - return { - hasAccount: false, - isLinkRequest: false, - ticket: resp.ticket!, - remoteUser: resp.remote_username!, - }; + if (returnData) + return { + ticket: resp.ticket!, + remoteUser: resp.remote_username!, + }; + + const ticket = btoa( + JSON.stringify({ + type: callbackType, + ticket: resp.ticket!, + remoteUsername: resp.remote_username!, + } satisfies TicketData), + ) + .replaceAll("+", "-") + .replaceAll("/", "_"); + + redirect(303, "/auth/callback/register/" + ticket); } catch (e) { if (isRedirect(e)) throw e; if (e instanceof ApiError) return { isLinkRequest: false, error: e.obj }; diff --git a/Foxnouns.Frontend/src/lib/i18n/locales/en.json b/Foxnouns.Frontend/src/lib/i18n/locales/en.json index e7ceed3..453e4dc 100644 --- a/Foxnouns.Frontend/src/lib/i18n/locales/en.json +++ b/Foxnouns.Frontend/src/lib/i18n/locales/en.json @@ -46,7 +46,7 @@ "remote-fediverse-account-label": "Your Fediverse account", "register-username-label": "Username", "register-button": "Register account", - "register-with-mastodon": "Register with a Fediverse account", + "register-with-fediverse": "Register with a Fediverse account", "log-in-with-fediverse-error-blurb": "Is your instance returning an error?", "log-in-with-fediverse-force-refresh-button": "Force a refresh on our end", "register-with-discord": "Register with a Discord account", diff --git a/Foxnouns.Frontend/src/routes/auth/callback/discord/+page.server.ts b/Foxnouns.Frontend/src/routes/auth/callback/discord/+page.server.ts index dc66812..b297e25 100644 --- a/Foxnouns.Frontend/src/routes/auth/callback/discord/+page.server.ts +++ b/Foxnouns.Frontend/src/routes/auth/callback/discord/+page.server.ts @@ -1,8 +1,3 @@ import createCallbackLoader from "$lib/actions/callback"; -import createRegisterAction from "$lib/actions/register"; export const load = createCallbackLoader("discord"); - -export const actions = { - default: createRegisterAction("/auth/discord/register"), -}; diff --git a/Foxnouns.Frontend/src/routes/auth/callback/discord/+page.svelte b/Foxnouns.Frontend/src/routes/auth/callback/discord/+page.svelte index e82f0f1..10fec06 100644 --- a/Foxnouns.Frontend/src/routes/auth/callback/discord/+page.svelte +++ b/Foxnouns.Frontend/src/routes/auth/callback/discord/+page.svelte @@ -1,12 +1,11 @@ @@ -17,15 +16,7 @@ {#if data.error}

{$t("auth.register-with-discord")}

- {:else if data.isLinkRequest} - {:else} - + {/if} diff --git a/Foxnouns.Frontend/src/routes/auth/callback/google/+page.server.ts b/Foxnouns.Frontend/src/routes/auth/callback/google/+page.server.ts index 49f963c..d3e1b93 100644 --- a/Foxnouns.Frontend/src/routes/auth/callback/google/+page.server.ts +++ b/Foxnouns.Frontend/src/routes/auth/callback/google/+page.server.ts @@ -1,8 +1,3 @@ import createCallbackLoader from "$lib/actions/callback"; -import createRegisterAction from "$lib/actions/register"; export const load = createCallbackLoader("google"); - -export const actions = { - default: createRegisterAction("/auth/google/register"), -}; diff --git a/Foxnouns.Frontend/src/routes/auth/callback/google/+page.svelte b/Foxnouns.Frontend/src/routes/auth/callback/google/+page.svelte index 284806a..eeff070 100644 --- a/Foxnouns.Frontend/src/routes/auth/callback/google/+page.svelte +++ b/Foxnouns.Frontend/src/routes/auth/callback/google/+page.svelte @@ -1,12 +1,11 @@ @@ -17,15 +16,7 @@ {#if data.error}

{$t("auth.register-with-google")}

- {:else if data.isLinkRequest} - {:else} - + {/if} diff --git a/Foxnouns.Frontend/src/routes/auth/callback/mastodon/[instance]/+page.server.ts b/Foxnouns.Frontend/src/routes/auth/callback/mastodon/[instance]/+page.server.ts index b092b1e..c2d290e 100644 --- a/Foxnouns.Frontend/src/routes/auth/callback/mastodon/[instance]/+page.server.ts +++ b/Foxnouns.Frontend/src/routes/auth/callback/mastodon/[instance]/+page.server.ts @@ -1,6 +1,5 @@ import ApiError, { ErrorCode } from "$api/error"; import createCallbackLoader from "$lib/actions/callback"; -import createRegisterAction from "$lib/actions/register"; export const load = createCallbackLoader("fediverse", async ({ params, url }) => { const code = url.searchParams.get("code") as string | null; @@ -10,7 +9,3 @@ export const load = createCallbackLoader("fediverse", async ({ params, url }) => return { code: code || token, state, instance: params.instance! }; }); - -export const actions = { - default: createRegisterAction("/auth/fediverse/register"), -}; diff --git a/Foxnouns.Frontend/src/routes/auth/callback/mastodon/[instance]/+page.svelte b/Foxnouns.Frontend/src/routes/auth/callback/mastodon/[instance]/+page.svelte index 99bd00c..fdc8d7a 100644 --- a/Foxnouns.Frontend/src/routes/auth/callback/mastodon/[instance]/+page.svelte +++ b/Foxnouns.Frontend/src/routes/auth/callback/mastodon/[instance]/+page.svelte @@ -1,12 +1,11 @@ @@ -17,15 +16,7 @@ {#if data.error}

{$t("auth.register-with-mastodon")}

- {:else if data.isLinkRequest} - {:else} - + {/if} diff --git a/Foxnouns.Frontend/src/lib/actions/register.ts b/Foxnouns.Frontend/src/routes/auth/callback/register/[ticket]/+page.server.ts similarity index 62% rename from Foxnouns.Frontend/src/lib/actions/register.ts rename to Foxnouns.Frontend/src/routes/auth/callback/register/[ticket]/+page.server.ts index d3c126d..b14c1a2 100644 --- a/Foxnouns.Frontend/src/lib/actions/register.ts +++ b/Foxnouns.Frontend/src/routes/auth/callback/register/[ticket]/+page.server.ts @@ -3,10 +3,23 @@ import ApiError, { ErrorCode, type RawApiError } from "$api/error"; import type { AuthResponse } from "$api/models/auth"; import { setToken } from "$lib"; import log from "$lib/log"; -import { isRedirect, redirect, type RequestEvent } from "@sveltejs/kit"; +import { isRedirect, redirect } from "@sveltejs/kit"; + +export type TicketData = { + type: string; + ticket: string; + remoteUsername: string; +}; + +export const load = async ({ params }) => { + const data = JSON.parse(atob(params.ticket)) as TicketData; + return data; +}; + +export const actions = { + default: async ({ request, fetch, cookies, params }) => { + const type = (JSON.parse(atob(params.ticket)) as TicketData).type; -export default function createRegisterAction(callbackUrl: string) { - return async function ({ request, fetch, cookies }: RequestEvent) { const data = await request.formData(); const username = data.get("username") as string | null; const ticket = data.get("ticket") as string | null; @@ -17,7 +30,7 @@ export default function createRegisterAction(callbackUrl: string) { }; try { - const resp = await apiRequest("POST", callbackUrl, { + const resp = await apiRequest("POST", `/auth/${type}/register`, { body: { username, ticket }, isInternal: true, fetch, @@ -31,5 +44,5 @@ export default function createRegisterAction(callbackUrl: string) { if (e instanceof ApiError) return { error: e.obj }; throw e; } - }; -} + }, +}; diff --git a/Foxnouns.Frontend/src/routes/auth/callback/register/[ticket]/+page.svelte b/Foxnouns.Frontend/src/routes/auth/callback/register/[ticket]/+page.svelte new file mode 100644 index 0000000..6d1b6fc --- /dev/null +++ b/Foxnouns.Frontend/src/routes/auth/callback/register/[ticket]/+page.svelte @@ -0,0 +1,22 @@ + + + + {$t(`auth.register-with-${data.type}`)} • pronouns.cc + + +
+ +
diff --git a/Foxnouns.Frontend/src/routes/auth/callback/tumblr/+page.server.ts b/Foxnouns.Frontend/src/routes/auth/callback/tumblr/+page.server.ts index 49346f1..813f19e 100644 --- a/Foxnouns.Frontend/src/routes/auth/callback/tumblr/+page.server.ts +++ b/Foxnouns.Frontend/src/routes/auth/callback/tumblr/+page.server.ts @@ -1,8 +1,3 @@ import createCallbackLoader from "$lib/actions/callback"; -import createRegisterAction from "$lib/actions/register"; export const load = createCallbackLoader("tumblr"); - -export const actions = { - default: createRegisterAction("/auth/tumblr/register"), -}; diff --git a/Foxnouns.Frontend/src/routes/auth/callback/tumblr/+page.svelte b/Foxnouns.Frontend/src/routes/auth/callback/tumblr/+page.svelte index c7c53e9..7a43261 100644 --- a/Foxnouns.Frontend/src/routes/auth/callback/tumblr/+page.svelte +++ b/Foxnouns.Frontend/src/routes/auth/callback/tumblr/+page.svelte @@ -1,12 +1,11 @@ @@ -17,15 +16,7 @@ {#if data.error}

{$t("auth.register-with-tumblr")}

- {:else if data.isLinkRequest} - {:else} - + {/if}