tsconfig ES2020

This commit is contained in:
Kalle 2023-11-25 14:04:26 +02:00
parent 0ba2b4c789
commit 7a613bd1da
6 changed files with 73 additions and 6 deletions

View File

@ -0,0 +1,4 @@
import { userDiscordIdIsAged } from "~/utils/users";
export const canAddNewEvent = (user: { discordId: string }) =>
userDiscordIdIsAged(user);

View File

@ -34,7 +34,7 @@ import type {
import { useIsMounted } from "~/hooks/useIsMounted";
import { useTranslation } from "~/hooks/useTranslation";
import { useUser } from "~/features/auth/core";
import { requireUserId } from "~/features/auth/core/user.server";
import { requireUser } from "~/features/auth/core/user.server";
import { i18next } from "~/modules/i18n";
import { MapPool } from "~/features/map-list-generator/core/map-pool";
import { canEditCalendarEvent, canEnableTOTools } from "~/permissions";
@ -71,6 +71,7 @@ import type { RankedModeShort } from "~/modules/in-game-lists";
import { rankedModesShort } from "~/modules/in-game-lists/modes";
import * as BadgeRepository from "~/features/badges/BadgeRepository.server";
import * as CalendarRepository from "~/features/calendar/CalendarRepository.server";
import { canAddNewEvent } from "../calendar-utils";
const MIN_DATE = new Date(Date.UTC(2015, 4, 28));
@ -136,12 +137,14 @@ const newCalendarEventActionSchema = z.object({
});
export const action: ActionFunction = async ({ request }) => {
const user = await requireUserId(request);
const user = await requireUser(request);
const data = await parseRequestFormData({
request,
schema: newCalendarEventActionSchema,
});
validate(canAddNewEvent(user), "Not authorized", 401);
const commonArgs = {
name: data.name,
description: data.description,
@ -207,9 +210,11 @@ export const handle: SendouRouteHandle = {
export const loader = async ({ request }: LoaderArgs) => {
const t = await i18next.getFixedT(request);
const user = await requireUserId(request);
const user = await requireUser(request);
const url = new URL(request.url);
validate(canAddNewEvent(user), "Not authorized", 401);
const eventId = Number(url.searchParams.get("eventId"));
const eventToEdit = Number.isNaN(eventId)
? undefined

View File

@ -44,6 +44,7 @@ import { Tags } from "../components/Tags";
import { Divider } from "~/components/Divider";
import { UsersIcon } from "~/components/icons/Users";
import * as CalendarRepository from "../CalendarRepository.server";
import { canAddNewEvent } from "../calendar-utils";
export const links: LinksFunction = () => {
return [{ rel: "stylesheet", href: styles }];
@ -176,7 +177,7 @@ export default function CalendarPage() {
<WeekLinks />
<EventsToReport />
<div className="stack md">
{user && (
{user && canAddNewEvent(user) && (
<LinkButton to="new" className="calendar__add-new-button" size="tiny">
{t("addNew")}
</LinkButton>

View File

@ -1,8 +1,10 @@
import { suite } from "uvu";
import * as assert from "uvu/assert";
import { queryToUserIdentifier } from "./users";
import { queryToUserIdentifier, userDiscordIdIsAged } from "./users";
import MockDate from "mockdate";
const QueryToUserIdentifier = suite("queryToUserIdentifier()");
const UserDiscordIdIsAged = suite("userDiscordIdIsAged()");
QueryToUserIdentifier("returns null if no match", () => {
assert.equal(queryToUserIdentifier("foo"), null);
@ -41,4 +43,32 @@ QueryToUserIdentifier("gets id", () => {
});
});
UserDiscordIdIsAged.before.each(() => {
MockDate.set(new Date("2023-11-25T00:00:00.000Z"));
});
UserDiscordIdIsAged.after.each(() => {
MockDate.reset();
});
UserDiscordIdIsAged("returns false if discord id is not aged", () => {
assert.equal(
userDiscordIdIsAged({ discordId: "1177730652641181871" }),
false,
);
});
UserDiscordIdIsAged("returns true if discord id is aged", () => {
assert.equal(userDiscordIdIsAged({ discordId: "79237403620945920" }), true);
});
UserDiscordIdIsAged("throws error if discord id missing", () => {
assert.throws(() => userDiscordIdIsAged({ discordId: "" }));
});
UserDiscordIdIsAged("throws error if discord id too short", () => {
assert.throws(() => userDiscordIdIsAged({ discordId: "1234" }));
});
QueryToUserIdentifier.run();
UserDiscordIdIsAged.run();

View File

@ -38,3 +38,30 @@ export function queryToUserIdentifier(
return null;
}
// snowflake logic from https://github.dev/vegeta897/snow-stamp/blob/main/src/util.js
const DISCORD_EPOCH = 1420070400000;
// Converts a snowflake ID string into a JS Date object using the provided epoch (in ms), or Discord's epoch if not provided
function convertSnowflakeToDate(snowflake: string) {
// Convert snowflake to BigInt to extract timestamp bits
// https://discord.com/developers/docs/reference#snowflakes
const milliseconds = BigInt(snowflake) >> 22n;
return new Date(Number(milliseconds) + DISCORD_EPOCH);
}
const AGED_CRITERIA = 1000 * 60 * 60 * 24 * 30 * 3; // 3 months
export function userDiscordIdIsAged(user: { discordId: string }) {
// types should catch this but since this is a permission related
// code playing it safe
if (!user.discordId) {
throw new Error("No discord id");
}
if (user.discordId.length < DISCORD_ID_MIN_LENGTH) {
throw new Error("Not a valid discord id");
}
const timestamp = convertSnowflakeToDate(user.discordId).getTime();
return Date.now() - timestamp > AGED_CRITERIA;
}

View File

@ -8,7 +8,7 @@
"jsx": "react-jsx",
"moduleResolution": "node",
"resolveJsonModule": true,
"target": "ES2019",
"target": "ES2020",
"strict": true,
"baseUrl": ".",
"paths": {