From 4fca18ac8d69772aef8f4abc3d9883c0011a9424 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Fri, 2 Jan 2026 18:34:45 +0200 Subject: [PATCH] User middleware (#2687) --- app/db/seed/index.ts | 2 + app/features/admin/actions/admin.server.ts | 2 +- app/features/admin/loaders/admin.server.ts | 2 +- app/features/api-private/routes/seed.ts | 2 + app/features/api/actions/api.server.ts | 2 +- app/features/api/loaders/api.server.ts | 5 +- app/features/art/actions/art.new.server.ts | 2 +- app/features/art/loaders/art.new.server.ts | 2 +- .../actions/associations.new.server.ts | 2 +- .../actions/associations.server.ts | 2 +- .../loaders/associations.server.ts | 4 +- app/features/auth/core/routes.server.ts | 6 +- app/features/auth/core/user-context.server.ts | 47 ++++++ .../auth/core/user-middleware.server.ts | 34 +++++ app/features/auth/core/user.server.ts | 51 +------ .../badges/actions/badges.$id.edit.server.ts | 2 +- app/features/ban/core/banned.server.ts | 19 ++- app/features/ban/core/banned.test.ts | 59 ++++++++ .../builds/loaders/builds.$slug.server.ts | 2 +- .../calendar.$id.report-winners.server.ts | 4 +- .../calendar/actions/calendar.$id.server.ts | 6 +- .../calendar/actions/calendar.new.server.ts | 2 +- app/features/calendar/actions/calendar.tsx | 4 +- .../calendar.$id.report-winners.server.ts | 4 +- .../calendar/loaders/calendar.new.server.ts | 2 +- .../calendar/loaders/calendar.server.ts | 2 +- .../front-page/loaders/index.server.ts | 7 +- .../img-upload/actions/upload.admin.server.ts | 2 +- .../img-upload/actions/upload.server.ts | 2 +- .../img-upload/loaders/upload.admin.server.ts | 5 +- .../img-upload/loaders/upload.server.ts | 2 +- .../loaders/leaderboards.server.ts | 2 +- app/features/lfg/actions/lfg.new.server.ts | 2 +- app/features/lfg/actions/lfg.server.ts | 2 +- app/features/lfg/loaders/lfg.new.server.ts | 2 +- app/features/lfg/loaders/lfg.server.ts | 5 +- .../loaders/notifications.server.ts | 7 +- .../routes/notifications.seen.ts | 4 +- .../routes/notifications.subscribe.ts | 2 +- ...uggestions.comment.$tier.$userId.server.ts | 2 +- .../actions/plus.suggestions.new.server.ts | 2 +- .../actions/plus.suggestions.server.ts | 2 +- .../plus-voting/actions/plus.voting.server.ts | 2 +- .../loaders/plus.voting.results.server.ts | 5 +- .../plus-voting/loaders/plus.voting.server.ts | 4 +- .../scrims/actions/scrims.$id.server.ts | 2 +- .../scrims/actions/scrims.new.server.ts | 2 +- app/features/scrims/actions/scrims.server.ts | 2 +- .../scrims/loaders/scrims.$id.server.ts | 4 +- .../scrims/loaders/scrims.new.server.ts | 7 +- app/features/scrims/loaders/scrims.server.ts | 2 +- .../actions/q.match.$id.server.ts | 2 +- .../loaders/q.match.$id.server.ts | 4 +- .../actions/q.settings.server.ts | 7 +- .../loaders/q.settings.server.ts | 7 +- .../sendouq/actions/q.looking.server.ts | 2 +- .../sendouq/actions/q.preparing.server.ts | 2 +- app/features/sendouq/actions/q.server.ts | 2 +- .../sendouq/loaders/q.looking.server.ts | 2 +- .../sendouq/loaders/q.preparing.server.ts | 7 +- app/features/sendouq/loaders/q.server.ts | 4 +- app/features/sendouq/routes/trusters.ts | 7 +- .../settings/actions/settings.server.ts | 2 +- .../settings/loaders/settings.server.ts | 7 +- .../team/actions/t.$customUrl.edit.server.ts | 2 +- .../team/actions/t.$customUrl.index.server.ts | 4 +- .../team/actions/t.$customUrl.join.server.ts | 2 +- .../actions/t.$customUrl.roster.server.ts | 2 +- app/features/team/actions/t.server.ts | 2 +- .../team/loaders/t.$customUrl.edit.server.ts | 4 +- .../team/loaders/t.$customUrl.join.server.ts | 2 +- .../loaders/t.$customUrl.roster.server.ts | 4 +- app/features/team/loaders/t.server.ts | 7 +- .../actions/xsearch.player.$id.server.ts | 4 +- .../to.$id.brackets.finalize.server.ts | 4 +- .../actions/to.$id.brackets.server.ts | 2 +- .../actions/to.$id.matches.$mid.server.ts | 2 +- .../to.$id.brackets.finalize.server.ts | 6 +- .../loaders/to.$id.divisions.server.ts | 4 +- .../actions/org.$slug.edit.server.ts | 2 +- .../actions/org.$slug.server.ts | 2 +- .../actions/org.new.server.ts | 2 +- .../loaders/org.$slug.edit.server.ts | 4 +- .../loaders/org.$slug.server.ts | 2 +- .../actions/to.$id.subs.new.server.ts | 2 +- .../actions/to.$id.subs.server.ts | 2 +- .../loaders/to.$id.subs.new.server.ts | 4 +- .../loaders/to.$id.subs.server.ts | 4 +- .../tournament/actions/to.$id.admin.server.ts | 2 +- .../tournament/actions/to.$id.join.server.ts | 4 +- .../actions/to.$id.register.server.ts | 2 +- .../tournament/actions/to.$id.seeds.server.ts | 2 +- .../loaders/to.$id.register.server.ts | 4 +- .../tournament/loaders/to.$id.seeds.server.ts | 4 +- .../tournament/loaders/to.$id.server.ts | 4 +- app/features/tournament/routes/to.search.ts | 4 +- .../actions/u.$identifier.admin.server.ts | 2 +- .../actions/u.$identifier.art.server.ts | 4 +- .../u.$identifier.builds.new.server.ts | 2 +- .../actions/u.$identifier.builds.server.ts | 2 +- .../actions/u.$identifier.edit.server.ts | 4 +- ...u.$identifier.results.highlights.server.ts | 2 +- .../loaders/u.$identifier.admin.server.ts | 4 +- .../loaders/u.$identifier.art.server.ts | 6 +- .../u.$identifier.builds.new.server.ts | 4 +- .../loaders/u.$identifier.builds.server.ts | 4 +- .../loaders/u.$identifier.edit.server.ts | 6 +- .../loaders/u.$identifier.seasons.server.ts | 2 +- .../user-page/loaders/u.$identifier.server.ts | 6 +- app/features/user-search/loaders/u.server.ts | 4 +- app/features/vods/actions/vods.$id.server.ts | 4 +- app/features/vods/actions/vods.new.server.ts | 2 +- app/features/vods/loaders/vods.new.server.ts | 2 +- app/root.tsx | 18 +-- app/utils/Test.ts | 80 ++++++---- e2e/ban.spec.ts | 141 ++++++++++++++++++ react-router.config.ts | 3 + 117 files changed, 532 insertions(+), 277 deletions(-) create mode 100644 app/features/auth/core/user-context.server.ts create mode 100644 app/features/auth/core/user-middleware.server.ts create mode 100644 app/features/ban/core/banned.test.ts create mode 100644 e2e/ban.spec.ts diff --git a/app/db/seed/index.ts b/app/db/seed/index.ts index 39c78f593..3ffb90d3e 100644 --- a/app/db/seed/index.ts +++ b/app/db/seed/index.ts @@ -289,6 +289,8 @@ function wipeDB() { "UserFriendCode", "NotificationUser", "Notification", + "BanLog", + "ModNote", "User", "PlusSuggestion", "PlusVote", diff --git a/app/features/admin/actions/admin.server.ts b/app/features/admin/actions/admin.server.ts index 88560732e..0c303496a 100644 --- a/app/features/admin/actions/admin.server.ts +++ b/app/features/admin/actions/admin.server.ts @@ -21,7 +21,7 @@ export const action = async ({ request }: ActionFunctionArgs) => { request, schema: adminActionSchema, }); - const user = await requireUser(request); + const user = await requireUser(); let message: string; switch (data._action) { diff --git a/app/features/admin/loaders/admin.server.ts b/app/features/admin/loaders/admin.server.ts index 212bf64f1..a85a18fde 100644 --- a/app/features/admin/loaders/admin.server.ts +++ b/app/features/admin/loaders/admin.server.ts @@ -8,7 +8,7 @@ import { DANGEROUS_CAN_ACCESS_DEV_CONTROLS } from "../core/dev-controls"; export const loader = async ({ request }: LoaderFunctionArgs) => { if (!DANGEROUS_CAN_ACCESS_DEV_CONTROLS) { - const user = await requireUser(request); + const user = await requireUser(); requireRole(user, "STAFF"); } diff --git a/app/features/api-private/routes/seed.ts b/app/features/api-private/routes/seed.ts index 540b52529..72095e185 100644 --- a/app/features/api-private/routes/seed.ts +++ b/app/features/api-private/routes/seed.ts @@ -3,6 +3,7 @@ import { z } from "zod"; import { seed } from "~/db/seed"; import { DANGEROUS_CAN_ACCESS_DEV_CONTROLS } from "~/features/admin/core/dev-controls"; import { SEED_VARIATIONS } from "~/features/api-private/constants"; +import { refreshBannedCache } from "~/features/ban/core/banned.server"; import { refreshSendouQInstance } from "~/features/sendouq/core/SendouQ.server"; import { parseRequestPayload } from "~/utils/remix.server"; @@ -26,6 +27,7 @@ export const action: ActionFunction = async ({ request }) => { await seed(variation); + await refreshBannedCache(); await refreshSendouQInstance(); return Response.json(null); diff --git a/app/features/api/actions/api.server.ts b/app/features/api/actions/api.server.ts index 3f8d7fb05..01400885d 100644 --- a/app/features/api/actions/api.server.ts +++ b/app/features/api/actions/api.server.ts @@ -16,7 +16,7 @@ export const action = async ({ request }: ActionFunctionArgs) => { request, schema: apiActionSchema, }); - const user = await requireUser(request); + const user = await requireUser(); const hasApiAccess = await checkUserHasApiAccess(user); if (!hasApiAccess) { diff --git a/app/features/api/loaders/api.server.ts b/app/features/api/loaders/api.server.ts index 35bf24694..8971d6bbd 100644 --- a/app/features/api/loaders/api.server.ts +++ b/app/features/api/loaders/api.server.ts @@ -1,10 +1,9 @@ -import type { LoaderFunctionArgs } from "react-router"; import { requireUser } from "~/features/auth/core/user.server"; import * as ApiRepository from "../ApiRepository.server"; import { checkUserHasApiAccess } from "../core/perms"; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await requireUser(request); +export const loader = async () => { + const user = await requireUser(); const hasApiAccess = await checkUserHasApiAccess(user); diff --git a/app/features/art/actions/art.new.server.ts b/app/features/art/actions/art.new.server.ts index 4db37c79c..5c1c7bb1e 100644 --- a/app/features/art/actions/art.new.server.ts +++ b/app/features/art/actions/art.new.server.ts @@ -20,7 +20,7 @@ import { NEW_ART_EXISTING_SEARCH_PARAM_KEY } from "../art-constants"; import { editArtSchema, newArtSchema } from "../art-schemas.server"; export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUser(); requireRole(user, "ARTIST"); const searchParams = new URL(request.url).searchParams; diff --git a/app/features/art/loaders/art.new.server.ts b/app/features/art/loaders/art.new.server.ts index 7402fb19c..8f4aa83b9 100644 --- a/app/features/art/loaders/art.new.server.ts +++ b/app/features/art/loaders/art.new.server.ts @@ -4,7 +4,7 @@ import * as ArtRepository from "../ArtRepository.server"; import { NEW_ART_EXISTING_SEARCH_PARAM_KEY } from "../art-constants"; export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const artIdRaw = new URL(request.url).searchParams.get( NEW_ART_EXISTING_SEARCH_PARAM_KEY, diff --git a/app/features/associations/actions/associations.new.server.ts b/app/features/associations/actions/associations.new.server.ts index a8b451809..de397eeec 100644 --- a/app/features/associations/actions/associations.new.server.ts +++ b/app/features/associations/actions/associations.new.server.ts @@ -7,7 +7,7 @@ import { associationsPage } from "~/utils/urls"; import * as AssociationRepository from "../AssociationRepository.server"; export const action = async ({ request }: ActionFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: createNewAssociationSchema, diff --git a/app/features/associations/actions/associations.server.ts b/app/features/associations/actions/associations.server.ts index 63e2e53f6..0769edc01 100644 --- a/app/features/associations/actions/associations.server.ts +++ b/app/features/associations/actions/associations.server.ts @@ -13,7 +13,7 @@ import { assertUnreachable } from "~/utils/types"; import * as AssociationRepository from "../AssociationRepository.server"; export const action = async ({ request }: ActionFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: associationsPageActionSchema, diff --git a/app/features/associations/loaders/associations.server.ts b/app/features/associations/loaders/associations.server.ts index e794b5cd3..3ff8c8745 100644 --- a/app/features/associations/loaders/associations.server.ts +++ b/app/features/associations/loaders/associations.server.ts @@ -1,5 +1,5 @@ import type { LoaderFunctionArgs } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import type { SerializeFrom } from "~/utils/remix"; import { parseSafeSearchParams } from "~/utils/remix.server"; import { inviteCodeObject } from "~/utils/zod"; @@ -8,7 +8,7 @@ import * as AssociationRepository from "../AssociationRepository.server"; export type AssociationsLoaderData = SerializeFrom; export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await requireUserId(request); + const user = await requireUser(); const associations = ( await AssociationRepository.findByMemberUserId(user.id, { diff --git a/app/features/auth/core/routes.server.ts b/app/features/auth/core/routes.server.ts index d0ac8010c..0f4824497 100644 --- a/app/features/auth/core/routes.server.ts +++ b/app/features/auth/core/routes.server.ts @@ -21,7 +21,7 @@ import { SESSION_KEY, } from "./authenticator.server"; import { authSessionStorage } from "./session.server"; -import { getUserId, requireUser } from "./user.server"; +import { getUser, requireUser } from "./user.server"; export const callbackLoader: LoaderFunction = async ({ request }) => { const url = new URL(request.url); @@ -76,7 +76,7 @@ export const logInAction: ActionFunction = async ({ request }) => { export const impersonateAction: ActionFunction = async ({ request }) => { if (!DANGEROUS_CAN_ACCESS_DEV_CONTROLS) { - const user = await requireUser(request); + const user = await requireUser(); requireRole(user, "ADMIN"); } @@ -163,7 +163,7 @@ export const logInViaLinkLoader: LoaderFunction = async ({ request }) => { request, schema: logInViaLinkActionSchema, }); - const user = await getUserId(request); + const user = await getUser(); if (user) { throw redirect("/"); diff --git a/app/features/auth/core/user-context.server.ts b/app/features/auth/core/user-context.server.ts new file mode 100644 index 000000000..55d0bd3d2 --- /dev/null +++ b/app/features/auth/core/user-context.server.ts @@ -0,0 +1,47 @@ +import { AsyncLocalStorage } from "node:async_hooks"; +import { redirect } from "react-router"; +import { userIsBanned } from "~/features/ban/core/banned.server"; +import * as UserRepository from "~/features/user-page/UserRepository.server"; +import { SUSPENDED_PAGE } from "~/utils/urls"; +import { IMPERSONATED_SESSION_KEY, SESSION_KEY } from "./authenticator.server"; +import { authSessionStorage } from "./session.server"; + +export type AuthenticatedUser = NonNullable< + Awaited> +>; + +interface UserContext { + getUserLazy: () => Promise; +} + +export const userAsyncLocalStorage = new AsyncLocalStorage(); + +export function getUserContext(): UserContext { + const context = userAsyncLocalStorage.getStore(); + if (!context) { + throw new Error("getUserContext called outside of user middleware context"); + } + return context; +} + +export async function getUserFromRequest( + request: Request, +): Promise { + const session = await authSessionStorage.getSession( + request.headers.get("Cookie"), + ); + + const userId = + session.get(IMPERSONATED_SESSION_KEY) ?? session.get(SESSION_KEY); + + if (!userId) return undefined; + + if (userIsBanned(userId)) { + const url = new URL(request.url); + if (url.pathname !== SUSPENDED_PAGE) { + throw redirect(SUSPENDED_PAGE); + } + } + + return UserRepository.findLeanById(userId); +} diff --git a/app/features/auth/core/user-middleware.server.ts b/app/features/auth/core/user-middleware.server.ts new file mode 100644 index 000000000..77db7dc9d --- /dev/null +++ b/app/features/auth/core/user-middleware.server.ts @@ -0,0 +1,34 @@ +import { + type AuthenticatedUser, + getUserFromRequest, + userAsyncLocalStorage, +} from "./user-context.server"; + +type MiddlewareArgs = { + request: Request; + context: unknown; +}; + +type MiddlewareFn = ( + args: MiddlewareArgs, + next: () => Promise, +) => Promise; + +function createLazyUserGetter( + request: Request, +): () => Promise { + let fetchPromise: Promise | undefined; + + return () => { + fetchPromise ??= getUserFromRequest(request); + return fetchPromise; + }; +} + +export const userMiddleware: MiddlewareFn = async ({ request }, next) => { + const context = { + getUserLazy: createLazyUserGetter(request), + }; + + return userAsyncLocalStorage.run(context, () => next()); +}; diff --git a/app/features/auth/core/user.server.ts b/app/features/auth/core/user.server.ts index 879740214..49661e8ec 100644 --- a/app/features/auth/core/user.server.ts +++ b/app/features/auth/core/user.server.ts @@ -1,51 +1,16 @@ -import { redirect } from "react-router"; -import type { Tables } from "~/db/tables"; -import { userIsBanned } from "~/features/ban/core/banned.server"; -import * as UserRepository from "~/features/user-page/UserRepository.server"; -import { SUSPENDED_PAGE } from "~/utils/urls"; -import { IMPERSONATED_SESSION_KEY, SESSION_KEY } from "./authenticator.server"; +import { IMPERSONATED_SESSION_KEY } from "./authenticator.server"; import { authSessionStorage } from "./session.server"; +import { type AuthenticatedUser, getUserContext } from "./user-context.server"; -export type AuthenticatedUser = NonNullable< - Awaited> ->; +export type { AuthenticatedUser }; -export async function getUserId( - request: Request, - redirectIfBanned = true, -): Promise | undefined> { - const session = await authSessionStorage.getSession( - request.headers.get("Cookie"), - ); - - const userId = - session.get(IMPERSONATED_SESSION_KEY) ?? session.get(SESSION_KEY); - - if (!userId) return; - - if (userIsBanned(userId) && redirectIfBanned) throw redirect(SUSPENDED_PAGE); - - return { id: userId }; +export async function getUser(): Promise { + const context = getUserContext(); + return context.getUserLazy(); } -export async function getUser(request: Request, redirectIfBanned = true) { - const userId = (await getUserId(request, redirectIfBanned))?.id; - - if (!userId) return; - - return UserRepository.findLeanById(userId); -} - -export async function requireUserId(request: Request) { - const user = await getUserId(request); - - if (!user) throw new Response(null, { status: 401 }); - - return user; -} - -export async function requireUser(request: Request) { - const user = await getUser(request); +export async function requireUser(): Promise { + const user = await getUser(); if (!user) throw new Response(null, { status: 401 }); diff --git a/app/features/badges/actions/badges.$id.edit.server.ts b/app/features/badges/actions/badges.$id.edit.server.ts index 7c8fbf9ab..c3935206f 100644 --- a/app/features/badges/actions/badges.$id.edit.server.ts +++ b/app/features/badges/actions/badges.$id.edit.server.ts @@ -21,7 +21,7 @@ export const action: ActionFunction = async ({ request, params }) => { schema: editBadgeActionSchema, }); const badgeId = z.preprocess(actualNumber, z.number()).parse(params.id); - const user = await requireUser(request); + const user = await requireUser(); const badge = notFoundIfFalsy(await BadgeRepository.findById(badgeId)); diff --git a/app/features/ban/core/banned.server.ts b/app/features/ban/core/banned.server.ts index 0388fc08f..e03ec7232 100644 --- a/app/features/ban/core/banned.server.ts +++ b/app/features/ban/core/banned.server.ts @@ -3,15 +3,22 @@ import { databaseTimestampToDate } from "~/utils/dates"; let bannedUsers = await AdminRepository.allBannedUsers(); +export function checkBanStatus( + banned: number | null | undefined, + now: Date = new Date(), +): boolean { + if (!banned) return false; + if (banned === 1) return true; + + const banExpiresAt = databaseTimestampToDate(banned); + + return banExpiresAt > now; +} + export function userIsBanned(userId: number) { const banStatus = bannedUsers.get(userId); - if (!banStatus?.banned) return false; - if (banStatus.banned === 1) return true; - - const banExpiresAt = databaseTimestampToDate(banStatus.banned); - - return banExpiresAt > new Date(); + return checkBanStatus(banStatus?.banned); } export async function refreshBannedCache() { diff --git a/app/features/ban/core/banned.test.ts b/app/features/ban/core/banned.test.ts new file mode 100644 index 000000000..b501496af --- /dev/null +++ b/app/features/ban/core/banned.test.ts @@ -0,0 +1,59 @@ +import { describe, expect, it } from "vitest"; +import { checkBanStatus } from "./banned.server"; + +describe("checkBanStatus", () => { + it("returns false when banned is null", () => { + expect(checkBanStatus(null)).toBe(false); + }); + + it("returns false when banned is undefined", () => { + expect(checkBanStatus(undefined)).toBe(false); + }); + + it("returns false when banned is 0", () => { + expect(checkBanStatus(0)).toBe(false); + }); + + it("returns true when banned is 1 (permanent ban)", () => { + expect(checkBanStatus(1)).toBe(true); + }); + + it("returns true when ban expires in the future", () => { + const now = new Date("2025-01-01T12:00:00Z"); + const futureTimestamp = Math.floor( + new Date("2025-01-01T13:00:00Z").getTime() / 1000, + ); + + expect(checkBanStatus(futureTimestamp, now)).toBe(true); + }); + + it("returns false when ban has expired", () => { + const now = new Date("2025-01-01T12:00:00Z"); + const pastTimestamp = Math.floor( + new Date("2025-01-01T11:00:00Z").getTime() / 1000, + ); + + expect(checkBanStatus(pastTimestamp, now)).toBe(false); + }); + + it("returns false when ban expires exactly at current time", () => { + const now = new Date("2025-01-01T12:00:00Z"); + const exactTimestamp = Math.floor(now.getTime() / 1000); + + expect(checkBanStatus(exactTimestamp, now)).toBe(false); + }); + + it("returns true when ban expires 1 second in the future", () => { + const now = new Date("2025-01-01T12:00:00Z"); + const oneSecondLater = Math.floor(now.getTime() / 1000) + 1; + + expect(checkBanStatus(oneSecondLater, now)).toBe(true); + }); + + it("returns false when ban expired 1 second ago", () => { + const now = new Date("2025-01-01T12:00:00Z"); + const oneSecondEarlier = Math.floor(now.getTime() / 1000) - 1; + + expect(checkBanStatus(oneSecondEarlier, now)).toBe(false); + }); +}); diff --git a/app/features/builds/loaders/builds.$slug.server.ts b/app/features/builds/loaders/builds.$slug.server.ts index 5805724fa..0cbf800f0 100644 --- a/app/features/builds/loaders/builds.$slug.server.ts +++ b/app/features/builds/loaders/builds.$slug.server.ts @@ -15,7 +15,7 @@ import { buildFiltersSearchParams } from "../builds-schemas.server"; import { filterBuilds } from "../core/filter.server"; export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const user = await getUser(request); + const user = await getUser(); const t = await i18next.getFixedT(request, ["weapons", "common"], { lng: "en", }); diff --git a/app/features/calendar/actions/calendar.$id.report-winners.server.ts b/app/features/calendar/actions/calendar.$id.report-winners.server.ts index 654bac55c..13ce1ac21 100644 --- a/app/features/calendar/actions/calendar.$id.report-winners.server.ts +++ b/app/features/calendar/actions/calendar.$id.report-winners.server.ts @@ -1,6 +1,6 @@ import type { ActionFunction } from "react-router"; import { redirect } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import * as CalendarRepository from "~/features/calendar/CalendarRepository.server"; import { errorToastIfFalsy, @@ -14,7 +14,7 @@ import { reportWinnersActionSchema } from "../calendar-schemas"; import { canReportCalendarEventWinners } from "../calendar-utils"; export const action: ActionFunction = async (args) => { - const user = await requireUserId(args.request); + const user = await requireUser(); const params = parseParams({ params: args.params, schema: idObject, diff --git a/app/features/calendar/actions/calendar.$id.server.ts b/app/features/calendar/actions/calendar.$id.server.ts index c01d11f78..f90188e34 100644 --- a/app/features/calendar/actions/calendar.$id.server.ts +++ b/app/features/calendar/actions/calendar.$id.server.ts @@ -1,7 +1,7 @@ import type { ActionFunction } from "react-router"; import { redirect } from "react-router"; import { z } from "zod"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import * as CalendarRepository from "~/features/calendar/CalendarRepository.server"; import * as ShowcaseTournaments from "~/features/front-page/core/ShowcaseTournaments.server"; import { @@ -14,8 +14,8 @@ import { CALENDAR_PAGE } from "~/utils/urls"; import { actualNumber, id } from "~/utils/zod"; import { canDeleteCalendarEvent } from "../calendar-utils"; -export const action: ActionFunction = async ({ params, request }) => { - const user = await requireUserId(request); +export const action: ActionFunction = async ({ params }) => { + const user = await requireUser(); const parsedParams = z .object({ id: z.preprocess(actualNumber, id) }) .parse(params); diff --git a/app/features/calendar/actions/calendar.new.server.ts b/app/features/calendar/actions/calendar.new.server.ts index 1fe55ef71..fbe761eeb 100644 --- a/app/features/calendar/actions/calendar.new.server.ts +++ b/app/features/calendar/actions/calendar.new.server.ts @@ -31,7 +31,7 @@ import { canEditCalendarEvent, regClosesAtDate } from "../calendar-utils"; import { findValidOrganizations } from "../loaders/calendar.new.server"; export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUser(); const { avatarFileName, formData } = await uploadImageIfSubmitted({ request, diff --git a/app/features/calendar/actions/calendar.tsx b/app/features/calendar/actions/calendar.tsx index cf84b7e43..01651a87b 100644 --- a/app/features/calendar/actions/calendar.tsx +++ b/app/features/calendar/actions/calendar.tsx @@ -1,5 +1,5 @@ import { type ActionFunctionArgs, redirect } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import { calendarFiltersSearchParamsSchema } from "~/features/calendar/calendar-schemas"; import * as UserRepository from "~/features/user-page/UserRepository.server"; import { @@ -10,7 +10,7 @@ import { calendarPage } from "~/utils/urls"; import { dayMonthYear } from "~/utils/zod"; export const action = async ({ request }: ActionFunctionArgs) => { - const user = await requireUserId(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: calendarFiltersSearchParamsSchema, diff --git a/app/features/calendar/loaders/calendar.$id.report-winners.server.ts b/app/features/calendar/loaders/calendar.$id.report-winners.server.ts index 783409b65..ef26a194f 100644 --- a/app/features/calendar/loaders/calendar.$id.report-winners.server.ts +++ b/app/features/calendar/loaders/calendar.$id.report-winners.server.ts @@ -1,5 +1,5 @@ import type { LoaderFunctionArgs } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import * as CalendarRepository from "~/features/calendar/CalendarRepository.server"; import { notFoundIfFalsy, @@ -14,7 +14,7 @@ export const loader = async (args: LoaderFunctionArgs) => { params: args.params, schema: idObject, }); - const user = await requireUserId(args.request); + const user = await requireUser(); const event = notFoundIfFalsy(await CalendarRepository.findById(params.id)); unauthorizedIfFalsy( diff --git a/app/features/calendar/loaders/calendar.new.server.ts b/app/features/calendar/loaders/calendar.new.server.ts index 062ffa573..2c217a149 100644 --- a/app/features/calendar/loaders/calendar.new.server.ts +++ b/app/features/calendar/loaders/calendar.new.server.ts @@ -11,7 +11,7 @@ import { tournamentBracketsPage } from "~/utils/urls"; import { canEditCalendarEvent } from "../calendar-utils"; export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); requireRole(user, "CALENDAR_EVENT_ADDER"); const url = new URL(request.url); diff --git a/app/features/calendar/loaders/calendar.server.ts b/app/features/calendar/loaders/calendar.server.ts index cf939d942..05ae8ed9c 100644 --- a/app/features/calendar/loaders/calendar.server.ts +++ b/app/features/calendar/loaders/calendar.server.ts @@ -16,7 +16,7 @@ import * as CalendarEvent from "../core/CalendarEvent"; export type CalendarLoaderData = SerializeFrom; export const loader = async (args: LoaderFunctionArgs) => { - const user = await getUser(args.request); + const user = await getUser(); const parsed = parseSafeSearchParams({ request: args.request, schema: dayMonthYear, diff --git a/app/features/front-page/loaders/index.server.ts b/app/features/front-page/loaders/index.server.ts index f5d2b538f..b3971f2d5 100644 --- a/app/features/front-page/loaders/index.server.ts +++ b/app/features/front-page/loaders/index.server.ts @@ -1,7 +1,6 @@ import cachified from "@epic-web/cachified"; -import type { LoaderFunctionArgs } from "react-router"; import type { Tables } from "~/db/tables"; -import { getUserId } from "~/features/auth/core/user.server"; +import { getUser } from "~/features/auth/core/user.server"; import * as Changelog from "~/features/front-page/core/Changelog.server"; import { cachedFullUserLeaderboard } from "~/features/leaderboards/core/leaderboards.server"; import * as LeaderboardRepository from "~/features/leaderboards/LeaderboardRepository.server"; @@ -10,8 +9,8 @@ import { cache, IN_MILLISECONDS, ttl } from "~/utils/cache.server"; import { discordAvatarUrl, teamPage, userPage } from "~/utils/urls"; import * as ShowcaseTournaments from "../core/ShowcaseTournaments.server"; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await getUserId(request); +export const loader = async () => { + const user = await getUser(); const [tournaments, changelog, leaderboards] = await Promise.all([ ShowcaseTournaments.frontPageTournamentsByUserId(user?.id ?? null), diff --git a/app/features/img-upload/actions/upload.admin.server.ts b/app/features/img-upload/actions/upload.admin.server.ts index 0a323e3bb..02670e9bb 100644 --- a/app/features/img-upload/actions/upload.admin.server.ts +++ b/app/features/img-upload/actions/upload.admin.server.ts @@ -12,7 +12,7 @@ import * as ImageRepository from "../ImageRepository.server"; import { validateImageSchema } from "../upload-schemas.server"; export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUser(); requireRole(user, "STAFF"); const data = await parseRequestPayload({ diff --git a/app/features/img-upload/actions/upload.server.ts b/app/features/img-upload/actions/upload.server.ts index 2ea170bb5..961486514 100644 --- a/app/features/img-upload/actions/upload.server.ts +++ b/app/features/img-upload/actions/upload.server.ts @@ -22,7 +22,7 @@ import { MAX_UNVALIDATED_IMG_COUNT } from "../upload-constants"; import { requestToImgType } from "../upload-utils"; export const action = async ({ request }: ActionFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const validatedType = requestToImgType(request); errorToastIfFalsy(validatedType, "Invalid image type"); diff --git a/app/features/img-upload/loaders/upload.admin.server.ts b/app/features/img-upload/loaders/upload.admin.server.ts index ccb002f62..d234d779f 100644 --- a/app/features/img-upload/loaders/upload.admin.server.ts +++ b/app/features/img-upload/loaders/upload.admin.server.ts @@ -1,10 +1,9 @@ -import type { LoaderFunctionArgs } from "react-router"; import { requireUser } from "~/features/auth/core/user.server"; import { requireRole } from "~/modules/permissions/guards.server"; import * as ImageRepository from "../ImageRepository.server"; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await requireUser(request); +export const loader = async () => { + const user = await requireUser(); requireRole(user, "STAFF"); return { diff --git a/app/features/img-upload/loaders/upload.server.ts b/app/features/img-upload/loaders/upload.server.ts index ab573dcb0..ed963779b 100644 --- a/app/features/img-upload/loaders/upload.server.ts +++ b/app/features/img-upload/loaders/upload.server.ts @@ -7,7 +7,7 @@ import * as ImageRepository from "../ImageRepository.server"; import { requestToImgType } from "../upload-utils"; export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const validatedType = requestToImgType(request); if (!validatedType) { diff --git a/app/features/leaderboards/loaders/leaderboards.server.ts b/app/features/leaderboards/loaders/leaderboards.server.ts index 7f71684ff..d52b68b94 100644 --- a/app/features/leaderboards/loaders/leaderboards.server.ts +++ b/app/features/leaderboards/loaders/leaderboards.server.ts @@ -28,7 +28,7 @@ import { } from "../queries/XPLeaderboard.server"; export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await getUser(request); + const user = await getUser(); const unvalidatedType = new URL(request.url).searchParams.get( TYPE_SEARCH_PARAM_KEY, ); diff --git a/app/features/lfg/actions/lfg.new.server.ts b/app/features/lfg/actions/lfg.new.server.ts index 1d1fa6e22..d5a4c31b0 100644 --- a/app/features/lfg/actions/lfg.new.server.ts +++ b/app/features/lfg/actions/lfg.new.server.ts @@ -11,7 +11,7 @@ import * as LFGRepository from "../LFGRepository.server"; import { LFG, TEAM_POST_TYPES, TIMEZONES } from "../lfg-constants"; export const action = async ({ request }: ActionFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema, diff --git a/app/features/lfg/actions/lfg.server.ts b/app/features/lfg/actions/lfg.server.ts index f069ae5cf..cf1cf75a9 100644 --- a/app/features/lfg/actions/lfg.server.ts +++ b/app/features/lfg/actions/lfg.server.ts @@ -6,7 +6,7 @@ import { _action, id } from "~/utils/zod"; import * as LFGRepository from "../LFGRepository.server"; export const action = async ({ request }: ActionFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema, diff --git a/app/features/lfg/loaders/lfg.new.server.ts b/app/features/lfg/loaders/lfg.new.server.ts index 7e4767eb7..b98340bd4 100644 --- a/app/features/lfg/loaders/lfg.new.server.ts +++ b/app/features/lfg/loaders/lfg.new.server.ts @@ -9,7 +9,7 @@ import { id } from "~/utils/zod"; import * as LFGRepository from "../LFGRepository.server"; export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const userProfileData = await UserRepository.findProfileByIdentifier( String(user.id), diff --git a/app/features/lfg/loaders/lfg.server.ts b/app/features/lfg/loaders/lfg.server.ts index a1c5c2df7..8cb248140 100644 --- a/app/features/lfg/loaders/lfg.server.ts +++ b/app/features/lfg/loaders/lfg.server.ts @@ -1,4 +1,3 @@ -import type { LoaderFunctionArgs } from "react-router"; import { getUser } from "~/features/auth/core/user.server"; import * as Seasons from "~/features/mmr/core/Seasons"; import type { TieredSkill } from "~/features/mmr/tiered.server"; @@ -6,8 +5,8 @@ import { userSkills } from "~/features/mmr/tiered.server"; import type { Unpacked } from "~/utils/types"; import * as LFGRepository from "../LFGRepository.server"; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await getUser(request); +export const loader = async () => { + const user = await getUser(); const posts = await LFGRepository.posts(user); return { diff --git a/app/features/notifications/loaders/notifications.server.ts b/app/features/notifications/loaders/notifications.server.ts index 3bc7a17da..fc32b25ba 100644 --- a/app/features/notifications/loaders/notifications.server.ts +++ b/app/features/notifications/loaders/notifications.server.ts @@ -1,9 +1,8 @@ -import type { LoaderFunctionArgs } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import * as NotificationRepository from "../NotificationRepository.server"; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await requireUserId(request); +export const loader = async () => { + const user = await requireUser(); return { notifications: await NotificationRepository.findByUserId(user.id), diff --git a/app/features/notifications/routes/notifications.seen.ts b/app/features/notifications/routes/notifications.seen.ts index 73c49cee9..d29b60a92 100644 --- a/app/features/notifications/routes/notifications.seen.ts +++ b/app/features/notifications/routes/notifications.seen.ts @@ -1,11 +1,11 @@ import type { ActionFunctionArgs } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import { parseRequestPayload } from "~/utils/remix.server"; import * as NotificationRepository from "../NotificationRepository.server"; import { markAsSeenActionSchema } from "../notifications-schemas"; export const action = async ({ request }: ActionFunctionArgs) => { - const user = await requireUserId(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: markAsSeenActionSchema, diff --git a/app/features/notifications/routes/notifications.subscribe.ts b/app/features/notifications/routes/notifications.subscribe.ts index e77b02e78..bacec02a0 100644 --- a/app/features/notifications/routes/notifications.subscribe.ts +++ b/app/features/notifications/routes/notifications.subscribe.ts @@ -5,7 +5,7 @@ import * as NotificationRepository from "../NotificationRepository.server"; import { subscribeSchema } from "../notifications-schemas"; export const action = async ({ request }: ActionFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: subscribeSchema, diff --git a/app/features/plus-suggestions/actions/plus.suggestions.comment.$tier.$userId.server.ts b/app/features/plus-suggestions/actions/plus.suggestions.comment.$tier.$userId.server.ts index b75c9ed2b..3aaa653de 100644 --- a/app/features/plus-suggestions/actions/plus.suggestions.comment.$tier.$userId.server.ts +++ b/app/features/plus-suggestions/actions/plus.suggestions.comment.$tier.$userId.server.ts @@ -20,7 +20,7 @@ export const action = async ({ request }: ActionFunctionArgs) => { request, schema: followUpCommentActionSchema, }); - const user = await requireUser(request); + const user = await requireUser(); const votingMonthYear = rangeToMonthYear( badRequestIfFalsy(nextNonCompletedVoting(new Date())), diff --git a/app/features/plus-suggestions/actions/plus.suggestions.new.server.ts b/app/features/plus-suggestions/actions/plus.suggestions.new.server.ts index c094730ad..bbc64ca83 100644 --- a/app/features/plus-suggestions/actions/plus.suggestions.new.server.ts +++ b/app/features/plus-suggestions/actions/plus.suggestions.new.server.ts @@ -23,7 +23,7 @@ import { } from "../plus-suggestions-utils"; export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, diff --git a/app/features/plus-suggestions/actions/plus.suggestions.server.ts b/app/features/plus-suggestions/actions/plus.suggestions.server.ts index 5696621cb..46e545ff0 100644 --- a/app/features/plus-suggestions/actions/plus.suggestions.server.ts +++ b/app/features/plus-suggestions/actions/plus.suggestions.server.ts @@ -21,7 +21,7 @@ export const action: ActionFunction = async ({ request }) => { request, schema: suggestionActionSchema, }); - const user = await requireUser(request); + const user = await requireUser(); const votingMonthYear = rangeToMonthYear( badRequestIfFalsy(nextNonCompletedVoting(new Date())), diff --git a/app/features/plus-voting/actions/plus.voting.server.ts b/app/features/plus-voting/actions/plus.voting.server.ts index 8cfc6f6f3..90731ba98 100644 --- a/app/features/plus-voting/actions/plus.voting.server.ts +++ b/app/features/plus-voting/actions/plus.voting.server.ts @@ -14,7 +14,7 @@ import { PLUS_UPVOTE } from "../plus-voting-constants"; import { votingActionSchema } from "../plus-voting-schemas"; export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: votingActionSchema, diff --git a/app/features/plus-voting/loaders/plus.voting.results.server.ts b/app/features/plus-voting/loaders/plus.voting.results.server.ts index 8c1e51559..df46702e7 100644 --- a/app/features/plus-voting/loaders/plus.voting.results.server.ts +++ b/app/features/plus-voting/loaders/plus.voting.results.server.ts @@ -1,4 +1,3 @@ -import type { LoaderFunctionArgs } from "react-router"; import type { UserWithPlusTier } from "~/db/tables"; import { getUser } from "~/features/auth/core/user.server"; import { lastCompletedVoting } from "~/features/plus-voting/core"; @@ -7,8 +6,8 @@ import { isSupporter } from "~/modules/permissions/utils"; import invariant from "~/utils/invariant"; import { roundToNDecimalPlaces } from "~/utils/number"; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await getUser(request); +export const loader = async () => { + const user = await getUser(); const results = await PlusVotingRepository.resultsByMonthYear( lastCompletedVoting(new Date()), ); diff --git a/app/features/plus-voting/loaders/plus.voting.server.ts b/app/features/plus-voting/loaders/plus.voting.server.ts index 7988fc557..070968831 100644 --- a/app/features/plus-voting/loaders/plus.voting.server.ts +++ b/app/features/plus-voting/loaders/plus.voting.server.ts @@ -33,8 +33,8 @@ export type PlusVotingLoaderData = }; }; -export const loader: LoaderFunction = async ({ request }) => { - const user = await getUser(request); +export const loader: LoaderFunction = async () => { + const user = await getUser(); const now = new Date(); const nextVotingRange = nextNonCompletedVoting(now); diff --git a/app/features/scrims/actions/scrims.$id.server.ts b/app/features/scrims/actions/scrims.$id.server.ts index 9801e92ad..16b013f0b 100644 --- a/app/features/scrims/actions/scrims.$id.server.ts +++ b/app/features/scrims/actions/scrims.$id.server.ts @@ -21,7 +21,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => { const { id } = parseParams({ params, schema: idObject }); const post = notFoundIfFalsy(await ScrimPostRepository.findById(id)); - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: cancelScrimSchema, diff --git a/app/features/scrims/actions/scrims.new.server.ts b/app/features/scrims/actions/scrims.new.server.ts index bc83188fb..e3ec4bb89 100644 --- a/app/features/scrims/actions/scrims.new.server.ts +++ b/app/features/scrims/actions/scrims.new.server.ts @@ -28,7 +28,7 @@ import { import { serializeLutiDiv } from "../scrims-utils"; export const action = async ({ request }: ActionFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: scrimsNewActionSchema, diff --git a/app/features/scrims/actions/scrims.server.ts b/app/features/scrims/actions/scrims.server.ts index 36e92c76c..68a4e7946 100644 --- a/app/features/scrims/actions/scrims.server.ts +++ b/app/features/scrims/actions/scrims.server.ts @@ -22,7 +22,7 @@ import { generateTimeOptions } from "../scrims-utils"; import { usersListForPost } from "./scrims.new.server"; export const action = async ({ request }: ActionFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, diff --git a/app/features/scrims/loaders/scrims.$id.server.ts b/app/features/scrims/loaders/scrims.$id.server.ts index 5a61b7357..e5c9a9a97 100644 --- a/app/features/scrims/loaders/scrims.$id.server.ts +++ b/app/features/scrims/loaders/scrims.$id.server.ts @@ -9,8 +9,8 @@ import { import * as Scrim from "../core/Scrim"; import * as ScrimPostRepository from "../ScrimPostRepository.server"; -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const user = await requireUser(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const user = await requireUser(); const post = notFoundIfFalsy( await ScrimPostRepository.findById(Number(params.id)), diff --git a/app/features/scrims/loaders/scrims.new.server.ts b/app/features/scrims/loaders/scrims.new.server.ts index d55325706..518849503 100644 --- a/app/features/scrims/loaders/scrims.new.server.ts +++ b/app/features/scrims/loaders/scrims.new.server.ts @@ -1,13 +1,12 @@ -import type { LoaderFunctionArgs } from "react-router"; import * as AssociationRepository from "~/features/associations/AssociationRepository.server"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import type { SerializeFrom } from "~/utils/remix"; import * as TeamRepository from "../../team/TeamRepository.server"; export type ScrimsNewLoaderData = SerializeFrom; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await requireUserId(request); +export const loader = async () => { + const user = await requireUser(); return { teams: await TeamRepository.teamsByMemberUserId(user.id), diff --git a/app/features/scrims/loaders/scrims.server.ts b/app/features/scrims/loaders/scrims.server.ts index f8ef4a99c..a6d600506 100644 --- a/app/features/scrims/loaders/scrims.server.ts +++ b/app/features/scrims/loaders/scrims.server.ts @@ -10,7 +10,7 @@ import { scrimsFiltersSearchParamsObject } from "../scrims-schemas"; import { dividePosts } from "../scrims-utils"; export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await getUser(request); + const user = await getUser(); const now = new Date(); const associations = user diff --git a/app/features/sendouq-match/actions/q.match.$id.server.ts b/app/features/sendouq-match/actions/q.match.$id.server.ts index d17229785..8c97988b4 100644 --- a/app/features/sendouq-match/actions/q.match.$id.server.ts +++ b/app/features/sendouq-match/actions/q.match.$id.server.ts @@ -50,7 +50,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => { params, schema: qMatchPageParamsSchema, }).id; - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: matchSchema, diff --git a/app/features/sendouq-match/loaders/q.match.$id.server.ts b/app/features/sendouq-match/loaders/q.match.$id.server.ts index 9fc13abfd..c938473a0 100644 --- a/app/features/sendouq-match/loaders/q.match.$id.server.ts +++ b/app/features/sendouq-match/loaders/q.match.$id.server.ts @@ -8,8 +8,8 @@ import * as SQMatchRepository from "~/features/sendouq-match/SQMatchRepository.s import { notFoundIfFalsy, parseParams } from "~/utils/remix.server"; import { qMatchPageParamsSchema } from "../q-match-schemas"; -export const loader = async ({ params, request }: LoaderFunctionArgs) => { - const user = await getUser(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const user = await getUser(); const matchId = parseParams({ params, schema: qMatchPageParamsSchema, diff --git a/app/features/sendouq-settings/actions/q.settings.server.ts b/app/features/sendouq-settings/actions/q.settings.server.ts index 723987b68..f5e66e1f2 100644 --- a/app/features/sendouq-settings/actions/q.settings.server.ts +++ b/app/features/sendouq-settings/actions/q.settings.server.ts @@ -1,12 +1,11 @@ -import type { ActionFunctionArgs } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import * as QSettingsRepository from "~/features/sendouq-settings/QSettingsRepository.server"; import { parseRequestPayload } from "~/utils/remix.server"; import { assertUnreachable } from "~/utils/types"; import { settingsActionSchema } from "../q-settings-schemas.server"; -export const action = async ({ request }: ActionFunctionArgs) => { - const user = await requireUserId(request); +export const action = async ({ request }: { request: Request }) => { + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: settingsActionSchema, diff --git a/app/features/sendouq-settings/loaders/q.settings.server.ts b/app/features/sendouq-settings/loaders/q.settings.server.ts index 04d1826af..eeb3bb83c 100644 --- a/app/features/sendouq-settings/loaders/q.settings.server.ts +++ b/app/features/sendouq-settings/loaders/q.settings.server.ts @@ -1,9 +1,8 @@ -import type { LoaderFunctionArgs } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import * as QSettingsRepository from "~/features/sendouq-settings/QSettingsRepository.server"; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await requireUserId(request); +export const loader = async () => { + const user = await requireUser(); return { settings: await QSettingsRepository.settingsByUserId(user.id), diff --git a/app/features/sendouq/actions/q.looking.server.ts b/app/features/sendouq/actions/q.looking.server.ts index 682f0fae7..c016c3287 100644 --- a/app/features/sendouq/actions/q.looking.server.ts +++ b/app/features/sendouq/actions/q.looking.server.ts @@ -23,7 +23,7 @@ import { SendouQError } from "../q-utils.server"; // if there is a validation error the user saw stale data // and when we return null we just force a refresh export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: lookingSchema, diff --git a/app/features/sendouq/actions/q.preparing.server.ts b/app/features/sendouq/actions/q.preparing.server.ts index 337350c8f..e55e95307 100644 --- a/app/features/sendouq/actions/q.preparing.server.ts +++ b/app/features/sendouq/actions/q.preparing.server.ts @@ -13,7 +13,7 @@ import { preparingSchema } from "../q-schemas.server"; export type SendouQPreparingAction = typeof action; export const action = async ({ request }: ActionFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: preparingSchema, diff --git a/app/features/sendouq/actions/q.server.ts b/app/features/sendouq/actions/q.server.ts index 40c10bdd1..eea51fbf2 100644 --- a/app/features/sendouq/actions/q.server.ts +++ b/app/features/sendouq/actions/q.server.ts @@ -21,7 +21,7 @@ import { frontPageSchema } from "../q-schemas.server"; import { userCanJoinQueueAt } from "../q-utils"; export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: frontPageSchema, diff --git a/app/features/sendouq/loaders/q.looking.server.ts b/app/features/sendouq/loaders/q.looking.server.ts index 6e0277155..968c55520 100644 --- a/app/features/sendouq/loaders/q.looking.server.ts +++ b/app/features/sendouq/loaders/q.looking.server.ts @@ -8,7 +8,7 @@ import * as PrivateUserNoteRepository from "../PrivateUserNoteRepository.server" import { sqRedirectIfNeeded } from "../q-utils.server"; export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const isPreview = new URL(request.url).searchParams.get("preview") === "true" && diff --git a/app/features/sendouq/loaders/q.preparing.server.ts b/app/features/sendouq/loaders/q.preparing.server.ts index 34f1d8a64..a5ea92524 100644 --- a/app/features/sendouq/loaders/q.preparing.server.ts +++ b/app/features/sendouq/loaders/q.preparing.server.ts @@ -1,10 +1,9 @@ -import type { LoaderFunctionArgs } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import { SendouQ } from "../core/SendouQ.server"; import { sqRedirectIfNeeded } from "../q-utils.server"; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await requireUserId(request); +export const loader = async () => { + const user = await requireUser(); const ownGroup = SendouQ.findOwnGroup(user.id); diff --git a/app/features/sendouq/loaders/q.server.ts b/app/features/sendouq/loaders/q.server.ts index ab0a1b9e3..e0d3f58f0 100644 --- a/app/features/sendouq/loaders/q.server.ts +++ b/app/features/sendouq/loaders/q.server.ts @@ -1,5 +1,5 @@ import type { LoaderFunctionArgs } from "react-router"; -import { getUserId } from "~/features/auth/core/user.server"; +import { getUser } from "~/features/auth/core/user.server"; import * as Seasons from "~/features/mmr/core/Seasons"; import * as UserRepository from "~/features/user-page/UserRepository.server"; import { SendouQ } from "../core/SendouQ.server"; @@ -7,7 +7,7 @@ import { JOIN_CODE_SEARCH_PARAM_KEY } from "../q-constants"; import { sqRedirectIfNeeded } from "../q-utils.server"; export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await getUserId(request); + const user = await getUser(); const code = new URL(request.url).searchParams.get( JOIN_CODE_SEARCH_PARAM_KEY, diff --git a/app/features/sendouq/routes/trusters.ts b/app/features/sendouq/routes/trusters.ts index c196beeeb..f6abae04b 100644 --- a/app/features/sendouq/routes/trusters.ts +++ b/app/features/sendouq/routes/trusters.ts @@ -1,12 +1,11 @@ -import type { LoaderFunctionArgs } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import * as SQGroupRepository from "~/features/sendouq/SQGroupRepository.server"; import type { SerializeFrom } from "~/utils/remix"; export type TrustersLoaderData = SerializeFrom; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const { id: userId } = await requireUserId(request); +export const loader = async () => { + const { id: userId } = await requireUser(); return { trusters: await SQGroupRepository.usersThatTrusted(userId), diff --git a/app/features/settings/actions/settings.server.ts b/app/features/settings/actions/settings.server.ts index b93d7a63b..b5552d8a2 100644 --- a/app/features/settings/actions/settings.server.ts +++ b/app/features/settings/actions/settings.server.ts @@ -7,7 +7,7 @@ import { assertUnreachable } from "~/utils/types"; import { settingsEditSchema } from "../settings-schemas"; export const action = async ({ request }: ActionFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: settingsEditSchema, diff --git a/app/features/settings/loaders/settings.server.ts b/app/features/settings/loaders/settings.server.ts index b62ddbee7..803104d9f 100644 --- a/app/features/settings/loaders/settings.server.ts +++ b/app/features/settings/loaders/settings.server.ts @@ -1,9 +1,8 @@ -import type { LoaderFunctionArgs } from "react-router"; -import { getUserId } from "~/features/auth/core/user.server"; +import { getUser } from "~/features/auth/core/user.server"; import * as UserRepository from "~/features/user-page/UserRepository.server"; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await getUserId(request); +export const loader = async () => { + const user = await getUser(); return { noScreen: user diff --git a/app/features/team/actions/t.$customUrl.edit.server.ts b/app/features/team/actions/t.$customUrl.edit.server.ts index 68746b7d2..0bb1ef1c9 100644 --- a/app/features/team/actions/t.$customUrl.edit.server.ts +++ b/app/features/team/actions/t.$customUrl.edit.server.ts @@ -13,7 +13,7 @@ import { editTeamSchema, teamParamsSchema } from "../team-schemas.server"; import { isTeamManager, isTeamOwner } from "../team-utils"; export const action: ActionFunction = async ({ request, params }) => { - const user = await requireUser(request); + const user = await requireUser(); const { customUrl } = teamParamsSchema.parse(params); const team = notFoundIfFalsy(await TeamRepository.findByCustomUrl(customUrl)); diff --git a/app/features/team/actions/t.$customUrl.index.server.ts b/app/features/team/actions/t.$customUrl.index.server.ts index 9b425cfac..6da2e8eea 100644 --- a/app/features/team/actions/t.$customUrl.index.server.ts +++ b/app/features/team/actions/t.$customUrl.index.server.ts @@ -1,5 +1,5 @@ import type { ActionFunction } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import { errorToastIfFalsy, notFoundIfFalsy, @@ -14,7 +14,7 @@ import { import { isTeamMember, isTeamOwner, resolveNewOwner } from "../team-utils"; export const action: ActionFunction = async ({ request, params }) => { - const user = await requireUserId(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: teamProfilePageActionSchema, diff --git a/app/features/team/actions/t.$customUrl.join.server.ts b/app/features/team/actions/t.$customUrl.join.server.ts index 3a2efc94d..35db7729f 100644 --- a/app/features/team/actions/t.$customUrl.join.server.ts +++ b/app/features/team/actions/t.$customUrl.join.server.ts @@ -8,7 +8,7 @@ import { TEAM } from "../team-constants"; import { teamParamsSchema } from "../team-schemas.server"; export const action: ActionFunction = async ({ request, params }) => { - const user = await requireUser(request); + const user = await requireUser(); const { customUrl } = teamParamsSchema.parse(params); const team = notFoundIfFalsy( diff --git a/app/features/team/actions/t.$customUrl.roster.server.ts b/app/features/team/actions/t.$customUrl.roster.server.ts index bd01a8157..f12ece60a 100644 --- a/app/features/team/actions/t.$customUrl.roster.server.ts +++ b/app/features/team/actions/t.$customUrl.roster.server.ts @@ -12,7 +12,7 @@ import { manageRosterSchema, teamParamsSchema } from "../team-schemas.server"; import { isTeamManager } from "../team-utils"; export const action: ActionFunction = async ({ request, params }) => { - const user = await requireUser(request); + const user = await requireUser(); const { customUrl } = teamParamsSchema.parse(params); const team = notFoundIfFalsy(await TeamRepository.findByCustomUrl(customUrl)); diff --git a/app/features/team/actions/t.server.ts b/app/features/team/actions/t.server.ts index 9fdc6d917..401d2ef8c 100644 --- a/app/features/team/actions/t.server.ts +++ b/app/features/team/actions/t.server.ts @@ -8,7 +8,7 @@ import { TEAM } from "../team-constants"; import { createTeamSchema } from "../team-schemas.server"; export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: createTeamSchema, diff --git a/app/features/team/loaders/t.$customUrl.edit.server.ts b/app/features/team/loaders/t.$customUrl.edit.server.ts index a330b2b64..95ce1bae3 100644 --- a/app/features/team/loaders/t.$customUrl.edit.server.ts +++ b/app/features/team/loaders/t.$customUrl.edit.server.ts @@ -7,8 +7,8 @@ import * as TeamRepository from "../TeamRepository.server"; import { teamParamsSchema } from "../team-schemas.server"; import { isTeamManager } from "../team-utils"; -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const user = await requireUser(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const user = await requireUser(); const { customUrl } = teamParamsSchema.parse(params); const team = notFoundIfFalsy(await TeamRepository.findByCustomUrl(customUrl)); diff --git a/app/features/team/loaders/t.$customUrl.join.server.ts b/app/features/team/loaders/t.$customUrl.join.server.ts index 1cfea23e5..6a5b9e5b8 100644 --- a/app/features/team/loaders/t.$customUrl.join.server.ts +++ b/app/features/team/loaders/t.$customUrl.join.server.ts @@ -10,7 +10,7 @@ import { teamParamsSchema } from "../team-schemas.server"; import { isTeamFull, isTeamMember } from "../team-utils"; export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const { customUrl } = teamParamsSchema.parse(params); const team = notFoundIfFalsy( diff --git a/app/features/team/loaders/t.$customUrl.roster.server.ts b/app/features/team/loaders/t.$customUrl.roster.server.ts index 98b121725..36accfd96 100644 --- a/app/features/team/loaders/t.$customUrl.roster.server.ts +++ b/app/features/team/loaders/t.$customUrl.roster.server.ts @@ -7,8 +7,8 @@ import * as TeamRepository from "../TeamRepository.server"; import { teamParamsSchema } from "../team-schemas.server"; import { isTeamManager } from "../team-utils"; -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const user = await requireUser(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const user = await requireUser(); const { customUrl } = teamParamsSchema.parse(params); const team = notFoundIfFalsy( diff --git a/app/features/team/loaders/t.server.ts b/app/features/team/loaders/t.server.ts index 6fe510fad..1f980fa9f 100644 --- a/app/features/team/loaders/t.server.ts +++ b/app/features/team/loaders/t.server.ts @@ -1,11 +1,10 @@ -import type { LoaderFunctionArgs } from "react-router"; import * as R from "remeda"; import type { UserWithPlusTier } from "~/db/tables"; -import { getUserId } from "~/features/auth/core/user.server"; +import { getUser } from "~/features/auth/core/user.server"; import * as TeamRepository from "../TeamRepository.server"; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await getUserId(request); +export const loader = async () => { + const user = await getUser(); const unsortedTeams = await TeamRepository.findAllUndisbanded(); diff --git a/app/features/top-search/actions/xsearch.player.$id.server.ts b/app/features/top-search/actions/xsearch.player.$id.server.ts index 81e8a756c..a017d6956 100644 --- a/app/features/top-search/actions/xsearch.player.$id.server.ts +++ b/app/features/top-search/actions/xsearch.player.$id.server.ts @@ -11,8 +11,8 @@ import { import { idObject } from "~/utils/zod"; import * as XRankPlacementRepository from "../XRankPlacementRepository.server"; -export const action = async ({ request, params }: ActionFunctionArgs) => { - const user = await requireUser(request); +export const action = async ({ params }: ActionFunctionArgs) => { + const user = await requireUser(); const { id } = parseParams({ params, schema: idObject, diff --git a/app/features/tournament-bracket/actions/to.$id.brackets.finalize.server.ts b/app/features/tournament-bracket/actions/to.$id.brackets.finalize.server.ts index 3e1623de8..72b43b137 100644 --- a/app/features/tournament-bracket/actions/to.$id.brackets.finalize.server.ts +++ b/app/features/tournament-bracket/actions/to.$id.brackets.finalize.server.ts @@ -1,5 +1,5 @@ import type { ActionFunctionArgs } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import * as BadgeRepository from "~/features/badges/BadgeRepository.server"; import * as CalendarRepository from "~/features/calendar/CalendarRepository.server"; import * as Seasons from "~/features/mmr/core/Seasons"; @@ -41,7 +41,7 @@ import { tournamentBracketsPage } from "~/utils/urls"; import { idObject } from "~/utils/zod"; export const action = async ({ request, params }: ActionFunctionArgs) => { - const user = await requireUserId(request); + const user = await requireUser(); const { id: tournamentId } = parseParams({ params, schema: idObject, diff --git a/app/features/tournament-bracket/actions/to.$id.brackets.server.ts b/app/features/tournament-bracket/actions/to.$id.brackets.server.ts index 02acef829..cce78c297 100644 --- a/app/features/tournament-bracket/actions/to.$id.brackets.server.ts +++ b/app/features/tournament-bracket/actions/to.$id.brackets.server.ts @@ -34,7 +34,7 @@ import { } from "../tournament-bracket-utils"; export const action: ActionFunction = async ({ params, request }) => { - const user = await requireUser(request); + const user = await requireUser(); const { id: tournamentId } = parseParams({ params, schema: idObject, diff --git a/app/features/tournament-bracket/actions/to.$id.matches.$mid.server.ts b/app/features/tournament-bracket/actions/to.$id.matches.$mid.server.ts index d03172e0a..23c0f1373 100644 --- a/app/features/tournament-bracket/actions/to.$id.matches.$mid.server.ts +++ b/app/features/tournament-bracket/actions/to.$id.matches.$mid.server.ts @@ -50,7 +50,7 @@ import { } from "../tournament-bracket-utils"; export const action: ActionFunction = async ({ params, request }) => { - const user = await requireUser(request); + const user = await requireUser(); const { mid: matchId, id: tournamentId } = parseParams({ params, schema: matchPageParamsSchema, diff --git a/app/features/tournament-bracket/loaders/to.$id.brackets.finalize.server.ts b/app/features/tournament-bracket/loaders/to.$id.brackets.finalize.server.ts index d332e1620..68542a6e6 100644 --- a/app/features/tournament-bracket/loaders/to.$id.brackets.finalize.server.ts +++ b/app/features/tournament-bracket/loaders/to.$id.brackets.finalize.server.ts @@ -1,5 +1,5 @@ import { type LoaderFunctionArgs, redirect } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import * as CalendarRepository from "~/features/calendar/CalendarRepository.server"; import * as Seasons from "~/features/mmr/core/Seasons"; import { @@ -21,8 +21,8 @@ import { idObject } from "~/utils/zod"; export type FinalizeTournamentLoaderData = SerializeFrom; -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const user = await requireUserId(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const user = await requireUser(); const { id: tournamentId } = parseParams({ params, schema: idObject, diff --git a/app/features/tournament-bracket/loaders/to.$id.divisions.server.ts b/app/features/tournament-bracket/loaders/to.$id.divisions.server.ts index b66ba37c9..8d47c13ca 100644 --- a/app/features/tournament-bracket/loaders/to.$id.divisions.server.ts +++ b/app/features/tournament-bracket/loaders/to.$id.divisions.server.ts @@ -6,8 +6,8 @@ import { idObject } from "~/utils/zod"; import type { Unwrapped } from "../../../utils/types"; import { tournamentFromDB } from "../core/Tournament.server"; -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const user = await getUser(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const user = await getUser(); const { id: tournamentId } = parseParams({ params, schema: idObject, diff --git a/app/features/tournament-organization/actions/org.$slug.edit.server.ts b/app/features/tournament-organization/actions/org.$slug.edit.server.ts index af443d5ac..43900ea6a 100644 --- a/app/features/tournament-organization/actions/org.$slug.edit.server.ts +++ b/app/features/tournament-organization/actions/org.$slug.edit.server.ts @@ -12,7 +12,7 @@ import { organizationEditSchema } from "../tournament-organization-schemas"; import { organizationFromParams } from "../tournament-organization-utils.server"; export const action = async ({ request, params }: ActionFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: organizationEditSchema, diff --git a/app/features/tournament-organization/actions/org.$slug.server.ts b/app/features/tournament-organization/actions/org.$slug.server.ts index 45a676fcb..0b510f40f 100644 --- a/app/features/tournament-organization/actions/org.$slug.server.ts +++ b/app/features/tournament-organization/actions/org.$slug.server.ts @@ -19,7 +19,7 @@ import { orgPageActionSchema } from "../tournament-organization-schemas"; import { organizationFromParams } from "../tournament-organization-utils.server"; export const action = async ({ request, params }: ActionFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const organization = await organizationFromParams(params); const data = await parseRequestPayload({ request, diff --git a/app/features/tournament-organization/actions/org.new.server.ts b/app/features/tournament-organization/actions/org.new.server.ts index a4e244a9e..8a617618d 100644 --- a/app/features/tournament-organization/actions/org.new.server.ts +++ b/app/features/tournament-organization/actions/org.new.server.ts @@ -8,7 +8,7 @@ import { TOURNAMENT_ORGANIZATION } from "../tournament-organization-constants"; import { newOrganizationSchema } from "../tournament-organization-schemas"; export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUser(); requireRole(user, "TOURNAMENT_ADDER"); const data = await parseRequestPayload({ diff --git a/app/features/tournament-organization/loaders/org.$slug.edit.server.ts b/app/features/tournament-organization/loaders/org.$slug.edit.server.ts index 938770f8c..e76afdbd3 100644 --- a/app/features/tournament-organization/loaders/org.$slug.edit.server.ts +++ b/app/features/tournament-organization/loaders/org.$slug.edit.server.ts @@ -4,8 +4,8 @@ import * as BadgeRepository from "~/features/badges/BadgeRepository.server"; import { requirePermission } from "~/modules/permissions/guards.server"; import { organizationFromParams } from "../tournament-organization-utils.server"; -export async function loader({ params, request }: LoaderFunctionArgs) { - const user = await requireUser(request); +export async function loader({ params }: LoaderFunctionArgs) { + const user = await requireUser(); const organization = await organizationFromParams(params); requirePermission(organization, "EDIT", user); diff --git a/app/features/tournament-organization/loaders/org.$slug.server.ts b/app/features/tournament-organization/loaders/org.$slug.server.ts index e5bcbc397..3f92d563e 100644 --- a/app/features/tournament-organization/loaders/org.$slug.server.ts +++ b/app/features/tournament-organization/loaders/org.$slug.server.ts @@ -12,7 +12,7 @@ import { organizationFromParams } from "../tournament-organization-utils.server" export type OrganizationPageLoaderData = SerializeFrom; export async function loader({ params, request }: LoaderFunctionArgs) { - const user = await getUser(request); + const user = await getUser(); const { month = new Date().getMonth(), year = new Date().getFullYear(), diff --git a/app/features/tournament-subs/actions/to.$id.subs.new.server.ts b/app/features/tournament-subs/actions/to.$id.subs.new.server.ts index 8ded9212b..49b4a95de 100644 --- a/app/features/tournament-subs/actions/to.$id.subs.new.server.ts +++ b/app/features/tournament-subs/actions/to.$id.subs.new.server.ts @@ -15,7 +15,7 @@ import * as TournamentSubRepository from "../TournamentSubRepository.server"; import { subSchema } from "../tournament-subs-schemas.server"; export const action: ActionFunction = async ({ params, request }) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: subSchema, diff --git a/app/features/tournament-subs/actions/to.$id.subs.server.ts b/app/features/tournament-subs/actions/to.$id.subs.server.ts index 8cf936231..91070f7b5 100644 --- a/app/features/tournament-subs/actions/to.$id.subs.server.ts +++ b/app/features/tournament-subs/actions/to.$id.subs.server.ts @@ -14,7 +14,7 @@ import { deleteSub } from "../queries/deleteSub.server"; import { deleteSubSchema } from "../tournament-subs-schemas.server"; export const action: ActionFunction = async ({ request, params }) => { - const user = await requireUser(request); + const user = await requireUser(); const { id: tournamentId } = parseParams({ params, schema: idObject, diff --git a/app/features/tournament-subs/loaders/to.$id.subs.new.server.ts b/app/features/tournament-subs/loaders/to.$id.subs.new.server.ts index 677e2761a..13d2ff4ae 100644 --- a/app/features/tournament-subs/loaders/to.$id.subs.new.server.ts +++ b/app/features/tournament-subs/loaders/to.$id.subs.new.server.ts @@ -7,8 +7,8 @@ import { tournamentSubsPage } from "~/utils/urls"; import { idObject } from "~/utils/zod"; import * as TournamentSubRepository from "../TournamentSubRepository.server"; -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const user = await requireUser(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const user = await requireUser(); const { id: tournamentId } = parseParams({ params, schema: idObject, diff --git a/app/features/tournament-subs/loaders/to.$id.subs.server.ts b/app/features/tournament-subs/loaders/to.$id.subs.server.ts index 64a1f7597..44fafd742 100644 --- a/app/features/tournament-subs/loaders/to.$id.subs.server.ts +++ b/app/features/tournament-subs/loaders/to.$id.subs.server.ts @@ -6,8 +6,8 @@ import { tournamentRegisterPage } from "~/utils/urls"; import { idObject } from "~/utils/zod"; import * as TournamentSubRepository from "../TournamentSubRepository.server"; -export const loader = async ({ params, request }: LoaderFunctionArgs) => { - const user = await getUser(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const user = await getUser(); const { id: tournamentId } = parseParams({ params, schema: idObject, diff --git a/app/features/tournament/actions/to.$id.admin.server.ts b/app/features/tournament/actions/to.$id.admin.server.ts index 302c44e83..fa2d0e388 100644 --- a/app/features/tournament/actions/to.$id.admin.server.ts +++ b/app/features/tournament/actions/to.$id.admin.server.ts @@ -34,7 +34,7 @@ import { } from "../tournament-utils.server"; export const action: ActionFunction = async ({ request, params }) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: adminActionSchema, diff --git a/app/features/tournament/actions/to.$id.join.server.ts b/app/features/tournament/actions/to.$id.join.server.ts index f3dd9fc47..e8d3a4668 100644 --- a/app/features/tournament/actions/to.$id.join.server.ts +++ b/app/features/tournament/actions/to.$id.join.server.ts @@ -1,6 +1,6 @@ import type { ActionFunction } from "react-router"; import { redirect } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import * as ShowcaseTournaments from "~/features/front-page/core/ShowcaseTournaments.server"; import { clearTournamentDataCache, @@ -31,7 +31,7 @@ export const action: ActionFunction = async ({ request, params }) => { params, schema: idObject, }); - const user = await requireUserId(request); + const user = await requireUser(); const url = new URL(request.url); const inviteCode = url.searchParams.get("code"); const data = await parseRequestPayload({ request, schema: joinSchema }); diff --git a/app/features/tournament/actions/to.$id.register.server.ts b/app/features/tournament/actions/to.$id.register.server.ts index 21c68dcdc..828b9939a 100644 --- a/app/features/tournament/actions/to.$id.register.server.ts +++ b/app/features/tournament/actions/to.$id.register.server.ts @@ -38,7 +38,7 @@ import { } from "../tournament-utils.server"; export const action: ActionFunction = async ({ request, params }) => { - const user = await requireUser(request); + const user = await requireUser(); const { avatarFileName, formData } = await uploadImageIfSubmitted({ request, fileNamePrefix: "pickup-logo", diff --git a/app/features/tournament/actions/to.$id.seeds.server.ts b/app/features/tournament/actions/to.$id.seeds.server.ts index ee5cf228d..886643a89 100644 --- a/app/features/tournament/actions/to.$id.seeds.server.ts +++ b/app/features/tournament/actions/to.$id.seeds.server.ts @@ -20,7 +20,7 @@ export const action: ActionFunction = async ({ request, params }) => { request, schema: seedsActionSchema, }); - const user = await requireUser(request); + const user = await requireUser(); const { id: tournamentId } = parseParams({ params, schema: idObject, diff --git a/app/features/tournament/loaders/to.$id.register.server.ts b/app/features/tournament/loaders/to.$id.register.server.ts index 4749ccef6..4533b4f79 100644 --- a/app/features/tournament/loaders/to.$id.register.server.ts +++ b/app/features/tournament/loaders/to.$id.register.server.ts @@ -7,8 +7,8 @@ import { parseParams } from "~/utils/remix.server"; import { idObject } from "~/utils/zod"; import { findOwnTournamentTeam } from "../queries/findOwnTournamentTeam.server"; -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const user = await getUser(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const user = await getUser(); if (!user) return null; const { id: tournamentId } = parseParams({ diff --git a/app/features/tournament/loaders/to.$id.seeds.server.ts b/app/features/tournament/loaders/to.$id.seeds.server.ts index 60ae29f33..55aa23ef5 100644 --- a/app/features/tournament/loaders/to.$id.seeds.server.ts +++ b/app/features/tournament/loaders/to.$id.seeds.server.ts @@ -6,8 +6,8 @@ import { parseParams } from "~/utils/remix.server"; import { tournamentBracketsPage } from "~/utils/urls"; import { idObject } from "~/utils/zod"; -export const loader = async ({ params, request }: LoaderFunctionArgs) => { - const user = await requireUser(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const user = await requireUser(); const { id: tournamentId } = parseParams({ params, schema: idObject, diff --git a/app/features/tournament/loaders/to.$id.server.ts b/app/features/tournament/loaders/to.$id.server.ts index a9f894352..e6e874fba 100644 --- a/app/features/tournament/loaders/to.$id.server.ts +++ b/app/features/tournament/loaders/to.$id.server.ts @@ -10,8 +10,8 @@ import { streamsByTournamentId } from "../core/streams.server"; export type TournamentLoaderData = SerializeFrom; -export const loader = async ({ params, request }: LoaderFunctionArgs) => { - const user = await getUser(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const user = await getUser(); const { id: tournamentId } = parseParams({ params, schema: idObject, diff --git a/app/features/tournament/routes/to.search.ts b/app/features/tournament/routes/to.search.ts index eb79844a2..835f73605 100644 --- a/app/features/tournament/routes/to.search.ts +++ b/app/features/tournament/routes/to.search.ts @@ -1,5 +1,5 @@ import type { LoaderFunctionArgs } from "react-router"; -import { getUserId } from "~/features/auth/core/user.server"; +import { getUser } from "~/features/auth/core/user.server"; import * as TournamentRepository from "~/features/tournament/TournamentRepository.server"; import type { SerializeFrom } from "~/utils/remix"; import { parseSearchParams } from "~/utils/remix.server"; @@ -8,7 +8,7 @@ import { tournamentSearchSearchParamsSchema } from "../tournament-schemas.server export type TournamentSearchLoaderData = SerializeFrom; export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await getUserId(request); + const user = await getUser(); if (!user) { return []; } diff --git a/app/features/user-page/actions/u.$identifier.admin.server.ts b/app/features/user-page/actions/u.$identifier.admin.server.ts index fdb75a953..5f2f99d13 100644 --- a/app/features/user-page/actions/u.$identifier.admin.server.ts +++ b/app/features/user-page/actions/u.$identifier.admin.server.ts @@ -12,7 +12,7 @@ import { import { assertUnreachable } from "~/utils/types"; export const action = async ({ request, params }: ActionFunctionArgs) => { - const loggedInUser = await requireUser(request); + const loggedInUser = await requireUser(); requireRole(loggedInUser, "STAFF"); diff --git a/app/features/user-page/actions/u.$identifier.art.server.ts b/app/features/user-page/actions/u.$identifier.art.server.ts index c9804be60..ab44298c0 100644 --- a/app/features/user-page/actions/u.$identifier.art.server.ts +++ b/app/features/user-page/actions/u.$identifier.art.server.ts @@ -1,7 +1,7 @@ import type { ActionFunction } from "react-router"; import * as ArtRepository from "~/features/art/ArtRepository.server"; import { userArtPageActionSchema } from "~/features/art/art-schemas.server"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import { logger } from "~/utils/logger"; import { errorToastIfFalsy, @@ -11,7 +11,7 @@ import { import { assertUnreachable } from "~/utils/types"; export const action: ActionFunction = async ({ request }) => { - const user = await requireUserId(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: userArtPageActionSchema, diff --git a/app/features/user-page/actions/u.$identifier.builds.new.server.ts b/app/features/user-page/actions/u.$identifier.builds.new.server.ts index f15d8cc95..8840bd4bf 100644 --- a/app/features/user-page/actions/u.$identifier.builds.new.server.ts +++ b/app/features/user-page/actions/u.$identifier.builds.new.server.ts @@ -32,7 +32,7 @@ import { } from "~/utils/zod"; export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: newBuildActionSchema, diff --git a/app/features/user-page/actions/u.$identifier.builds.server.ts b/app/features/user-page/actions/u.$identifier.builds.server.ts index 9da0ab824..029df9d9f 100644 --- a/app/features/user-page/actions/u.$identifier.builds.server.ts +++ b/app/features/user-page/actions/u.$identifier.builds.server.ts @@ -18,7 +18,7 @@ import { } from "~/utils/zod"; export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: buildsActionSchema, diff --git a/app/features/user-page/actions/u.$identifier.edit.server.ts b/app/features/user-page/actions/u.$identifier.edit.server.ts index 6ea8ce170..e5acb1c17 100644 --- a/app/features/user-page/actions/u.$identifier.edit.server.ts +++ b/app/features/user-page/actions/u.$identifier.edit.server.ts @@ -1,5 +1,5 @@ import { type ActionFunction, redirect } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import * as TournamentTeamRepository from "~/features/tournament/TournamentTeamRepository.server"; import { clearTournamentDataCache } from "~/features/tournament-bracket/core/Tournament.server"; import * as UserRepository from "~/features/user-page/UserRepository.server"; @@ -22,7 +22,7 @@ export const action: ActionFunction = async ({ request }) => { const { inGameNameText, inGameNameDiscriminator, ...data } = parsedInput.data; - const user = await requireUserId(request); + const user = await requireUser(); const inGameName = inGameNameText && inGameNameDiscriminator ? `${inGameNameText}#${inGameNameDiscriminator}` diff --git a/app/features/user-page/actions/u.$identifier.results.highlights.server.ts b/app/features/user-page/actions/u.$identifier.results.highlights.server.ts index c048be10d..4b7a93b73 100644 --- a/app/features/user-page/actions/u.$identifier.results.highlights.server.ts +++ b/app/features/user-page/actions/u.$identifier.results.highlights.server.ts @@ -11,7 +11,7 @@ import { userResultsPage } from "~/utils/urls"; import { editHighlightsActionSchema } from "../user-page-schemas"; export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUser(); const data = await parseRequestPayload({ request, schema: editHighlightsActionSchema, diff --git a/app/features/user-page/loaders/u.$identifier.admin.server.ts b/app/features/user-page/loaders/u.$identifier.admin.server.ts index e197d1ee1..f0657f96a 100644 --- a/app/features/user-page/loaders/u.$identifier.admin.server.ts +++ b/app/features/user-page/loaders/u.$identifier.admin.server.ts @@ -6,8 +6,8 @@ import { logger } from "~/utils/logger"; import { notFoundIfFalsy } from "~/utils/remix.server"; import { convertSnowflakeToDate } from "~/utils/users"; -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const loggedInUser = await requireUser(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const loggedInUser = await requireUser(); requireRole(loggedInUser, "STAFF"); diff --git a/app/features/user-page/loaders/u.$identifier.art.server.ts b/app/features/user-page/loaders/u.$identifier.art.server.ts index df1a99b1e..942f7a872 100644 --- a/app/features/user-page/loaders/u.$identifier.art.server.ts +++ b/app/features/user-page/loaders/u.$identifier.art.server.ts @@ -1,13 +1,13 @@ import type { LoaderFunctionArgs } from "react-router"; import * as ArtRepository from "~/features/art/ArtRepository.server"; -import { getUserId } from "~/features/auth/core/user.server"; +import { getUser } from "~/features/auth/core/user.server"; import * as ImageRepository from "~/features/img-upload/ImageRepository.server"; import * as UserRepository from "~/features/user-page/UserRepository.server"; import { notFoundIfFalsy } from "~/utils/remix.server"; import { userParamsSchema } from "../user-page-schemas"; -export const loader = async ({ params, request }: LoaderFunctionArgs) => { - const loggedInUser = await getUserId(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const loggedInUser = await getUser(); const { identifier } = userParamsSchema.parse(params); const user = notFoundIfFalsy( diff --git a/app/features/user-page/loaders/u.$identifier.builds.new.server.ts b/app/features/user-page/loaders/u.$identifier.builds.new.server.ts index 2f13677d1..7a03d3e85 100644 --- a/app/features/user-page/loaders/u.$identifier.builds.new.server.ts +++ b/app/features/user-page/loaders/u.$identifier.builds.new.server.ts @@ -1,6 +1,6 @@ import type { LoaderFunctionArgs } from "react-router"; import { z } from "zod"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import * as BuildRepository from "~/features/builds/BuildRepository.server"; import type { Ability } from "~/modules/in-game-lists/types"; import { actualNumber, id } from "~/utils/zod"; @@ -10,7 +10,7 @@ const newBuildLoaderParamsSchema = z.object({ }); export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await requireUserId(request); + const user = await requireUser(); const url = new URL(request.url); const params = newBuildLoaderParamsSchema.safeParse( diff --git a/app/features/user-page/loaders/u.$identifier.builds.server.ts b/app/features/user-page/loaders/u.$identifier.builds.server.ts index 84cb2514b..d36e98dd0 100644 --- a/app/features/user-page/loaders/u.$identifier.builds.server.ts +++ b/app/features/user-page/loaders/u.$identifier.builds.server.ts @@ -10,8 +10,8 @@ import { userParamsSchema } from "../user-page-schemas"; export type UserBuildsPageData = SerializeFrom; -export const loader = async ({ params, request }: LoaderFunctionArgs) => { - const loggedInUser = await getUser(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const loggedInUser = await getUser(); const { identifier } = userParamsSchema.parse(params); const user = notFoundIfFalsy( await UserRepository.identifierToBuildFields(identifier), diff --git a/app/features/user-page/loaders/u.$identifier.edit.server.ts b/app/features/user-page/loaders/u.$identifier.edit.server.ts index b099a386c..fa12ad78b 100644 --- a/app/features/user-page/loaders/u.$identifier.edit.server.ts +++ b/app/features/user-page/loaders/u.$identifier.edit.server.ts @@ -1,12 +1,12 @@ import { type LoaderFunctionArgs, redirect } from "react-router"; -import { requireUserId } from "~/features/auth/core/user.server"; +import { requireUser } from "~/features/auth/core/user.server"; import * as UserRepository from "~/features/user-page/UserRepository.server"; import { notFoundIfFalsy } from "~/utils/remix.server"; import { userPage } from "~/utils/urls"; import { userParamsSchema } from "../user-page-schemas"; -export const loader = async ({ request, params }: LoaderFunctionArgs) => { - const user = await requireUserId(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const user = await requireUser(); const { identifier } = userParamsSchema.parse(params); const userToBeEdited = notFoundIfFalsy( await UserRepository.findLayoutDataByIdentifier(identifier), diff --git a/app/features/user-page/loaders/u.$identifier.seasons.server.ts b/app/features/user-page/loaders/u.$identifier.seasons.server.ts index 771c5f6da..20a36b74d 100644 --- a/app/features/user-page/loaders/u.$identifier.seasons.server.ts +++ b/app/features/user-page/loaders/u.$identifier.seasons.server.ts @@ -22,7 +22,7 @@ export type UserSeasonsPageLoaderData = NonNullable< >; export const loader = async ({ params, request }: LoaderFunctionArgs) => { - const loggedInUser = await getUser(request); + const loggedInUser = await getUser(); const { identifier } = userParamsSchema.parse(params); const parsedSearchParams = seasonsSearchParamsSchema.safeParse( Object.fromEntries(new URL(request.url).searchParams), diff --git a/app/features/user-page/loaders/u.$identifier.server.ts b/app/features/user-page/loaders/u.$identifier.server.ts index 8467cb6e8..6929ba3c0 100644 --- a/app/features/user-page/loaders/u.$identifier.server.ts +++ b/app/features/user-page/loaders/u.$identifier.server.ts @@ -1,13 +1,13 @@ import type { LoaderFunctionArgs } from "react-router"; -import { getUserId } from "~/features/auth/core/user.server"; +import { getUser } from "~/features/auth/core/user.server"; import * as UserRepository from "~/features/user-page/UserRepository.server"; import type { SerializeFrom } from "~/utils/remix"; import { notFoundIfFalsy } from "~/utils/remix.server"; export type UserPageLoaderData = SerializeFrom; -export const loader = async ({ params, request }: LoaderFunctionArgs) => { - const loggedInUser = await getUserId(request); +export const loader = async ({ params }: LoaderFunctionArgs) => { + const loggedInUser = await getUser(); const user = notFoundIfFalsy( await UserRepository.findLayoutDataByIdentifier( diff --git a/app/features/user-search/loaders/u.server.ts b/app/features/user-search/loaders/u.server.ts index 87c876d08..871102aa3 100644 --- a/app/features/user-search/loaders/u.server.ts +++ b/app/features/user-search/loaders/u.server.ts @@ -1,7 +1,7 @@ import type { LoaderFunctionArgs } from "react-router"; import { z } from "zod"; import { DANGEROUS_CAN_ACCESS_DEV_CONTROLS } from "~/features/admin/core/dev-controls"; -import { getUserId } from "~/features/auth/core/user.server"; +import { getUser } from "~/features/auth/core/user.server"; import * as UserRepository from "~/features/user-page/UserRepository.server"; import type { SerializeFrom } from "~/utils/remix"; import { parseSearchParams } from "~/utils/remix.server"; @@ -16,7 +16,7 @@ const searchParamsSchema = z.object({ export const loader = async ({ request }: LoaderFunctionArgs) => { if (!DANGEROUS_CAN_ACCESS_DEV_CONTROLS) { - const user = await getUserId(request); + const user = await getUser(); if (!user) { return null; } diff --git a/app/features/vods/actions/vods.$id.server.ts b/app/features/vods/actions/vods.$id.server.ts index af934c0c2..f8d750ae8 100644 --- a/app/features/vods/actions/vods.$id.server.ts +++ b/app/features/vods/actions/vods.$id.server.ts @@ -5,8 +5,8 @@ import { userVodsPage } from "~/utils/urls"; import * as VodRepository from "../VodRepository.server"; import { canEditVideo } from "../vods-utils"; -export const action = async ({ request, params }: ActionFunctionArgs) => { - const user = await requireUser(request); +export const action = async ({ params }: ActionFunctionArgs) => { + const user = await requireUser(); const vod = badRequestIfFalsy( await VodRepository.findVodById(Number(params.id)), diff --git a/app/features/vods/actions/vods.new.server.ts b/app/features/vods/actions/vods.new.server.ts index fbf72ee42..7fdf125d7 100644 --- a/app/features/vods/actions/vods.new.server.ts +++ b/app/features/vods/actions/vods.new.server.ts @@ -9,7 +9,7 @@ import { videoInputSchema } from "../vods-schemas"; import { canEditVideo } from "../vods-utils"; export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUser(); requireRole(user, "VIDEO_ADDER"); const data = await parseRequestPayload({ diff --git a/app/features/vods/loaders/vods.new.server.ts b/app/features/vods/loaders/vods.new.server.ts index 6f25ae51a..39e883b4f 100644 --- a/app/features/vods/loaders/vods.new.server.ts +++ b/app/features/vods/loaders/vods.new.server.ts @@ -11,7 +11,7 @@ const newVodLoaderParamsSchema = z.object({ }); export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await requireUser(request); + const user = await requireUser(); const url = new URL(request.url); const params = newVodLoaderParamsSchema.safeParse( diff --git a/app/root.tsx b/app/root.tsx index 36c29a65d..2acfe022a 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -14,7 +14,6 @@ import { Links, Meta, Outlet, - redirect, Scripts, ScrollRestoration, type ShouldRevalidateFunction, @@ -30,12 +29,13 @@ import { useChangeLanguage } from "remix-i18next/react"; import * as NotificationRepository from "~/features/notifications/NotificationRepository.server"; import { NOTIFICATIONS } from "~/features/notifications/notifications-contants"; import type { SendouRouteHandle } from "~/utils/remix.server"; +import type { Route } from "./+types/root"; import { Catcher } from "./components/Catcher"; import { SendouToastRegion, toastQueue } from "./components/elements/Toast"; import { Layout } from "./components/layout"; import { Ramp } from "./components/ramp/Ramp"; import { getUser } from "./features/auth/core/user.server"; -import { userIsBanned } from "./features/ban/core/banned.server"; +import { userMiddleware } from "./features/auth/core/user-middleware.server"; import { isTheme, Theme, @@ -50,7 +50,8 @@ import { i18nCookie, i18next } from "./modules/i18n/i18next.server"; import { IS_E2E_TEST_RUN } from "./utils/e2e"; import { allI18nNamespaces } from "./utils/i18n"; import { isRevalidation, metaTags, type SerializeFrom } from "./utils/remix"; -import { SUSPENDED_PAGE } from "./utils/urls"; + +export const middleware: Route.MiddlewareFunction[] = [userMiddleware]; import "nprogress/nprogress.css"; import "~/styles/common.css"; @@ -85,19 +86,10 @@ export type RootLoaderData = SerializeFrom; export type LoggedInUser = NonNullable; export const loader = async ({ request }: LoaderFunctionArgs) => { - const user = await getUser(request, false); + const user = await getUser(); const locale = await i18next.getLocale(request); const themeSession = await getThemeSession(request); - // avoid redirection loop - if ( - user && - userIsBanned(user?.id) && - new URL(request.url).pathname !== SUSPENDED_PAGE - ) { - return redirect(SUSPENDED_PAGE); - } - return data( { locale, diff --git a/app/utils/Test.ts b/app/utils/Test.ts index 536c6596f..449bfd044 100644 --- a/app/utils/Test.ts +++ b/app/utils/Test.ts @@ -10,6 +10,10 @@ import { db, sql } from "~/db/sql"; import { ADMIN_ID } from "~/features/admin/admin-constants"; import { SESSION_KEY } from "~/features/auth/core/authenticator.server"; import { authSessionStorage } from "~/features/auth/core/session.server"; +import { + getUserFromRequest, + userAsyncLocalStorage, +} from "~/features/auth/core/user-context.server"; import { logger } from "./logger"; export function arrayContainsSameItems(arr1: T[], arr2: T[]) { @@ -61,28 +65,34 @@ export function wrappedAction({ ], }); - try { - const response = await action({ - request, - context: {}, - params, - unstable_pattern: "", - }); + const userContext = { + getUserLazy: () => getUserFromRequest(request), + }; - return response; - } catch (thrown) { - // we only log errors in vitest for failed tests so this is okay (more context) - logger.error("Error in wrappedAction:", thrown); + return userAsyncLocalStorage.run(userContext, async () => { + try { + const response = await action({ + request, + context: {} as any, + params, + unstable_pattern: "", + }); - if (thrown instanceof Response) { - // it was a redirect - if (thrown.status === 302) return thrown; + return response; + } catch (thrown) { + // we only log errors in vitest for failed tests so this is okay (more context) + logger.error("Error in wrappedAction:", thrown); - throw new Error(`Response thrown with status code: ${thrown.status}`); + if (thrown instanceof Response) { + // it was a redirect + if (thrown.status === 302) return thrown; + + throw new Error(`Response thrown with status code: ${thrown.status}`); + } + + throw thrown; } - - throw thrown; - } + }); }; } @@ -106,22 +116,28 @@ export function wrappedLoader({ ], }); - try { - const data = await loader({ - request, - params, - context: {}, - unstable_pattern: "", - }); + const userContext = { + getUserLazy: () => getUserFromRequest(request), + }; - return data as T; - } catch (thrown) { - if (thrown instanceof Response) { - throw new Error(`Response thrown with status code: ${thrown.status}`); + return userAsyncLocalStorage.run(userContext, async () => { + try { + const data = await loader({ + request, + params, + context: {} as any, + unstable_pattern: "", + }); + + return data as T; + } catch (thrown) { + if (thrown instanceof Response) { + throw new Error(`Response thrown with status code: ${thrown.status}`); + } + + throw thrown; } - - throw thrown; - } + }); }; } diff --git a/e2e/ban.spec.ts b/e2e/ban.spec.ts new file mode 100644 index 000000000..e46d0e1f9 --- /dev/null +++ b/e2e/ban.spec.ts @@ -0,0 +1,141 @@ +import test, { expect, type Page } from "@playwright/test"; +import { NZAP_TEST_ID } from "~/db/seed/constants"; +import { ADMIN_ID } from "~/features/admin/admin-constants"; +import { + impersonate, + navigate, + seed, + waitForPOSTResponse, +} from "~/utils/playwright"; +import { ADMIN_PAGE, SUSPENDED_PAGE } from "~/utils/urls"; + +async function banUser( + page: Page, + options: { duration?: string; reason?: string }, +) { + const banForm = page + .locator("form") + .filter({ has: page.locator("h2", { hasText: /^Ban user$/ }) }); + + const comboboxButton = banForm.getByRole("button", { + name: "User search User", + }); + await expect(comboboxButton).not.toBeDisabled(); + await comboboxButton.click(); + + const searchInput = page.getByTestId("user-search-input"); + await searchInput.fill("N-ZAP"); + + const option = page.getByTestId("user-search-item").first(); + await option.click(); + + if (options.duration) { + await banForm.locator('input[name="duration"]').fill(options.duration); + } + if (options.reason) { + await banForm.locator('input[name="reason"]').fill(options.reason); + } + + await waitForPOSTResponse(page, () => + banForm.getByRole("button", { name: "Save" }).click(), + ); + + // Verify ban was successful + await expect(page.getByText("User banned")).toBeVisible(); +} + +async function unbanUser(page: Page) { + const unbanForm = page + .locator("form") + .filter({ has: page.locator("h2", { hasText: /^Unban user$/ }) }); + + const comboboxButton = unbanForm.getByRole("button", { + name: "User search User", + }); + await expect(comboboxButton).not.toBeDisabled(); + await comboboxButton.click(); + + const searchInput = page.getByTestId("user-search-input"); + await searchInput.fill("N-ZAP"); + + const option = page.getByTestId("user-search-item").first(); + await option.click(); + + await waitForPOSTResponse(page, () => + unbanForm.getByRole("button", { name: "Save" }).click(), + ); +} + +test.describe("User banning", () => { + test("banned user is redirected to suspended page and cannot access site", async ({ + page, + }) => { + await seed(page); + + // 1. As admin, ban NZAP user + await impersonate(page, ADMIN_ID); + await navigate({ page, url: ADMIN_PAGE }); + await banUser(page, { reason: "Test ban reason" }); + + // 2. As the banned user, try to access the site + await impersonate(page, NZAP_TEST_ID); + await navigate({ page, url: "/" }); + + // Should be redirected to suspended page + await expect(page).toHaveURL(SUSPENDED_PAGE); + await expect(page.getByText("Account suspended")).toBeVisible(); + await expect(page.getByText("Reason: Test ban reason")).toBeVisible(); + await expect(page.getByText("no end time set")).toBeVisible(); + + // 3. Verify user cannot navigate to other pages + await navigate({ page, url: "/builds" }); + await expect(page).toHaveURL(SUSPENDED_PAGE); + + await navigate({ page, url: "/calendar" }); + await expect(page).toHaveURL(SUSPENDED_PAGE); + + // 4. As admin, unban the user + await impersonate(page, ADMIN_ID); + await navigate({ page, url: ADMIN_PAGE }); + await unbanUser(page); + + // 5. As the unbanned user, verify they can access the site + await impersonate(page, NZAP_TEST_ID); + await navigate({ page, url: "/" }); + + // Should not be redirected to suspended page + await expect(page).not.toHaveURL(SUSPENDED_PAGE); + }); + + test("timed ban shows expiration date on suspended page", async ({ + page, + }) => { + await seed(page); + + // 1. As admin, ban NZAP user with a future duration + await impersonate(page, ADMIN_ID); + await navigate({ page, url: ADMIN_PAGE }); + + // Set ban to expire tomorrow (well in the future) + const tomorrow = new Date(Date.now() + 24 * 60 * 60 * 1000); + const year = tomorrow.getFullYear(); + const month = String(tomorrow.getMonth() + 1).padStart(2, "0"); + const day = String(tomorrow.getDate()).padStart(2, "0"); + const formattedDate = `${year}-${month}-${day}T12:00`; + await banUser(page, { duration: formattedDate, reason: "Temporary ban" }); + + // 2. As the banned user, verify redirected to suspended page + await impersonate(page, NZAP_TEST_ID); + await navigate({ page, url: "/" }); + + await expect(page).toHaveURL(SUSPENDED_PAGE); + await expect(page.getByText("Account suspended")).toBeVisible(); + await expect(page.getByText("Reason: Temporary ban")).toBeVisible(); + // Should show expiration time, not "no end time set" + await expect(page.getByText("no end time set")).not.toBeVisible(); + await expect(page.getByText("Ends:")).toBeVisible(); + + // Note: The actual time-based expiration logic is tested in unit tests + // (see app/features/ban/core/banned.test.ts) + }); +}); diff --git a/react-router.config.ts b/react-router.config.ts index 0b1e9bd4d..6ae19002c 100644 --- a/react-router.config.ts +++ b/react-router.config.ts @@ -5,4 +5,7 @@ export default { // also lazy loading causes more load on the server // this matches old Remix v2 behavior routeDiscovery: { mode: "initial" }, + future: { + v8_middleware: true, + }, } satisfies Config;