游戏用户白名单
This commit is contained in:
parent
0299d57aa4
commit
e021c51500
@ -2406,6 +2406,100 @@
|
|||||||
"x-permissions": ["game:status"]
|
"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": {
|
"/admin/game/platforms": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "listPlatforms",
|
"operationId": "listPlatforms",
|
||||||
|
|||||||
@ -40,6 +40,17 @@ export interface GameCatalogDto {
|
|||||||
tags: string[];
|
tags: string[];
|
||||||
createdAtMs?: number;
|
createdAtMs?: number;
|
||||||
updatedAtMs?: 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 {
|
export interface GameCatalogPage {
|
||||||
@ -254,7 +265,7 @@ export type GamePlatformPayload = Omit<
|
|||||||
GamePlatformDto,
|
GamePlatformDto,
|
||||||
"appCode" | "createdAtMs" | "updatedAtMs" | "callbackSecretSet"
|
"appCode" | "createdAtMs" | "updatedAtMs" | "callbackSecretSet"
|
||||||
>;
|
>;
|
||||||
export type GameCatalogPayload = Omit<GameCatalogDto, "appCode" | "createdAtMs" | "updatedAtMs">;
|
export type GameCatalogPayload = Omit<GameCatalogDto, "appCode" | "createdAtMs" | "updatedAtMs" | "whitelistEnabled">;
|
||||||
|
|
||||||
export interface GameCatalogQuery {
|
export interface GameCatalogQuery {
|
||||||
platformCode?: string;
|
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> {
|
export function syncGamePlatformCatalog(platformCode: string, payload: GameSyncPayload = {}): Promise<GameSyncResult> {
|
||||||
return apiRequest<GameSyncResult, GameSyncPayload>(
|
return apiRequest<GameSyncResult, GameSyncPayload>(
|
||||||
`/v1/admin/game/platforms/${encodeURIComponent(platformCode)}/sync-games`,
|
`/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) : [],
|
tags: Array.isArray(game.tags) ? game.tags.map(String).filter(Boolean) : [],
|
||||||
createdAtMs: numberValue(game.createdAtMs),
|
createdAtMs: numberValue(game.createdAtMs),
|
||||||
updatedAtMs: numberValue(game.updatedAtMs),
|
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),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
119
src/features/games/components/GameWhitelistDialog.jsx
Normal file
119
src/features/games/components/GameWhitelistDialog.jsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
48
src/features/games/components/GameWhitelistDialog.test.jsx
Normal file
48
src/features/games/components/GameWhitelistDialog.test.jsx
Normal 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]);
|
||||||
|
});
|
||||||
@ -334,3 +334,98 @@
|
|||||||
max-width: 360px;
|
max-width: 360px;
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
|
addGameWhitelistUser,
|
||||||
deleteGameCatalog,
|
deleteGameCatalog,
|
||||||
|
deleteGameWhitelistUser,
|
||||||
listGameCatalog,
|
listGameCatalog,
|
||||||
listGamePlatforms,
|
listGamePlatforms,
|
||||||
|
listGameWhitelistUsers,
|
||||||
setGameStatus,
|
setGameStatus,
|
||||||
|
setGameWhitelistEnabled,
|
||||||
syncGamePlatformCatalog,
|
syncGamePlatformCatalog,
|
||||||
upsertGameCatalog,
|
upsertGameCatalog,
|
||||||
upsertGamePlatform,
|
upsertGamePlatform,
|
||||||
@ -89,6 +93,10 @@ export function useGamesPage() {
|
|||||||
const [syncPlatform, setSyncPlatform] = useState(null);
|
const [syncPlatform, setSyncPlatform] = useState(null);
|
||||||
const [syncCandidates, setSyncCandidates] = useState([]);
|
const [syncCandidates, setSyncCandidates] = useState([]);
|
||||||
const [selectedSyncGameIds, setSelectedSyncGameIds] = 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 catalogRequestRef = useRef(0);
|
||||||
|
|
||||||
const platformsQueryFn = useCallback(() => listGamePlatforms(), []);
|
const platformsQueryFn = useCallback(() => listGamePlatforms(), []);
|
||||||
@ -208,6 +216,29 @@ export function useGamesPage() {
|
|||||||
setActiveAction("edit-game");
|
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 = () => {
|
const openCreatePlatform = () => {
|
||||||
setEditingPlatformCode("");
|
setEditingPlatformCode("");
|
||||||
setPlatformForm(defaultPlatformForm);
|
setPlatformForm(defaultPlatformForm);
|
||||||
@ -238,6 +269,11 @@ export function useGamesPage() {
|
|||||||
if (activeAction === "bridge-script") {
|
if (activeAction === "bridge-script") {
|
||||||
setBridgeScriptForm(defaultBridgeScriptForm);
|
setBridgeScriptForm(defaultBridgeScriptForm);
|
||||||
}
|
}
|
||||||
|
if (activeAction === "game-whitelist") {
|
||||||
|
setWhitelistGame(null);
|
||||||
|
setWhitelistItems([]);
|
||||||
|
setWhitelistUserId("");
|
||||||
|
}
|
||||||
setActiveAction("");
|
setActiveAction("");
|
||||||
setEditingGameId("");
|
setEditingGameId("");
|
||||||
setEditingPlatformCode("");
|
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) => {
|
const fetchPlatformGames = async (platform) => {
|
||||||
setLoadingAction(`fetch-games-${platform.platformCode}`);
|
setLoadingAction(`fetch-games-${platform.platformCode}`);
|
||||||
try {
|
try {
|
||||||
@ -464,7 +570,9 @@ export function useGamesPage() {
|
|||||||
activeAction,
|
activeAction,
|
||||||
allSyncGamesSelected,
|
allSyncGamesSelected,
|
||||||
bridgeScriptForm,
|
bridgeScriptForm,
|
||||||
|
addWhitelistUser,
|
||||||
changeGameStatus,
|
changeGameStatus,
|
||||||
|
changeWhitelistEnabled,
|
||||||
deleteGame,
|
deleteGame,
|
||||||
closeAction,
|
closeAction,
|
||||||
data: {
|
data: {
|
||||||
@ -487,6 +595,7 @@ export function useGamesPage() {
|
|||||||
openBridgeScript,
|
openBridgeScript,
|
||||||
openEditGame,
|
openEditGame,
|
||||||
openEditPlatform,
|
openEditPlatform,
|
||||||
|
openWhitelist,
|
||||||
openManagePlatforms,
|
openManagePlatforms,
|
||||||
pageSize,
|
pageSize,
|
||||||
platformCode,
|
platformCode,
|
||||||
@ -496,6 +605,7 @@ export function useGamesPage() {
|
|||||||
loadNextPage,
|
loadNextPage,
|
||||||
reload: reloadCatalog,
|
reload: reloadCatalog,
|
||||||
resetFilters,
|
resetFilters,
|
||||||
|
removeWhitelistUser,
|
||||||
resolveGameURL,
|
resolveGameURL,
|
||||||
changePageSize,
|
changePageSize,
|
||||||
setGameForm,
|
setGameForm,
|
||||||
@ -512,6 +622,11 @@ export function useGamesPage() {
|
|||||||
selectedSyncGameIds,
|
selectedSyncGameIds,
|
||||||
toggleAllSyncGames,
|
toggleAllSyncGames,
|
||||||
toggleSyncGame,
|
toggleSyncGame,
|
||||||
|
whitelistGame,
|
||||||
|
whitelistItems,
|
||||||
|
whitelistLoading,
|
||||||
|
whitelistUserId,
|
||||||
|
setWhitelistUserId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import CodeOutlined from "@mui/icons-material/CodeOutlined";
|
|||||||
import DeleteOutlineOutlined from "@mui/icons-material/DeleteOutlineOutlined";
|
import DeleteOutlineOutlined from "@mui/icons-material/DeleteOutlineOutlined";
|
||||||
import EditOutlined from "@mui/icons-material/EditOutlined";
|
import EditOutlined from "@mui/icons-material/EditOutlined";
|
||||||
import SettingsOutlined from "@mui/icons-material/SettingsOutlined";
|
import SettingsOutlined from "@mui/icons-material/SettingsOutlined";
|
||||||
|
import GroupOutlined from "@mui/icons-material/GroupOutlined";
|
||||||
import SportsEsportsOutlined from "@mui/icons-material/SportsEsportsOutlined";
|
import SportsEsportsOutlined from "@mui/icons-material/SportsEsportsOutlined";
|
||||||
import SyncOutlined from "@mui/icons-material/SyncOutlined";
|
import SyncOutlined from "@mui/icons-material/SyncOutlined";
|
||||||
import { AdminSwitch } from "@/shared/ui/AdminSwitch.jsx";
|
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 { UploadField } from "@/shared/ui/UploadField.jsx";
|
||||||
import { formatMillis } from "@/shared/utils/time.js";
|
import { formatMillis } from "@/shared/utils/time.js";
|
||||||
import { useGamesPage } from "@/features/games/hooks/useGamesPage.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";
|
import styles from "@/features/games/games.module.css";
|
||||||
|
|
||||||
const statusOptions = [
|
const statusOptions = [
|
||||||
@ -105,7 +107,7 @@ const baseColumns = [
|
|||||||
{
|
{
|
||||||
key: "actions",
|
key: "actions",
|
||||||
label: "操作",
|
label: "操作",
|
||||||
width: "minmax(112px, 0.5fr)",
|
width: "minmax(152px, 0.65fr)",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -231,6 +233,7 @@ export function GameListPage() {
|
|||||||
onClose={page.closeAction}
|
onClose={page.closeAction}
|
||||||
/>
|
/>
|
||||||
<ProviderGameListDialog open={page.activeAction === "sync-games"} page={page} 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>
|
</AdminListPage>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -269,6 +272,15 @@ function GameStatusSwitch({ page, game }) {
|
|||||||
function GameRowActions({ page, game }) {
|
function GameRowActions({ page, game }) {
|
||||||
return (
|
return (
|
||||||
<AdminRowActions>
|
<AdminRowActions>
|
||||||
|
<AdminActionIconButton
|
||||||
|
disabled={!page.abilities.canView}
|
||||||
|
label="白名单"
|
||||||
|
primary={game.whitelistEnabled === true}
|
||||||
|
tooltip={game.whitelistEnabled ? "白名单已开启" : "管理白名单"}
|
||||||
|
onClick={() => page.openWhitelist(game)}
|
||||||
|
>
|
||||||
|
<GroupOutlined fontSize="small" />
|
||||||
|
</AdminActionIconButton>
|
||||||
<AdminActionIconButton
|
<AdminActionIconButton
|
||||||
disabled={!page.abilities.canUpdate}
|
disabled={!page.abilities.canUpdate}
|
||||||
label="编辑游戏"
|
label="编辑游戏"
|
||||||
|
|||||||
@ -13,6 +13,7 @@ export interface ApiEndpoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const API_OPERATIONS = {
|
export const API_OPERATIONS = {
|
||||||
|
addGameWhitelistUser: "addGameWhitelistUser",
|
||||||
adjustDicePool: "adjustDicePool",
|
adjustDicePool: "adjustDicePool",
|
||||||
adminAddAgencyHost: "adminAddAgencyHost",
|
adminAddAgencyHost: "adminAddAgencyHost",
|
||||||
appAdjustUserLevels: "appAdjustUserLevels",
|
appAdjustUserLevels: "appAdjustUserLevels",
|
||||||
@ -88,6 +89,7 @@ export const API_OPERATIONS = {
|
|||||||
deleteCountry: "deleteCountry",
|
deleteCountry: "deleteCountry",
|
||||||
deleteDiceRobot: "deleteDiceRobot",
|
deleteDiceRobot: "deleteDiceRobot",
|
||||||
deleteExploreTab: "deleteExploreTab",
|
deleteExploreTab: "deleteExploreTab",
|
||||||
|
deleteGameWhitelistUser: "deleteGameWhitelistUser",
|
||||||
deleteGift: "deleteGift",
|
deleteGift: "deleteGift",
|
||||||
deleteH5Link: "deleteH5Link",
|
deleteH5Link: "deleteH5Link",
|
||||||
deleteHostAgencyPolicy: "deleteHostAgencyPolicy",
|
deleteHostAgencyPolicy: "deleteHostAgencyPolicy",
|
||||||
@ -197,6 +199,7 @@ export const API_OPERATIONS = {
|
|||||||
listFinanceCoinSellerRechargeOrders: "listFinanceCoinSellerRechargeOrders",
|
listFinanceCoinSellerRechargeOrders: "listFinanceCoinSellerRechargeOrders",
|
||||||
listFinanceWithdrawalApplications: "listFinanceWithdrawalApplications",
|
listFinanceWithdrawalApplications: "listFinanceWithdrawalApplications",
|
||||||
listFirstRechargeRewardClaims: "listFirstRechargeRewardClaims",
|
listFirstRechargeRewardClaims: "listFirstRechargeRewardClaims",
|
||||||
|
listGameWhitelistUsers: "listGameWhitelistUsers",
|
||||||
listGifts: "listGifts",
|
listGifts: "listGifts",
|
||||||
listGiftTypes: "listGiftTypes",
|
listGiftTypes: "listGiftTypes",
|
||||||
listH5Links: "listH5Links",
|
listH5Links: "listH5Links",
|
||||||
@ -295,6 +298,7 @@ export const API_OPERATIONS = {
|
|||||||
setCoinSellerStatus: "setCoinSellerStatus",
|
setCoinSellerStatus: "setCoinSellerStatus",
|
||||||
setDiceRobotStatus: "setDiceRobotStatus",
|
setDiceRobotStatus: "setDiceRobotStatus",
|
||||||
setGameStatus: "setGameStatus",
|
setGameStatus: "setGameStatus",
|
||||||
|
setGameWhitelistEnabled: "setGameWhitelistEnabled",
|
||||||
setPrettyIdStatus: "setPrettyIdStatus",
|
setPrettyIdStatus: "setPrettyIdStatus",
|
||||||
setTaskDefinitionStatus: "setTaskDefinitionStatus",
|
setTaskDefinitionStatus: "setTaskDefinitionStatus",
|
||||||
setThirdPartyPaymentMethodStatus: "setThirdPartyPaymentMethodStatus",
|
setThirdPartyPaymentMethodStatus: "setThirdPartyPaymentMethodStatus",
|
||||||
@ -375,6 +379,13 @@ export const API_OPERATIONS = {
|
|||||||
export type ApiOperationId = (typeof API_OPERATIONS)[keyof typeof API_OPERATIONS];
|
export type ApiOperationId = (typeof API_OPERATIONS)[keyof typeof API_OPERATIONS];
|
||||||
|
|
||||||
export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
|
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: {
|
adjustDicePool: {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
operationId: API_OPERATIONS.adjustDicePool,
|
operationId: API_OPERATIONS.adjustDicePool,
|
||||||
@ -898,6 +909,13 @@ export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
|
|||||||
permission: "app-config:update",
|
permission: "app-config:update",
|
||||||
permissions: ["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: {
|
deleteGift: {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
operationId: API_OPERATIONS.deleteGift,
|
operationId: API_OPERATIONS.deleteGift,
|
||||||
@ -1659,6 +1677,13 @@ export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
|
|||||||
permission: "first-recharge-reward:view",
|
permission: "first-recharge-reward:view",
|
||||||
permissions: ["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: {
|
listGifts: {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
operationId: API_OPERATIONS.listGifts,
|
operationId: API_OPERATIONS.listGifts,
|
||||||
@ -2333,6 +2358,13 @@ export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
|
|||||||
permission: "game:status",
|
permission: "game:status",
|
||||||
permissions: ["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: {
|
setPrettyIdStatus: {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
operationId: API_OPERATIONS.setPrettyIdStatus,
|
operationId: API_OPERATIONS.setPrettyIdStatus,
|
||||||
|
|||||||
105
src/shared/api/generated/schema.d.ts
vendored
105
src/shared/api/generated/schema.d.ts
vendored
@ -1516,6 +1516,54 @@ export interface paths {
|
|||||||
patch: operations["setGameStatus"];
|
patch: operations["setGameStatus"];
|
||||||
trace?: never;
|
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": {
|
"/admin/game/platforms": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@ -8466,6 +8514,63 @@ export interface operations {
|
|||||||
200: components["responses"]["EmptyResponse"];
|
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: {
|
listPlatforms: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user