Compare commits
2 Commits
102b29c54f
...
64edc6169b
| Author | SHA1 | Date | |
|---|---|---|---|
|
64edc6169b
|
|||
|
cf84722418
|
174
app/routes/gmm.tsx
Normal file
174
app/routes/gmm.tsx
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Container,
|
||||||
|
FormControl,
|
||||||
|
FormLabel,
|
||||||
|
Input,
|
||||||
|
Modal,
|
||||||
|
ModalBody,
|
||||||
|
ModalCloseButton,
|
||||||
|
ModalContent,
|
||||||
|
ModalFooter,
|
||||||
|
ModalHeader,
|
||||||
|
ModalOverlay,
|
||||||
|
Table,
|
||||||
|
TableCaption,
|
||||||
|
TableContainer,
|
||||||
|
Tbody,
|
||||||
|
Td,
|
||||||
|
Th,
|
||||||
|
Thead,
|
||||||
|
Tr,
|
||||||
|
useDisclosure,
|
||||||
|
useToast,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import { useLoaderData } from "@remix-run/react";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
export async function loader({ context }: { context: RequestContext }) {
|
||||||
|
if (!context.data.current_user)
|
||||||
|
throw new Response(null, {
|
||||||
|
status: 401,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
![
|
||||||
|
"165594923586945025",
|
||||||
|
"289372404541554689",
|
||||||
|
"396347223736057866",
|
||||||
|
].includes(context.data.current_user.id)
|
||||||
|
)
|
||||||
|
throw new Response(null, {
|
||||||
|
status: 403,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (await context.env.DATA.list({ prefix: "gamemod_" }))?.keys ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const data: { [k: string]: any }[] = useLoaderData<typeof loader>();
|
||||||
|
const { isOpen, onClose, onOpen } = useDisclosure();
|
||||||
|
const [idToAdd, setIdToAdd] = useState<string>("");
|
||||||
|
const [nameToAdd, setNameToAdd] = useState<string>("");
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
async function addMod(id: string, name: string) {
|
||||||
|
const response = await fetch("/api/gme/add", {
|
||||||
|
body: JSON.stringify({ name, user: id }),
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
let msg = "Unknown error";
|
||||||
|
|
||||||
|
try {
|
||||||
|
msg = ((await response.json()) as { error: string }).error;
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
toast({
|
||||||
|
description: msg,
|
||||||
|
status: "error",
|
||||||
|
title: "Cannot add game mod",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toast({
|
||||||
|
description: `${name} was added as a game mod`,
|
||||||
|
status: "success",
|
||||||
|
title: "Game mod added",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setIdToAdd("");
|
||||||
|
setNameToAdd("");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container maxW="container.lg">
|
||||||
|
<TableContainer>
|
||||||
|
<Table variant="simple">
|
||||||
|
<TableCaption>Currently active game mods</TableCaption>
|
||||||
|
<Thead>
|
||||||
|
<Tr>
|
||||||
|
<Th>ID</Th>
|
||||||
|
<Th>Name</Th>
|
||||||
|
<Th>Added At</Th>
|
||||||
|
<Th>Added By</Th>
|
||||||
|
<Th>Remove</Th>
|
||||||
|
</Tr>
|
||||||
|
</Thead>
|
||||||
|
<Tbody>
|
||||||
|
{data.map((item) => {
|
||||||
|
return (
|
||||||
|
<Tr key={item.metadata.id}>
|
||||||
|
<Td>{item.metadata.id}</Td>
|
||||||
|
<Td>{item.metadata.name}</Td>
|
||||||
|
<Td>{item.metadata.created_at}</Td>
|
||||||
|
<Td>{item.metadata.created_by}</Td>
|
||||||
|
<Td>
|
||||||
|
<Button>Remove</Button>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Tbody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
<Button alignSelf="end" onClick={onOpen} pt="16px">
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
<Modal isOpen={isOpen} onClose={onClose}>
|
||||||
|
<ModalOverlay />
|
||||||
|
<ModalContent>
|
||||||
|
<ModalHeader>Add Game Mod</ModalHeader>
|
||||||
|
<ModalCloseButton />
|
||||||
|
<ModalBody>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel>Name</FormLabel>
|
||||||
|
<Input
|
||||||
|
maxLength={32}
|
||||||
|
onChange={(e) => setNameToAdd(e.target.value)}
|
||||||
|
value={nameToAdd}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<br />
|
||||||
|
<FormControl pt="16px">
|
||||||
|
<FormLabel>ID</FormLabel>
|
||||||
|
<Input
|
||||||
|
maxLength={19}
|
||||||
|
onChange={(e) => setIdToAdd(e.target.value)}
|
||||||
|
value={idToAdd}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter gap="8px">
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setIdToAdd("");
|
||||||
|
setNameToAdd("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
colorScheme="blue"
|
||||||
|
disabled={
|
||||||
|
idToAdd.length < 17 ||
|
||||||
|
idToAdd.length > 19 ||
|
||||||
|
nameToAdd.length < 1 ||
|
||||||
|
nameToAdd.length > 32
|
||||||
|
}
|
||||||
|
onClick={async () => {
|
||||||
|
await addMod(idToAdd, nameToAdd);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -30,7 +30,6 @@ import { useLoaderData } from "@remix-run/react";
|
|||||||
import AppealBans from "../../components/AppealBans.js";
|
import AppealBans from "../../components/AppealBans.js";
|
||||||
import AppealCard from "../../components/AppealCard.js";
|
import AppealCard from "../../components/AppealCard.js";
|
||||||
import GameAppealCard from "../../components/GameAppealCard.js";
|
import GameAppealCard from "../../components/GameAppealCard.js";
|
||||||
import GameModManagementModal from "../../components/GameModManagementModal.js";
|
|
||||||
import NewGameBan from "../../components/NewGameBan.js";
|
import NewGameBan from "../../components/NewGameBan.js";
|
||||||
import NewInfractionModal from "../../components/NewInfractionModal.js";
|
import NewInfractionModal from "../../components/NewInfractionModal.js";
|
||||||
import ReportCard from "../../components/ReportCard.js";
|
import ReportCard from "../../components/ReportCard.js";
|
||||||
@@ -151,7 +150,7 @@ export default function () {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (messageChannel.current) {
|
if (messageChannel.current) {
|
||||||
messageChannel.current.port1.onmessage = function (ev) {
|
messageChannel.current.port1.onmessage = function (ev: any) {
|
||||||
const { data }: { data: string } = ev;
|
const { data }: { data: string } = ev;
|
||||||
|
|
||||||
setEntries([...entries].filter((entry) => entry.id !== data));
|
setEntries([...entries].filter((entry) => entry.id !== data));
|
||||||
@@ -337,7 +336,11 @@ export default function () {
|
|||||||
},
|
},
|
||||||
appeal_bans: useDisclosure(),
|
appeal_bans: useDisclosure(),
|
||||||
game_ban: useDisclosure(),
|
game_ban: useDisclosure(),
|
||||||
gme: useDisclosure(),
|
gme: {
|
||||||
|
isOpen: false,
|
||||||
|
onClose: () => {},
|
||||||
|
onOpen: () => location.assign("/gmm"),
|
||||||
|
},
|
||||||
inactivity: useDisclosure(),
|
inactivity: useDisclosure(),
|
||||||
infraction: useDisclosure(),
|
infraction: useDisclosure(),
|
||||||
user_lookup: {
|
user_lookup: {
|
||||||
@@ -431,10 +434,6 @@ export default function () {
|
|||||||
isOpen={itemModals.appeal_bans.isOpen}
|
isOpen={itemModals.appeal_bans.isOpen}
|
||||||
onClose={itemModals.appeal_bans.onClose}
|
onClose={itemModals.appeal_bans.onClose}
|
||||||
/>
|
/>
|
||||||
<GameModManagementModal
|
|
||||||
isOpen={itemModals.gme.isOpen}
|
|
||||||
onClose={itemModals.gme.onClose}
|
|
||||||
/>
|
|
||||||
<NewGameBan
|
<NewGameBan
|
||||||
isOpen={itemModals.game_ban.isOpen}
|
isOpen={itemModals.game_ban.isOpen}
|
||||||
onClose={itemModals.game_ban.onClose}
|
onClose={itemModals.game_ban.onClose}
|
||||||
|
|||||||
@@ -1,157 +0,0 @@
|
|||||||
import {
|
|
||||||
Button,
|
|
||||||
HStack,
|
|
||||||
Input,
|
|
||||||
Link,
|
|
||||||
Modal,
|
|
||||||
ModalBody,
|
|
||||||
ModalCloseButton,
|
|
||||||
ModalContent,
|
|
||||||
ModalHeader,
|
|
||||||
ModalOverlay,
|
|
||||||
Table,
|
|
||||||
TableContainer,
|
|
||||||
Tbody,
|
|
||||||
Td,
|
|
||||||
Th,
|
|
||||||
Thead,
|
|
||||||
Tr,
|
|
||||||
useToast,
|
|
||||||
} from "@chakra-ui/react";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
export default function (props: { isOpen: boolean; onClose: () => void }) {
|
|
||||||
const [mods, setMods] = useState([]);
|
|
||||||
const [userToAdd, setUserToAdd] = useState("");
|
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!props.isOpen) return;
|
|
||||||
|
|
||||||
(async function () {
|
|
||||||
const gmeResp = await fetch("/api/gme/list");
|
|
||||||
|
|
||||||
if (!gmeResp.ok) {
|
|
||||||
toast({
|
|
||||||
description: "Failed to load GME data",
|
|
||||||
status: "error",
|
|
||||||
title: "Oops",
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setMods(await gmeResp.json());
|
|
||||||
})();
|
|
||||||
}, [props.isOpen]);
|
|
||||||
|
|
||||||
async function addUser() {
|
|
||||||
if (!userToAdd || !userToAdd.match(/^\d{17,19}$/)) {
|
|
||||||
toast({
|
|
||||||
description: "Please check your input and try again",
|
|
||||||
status: "error",
|
|
||||||
title: "Invalid user",
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const addResp = await fetch("/api/gme/add", {
|
|
||||||
body: JSON.stringify({ user: userToAdd }),
|
|
||||||
headers: {
|
|
||||||
"content-type": "application/json",
|
|
||||||
},
|
|
||||||
method: "POST",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!addResp.ok) {
|
|
||||||
toast({
|
|
||||||
description: ((await addResp.json()) as { error: string }).error,
|
|
||||||
status: "error",
|
|
||||||
title: "Oops",
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
toast({
|
|
||||||
description: `User ${userToAdd} added`,
|
|
||||||
status: "success",
|
|
||||||
title: "Success",
|
|
||||||
});
|
|
||||||
|
|
||||||
props.onClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function removeUser(user: string) {
|
|
||||||
const removeResp = await fetch("/api/gme/remove", {
|
|
||||||
body: JSON.stringify({ user }),
|
|
||||||
headers: {
|
|
||||||
"content-type": "application/json",
|
|
||||||
},
|
|
||||||
method: "POST",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!removeResp.ok) {
|
|
||||||
toast({
|
|
||||||
description: ((await removeResp.json()) as { error: string }).error,
|
|
||||||
status: "error",
|
|
||||||
title: "Oops",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
toast({
|
|
||||||
description: `User ${user} removed`,
|
|
||||||
status: "success",
|
|
||||||
title: "Success",
|
|
||||||
});
|
|
||||||
|
|
||||||
setMods(mods.filter((mod: any) => mod.user !== user));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal isCentered isOpen={props.isOpen} onClose={props.onClose}>
|
|
||||||
<ModalOverlay />
|
|
||||||
<ModalContent>
|
|
||||||
<ModalHeader>Game Moderators</ModalHeader>
|
|
||||||
<ModalCloseButton />
|
|
||||||
<ModalBody>
|
|
||||||
<TableContainer>
|
|
||||||
<Table variant="simple">
|
|
||||||
<Thead>
|
|
||||||
<Tr>
|
|
||||||
<Th>User</Th>
|
|
||||||
<Th>Added At</Th>
|
|
||||||
<Th>Added By</Th>
|
|
||||||
<Th>Remove</Th>
|
|
||||||
</Tr>
|
|
||||||
</Thead>
|
|
||||||
<Tbody>
|
|
||||||
{mods.map((mod: any) => (
|
|
||||||
<Tr>
|
|
||||||
<Td>{mod.user}</Td>
|
|
||||||
<Td>{new Date(mod.metadata.time).toLocaleString()}</Td>
|
|
||||||
<Td>{mod.metadata.user}</Td>
|
|
||||||
<Td>
|
|
||||||
<Link onClick={async () => await removeUser(mod.user)}>
|
|
||||||
Remove
|
|
||||||
</Link>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
))}
|
|
||||||
</Tbody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
<HStack mt="8px">
|
|
||||||
<Input
|
|
||||||
maxLength={19}
|
|
||||||
onChange={(e) => setUserToAdd(e.target.value)}
|
|
||||||
placeholder="1234567890987654321"
|
|
||||||
/>
|
|
||||||
<Button onClick={async () => await addUser()}>Add</Button>
|
|
||||||
</HStack>
|
|
||||||
</ModalBody>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
import { jsonError } from "../../common.js";
|
import { jsonError } from "../../common.js";
|
||||||
|
|
||||||
export async function onRequestPost(context: RequestContext) {
|
export async function onRequestPost(context: RequestContext) {
|
||||||
const { user } = context.data.body;
|
const { user, name } = context.data.body;
|
||||||
|
|
||||||
if (!user) return jsonError("No user provided", 400);
|
if (!user) return jsonError("No user provided", 400);
|
||||||
|
|
||||||
|
if (typeof name !== "string" || name.length > 32)
|
||||||
|
return jsonError("Invalid name", 400);
|
||||||
|
|
||||||
const existingUser = await context.env.DATA.get(`gamemod_${user}`);
|
const existingUser = await context.env.DATA.get(`gamemod_${user}`);
|
||||||
|
|
||||||
if (existingUser) return jsonError("Cannot add an existing user", 400);
|
if (existingUser) return jsonError("Cannot add an existing user", 400);
|
||||||
@@ -21,9 +24,10 @@ export async function onRequestPost(context: RequestContext) {
|
|||||||
if (!user.match(/^\d{17,19}$/)) return jsonError("Invalid User ID", 400);
|
if (!user.match(/^\d{17,19}$/)) return jsonError("Invalid User ID", 400);
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
time: Date.now(),
|
created_at: Date.now(),
|
||||||
user: context.data.current_user.id,
|
created_by: context.data.current_user.id,
|
||||||
name: context.data.current_user.username,
|
id: user,
|
||||||
|
name,
|
||||||
};
|
};
|
||||||
|
|
||||||
await context.env.DATA.put(`gamemod_${user}`, JSON.stringify(data), {
|
await context.env.DATA.put(`gamemod_${user}`, JSON.stringify(data), {
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { jsonResponse } from "../../common.js";
|
|
||||||
|
|
||||||
export async function onRequestGet(context: RequestContext) {
|
|
||||||
const list = await context.env.DATA.list({ prefix: "gamemod_" });
|
|
||||||
const entries = [];
|
|
||||||
|
|
||||||
for (const key of list.keys)
|
|
||||||
entries.push({
|
|
||||||
metadata: key.metadata,
|
|
||||||
user: key.name.replace("gamemod_", ""),
|
|
||||||
});
|
|
||||||
|
|
||||||
return jsonResponse(JSON.stringify(entries));
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user