diff --git a/app/constants.ts b/app/constants.ts index 0d870756c..a8d688b9b 100644 --- a/app/constants.ts +++ b/app/constants.ts @@ -64,5 +64,6 @@ export const PLUS_UPVOTE = 1; export const PLUS_DOWNVOTE = -1; export const ADMIN_DISCORD_ID = "79237403620945920"; +export const ADMIN_ID = process.env.NODE_ENV === "production" ? 274 : 1; export const LOHI_TOKEN_HEADER_NAME = "Lohi-Token"; diff --git a/app/features/img-upload/routes/upload.admin.tsx b/app/features/img-upload/routes/upload.admin.tsx index 537f9589c..a4e49b375 100644 --- a/app/features/img-upload/routes/upload.admin.tsx +++ b/app/features/img-upload/routes/upload.admin.tsx @@ -2,7 +2,7 @@ import type { ActionFunction, LoaderArgs } from "@remix-run/node"; import { Form, useLoaderData } from "@remix-run/react"; import { Main } from "~/components/Main"; import { SubmitButton } from "~/components/SubmitButton"; -import { requireUser } from "~/modules/auth"; +import { requireUserId } from "~/modules/auth/user.server"; import { isAdmin } from "~/permissions"; import { notFoundIfFalsy, parseRequestFormData, validate } from "~/utils/remix"; import { userSubmittedImage } from "~/utils/urls"; @@ -11,7 +11,7 @@ import { validateImage } from "../queries/validateImage"; import { validateImageSchema } from "../upload-schemas.server"; export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUserId(request); const data = await parseRequestFormData({ schema: validateImageSchema, request, @@ -25,7 +25,7 @@ export const action: ActionFunction = async ({ request }) => { }; export const loader = async ({ request }: LoaderArgs) => { - const user = await requireUser(request); + const user = await requireUserId(request); notFoundIfFalsy(isAdmin(user)); diff --git a/app/features/team/routes/t.$customUrl.edit.tsx b/app/features/team/routes/t.$customUrl.edit.tsx index 7ab2aab7d..1181e0b7b 100644 --- a/app/features/team/routes/t.$customUrl.edit.tsx +++ b/app/features/team/routes/t.$customUrl.edit.tsx @@ -4,24 +4,29 @@ import type { SerializeFrom, } from "@remix-run/node"; import { - type LoaderArgs, redirect, type ActionFunction, + type LoaderArgs, } from "@remix-run/node"; -import * as React from "react"; import { Form, Link, useLoaderData } from "@remix-run/react"; +import * as React from "react"; +import { Button } from "~/components/Button"; +import { FormErrors } from "~/components/FormErrors"; import { FormMessage } from "~/components/FormMessage"; +import { FormWithConfirm } from "~/components/FormWithConfirm"; import { Label } from "~/components/Label"; import { Main } from "~/components/Main"; import { SubmitButton } from "~/components/SubmitButton"; import { useTranslation } from "~/hooks/useTranslation"; -import { requireUser } from "~/modules/auth"; +import { requireUserId } from "~/modules/auth/user.server"; import { notFoundIfFalsy, parseRequestFormData, - type SendouRouteHandle, validate, + type SendouRouteHandle, } from "~/utils/remix"; +import { makeTitle } from "~/utils/strings"; +import { assertUnreachable } from "~/utils/types"; import { mySlugify, navIconUrl, @@ -29,18 +34,13 @@ import { TEAM_SEARCH_PAGE, uploadImagePage, } from "~/utils/urls"; +import { deleteTeam } from "../queries/deleteTeam.server"; import { edit } from "../queries/edit.server"; import { findByIdentifier } from "../queries/findByIdentifier.server"; import { TEAM } from "../team-constants"; import { editTeamSchema, teamParamsSchema } from "../team-schemas.server"; import { isTeamOwner } from "../team-utils"; -import { FormErrors } from "~/components/FormErrors"; -import { FormWithConfirm } from "~/components/FormWithConfirm"; -import { deleteTeam } from "../queries/deleteTeam.server"; -import { Button } from "~/components/Button"; -import { assertUnreachable } from "~/utils/types"; import styles from "../team.css"; -import { makeTitle } from "~/utils/strings"; export const links: LinksFunction = () => { return [{ rel: "stylesheet", href: styles }]; @@ -81,7 +81,7 @@ export const handle: SendouRouteHandle = { }; export const action: ActionFunction = async ({ request, params }) => { - const user = await requireUser(request); + const user = await requireUserId(request); const { customUrl } = teamParamsSchema.parse(params); const team = notFoundIfFalsy(findByIdentifier(customUrl)); @@ -125,7 +125,7 @@ export const action: ActionFunction = async ({ request, params }) => { }; export const loader = async ({ request, params }: LoaderArgs) => { - const user = await requireUser(request); + const user = await requireUserId(request); const { customUrl } = teamParamsSchema.parse(params); const team = notFoundIfFalsy(findByIdentifier(customUrl)); diff --git a/app/features/team/routes/t.$customUrl.roster.tsx b/app/features/team/routes/t.$customUrl.roster.tsx index b7798967c..bde3b316d 100644 --- a/app/features/team/routes/t.$customUrl.roster.tsx +++ b/app/features/team/routes/t.$customUrl.roster.tsx @@ -19,7 +19,8 @@ import { Main } from "~/components/Main"; import { SubmitButton } from "~/components/SubmitButton"; import { useBaseUrl } from "~/hooks/useBaseUrl"; import { useTranslation } from "~/hooks/useTranslation"; -import { requireUser, useUser } from "~/modules/auth"; +import { useUser } from "~/modules/auth"; +import { requireUserId } from "~/modules/auth/user.server"; import type { SendouRouteHandle } from "~/utils/remix"; import { notFoundIfFalsy, parseRequestFormData, validate } from "~/utils/remix"; import { discordFullName, makeTitle } from "~/utils/strings"; @@ -59,7 +60,7 @@ export const meta: MetaFunction = ({ }; export const action: ActionFunction = async ({ request, params }) => { - const user = await requireUser(request); + const user = await requireUserId(request); const { customUrl } = teamParamsSchema.parse(params); const team = notFoundIfFalsy(findByIdentifier(customUrl)); @@ -128,7 +129,7 @@ export const handle: SendouRouteHandle = { }; export const loader = async ({ request, params }: LoaderArgs) => { - const user = await requireUser(request); + const user = await requireUserId(request); const { customUrl } = teamParamsSchema.parse(params); const team = notFoundIfFalsy(findByIdentifier(customUrl)); diff --git a/app/features/team/routes/t.$customUrl.tsx b/app/features/team/routes/t.$customUrl.tsx index 86d8f5427..e32a70986 100644 --- a/app/features/team/routes/t.$customUrl.tsx +++ b/app/features/team/routes/t.$customUrl.tsx @@ -19,9 +19,13 @@ import { WeaponImage } from "~/components/Image"; import { Main } from "~/components/Main"; import { Placement } from "~/components/Placement"; import { useTranslation } from "~/hooks/useTranslation"; -import { requireUser, useUser } from "~/modules/auth"; -import { type SendouRouteHandle, validate } from "~/utils/remix"; -import { notFoundIfFalsy } from "~/utils/remix"; +import { useUser } from "~/modules/auth"; +import { requireUserId } from "~/modules/auth/user.server"; +import { + notFoundIfFalsy, + validate, + type SendouRouteHandle, +} from "~/utils/remix"; import { makeTitle } from "~/utils/strings"; import { editTeamPage, @@ -58,7 +62,7 @@ export const links: LinksFunction = () => { }; export const action: ActionFunction = async ({ request, params }) => { - const user = await requireUser(request); + const user = await requireUserId(request); const { customUrl } = teamParamsSchema.parse(params); const team = notFoundIfFalsy(findByIdentifier(customUrl)); diff --git a/app/features/team/routes/t.tsx b/app/features/team/routes/t.tsx index fc27db440..635edd610 100644 --- a/app/features/team/routes/t.tsx +++ b/app/features/team/routes/t.tsx @@ -22,7 +22,8 @@ import { Input } from "~/components/Input"; import { Main } from "~/components/Main"; import { SubmitButton } from "~/components/SubmitButton"; import { useTranslation } from "~/hooks/useTranslation"; -import { getUser, requireUser, useUser } from "~/modules/auth"; +import { useUser } from "~/modules/auth"; +import { getUserId, requireUserId } from "~/modules/auth/user.server"; import { i18next } from "~/modules/i18n"; import { joinListToNaturalString } from "~/utils/arrays"; import type { SendouRouteHandle } from "~/utils/remix"; @@ -58,7 +59,7 @@ export const links: LinksFunction = () => { }; export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUserId(request); const data = await parseRequestFormData({ request, schema: createTeamSchema, @@ -98,7 +99,7 @@ export const handle: SendouRouteHandle = { }; export const loader = async ({ request }: LoaderArgs) => { - const user = await getUser(request); + const user = await getUserId(request); const t = await i18next.getFixedT(request); const teams = allTeams().sort((teamA, teamB) => { diff --git a/app/features/tournament/routes/to.$id.join.tsx b/app/features/tournament/routes/to.$id.join.tsx index f32751167..0ef102747 100644 --- a/app/features/tournament/routes/to.$id.join.tsx +++ b/app/features/tournament/routes/to.$id.join.tsx @@ -4,7 +4,8 @@ import { Form, useLoaderData, useOutletContext } from "@remix-run/react"; import invariant from "tiny-invariant"; import { SubmitButton } from "~/components/SubmitButton"; import { INVITE_CODE_LENGTH } from "~/constants"; -import { requireUser, useUser } from "~/modules/auth"; +import { useUser } from "~/modules/auth"; +import { requireUserId } from "~/modules/auth/user.server"; import { notFoundIfFalsy, validate } from "~/utils/remix"; import { assertUnreachable } from "~/utils/types"; import { toToolsPage } from "~/utils/urls"; @@ -22,7 +23,7 @@ import type { TournamentToolsLoaderData } from "./to.$id"; // 3) team and captain, can join, tournament disbands IF tournament not checked in export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUserId(request); const url = new URL(request.url); const inviteCode = url.searchParams.get("code"); // xxx: don't throw here diff --git a/app/features/tournament/routes/to.$id.register.tsx b/app/features/tournament/routes/to.$id.register.tsx index b6829be2a..acd33f019 100644 --- a/app/features/tournament/routes/to.$id.register.tsx +++ b/app/features/tournament/routes/to.$id.register.tsx @@ -5,6 +5,7 @@ import type { } from "@remix-run/node"; import { useFetcher, useLoaderData, useOutletContext } from "@remix-run/react"; import clsx from "clsx"; +import * as React from "react"; import { useCopyToClipboard } from "react-use"; import invariant from "tiny-invariant"; import { Alert } from "~/components/Alert"; @@ -16,7 +17,8 @@ import { Input } from "~/components/Input"; import { Label } from "~/components/Label"; import { SubmitButton } from "~/components/SubmitButton"; import { useTranslation } from "~/hooks/useTranslation"; -import { getUser, requireUser, useUser } from "~/modules/auth"; +import { useUser } from "~/modules/auth"; +import { getUserId, requireUserId } from "~/modules/auth/user.server"; import type { RankedModeShort, StageId } from "~/modules/in-game-lists"; import { stageIds } from "~/modules/in-game-lists"; import { rankedModesShort } from "~/modules/in-game-lists/modes"; @@ -46,7 +48,6 @@ import { useSelectCounterpickMapPoolState } from "../tournament-hooks"; import { registerSchema } from "../tournament-schemas.server"; import { idFromParams, resolveOwnedTeam } from "../tournament-utils"; import type { TournamentToolsLoaderData } from "./to.$id"; -import * as React from "react"; export const handle: SendouRouteHandle = { breadcrumb: () => ({ @@ -57,7 +58,7 @@ export const handle: SendouRouteHandle = { }; export const action: ActionFunction = async ({ request, params }) => { - const user = await requireUser(request); + const user = await requireUserId(request); const data = await parseRequestFormData({ request, schema: registerSchema }); const eventId = idFromParams(params); @@ -122,7 +123,7 @@ export const action: ActionFunction = async ({ request, params }) => { }; export const loader = async ({ request, params }: LoaderArgs) => { - const user = await getUser(request); + const user = await getUserId(request); if (!user) return null; diff --git a/app/features/tournament/routes/to.$id.tsx b/app/features/tournament/routes/to.$id.tsx index 35a417723..af56c1b98 100644 --- a/app/features/tournament/routes/to.$id.tsx +++ b/app/features/tournament/routes/to.$id.tsx @@ -9,7 +9,8 @@ import { Main } from "~/components/Main"; import { SubNav, SubNavLink } from "~/components/SubNav"; import { db } from "~/db"; import { useTranslation } from "~/hooks/useTranslation"; -import { getUser, useUser } from "~/modules/auth"; +import { useUser } from "~/modules/auth"; +import { getUserId } from "~/modules/auth/user.server"; import { canAdminCalendarTOTools } from "~/permissions"; import { notFoundIfFalsy, type SendouRouteHandle } from "~/utils/remix"; import { makeTitle } from "~/utils/strings"; @@ -45,7 +46,7 @@ export type TournamentToolsTeam = Unpacked; export type TournamentToolsLoaderData = SerializeFrom; export const loader = async ({ params, request }: LoaderArgs) => { - const user = await getUser(request); + const user = await getUserId(request); const eventId = idFromParams(params); const event = notFoundIfFalsy(findByIdentifier(eventId)); diff --git a/app/modules/auth/routes.server.ts b/app/modules/auth/routes.server.ts index 609af9fbf..426e93a5e 100644 --- a/app/modules/auth/routes.server.ts +++ b/app/modules/auth/routes.server.ts @@ -8,7 +8,7 @@ import { IMPERSONATED_SESSION_KEY, } from "./authenticator.server"; import { authSessionStorage } from "./session.server"; -import { getUser } from "./user.server"; +import { getUserId } from "./user.server"; export const callbackLoader: LoaderFunction = async ({ request }) => { const url = new URL(request.url); @@ -38,7 +38,7 @@ export const logInAction: ActionFunction = async ({ request }) => { }; export const impersonateAction: ActionFunction = async ({ request }) => { - const user = await getUser(request); + const user = await getUserId(request); if (!canPerformAdminActions(user)) { throw new Response(null, { status: 403 }); } diff --git a/app/modules/auth/user.server.ts b/app/modules/auth/user.server.ts index 3a0c94c77..d49840ef4 100644 --- a/app/modules/auth/user.server.ts +++ b/app/modules/auth/user.server.ts @@ -1,8 +1,11 @@ import { db } from "~/db"; +import type { User } from "~/db/types"; import { IMPERSONATED_SESSION_KEY, SESSION_KEY } from "./authenticator.server"; import { authSessionStorage } from "./session.server"; -export async function getUser(request: Request) { +export async function getUserId( + request: Request +): Promise | undefined> { const session = await authSessionStorage.getSession( request.headers.get("Cookie") ); @@ -12,9 +15,25 @@ export async function getUser(request: Request) { if (!userId) return; + return { id: userId }; +} + +export async function getUser(request: Request) { + const userId = (await getUserId(request))?.id; + + if (!userId) return; + return db.users.findByIdentifier(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); diff --git a/app/permissions.ts b/app/permissions.ts index 5525f1387..7ccaa9d78 100644 --- a/app/permissions.ts +++ b/app/permissions.ts @@ -7,16 +7,16 @@ import type { UserWithPlusTier, } from "./db/types"; import { allTruthy } from "./utils/arrays"; -import { ADMIN_DISCORD_ID, LOHI_TOKEN_HEADER_NAME } from "./constants"; +import { ADMIN_ID, LOHI_TOKEN_HEADER_NAME } from "./constants"; import invariant from "tiny-invariant"; import type { ManagersByBadgeId } from "./db/models/badges/queries.server"; import { databaseTimestampToDate } from "./utils/dates"; // TODO: 1) move "root checkers" to one file and utils to one file 2) make utils const for more terseness -type IsAdminUser = Pick; +type IsAdminUser = Pick; export function isAdmin(user?: IsAdminUser) { - return user?.discordId === ADMIN_DISCORD_ID; + return user?.id === ADMIN_ID; } export function canPerformAdminActions(user?: IsAdminUser) { @@ -241,7 +241,7 @@ export function canAccessLohiEndpoint(request: Request) { } interface CanEditBadgeOwnersArgs { - user?: Pick; + user?: Pick; managers: ManagersByBadgeId; } @@ -262,7 +262,7 @@ export function canEditBadgeManagers(user?: IsAdminUser) { } interface CanEditCalendarEventArgs { - user?: Pick; + user?: Pick; event: Pick; } export function canEditCalendarEvent({ @@ -283,7 +283,7 @@ export function canDeleteCalendarEvent({ } interface CanReportCalendarEventWinnersArgs { - user?: Pick; + user?: Pick; event: Pick; startTimes: number[]; } diff --git a/app/routes/admin.tsx b/app/routes/admin.tsx index 8490107ac..0357438d2 100644 --- a/app/routes/admin.tsx +++ b/app/routes/admin.tsx @@ -1,9 +1,9 @@ -import { json, redirect } from "@remix-run/node"; import type { + ActionFunction, LoaderFunction, MetaFunction, - ActionFunction, } from "@remix-run/node"; +import { json, redirect } from "@remix-run/node"; import { Form, useFetcher, @@ -11,22 +11,25 @@ import { useTransition, } from "@remix-run/react"; import * as React from "react"; +import { z } from "zod"; import { Button } from "~/components/Button"; import { Catcher } from "~/components/Catcher"; import { UserCombobox } from "~/components/Combobox"; import { Main } from "~/components/Main"; -import { requireUser } from "~/modules/auth"; -import { getUser, isImpersonating } from "~/modules/auth/user.server"; +import { db } from "~/db"; +import { + getUserId, + isImpersonating, + requireUserId, +} from "~/modules/auth/user.server"; import { canPerformAdminActions } from "~/permissions"; import { parseRequestFormData, - type SendouRouteHandle, validate, + type SendouRouteHandle, } from "~/utils/remix"; import { makeTitle } from "~/utils/strings"; import { impersonateUrl, SEED_URL, STOP_IMPERSONATING_URL } from "~/utils/urls"; -import { db } from "~/db"; -import { z } from "zod"; import { actualNumber } from "~/utils/zod"; export const meta: MetaFunction = () => { @@ -45,7 +48,7 @@ export const action: ActionFunction = async ({ request }) => { request, schema: adminActionSchema, }); - const user = await requireUser(request); + const user = await requireUserId(request); validate(canPerformAdminActions(user)); @@ -62,7 +65,7 @@ interface AdminPageLoaderData { } export const loader: LoaderFunction = async ({ request }) => { - const user = await getUser(request); + const user = await getUserId(request); if (!canPerformAdminActions(user)) { return redirect("/"); diff --git a/app/routes/badges/$id/edit.tsx b/app/routes/badges/$id/edit.tsx index 9c64e2719..09571ae73 100644 --- a/app/routes/badges/$id/edit.tsx +++ b/app/routes/badges/$id/edit.tsx @@ -1,24 +1,25 @@ -import * as React from "react"; -import { Form, useMatches, useOutletContext } from "@remix-run/react"; -import { Button, LinkButton } from "~/components/Button"; -import { Dialog } from "~/components/Dialog"; -import { atOrError } from "~/utils/arrays"; -import type { BadgeDetailsContext, BadgeDetailsLoaderData } from "../$id"; -import { discordFullName } from "~/utils/strings"; -import { UserCombobox } from "~/components/Combobox"; -import { TrashIcon } from "~/components/icons/Trash"; -import { z } from "zod"; -import { actualNumber, noDuplicates, safeJSONParse, id } from "~/utils/zod"; -import { redirect } from "@remix-run/node"; import type { ActionFunction } from "@remix-run/node"; -import { requireUser, useUser } from "~/modules/auth"; -import { parseRequestFormData, validate } from "~/utils/remix"; -import { canEditBadgeManagers, canEditBadgeOwners } from "~/permissions"; -import { assertUnreachable } from "~/utils/types"; +import { redirect } from "@remix-run/node"; +import { Form, useMatches, useOutletContext } from "@remix-run/react"; +import * as React from "react"; +import { z } from "zod"; +import { Button, LinkButton } from "~/components/Button"; +import { UserCombobox } from "~/components/Combobox"; +import { Dialog } from "~/components/Dialog"; +import { TrashIcon } from "~/components/icons/Trash"; +import { Label } from "~/components/Label"; import { db } from "~/db"; import type { User } from "~/db/types"; +import { useUser } from "~/modules/auth"; +import { requireUserId } from "~/modules/auth/user.server"; +import { canEditBadgeManagers, canEditBadgeOwners } from "~/permissions"; +import { atOrError } from "~/utils/arrays"; +import { parseRequestFormData, validate } from "~/utils/remix"; +import { discordFullName } from "~/utils/strings"; +import { assertUnreachable } from "~/utils/types"; import { badgePage } from "~/utils/urls"; -import { Label } from "~/components/Label"; +import { actualNumber, id, noDuplicates, safeJSONParse } from "~/utils/zod"; +import type { BadgeDetailsContext, BadgeDetailsLoaderData } from "../$id"; const editBadgeActionSchema = z.union([ z.object({ @@ -37,7 +38,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 requireUserId(request); switch (data._action) { case "MANAGERS": { diff --git a/app/routes/calendar/$id/index.tsx b/app/routes/calendar/$id/index.tsx index 71190a1cd..98a682c8b 100644 --- a/app/routes/calendar/$id/index.tsx +++ b/app/routes/calendar/$id/index.tsx @@ -1,27 +1,31 @@ import type { ActionFunction, SerializeFrom } from "@remix-run/node"; -import { redirect } from "@remix-run/node"; import { json, - type MetaFunction, + redirect, type LinksFunction, type LoaderArgs, + type MetaFunction, } from "@remix-run/node"; import { useLoaderData } from "@remix-run/react"; import { Link } from "@remix-run/react/dist/components"; import clsx from "clsx"; import * as React from "react"; -import { useTranslation } from "~/hooks/useTranslation"; import { z } from "zod"; import { Avatar } from "~/components/Avatar"; import { Button, LinkButton } from "~/components/Button"; +import { FormWithConfirm } from "~/components/FormWithConfirm"; import { Image } from "~/components/Image"; import { Main } from "~/components/Main"; +import { MapPoolStages } from "~/components/MapPoolSelector"; import { Placement } from "~/components/Placement"; import { Section } from "~/components/Section"; import { db } from "~/db"; import { useIsMounted } from "~/hooks/useIsMounted"; -import { requireUser, useUser } from "~/modules/auth"; +import { useTranslation } from "~/hooks/useTranslation"; +import { useUser } from "~/modules/auth"; +import { requireUserId } from "~/modules/auth/user.server"; import { i18next } from "~/modules/i18n"; +import { MapPool } from "~/modules/map-pool-serializer"; import { canDeleteCalendarEvent, canEditCalendarEvent, @@ -47,13 +51,10 @@ import { userPage, } from "~/utils/urls"; import { actualNumber, id } from "~/utils/zod"; -import { MapPoolStages } from "~/components/MapPoolSelector"; import { Tags } from "../components/Tags"; -import { MapPool } from "~/modules/map-pool-serializer"; -import { FormWithConfirm } from "~/components/FormWithConfirm"; export const action: ActionFunction = async ({ params, request }) => { - const user = await requireUser(request); + const user = await requireUserId(request); const parsedParams = z .object({ id: z.preprocess(actualNumber, id) }) .parse(params); diff --git a/app/routes/calendar/$id/report-winners.tsx b/app/routes/calendar/$id/report-winners.tsx index 5e357c1ed..20d8f1289 100644 --- a/app/routes/calendar/$id/report-winners.tsx +++ b/app/routes/calendar/$id/report-winners.tsx @@ -1,17 +1,25 @@ import type { SerializeFrom } from "@remix-run/node"; import { - type ActionFunction, - type LoaderArgs, json, redirect, + type ActionFunction, + type LoaderArgs, } from "@remix-run/node"; import { Form, useLoaderData } from "@remix-run/react"; +import clsx from "clsx"; +import * as React from "react"; import { z } from "zod"; +import { Button } from "~/components/Button"; +import { UserCombobox } from "~/components/Combobox"; +import { FormErrors } from "~/components/FormErrors"; +import { FormMessage } from "~/components/FormMessage"; import { Label } from "~/components/Label"; import { Main } from "~/components/Main"; import { CALENDAR_EVENT_RESULT } from "~/constants"; import { db } from "~/db"; -import { requireUser } from "~/modules/auth"; +import type { User } from "~/db/types"; +import { useTranslation } from "~/hooks/useTranslation"; +import { requireUserId } from "~/modules/auth/user.server"; import { canReportCalendarEventWinners } from "~/permissions"; import { notFoundIfFalsy, @@ -19,17 +27,9 @@ import { validate, type SendouRouteHandle, } from "~/utils/remix"; -import { actualNumber, id, safeJSONParse, toArray } from "~/utils/zod"; -import * as React from "react"; -import type { User } from "~/db/types"; -import { Button } from "~/components/Button"; -import clsx from "clsx"; -import { UserCombobox } from "~/components/Combobox"; -import { FormMessage } from "~/components/FormMessage"; -import { FormErrors } from "~/components/FormErrors"; import type { Unpacked } from "~/utils/types"; import { calendarEventPage } from "~/utils/urls"; -import { useTranslation } from "~/hooks/useTranslation"; +import { actualNumber, id, safeJSONParse, toArray } from "~/utils/zod"; const playersSchema = z .array( @@ -97,7 +97,7 @@ const reportWinnersParamsSchema = z.object({ }); export const action: ActionFunction = async ({ request, params }) => { - const user = await requireUser(request); + const user = await requireUserId(request); const parsedParams = reportWinnersParamsSchema.parse(params); const parsedInput = await safeParseRequestFormData({ request, @@ -142,7 +142,7 @@ export const handle: SendouRouteHandle = { export const loader = async ({ request, params }: LoaderArgs) => { const parsedParams = reportWinnersParamsSchema.parse(params); - const user = await requireUser(request); + const user = await requireUserId(request); const event = notFoundIfFalsy(db.calendarEvents.findById(parsedParams.id)); validate( diff --git a/app/routes/calendar/index.tsx b/app/routes/calendar/index.tsx index 2688bbfca..4e27500c8 100644 --- a/app/routes/calendar/index.tsx +++ b/app/routes/calendar/index.tsx @@ -5,13 +5,14 @@ import clsx from "clsx"; import { addDays, addMonths, subDays, subMonths } from "date-fns"; import React from "react"; import { Flipped, Flipper } from "react-flip-toolkit"; -import { useTranslation } from "~/hooks/useTranslation"; import { z } from "zod"; import { Alert } from "~/components/Alert"; import { LinkButton } from "~/components/Button"; import { db } from "~/db"; import { useIsMounted } from "~/hooks/useIsMounted"; -import { getUser, useUser } from "~/modules/auth"; +import { useTranslation } from "~/hooks/useTranslation"; +import { useUser } from "~/modules/auth"; +import { getUserId } from "~/modules/auth/user.server"; import { i18next } from "~/modules/i18n"; import styles from "~/styles/calendar.css"; import { joinListToNaturalString } from "~/utils/arrays"; @@ -21,6 +22,7 @@ import { dateToWeekNumber, weekNumberToDate, } from "~/utils/dates"; +import { type SendouRouteHandle } from "~/utils/remix"; import { discordFullName, makeTitle } from "~/utils/strings"; import type { Unpacked } from "~/utils/types"; import { @@ -31,7 +33,6 @@ import { } from "~/utils/urls"; import { actualNumber } from "~/utils/zod"; import { Tags } from "./components/Tags"; -import { type SendouRouteHandle } from "~/utils/remix"; export const links: LinksFunction = () => { return [{ rel: "stylesheet", href: styles }]; @@ -67,7 +68,7 @@ const loaderSearchParamsSchema = z.object({ }); export const loader = async ({ request }: LoaderArgs) => { - const user = await getUser(request); + const user = await getUserId(request); const t = await i18next.getFixedT(request); const url = new URL(request.url); const parsedParams = loaderSearchParamsSchema.safeParse({ diff --git a/app/routes/calendar/new.tsx b/app/routes/calendar/new.tsx index 57c3b8c2d..73614a8fa 100644 --- a/app/routes/calendar/new.tsx +++ b/app/routes/calendar/new.tsx @@ -10,7 +10,6 @@ import { import { Form, useLoaderData } from "@remix-run/react"; import clsx from "clsx"; import * as React from "react"; -import { useTranslation } from "~/hooks/useTranslation"; import { z } from "zod"; import type { AlertVariation } from "~/components/Alert"; import { Alert } from "~/components/Alert"; @@ -18,28 +17,33 @@ import { Badge } from "~/components/Badge"; import { Button } from "~/components/Button"; import { DateInput } from "~/components/DateInput"; import { FormMessage } from "~/components/FormMessage"; +import { CrossIcon } from "~/components/icons/Cross"; import { TrashIcon } from "~/components/icons/Trash"; import { Input } from "~/components/Input"; import { Label } from "~/components/Label"; import { Main } from "~/components/Main"; import { MapPoolSelector } from "~/components/MapPoolSelector"; import { RequiredHiddenInput } from "~/components/RequiredHiddenInput"; +import { SubmitButton } from "~/components/SubmitButton"; import { Toggle } from "~/components/Toggle"; import { CALENDAR_EVENT } from "~/constants"; import { db } from "~/db"; import type { Badge as BadgeType, CalendarEventTag } from "~/db/types"; import { useIsMounted } from "~/hooks/useIsMounted"; -import { requireUser, useUser } from "~/modules/auth"; +import { useTranslation } from "~/hooks/useTranslation"; +import { useUser } from "~/modules/auth"; +import { requireUserId } from "~/modules/auth/user.server"; import { i18next } from "~/modules/i18n"; import { MapPool } from "~/modules/map-pool-serializer"; import { canEditCalendarEvent, canEnableTOTools } from "~/permissions"; import calendarNewStyles from "~/styles/calendar-new.css"; import mapsStyles from "~/styles/maps.css"; +import { isDefined } from "~/utils/arrays"; import { - dateToDatabaseTimestamp, databaseTimestampToDate, - getDateWithHoursOffset, + dateToDatabaseTimestamp, getDateAtNextFullHour, + getDateWithHoursOffset, } from "~/utils/dates"; import { badRequestIfFalsy, @@ -61,9 +65,6 @@ import { toArray, } from "~/utils/zod"; import { Tags } from "./components/Tags"; -import { isDefined } from "~/utils/arrays"; -import { CrossIcon } from "~/components/icons/Cross"; -import { SubmitButton } from "~/components/SubmitButton"; const MIN_DATE = new Date(Date.UTC(2015, 4, 28)); @@ -130,7 +131,7 @@ const newCalendarEventActionSchema = z.object({ }); export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUserId(request); const data = await parseRequestFormData({ request, schema: newCalendarEventActionSchema, @@ -191,7 +192,7 @@ export const handle: SendouRouteHandle = { export const loader = async ({ request }: LoaderArgs) => { const t = await i18next.getFixedT(request); - const user = await requireUser(request); + const user = await requireUserId(request); const url = new URL(request.url); const eventId = Number(url.searchParams.get("eventId")); diff --git a/app/routes/maps.tsx b/app/routes/maps.tsx index d7179bebb..5336687d6 100644 --- a/app/routes/maps.tsx +++ b/app/routes/maps.tsx @@ -5,17 +5,20 @@ import type { SerializeFrom, } from "@remix-run/node"; import type { ShouldReloadFunction } from "@remix-run/react"; -import { Link } from "@remix-run/react"; -import { useLoaderData, useSearchParams } from "@remix-run/react"; +import { Link, useLoaderData, useSearchParams } from "@remix-run/react"; import * as React from "react"; -import { useTranslation } from "~/hooks/useTranslation"; import { useCopyToClipboard } from "react-use"; import invariant from "tiny-invariant"; import { Button } from "~/components/Button"; +import { EditIcon } from "~/components/icons/Edit"; import { Label } from "~/components/Label"; import { Main } from "~/components/Main"; +import { MapPoolSelector, MapPoolStages } from "~/components/MapPoolSelector"; import { Toggle } from "~/components/Toggle"; import { db } from "~/db"; +import type { CalendarEvent } from "~/db/types"; +import { useTranslation } from "~/hooks/useTranslation"; +import { getUserId } from "~/modules/auth/user.server"; import { i18next } from "~/modules/i18n"; import { stageIds, type ModeWithStage } from "~/modules/in-game-lists"; import { @@ -25,6 +28,7 @@ import { } from "~/modules/map-list-generator"; import { MapPool } from "~/modules/map-pool-serializer"; import styles from "~/styles/maps.css"; +import { type SendouRouteHandle } from "~/utils/remix"; import { makeTitle } from "~/utils/strings"; import { calendarEventPage, @@ -32,11 +36,6 @@ import { MAPS_URL, navIconUrl, } from "~/utils/urls"; -import { type SendouRouteHandle } from "~/utils/remix"; -import { MapPoolSelector, MapPoolStages } from "~/components/MapPoolSelector"; -import { EditIcon } from "~/components/icons/Edit"; -import { getUser } from "~/modules/auth"; -import type { CalendarEvent } from "~/db/types"; const AMOUNT_OF_MAPS_IN_MAP_LIST = stageIds.length * 2; @@ -71,7 +70,7 @@ export const handle: SendouRouteHandle = { }; export const loader = async ({ request }: LoaderArgs) => { - const user = await getUser(request); + const user = await getUserId(request); const url = new URL(request.url); const calendarEventId = url.searchParams.get("eventId"); const t = await i18next.getFixedT(request); diff --git a/app/routes/patrons.tsx b/app/routes/patrons.tsx index aa7d9094a..b30afb884 100644 --- a/app/routes/patrons.tsx +++ b/app/routes/patrons.tsx @@ -1,12 +1,12 @@ import type { ActionFunction, LoaderArgs } from "@remix-run/node"; import { db } from "~/db"; -import { getUser } from "~/modules/auth"; +import { getUserId } from "~/modules/auth/user.server"; import { updatePatreonData } from "~/modules/patreon"; import { canAccessLohiEndpoint, canPerformAdminActions } from "~/permissions"; import { validate } from "~/utils/remix"; export const action: ActionFunction = async ({ request }) => { - const user = await getUser(request); + const user = await getUserId(request); if (!canPerformAdminActions(user) && !canAccessLohiEndpoint(request)) { throw new Response("Not authorized", { status: 403 }); diff --git a/app/routes/u.$identifier/builds/index.tsx b/app/routes/u.$identifier/builds/index.tsx index 48ff86e5a..6081a1ac8 100644 --- a/app/routes/u.$identifier/builds/index.tsx +++ b/app/routes/u.$identifier/builds/index.tsx @@ -7,7 +7,8 @@ import { LinkButton } from "~/components/Button"; import { BUILD } from "~/constants"; import { db } from "~/db"; import { useTranslation } from "~/hooks/useTranslation"; -import { getUser, requireUser, useUser } from "~/modules/auth"; +import { useUser } from "~/modules/auth"; +import { getUserId, requireUserId } from "~/modules/auth/user.server"; import { atOrError } from "~/utils/arrays"; import { notFoundIfFalsy, @@ -23,7 +24,7 @@ const buildsActionSchema = z.object({ }); export const action: ActionFunction = async ({ request }) => { - const user = await requireUser(request); + const user = await requireUserId(request); const data = await parseRequestFormData({ request, schema: buildsActionSchema, @@ -47,7 +48,7 @@ export const handle: SendouRouteHandle = { }; export const loader = async ({ params, request }: LoaderArgs) => { - const loggedInUser = await getUser(request); + const loggedInUser = await getUserId(request); const { identifier } = userParamsSchema.parse(params); const user = notFoundIfFalsy(db.users.findByIdentifier(identifier)); diff --git a/app/routes/u.$identifier/builds/new.tsx b/app/routes/u.$identifier/builds/new.tsx index ee2d28482..0688f39ec 100644 --- a/app/routes/u.$identifier/builds/new.tsx +++ b/app/routes/u.$identifier/builds/new.tsx @@ -19,6 +19,7 @@ import { db } from "~/db"; import type { GearType } from "~/db/types"; import { useTranslation } from "~/hooks/useTranslation"; import { requireUser } from "~/modules/auth"; +import { requireUserId } from "~/modules/auth/user.server"; import { clothesGearIds, headGearIds, @@ -166,7 +167,7 @@ const newBuildLoaderParamsSchema = z.object({ }); export const loader = async ({ request }: LoaderArgs) => { - const user = await requireUser(request); + const user = await requireUserId(request); const url = new URL(request.url); const params = newBuildLoaderParamsSchema.safeParse( diff --git a/app/routes/u.$identifier/edit.tsx b/app/routes/u.$identifier/edit.tsx index ac320d2be..de75ffda9 100644 --- a/app/routes/u.$identifier/edit.tsx +++ b/app/routes/u.$identifier/edit.tsx @@ -23,7 +23,7 @@ import { USER } from "~/constants"; import { db } from "~/db"; import { type User } from "~/db/types"; import { useTranslation } from "~/hooks/useTranslation"; -import { requireUser } from "~/modules/auth"; +import { requireUserId } from "~/modules/auth/user.server"; import { i18next } from "~/modules/i18n"; import { mainWeaponIds, type MainWeaponId } from "~/modules/in-game-lists"; import styles from "~/styles/u-edit.css"; @@ -145,7 +145,7 @@ export const action: ActionFunction = async ({ request }) => { const { inGameNameText, inGameNameDiscriminator, ...data } = parsedInput.data; - const user = await requireUser(request); + const user = await requireUserId(request); try { const editedUser = db.users.updateProfile({ @@ -173,7 +173,7 @@ export const action: ActionFunction = async ({ request }) => { export const loader = async ({ request, params }: LoaderArgs) => { const locale = await i18next.getLocale(request); - const user = await requireUser(request); + const user = await requireUserId(request); const { identifier } = userParamsSchema.parse(params); const userToBeEdited = notFoundIfFalsy(db.users.findByIdentifier(identifier)); if (user.id !== userToBeEdited.id) { diff --git a/app/utils/users.ts b/app/utils/users.ts index 38fc1e1bf..42afbaef8 100644 --- a/app/utils/users.ts +++ b/app/utils/users.ts @@ -2,7 +2,7 @@ import type { User } from "~/db/types"; import { isAdmin } from "~/permissions"; export function isAtLeastFiveDollarTierPatreon( - user?: Pick + user?: Pick ) { if (!user) return false;