mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-10 13:15:47 -05:00
Introduce getUserId and requireUserId
Idea is to avoid doing DB calls when all we need is just the user's id.
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<TournamentToolsLoaderData["teams"]>;
|
||||
export type TournamentToolsLoaderData = SerializeFrom<typeof loader>;
|
||||
|
||||
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));
|
||||
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
@@ -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<Pick<User, "id"> | 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);
|
||||
|
||||
|
||||
@@ -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<User, "discordId">;
|
||||
type IsAdminUser = Pick<User, "id">;
|
||||
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, "id" | "discordId">;
|
||||
user?: Pick<User, "id">;
|
||||
managers: ManagersByBadgeId;
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ export function canEditBadgeManagers(user?: IsAdminUser) {
|
||||
}
|
||||
|
||||
interface CanEditCalendarEventArgs {
|
||||
user?: Pick<User, "id" | "discordId">;
|
||||
user?: Pick<User, "id">;
|
||||
event: Pick<CalendarEvent, "authorId">;
|
||||
}
|
||||
export function canEditCalendarEvent({
|
||||
@@ -283,7 +283,7 @@ export function canDeleteCalendarEvent({
|
||||
}
|
||||
|
||||
interface CanReportCalendarEventWinnersArgs {
|
||||
user?: Pick<User, "id" | "discordId">;
|
||||
user?: Pick<User, "id">;
|
||||
event: Pick<CalendarEvent, "authorId">;
|
||||
startTimes: number[];
|
||||
}
|
||||
|
||||
@@ -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("/");
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { User } from "~/db/types";
|
||||
import { isAdmin } from "~/permissions";
|
||||
|
||||
export function isAtLeastFiveDollarTierPatreon(
|
||||
user?: Pick<User, "patronTier" | "discordId">
|
||||
user?: Pick<User, "patronTier" | "id">
|
||||
) {
|
||||
if (!user) return false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user