diff --git a/app/features/admin/actions/admin.server.ts b/app/features/admin/actions/admin.server.ts
index aaf1babcc..9e83c09cb 100644
--- a/app/features/admin/actions/admin.server.ts
+++ b/app/features/admin/actions/admin.server.ts
@@ -4,6 +4,8 @@ import * as AdminRepository from "~/features/admin/AdminRepository.server";
import { makeArtist } from "~/features/art/queries/makeArtist.server";
import { requireUserId } from "~/features/auth/core/user.server";
import { refreshBannedCache } from "~/features/ban/core/banned.server";
+import { FRIEND_CODE_REGEXP } from "~/features/sendouq/q-constants";
+import * as UserRepository from "~/features/user-page/UserRepository.server";
import { isAdmin, isMod } from "~/permissions";
import { logger } from "~/utils/logger";
import { parseRequestPayload, validate } from "~/utils/remix";
@@ -112,6 +114,17 @@ export const action = async ({ request }: ActionFunctionArgs) => {
break;
}
+ case "UPDATE_FRIEND_CODE": {
+ validate(isMod(user), "Mod needed", 401);
+
+ await UserRepository.insertFriendCode({
+ friendCode: data.friendCode,
+ submitterUserId: user.id,
+ userId: data.user,
+ });
+
+ break;
+ }
default: {
assertUnreachable(data);
}
@@ -161,4 +174,9 @@ export const adminActionSchema = z.union([
_action: _action("UNBAN_USER"),
user: z.preprocess(actualNumber, z.number().positive()),
}),
+ z.object({
+ _action: _action("UPDATE_FRIEND_CODE"),
+ friendCode: z.string().regex(FRIEND_CODE_REGEXP),
+ user: z.preprocess(actualNumber, z.number().positive()),
+ }),
]);
diff --git a/app/features/admin/routes/admin.tsx b/app/features/admin/routes/admin.tsx
index 0b8d8c652..4b9a94c7a 100644
--- a/app/features/admin/routes/admin.tsx
+++ b/app/features/admin/routes/admin.tsx
@@ -8,10 +8,12 @@ import {
import * as React from "react";
import { Button } from "~/components/Button";
import { Catcher } from "~/components/Catcher";
+import { Input } from "~/components/Input";
import { Main } from "~/components/Main";
import { SubmitButton } from "~/components/SubmitButton";
import { UserSearch } from "~/components/UserSearch";
import { useUser } from "~/features/auth/core/user";
+import { FRIEND_CODE_REGEXP_PATTERN } from "~/features/sendouq/q-constants";
import { isAdmin, isMod } from "~/permissions";
import type { SendouRouteHandle } from "~/utils/remix";
import { makeTitle } from "~/utils/strings";
@@ -39,6 +41,7 @@ export default function AdminPage() {
{isMod(user) ? : null}
{isMod(user) ? : null}
{isMod(user) ? : null}
+ {isMod(user) ? : null}
{process.env.NODE_ENV !== "production" || isAdmin(user) ? (
@@ -199,6 +202,41 @@ function GiveVideoAdder() {
);
}
+function UpdateFriendCode() {
+ const fetcher = useFetcher();
+
+ return (
+
+ Update friend code
+
+
+
+
+
+
+
+
+
+
+
+
+ Submit
+
+
+
+ );
+}
+
function ForcePatron() {
const fetcher = useFetcher();
diff --git a/scripts/update-fc.ts b/scripts/update-fc.ts
deleted file mode 100644
index 7e55b4c12..000000000
--- a/scripts/update-fc.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import "dotenv/config";
-import { ADMIN_ID } from "~/constants";
-import { FRIEND_CODE_REGEXP } from "~/features/sendouq/q-constants";
-import * as UserRepository from "~/features/user-page/UserRepository.server";
-import invariant from "~/utils/invariant";
-import { logger } from "~/utils/logger";
-
-async function main() {
- const discordId = process.argv[2]?.trim();
-
- invariant(discordId, "discord id is required (argument 1)");
-
- const newFriendCode = process.argv[3]?.trim();
-
- invariant(discordId, "friend code is required (argument 2)");
-
- invariant(FRIEND_CODE_REGEXP.test(newFriendCode), "Invalid friend code");
-
- await UserRepository.insertFriendCode({
- friendCode: newFriendCode,
- submitterUserId: ADMIN_ID,
- userId: await UserRepository.findByIdentifier(discordId).then((u) => u!.id),
- });
- logger.info(`Friend code updated: ${discordId} - ${newFriendCode}`);
-}
-
-void main();