游戏用户白名单

This commit is contained in:
zhx 2026-07-14 15:09:35 +08:00
parent 0299d57aa4
commit e021c51500
9 changed files with 680 additions and 2 deletions

View File

@ -2406,6 +2406,100 @@
"x-permissions": ["game:status"]
}
},
"/admin/game/games/{game_id}/whitelist": {
"patch": {
"operationId": "setGameWhitelistEnabled",
"responses": {
"200": {
"$ref": "#/components/responses/EmptyResponse"
}
},
"parameters": [
{
"in": "path",
"name": "game_id",
"required": true,
"schema": {
"type": "string"
}
}
],
"x-permission": "game:update",
"x-permissions": ["game:update"]
}
},
"/admin/game/games/{game_id}/whitelist/users": {
"get": {
"operationId": "listGameWhitelistUsers",
"responses": {
"200": {
"$ref": "#/components/responses/EmptyResponse"
}
},
"parameters": [
{
"in": "path",
"name": "game_id",
"required": true,
"schema": {
"type": "string"
}
}
],
"x-permission": "game:view",
"x-permissions": ["game:view"]
},
"post": {
"operationId": "addGameWhitelistUser",
"responses": {
"200": {
"$ref": "#/components/responses/EmptyResponse"
}
},
"parameters": [
{
"in": "path",
"name": "game_id",
"required": true,
"schema": {
"type": "string"
}
}
],
"x-permission": "game:update",
"x-permissions": ["game:update"]
}
},
"/admin/game/games/{game_id}/whitelist/users/{user_id}": {
"delete": {
"operationId": "deleteGameWhitelistUser",
"responses": {
"200": {
"$ref": "#/components/responses/EmptyResponse"
}
},
"parameters": [
{
"in": "path",
"name": "game_id",
"required": true,
"schema": {
"type": "string"
}
},
{
"in": "path",
"name": "user_id",
"required": true,
"schema": {
"type": "string"
}
}
],
"x-permission": "game:update",
"x-permissions": ["game:update"]
}
},
"/admin/game/platforms": {
"get": {
"operationId": "listPlatforms",

View File

@ -40,6 +40,17 @@ export interface GameCatalogDto {
tags: string[];
createdAtMs?: number;
updatedAtMs?: number;
whitelistEnabled: boolean;
}
export interface GameWhitelistUserDto {
userId: string;
displayUserId?: string;
username?: string;
avatar?: string;
status?: string;
createdByAdminId?: number;
createdAtMs?: number;
}
export interface GameCatalogPage {
@ -254,7 +265,7 @@ export type GamePlatformPayload = Omit<
GamePlatformDto,
"appCode" | "createdAtMs" | "updatedAtMs" | "callbackSecretSet"
>;
export type GameCatalogPayload = Omit<GameCatalogDto, "appCode" | "createdAtMs" | "updatedAtMs">;
export type GameCatalogPayload = Omit<GameCatalogDto, "appCode" | "createdAtMs" | "updatedAtMs" | "whitelistEnabled">;
export interface GameCatalogQuery {
platformCode?: string;
@ -338,6 +349,40 @@ export function deleteGameCatalog(gameId: string): Promise<void> {
});
}
export function setGameWhitelistEnabled(gameId: string, enabled: boolean): Promise<GameCatalogDto> {
const endpoint = API_ENDPOINTS.setGameWhitelistEnabled;
return apiRequest<GameCatalogDto, { enabled: boolean }>(
apiEndpointPath(API_OPERATIONS.setGameWhitelistEnabled, { game_id: gameId }),
{ body: { enabled }, method: endpoint.method },
).then(normalizeCatalog);
}
export function listGameWhitelistUsers(
gameId: string,
): Promise<{ items: GameWhitelistUserDto[]; serverTimeMs?: number }> {
const endpoint = API_ENDPOINTS.listGameWhitelistUsers;
return apiRequest<{ items: GameWhitelistUserDto[]; serverTimeMs?: number }>(
apiEndpointPath(API_OPERATIONS.listGameWhitelistUsers, { game_id: gameId }),
{ method: endpoint.method, query: { pageSize: 200 } },
).then((result) => ({ ...result, items: (result.items || []).map(normalizeGameWhitelistUser) }));
}
export function addGameWhitelistUser(gameId: string, userId: string): Promise<GameWhitelistUserDto> {
const endpoint = API_ENDPOINTS.addGameWhitelistUser;
return apiRequest<GameWhitelistUserDto, { userId: string }>(
apiEndpointPath(API_OPERATIONS.addGameWhitelistUser, { game_id: gameId }),
{ body: { userId }, method: endpoint.method },
).then(normalizeGameWhitelistUser);
}
export function deleteGameWhitelistUser(gameId: string, userId: string): Promise<void> {
const endpoint = API_ENDPOINTS.deleteGameWhitelistUser;
return apiRequest<void>(
apiEndpointPath(API_OPERATIONS.deleteGameWhitelistUser, { game_id: gameId, user_id: userId }),
{ method: endpoint.method },
);
}
export function syncGamePlatformCatalog(platformCode: string, payload: GameSyncPayload = {}): Promise<GameSyncResult> {
return apiRequest<GameSyncResult, GameSyncPayload>(
`/v1/admin/game/platforms/${encodeURIComponent(platformCode)}/sync-games`,
@ -559,6 +604,19 @@ function normalizeCatalog(game: Partial<GameCatalogDto>): GameCatalogDto {
tags: Array.isArray(game.tags) ? game.tags.map(String).filter(Boolean) : [],
createdAtMs: numberValue(game.createdAtMs),
updatedAtMs: numberValue(game.updatedAtMs),
whitelistEnabled: game.whitelistEnabled === true,
};
}
function normalizeGameWhitelistUser(user: Partial<GameWhitelistUserDto>): GameWhitelistUserDto {
return {
userId: stringValue(user.userId),
displayUserId: stringValue(user.displayUserId),
username: stringValue(user.username),
avatar: stringValue(user.avatar),
status: stringValue(user.status),
createdByAdminId: numberValue(user.createdByAdminId),
createdAtMs: numberValue(user.createdAtMs),
};
}

View File

@ -0,0 +1,119 @@
import DeleteOutlineOutlined from "@mui/icons-material/DeleteOutlineOutlined";
import PersonAddAltOutlined from "@mui/icons-material/PersonAddAltOutlined";
import Avatar from "@mui/material/Avatar";
import CircularProgress from "@mui/material/CircularProgress";
import Dialog from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogTitle from "@mui/material/DialogTitle";
import TextField from "@mui/material/TextField";
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
import { Button } from "@/shared/ui/Button.jsx";
import { AdminActionIconButton } from "@/shared/ui/AdminListLayout.jsx";
import { formatMillis } from "@/shared/utils/time.js";
import styles from "@/features/games/games.module.css";
export function GameWhitelistDialog({ open, page, onClose }) {
const game = page.whitelistGame;
const enabledLoading = page.loadingAction === `whitelist-enabled-${game?.gameId}`;
const addLoading = page.loadingAction === `whitelist-add-${game?.gameId}`;
return (
<Dialog
className={styles.whitelistDialog}
maxWidth={false}
open={open}
slotProps={{ paper: { className: styles.whitelistDialogPaper } }}
onClose={onClose}
>
<DialogTitle>游戏白名单 · {game?.gameName || game?.gameId || ""}</DialogTitle>
<DialogContent className={styles.whitelistDialogContent}>
<div className={styles.whitelistPolicyRow}>
<div className={styles.stack}>
<span className={styles.name}>仅对白名单用户开放</span>
<span className={styles.meta}>开启后非成员不会在游戏列表看到此游戏也不能直接启动</span>
</div>
<AdminSwitch
checked={game?.whitelistEnabled === true}
checkedLabel="开启"
disabled={!page.abilities.canUpdate || enabledLoading}
label="游戏白名单状态"
size="small"
uncheckedLabel="关闭"
onChange={(event) => page.changeWhitelistEnabled(event.target.checked)}
/>
</div>
<form className={styles.whitelistSearchRow} onSubmit={page.addWhitelistUser}>
<TextField
fullWidth
label="搜索用户 ID"
placeholder="输入内部用户 ID"
slotProps={{ htmlInput: { inputMode: "numeric" } }}
value={page.whitelistUserId}
onChange={(event) => page.setWhitelistUserId(event.target.value)}
/>
<Button
disabled={!page.abilities.canUpdate || addLoading || !page.whitelistUserId.trim()}
startIcon={
addLoading ? <CircularProgress color="inherit" size={16} /> : <PersonAddAltOutlined />
}
type="submit"
variant="primary"
>
添加
</Button>
</form>
<div className={styles.whitelistTable}>
<div className={styles.whitelistTableHeader}>
<span>用户</span>
<span>用户 ID</span>
<span>状态</span>
<span>加入时间</span>
<span>操作</span>
</div>
{page.whitelistLoading ? (
<div className={styles.whitelistEmpty}>
<CircularProgress size={24} />
</div>
) : page.whitelistItems.length > 0 ? (
page.whitelistItems.map((item) => (
<div className={styles.whitelistTableRow} key={item.userId}>
<div className={styles.whitelistUserIdentity}>
<Avatar className={styles.whitelistAvatar} src={item.avatar || undefined}>
{(item.username || item.displayUserId || item.userId).slice(0, 1)}
</Avatar>
<span className={styles.name}>{item.username || "-"}</span>
</div>
<div className={styles.stack}>
<span>{item.userId}</span>
{item.displayUserId ? (
<span className={styles.meta}>{item.displayUserId}</span>
) : null}
</div>
<span>{item.status || "-"}</span>
<span>{formatMillis(item.createdAtMs)}</span>
<AdminActionIconButton
disabled={
!page.abilities.canUpdate ||
page.loadingAction === `whitelist-delete-${item.userId}`
}
label="移出白名单"
onClick={() => page.removeWhitelistUser(item)}
>
<DeleteOutlineOutlined fontSize="small" />
</AdminActionIconButton>
</div>
))
) : (
<div className={styles.whitelistEmpty}>当前没有白名单用户</div>
)}
</div>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>关闭</Button>
</DialogActions>
</Dialog>
);
}

View File

@ -0,0 +1,48 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { expect, test, vi } from "vitest";
import { AppProviders } from "@/app/providers.jsx";
import { GameWhitelistDialog } from "./GameWhitelistDialog.jsx";
test("renders current members and exposes whitelist toggle, add and remove actions", async () => {
const user = userEvent.setup();
const page = {
abilities: { canUpdate: true },
addWhitelistUser: vi.fn((event) => event.preventDefault()),
changeWhitelistEnabled: vi.fn(),
loadingAction: "",
removeWhitelistUser: vi.fn(),
setWhitelistUserId: vi.fn(),
whitelistGame: { gameId: "private_slot", gameName: "Private Slot", whitelistEnabled: true },
whitelistItems: [
{
createdAtMs: 1700000000000,
displayUserId: "90001",
status: "active",
userId: "42",
username: "Alice",
},
],
whitelistLoading: false,
whitelistUserId: "42",
};
render(
<AppProviders>
<GameWhitelistDialog open page={page} onClose={vi.fn()} />
</AppProviders>,
);
expect(screen.getByText("游戏白名单 · Private Slot")).toBeInTheDocument();
expect(screen.getByText("Alice")).toBeInTheDocument();
expect(screen.getByText("90001")).toBeInTheDocument();
await user.click(screen.getByRole("switch", { name: "游戏白名单状态" }));
expect(page.changeWhitelistEnabled).toHaveBeenCalledWith(false);
await user.click(screen.getByRole("button", { name: "添加" }));
expect(page.addWhitelistUser).toHaveBeenCalledOnce();
await user.click(screen.getByRole("button", { name: "移出白名单" }));
expect(page.removeWhitelistUser).toHaveBeenCalledWith(page.whitelistItems[0]);
});

View File

@ -334,3 +334,98 @@
max-width: 360px;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
}
.whitelistDialogPaper {
width: min(980px, calc(100vw - 32px));
max-height: min(760px, calc(100vh - 32px));
border-radius: 12px;
}
.whitelistDialogContent {
display: grid;
gap: 16px;
}
.whitelistPolicyRow,
.whitelistSearchRow {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 16px;
align-items: center;
}
.whitelistPolicyRow {
padding: 14px 16px;
border: 1px solid var(--border);
border-radius: 10px;
background: var(--bg-card-strong);
}
.whitelistSearchRow > :last-child {
min-width: 104px;
min-height: 52px;
}
.whitelistTable {
overflow: auto;
max-height: 440px;
border: 1px solid var(--border);
border-radius: 10px;
}
.whitelistTableHeader,
.whitelistTableRow {
display: grid;
grid-template-columns: minmax(180px, 1.2fr) minmax(150px, 1fr) 88px 180px 64px;
gap: 12px;
align-items: center;
min-width: 760px;
padding: 10px 14px;
}
.whitelistTableHeader {
position: sticky;
z-index: 1;
top: 0;
border-bottom: 1px solid var(--border);
background: var(--bg-table-head);
color: var(--text-secondary);
font-size: 12px;
font-weight: 600;
}
.whitelistTableRow {
border-bottom: 1px solid var(--border-soft);
}
.whitelistTableRow:last-child {
border-bottom: 0;
}
.whitelistUserIdentity {
display: flex;
min-width: 0;
align-items: center;
gap: 10px;
}
.whitelistAvatar {
width: 36px;
height: 36px;
flex: 0 0 auto;
}
.whitelistEmpty {
display: flex;
min-height: 160px;
align-items: center;
justify-content: center;
color: var(--text-tertiary);
}
@media (max-width: 720px) {
.whitelistPolicyRow,
.whitelistSearchRow {
grid-template-columns: minmax(0, 1fr);
}
}

View File

@ -1,9 +1,13 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
addGameWhitelistUser,
deleteGameCatalog,
deleteGameWhitelistUser,
listGameCatalog,
listGamePlatforms,
listGameWhitelistUsers,
setGameStatus,
setGameWhitelistEnabled,
syncGamePlatformCatalog,
upsertGameCatalog,
upsertGamePlatform,
@ -89,6 +93,10 @@ export function useGamesPage() {
const [syncPlatform, setSyncPlatform] = useState(null);
const [syncCandidates, setSyncCandidates] = useState([]);
const [selectedSyncGameIds, setSelectedSyncGameIds] = useState([]);
const [whitelistGame, setWhitelistGame] = useState(null);
const [whitelistItems, setWhitelistItems] = useState([]);
const [whitelistUserId, setWhitelistUserId] = useState("");
const [whitelistLoading, setWhitelistLoading] = useState(false);
const catalogRequestRef = useRef(0);
const platformsQueryFn = useCallback(() => listGamePlatforms(), []);
@ -208,6 +216,29 @@ export function useGamesPage() {
setActiveAction("edit-game");
};
const loadWhitelist = async (game) => {
if (!game?.gameId) {
return;
}
setWhitelistLoading(true);
try {
const result = await listGameWhitelistUsers(game.gameId);
setWhitelistItems(result.items || []);
} catch (err) {
showToast(err.message || "加载游戏白名单失败", "error");
} finally {
setWhitelistLoading(false);
}
};
const openWhitelist = (game) => {
setWhitelistGame(game);
setWhitelistItems([]);
setWhitelistUserId("");
setActiveAction("game-whitelist");
loadWhitelist(game);
};
const openCreatePlatform = () => {
setEditingPlatformCode("");
setPlatformForm(defaultPlatformForm);
@ -238,6 +269,11 @@ export function useGamesPage() {
if (activeAction === "bridge-script") {
setBridgeScriptForm(defaultBridgeScriptForm);
}
if (activeAction === "game-whitelist") {
setWhitelistGame(null);
setWhitelistItems([]);
setWhitelistUserId("");
}
setActiveAction("");
setEditingGameId("");
setEditingPlatformCode("");
@ -377,6 +413,76 @@ export function useGamesPage() {
}
};
const changeWhitelistEnabled = async (checked) => {
if (!whitelistGame) {
return;
}
setLoadingAction(`whitelist-enabled-${whitelistGame.gameId}`);
try {
const updated = await setGameWhitelistEnabled(whitelistGame.gameId, checked);
setWhitelistGame((current) =>
current ? { ...current, whitelistEnabled: updated.whitelistEnabled } : current,
);
setCatalogItems((current) =>
current.map((game) =>
game.gameId === whitelistGame.gameId
? { ...game, whitelistEnabled: updated.whitelistEnabled }
: game,
),
);
showToast(checked ? "游戏白名单已开启" : "游戏白名单已关闭", "success");
} catch (err) {
showToast(err.message || "白名单状态更新失败", "error");
} finally {
setLoadingAction("");
}
};
const addWhitelistUser = async (event) => {
event.preventDefault();
const userId = whitelistUserId.trim();
if (!whitelistGame || !/^\d+$/.test(userId) || userId === "0") {
showToast("请输入正确的用户 ID", "error");
return;
}
setLoadingAction(`whitelist-add-${whitelistGame.gameId}`);
try {
await addGameWhitelistUser(whitelistGame.gameId, userId);
setWhitelistUserId("");
await loadWhitelist(whitelistGame);
showToast("用户已加入白名单", "success");
} catch (err) {
showToast(err.message || "添加白名单用户失败", "error");
} finally {
setLoadingAction("");
}
};
const removeWhitelistUser = async (item) => {
if (!whitelistGame) {
return;
}
const ok = await confirm({
confirmText: "移除",
message: `移除用户 ${item.displayUserId || item.userId} 后,该用户将看不到且无法启动此游戏。`,
title: "移除白名单用户",
tone: "danger",
});
if (!ok) {
return;
}
setLoadingAction(`whitelist-delete-${item.userId}`);
try {
await deleteGameWhitelistUser(whitelistGame.gameId, item.userId);
setWhitelistItems((current) => current.filter((user) => user.userId !== item.userId));
showToast("用户已移出白名单", "success");
} catch (err) {
showToast(err.message || "移除白名单用户失败", "error");
} finally {
setLoadingAction("");
}
};
const fetchPlatformGames = async (platform) => {
setLoadingAction(`fetch-games-${platform.platformCode}`);
try {
@ -464,7 +570,9 @@ export function useGamesPage() {
activeAction,
allSyncGamesSelected,
bridgeScriptForm,
addWhitelistUser,
changeGameStatus,
changeWhitelistEnabled,
deleteGame,
closeAction,
data: {
@ -487,6 +595,7 @@ export function useGamesPage() {
openBridgeScript,
openEditGame,
openEditPlatform,
openWhitelist,
openManagePlatforms,
pageSize,
platformCode,
@ -496,6 +605,7 @@ export function useGamesPage() {
loadNextPage,
reload: reloadCatalog,
resetFilters,
removeWhitelistUser,
resolveGameURL,
changePageSize,
setGameForm,
@ -512,6 +622,11 @@ export function useGamesPage() {
selectedSyncGameIds,
toggleAllSyncGames,
toggleSyncGame,
whitelistGame,
whitelistItems,
whitelistLoading,
whitelistUserId,
setWhitelistUserId,
};
}

View File

@ -3,6 +3,7 @@ import CodeOutlined from "@mui/icons-material/CodeOutlined";
import DeleteOutlineOutlined from "@mui/icons-material/DeleteOutlineOutlined";
import EditOutlined from "@mui/icons-material/EditOutlined";
import SettingsOutlined from "@mui/icons-material/SettingsOutlined";
import GroupOutlined from "@mui/icons-material/GroupOutlined";
import SportsEsportsOutlined from "@mui/icons-material/SportsEsportsOutlined";
import SyncOutlined from "@mui/icons-material/SyncOutlined";
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
@ -25,6 +26,7 @@ import { createOptionsColumnFilter } from "@/shared/ui/tableFilters.js";
import { UploadField } from "@/shared/ui/UploadField.jsx";
import { formatMillis } from "@/shared/utils/time.js";
import { useGamesPage } from "@/features/games/hooks/useGamesPage.js";
import { GameWhitelistDialog } from "@/features/games/components/GameWhitelistDialog.jsx";
import styles from "@/features/games/games.module.css";
const statusOptions = [
@ -105,7 +107,7 @@ const baseColumns = [
{
key: "actions",
label: "操作",
width: "minmax(112px, 0.5fr)",
width: "minmax(152px, 0.65fr)",
},
];
@ -231,6 +233,7 @@ export function GameListPage() {
onClose={page.closeAction}
/>
<ProviderGameListDialog open={page.activeAction === "sync-games"} page={page} onClose={page.closeAction} />
<GameWhitelistDialog open={page.activeAction === "game-whitelist"} page={page} onClose={page.closeAction} />
</AdminListPage>
);
}
@ -269,6 +272,15 @@ function GameStatusSwitch({ page, game }) {
function GameRowActions({ page, game }) {
return (
<AdminRowActions>
<AdminActionIconButton
disabled={!page.abilities.canView}
label="白名单"
primary={game.whitelistEnabled === true}
tooltip={game.whitelistEnabled ? "白名单已开启" : "管理白名单"}
onClick={() => page.openWhitelist(game)}
>
<GroupOutlined fontSize="small" />
</AdminActionIconButton>
<AdminActionIconButton
disabled={!page.abilities.canUpdate}
label="编辑游戏"

View File

@ -13,6 +13,7 @@ export interface ApiEndpoint {
}
export const API_OPERATIONS = {
addGameWhitelistUser: "addGameWhitelistUser",
adjustDicePool: "adjustDicePool",
adminAddAgencyHost: "adminAddAgencyHost",
appAdjustUserLevels: "appAdjustUserLevels",
@ -88,6 +89,7 @@ export const API_OPERATIONS = {
deleteCountry: "deleteCountry",
deleteDiceRobot: "deleteDiceRobot",
deleteExploreTab: "deleteExploreTab",
deleteGameWhitelistUser: "deleteGameWhitelistUser",
deleteGift: "deleteGift",
deleteH5Link: "deleteH5Link",
deleteHostAgencyPolicy: "deleteHostAgencyPolicy",
@ -197,6 +199,7 @@ export const API_OPERATIONS = {
listFinanceCoinSellerRechargeOrders: "listFinanceCoinSellerRechargeOrders",
listFinanceWithdrawalApplications: "listFinanceWithdrawalApplications",
listFirstRechargeRewardClaims: "listFirstRechargeRewardClaims",
listGameWhitelistUsers: "listGameWhitelistUsers",
listGifts: "listGifts",
listGiftTypes: "listGiftTypes",
listH5Links: "listH5Links",
@ -295,6 +298,7 @@ export const API_OPERATIONS = {
setCoinSellerStatus: "setCoinSellerStatus",
setDiceRobotStatus: "setDiceRobotStatus",
setGameStatus: "setGameStatus",
setGameWhitelistEnabled: "setGameWhitelistEnabled",
setPrettyIdStatus: "setPrettyIdStatus",
setTaskDefinitionStatus: "setTaskDefinitionStatus",
setThirdPartyPaymentMethodStatus: "setThirdPartyPaymentMethodStatus",
@ -375,6 +379,13 @@ export const API_OPERATIONS = {
export type ApiOperationId = (typeof API_OPERATIONS)[keyof typeof API_OPERATIONS];
export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
addGameWhitelistUser: {
method: "POST",
operationId: API_OPERATIONS.addGameWhitelistUser,
path: "/v1/admin/game/games/{game_id}/whitelist/users",
permission: "game:update",
permissions: ["game:update"]
},
adjustDicePool: {
method: "POST",
operationId: API_OPERATIONS.adjustDicePool,
@ -898,6 +909,13 @@ export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
permission: "app-config:update",
permissions: ["app-config:update"]
},
deleteGameWhitelistUser: {
method: "DELETE",
operationId: API_OPERATIONS.deleteGameWhitelistUser,
path: "/v1/admin/game/games/{game_id}/whitelist/users/{user_id}",
permission: "game:update",
permissions: ["game:update"]
},
deleteGift: {
method: "DELETE",
operationId: API_OPERATIONS.deleteGift,
@ -1659,6 +1677,13 @@ export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
permission: "first-recharge-reward:view",
permissions: ["first-recharge-reward:view"]
},
listGameWhitelistUsers: {
method: "GET",
operationId: API_OPERATIONS.listGameWhitelistUsers,
path: "/v1/admin/game/games/{game_id}/whitelist/users",
permission: "game:view",
permissions: ["game:view"]
},
listGifts: {
method: "GET",
operationId: API_OPERATIONS.listGifts,
@ -2333,6 +2358,13 @@ export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
permission: "game:status",
permissions: ["game:status"]
},
setGameWhitelistEnabled: {
method: "PATCH",
operationId: API_OPERATIONS.setGameWhitelistEnabled,
path: "/v1/admin/game/games/{game_id}/whitelist",
permission: "game:update",
permissions: ["game:update"]
},
setPrettyIdStatus: {
method: "POST",
operationId: API_OPERATIONS.setPrettyIdStatus,

View File

@ -1516,6 +1516,54 @@ export interface paths {
patch: operations["setGameStatus"];
trace?: never;
};
"/admin/game/games/{game_id}/whitelist": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch: operations["setGameWhitelistEnabled"];
trace?: never;
};
"/admin/game/games/{game_id}/whitelist/users": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: operations["listGameWhitelistUsers"];
put?: never;
post: operations["addGameWhitelistUser"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/admin/game/games/{game_id}/whitelist/users/{user_id}": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
post?: never;
delete: operations["deleteGameWhitelistUser"];
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/admin/game/platforms": {
parameters: {
query?: never;
@ -8466,6 +8514,63 @@ export interface operations {
200: components["responses"]["EmptyResponse"];
};
};
setGameWhitelistEnabled: {
parameters: {
query?: never;
header?: never;
path: {
game_id: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
200: components["responses"]["EmptyResponse"];
};
};
listGameWhitelistUsers: {
parameters: {
query?: never;
header?: never;
path: {
game_id: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
200: components["responses"]["EmptyResponse"];
};
};
addGameWhitelistUser: {
parameters: {
query?: never;
header?: never;
path: {
game_id: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
200: components["responses"]["EmptyResponse"];
};
};
deleteGameWhitelistUser: {
parameters: {
query?: never;
header?: never;
path: {
game_id: string;
user_id: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
200: components["responses"]["EmptyResponse"];
};
};
listPlatforms: {
parameters: {
query?: never;