Skip to content

Commit

Permalink
Add TO permissions via UI
Browse files Browse the repository at this point in the history
  • Loading branch information
Sendouc committed Aug 25, 2024
1 parent d753365 commit ba12a61
Show file tree
Hide file tree
Showing 13 changed files with 85 additions and 23 deletions.
7 changes: 7 additions & 0 deletions app/db/seed/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ const basicSeeds = (variation?: SeedVariation | null) => [
adminUser,
makeAdminPatron,
makeAdminVideoAdder,
makeAdminTournamentOrganizer,
nzapUser,
users,
fixAdminId,
Expand Down Expand Up @@ -251,6 +252,12 @@ function makeAdminVideoAdder() {
sql.prepare(`update "User" set "isVideoAdder" = 1 where id = 1`).run();
}

function makeAdminTournamentOrganizer() {
sql
.prepare(`update "User" set "isTournamentOrganizer" = 1 where id = 1`)
.run();
}

function adminUserWeaponPool() {
for (const [i, weaponSplId] of [200, 1100, 2000, 4000].entries()) {
sql
Expand Down
1 change: 1 addition & 0 deletions app/db/tables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,7 @@ export interface User {
inGameName: string | null;
isArtist: Generated<number | null>;
isVideoAdder: Generated<number | null>;
isTournamentOrganizer: Generated<number | null>;
languages: string | null;
motionSens: number | null;
patronSince: number | null;
Expand Down
8 changes: 8 additions & 0 deletions app/features/admin/AdminRepository.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ export function makeVideoAdderByUserId(userId: number) {
.execute();
}

export function makeTournamentOrganizerByUserId(userId: number) {
return db
.updateTable("User")
.set({ isTournamentOrganizer: 1 })
.where("User.id", "=", userId)
.execute();
}

export async function linkUserAndPlayer({
userId,
playerId,
Expand Down
10 changes: 10 additions & 0 deletions app/features/admin/actions/admin.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ export const action = async ({ request }: ActionFunctionArgs) => {
await AdminRepository.makeVideoAdderByUserId(data.user);
break;
}
case "TOURNAMENT_ORGANIZER": {
validate(isMod(user), "Mod needed", 401);

await AdminRepository.makeTournamentOrganizerByUserId(data.user);
break;
}
case "LINK_PLAYER": {
validate(isMod(user), "Mod needed", 401);

Expand Down Expand Up @@ -155,6 +161,10 @@ export const adminActionSchema = z.union([
_action: _action("VIDEO_ADDER"),
user: z.preprocess(actualNumber, z.number().positive()),
}),
z.object({
_action: _action("TOURNAMENT_ORGANIZER"),
user: z.preprocess(actualNumber, z.number().positive()),
}),
z.object({
_action: _action("ARTIST"),
user: z.preprocess(actualNumber, z.number().positive()),
Expand Down
26 changes: 26 additions & 0 deletions app/features/admin/routes/admin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export default function AdminPage() {
{isMod(user) ? <LinkPlayer /> : null}
{isMod(user) ? <GiveArtist /> : null}
{isMod(user) ? <GiveVideoAdder /> : null}
{isMod(user) ? <GiveTournamentOrganizer /> : null}
{isMod(user) ? <UpdateFriendCode /> : null}

{process.env.NODE_ENV !== "production" || isAdmin(user) ? (
Expand Down Expand Up @@ -202,6 +203,31 @@ function GiveVideoAdder() {
);
}

function GiveTournamentOrganizer() {
const fetcher = useFetcher();

return (
<fetcher.Form className="stack md" method="post">
<h2>Give tournament organizer</h2>
<div className="stack horizontal md">
<div>
<label>User</label>
<UserSearch inputName="user" />
</div>
</div>
<div className="stack horizontal md">
<SubmitButton
type="submit"
_action="TOURNAMENT_ORGANIZER"
state={fetcher.state}
>
Add as tournament organizer
</SubmitButton>
</div>
</fetcher.Form>
);
}

function UpdateFriendCode() {
const fetcher = useFetcher();

Expand Down
9 changes: 4 additions & 5 deletions app/features/calendar/actions/calendar.new.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,7 @@ import {
canAddNewEvent,
regClosesAtDate,
} from "../calendar-utils";
import {
canCreateTournament,
formValuesToBracketProgression,
} from "../calendar-utils.server";
import { formValuesToBracketProgression } from "../calendar-utils.server";

export const action: ActionFunction = async ({ request }) => {
const user = await requireUser(request);
Expand Down Expand Up @@ -97,7 +94,9 @@ export const action: ActionFunction = async ({ request }) => {
}
: undefined,
autoValidateAvatar: Boolean(user.patronTier),
toToolsEnabled: canCreateTournament(user) ? Number(data.toToolsEnabled) : 0,
toToolsEnabled: user.isTournamentOrganizer
? Number(data.toToolsEnabled)
: 0,
toToolsMode:
rankedModesShort.find((mode) => mode === data.toToolsMode) ?? null,
bracketProgression: formValuesToBracketProgression(data),
Expand Down
9 changes: 0 additions & 9 deletions app/features/calendar/calendar-utils.server.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
import type { z } from "zod";
import type { TournamentSettings } from "~/db/tables";
import { isAdmin } from "~/permissions";
import { BRACKET_NAMES } from "../tournament/tournament-constants";
import type { newCalendarEventActionSchema } from "./actions/calendar.new.server";
import { validateFollowUpBrackets } from "./calendar-utils";

const usersWithTournamentPerms =
process.env.TOURNAMENT_PERMS?.split(",").map(Number) ?? [];
export function canCreateTournament(user?: { id: number }) {
if (!user) return false;

return isAdmin(user) || usersWithTournamentPerms.includes(user.id);
}

export function formValuesToBracketProgression(
args: z.infer<typeof newCalendarEventActionSchema>,
) {
Expand Down
8 changes: 2 additions & 6 deletions app/features/calendar/loaders/calendar.new.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { validate } from "~/utils/remix";
import { makeTitle } from "~/utils/strings";
import { tournamentBracketsPage } from "~/utils/urls";
import { canAddNewEvent } from "../calendar-utils";
import { canCreateTournament } from "../calendar-utils.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
const t = await i18next.getFixedT(request);
Expand Down Expand Up @@ -72,23 +71,20 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
);
}

const userCanCreateTournament = canCreateTournament(user);

return json({
managedBadges: await BadgeRepository.findManagedByUserId(user.id),
recentEventsWithMapPools:
await CalendarRepository.findRecentMapPoolsByAuthorId(user.id),
eventToEdit: canEditEvent ? eventToEdit : undefined,
eventToCopy:
userCanCreateTournament && !eventToEdit
user.isTournamentOrganizer && !eventToEdit
? await eventWithTournament("copyEventId")
: undefined,
recentTournaments:
userCanCreateTournament && !eventToEdit
user.isTournamentOrganizer && !eventToEdit
? await CalendarRepository.findRecentTournamentsByAuthorId(user.id)
: undefined,
title: makeTitle([canEditEvent ? "Edit" : "New", t("pages.calendar")]),
canCreateTournament: userCanCreateTournament,
organizations: await TournamentOrganizationRepository.findByOrganizerUserId(
user.id,
),
Expand Down
7 changes: 4 additions & 3 deletions app/features/calendar/routes/calendar.new.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { CrossIcon } from "~/components/icons/Cross";
import { TrashIcon } from "~/components/icons/Trash";
import type { Tables } from "~/db/tables";
import type { Badge as BadgeType, CalendarEventTag } from "~/db/types";
import { useUser } from "~/features/auth/core/user";
import { MapPool } from "~/features/map-list-generator/core/map-pool";
import {
BRACKET_NAMES,
Expand Down Expand Up @@ -133,7 +134,6 @@ function TemplateTournamentForm() {

function EventForm() {
const fetcher = useFetcher();
const data = useLoaderData<typeof loader>();
const { t } = useTranslation();
const { eventToEdit, eventToCopy } = useLoaderData<typeof loader>();
const baseEvent = useBaseEvent();
Expand All @@ -142,6 +142,7 @@ function EventForm() {
);
const ref = React.useRef<HTMLFormElement>(null);
const [avatarImg, setAvatarImg] = React.useState<File | null>(null);
const user = useUser();

const handleSubmit = () => {
const formData = new FormData(ref.current!);
Expand Down Expand Up @@ -179,12 +180,12 @@ function EventForm() {
value={eventToCopy.tournamentId}
/>
) : null}
{data.canCreateTournament && !eventToEdit && (
{user?.isTournamentOrganizer && !eventToEdit ? (
<TournamentEnabler
checked={isTournament}
setChecked={setIsTournament}
/>
)}
) : null}
<NameInput />
<DescriptionTextarea supportsMarkdown={isTournament} />
<OrganizationSelect />
Expand Down
1 change: 1 addition & 0 deletions app/features/user-page/UserRepository.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ export function findLeanById(id: number) {
...COMMON_USER_FIELDS,
"User.isArtist",
"User.isVideoAdder",
"User.isTournamentOrganizer",
"User.patronTier",
"User.favoriteBadgeId",
"User.languages",
Expand Down
1 change: 1 addition & 0 deletions app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
patronTier: user.patronTier,
isArtist: user.isArtist,
isVideoAdder: user.isVideoAdder,
isTournamentOrganizer: user.isTournamentOrganizer,
inGameName: user.inGameName,
friendCode: user.friendCode,
languages: user.languages ? user.languages.split(",") : [],
Expand Down
5 changes: 5 additions & 0 deletions migrations/067-tournament-organizer-perms.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function up(db) {
db.prepare(
`alter table "User" add column "isTournamentOrganizer" integer default 0`,
).run();
}
16 changes: 16 additions & 0 deletions scripts/add-tos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import "dotenv/config";
import * as AdminRepository from "~/features/admin/AdminRepository.server";
import { logger } from "~/utils/logger";

async function main() {
const input = process.argv[2]?.trim();

const userIds = input.split(",").map((id) => Number(id));

for (const userId of userIds) {
await AdminRepository.makeTournamentOrganizerByUserId(userId);
}
logger.info(`Added TOs: ${userIds}`);
}

main();

0 comments on commit ba12a61

Please sign in to comment.