Add UI for allowlist
This commit is contained in:
parent
bf16eb5c65
commit
cd70f6d77a
|
@ -57,6 +57,7 @@ public enum DataID {
|
|||
JOIN_ADDRESSES_BY_DAY,
|
||||
PLAYER_RETENTION,
|
||||
PLAYER_JOIN_ADDRESSES,
|
||||
PLAYER_ALLOWLIST_BOUNCES,
|
||||
;
|
||||
|
||||
public String of(ServerUUID serverUUID) {
|
||||
|
|
|
@ -108,7 +108,7 @@ public class AllowlistJSONResolver extends JSONResolver {
|
|||
|
||||
ServerUUID serverUUID = identifiers.getServerUUID(request);
|
||||
Database database = dbSystem.getDatabase();
|
||||
return jsonResolverService.resolve(timestamp, DataID.PLAYER_RETENTION, serverUUID,
|
||||
return jsonResolverService.resolve(timestamp, DataID.PLAYER_ALLOWLIST_BOUNCES, serverUUID,
|
||||
theUUID -> Map.of(
|
||||
"allowlist_bounces", database.query(AllowlistQueries.getBounces(serverUUID)),
|
||||
"last_seen_by_uuid", database.query(SessionQueries.getLastSeen(serverUUID))
|
||||
|
|
|
@ -285,6 +285,15 @@ public enum HtmlLang implements Lang {
|
|||
LABEL_TABLE_SHOW_PER_PAGE("html.label.table.showPerPage", "Show per page"),
|
||||
LABEL_EXPORT("html.label.export", "Export"),
|
||||
|
||||
LABEL_ALLOWLIST("html.label.allowlist", "Allowlist"),
|
||||
LABEL_ALLOWLIST_BOUNCES("html.label.allowlistBounces", "Allowlist Bounces"),
|
||||
LABEL_ATTEMPTS("html.label.attempts", "Attempts"),
|
||||
LABEL_LAST_KNOWN_ATTEMPT("html.label.lastKnownAttempt", "Last Known Attempt"),
|
||||
LABEL_PREVIOUS_ATTEMPT("html.label.lastBlocked", "Last Blocked"),
|
||||
LABEL_LAST_ALLOWED_LOGIN("html.label.lastAllowed", "Last Allowed"),
|
||||
LABEL_BLOCKED("html.label.blocked", "Blocked"),
|
||||
LABEL_ALLOWED("html.label.allowed", "Allowed"),
|
||||
|
||||
LOGIN_LOGIN("html.login.login", "Login"),
|
||||
LOGIN_LOGOUT("html.login.logout", "Logout"),
|
||||
LOGIN_USERNAME("html.login.username", "Username"),
|
||||
|
|
|
@ -63,6 +63,7 @@ public class AllowlistBounceTable {
|
|||
.column(USER_NAME, Sql.varchar(36)).notNull()
|
||||
.column(SERVER_ID, Sql.INT).notNull()
|
||||
.column(TIMES, Sql.INT).notNull().defaultValue("0")
|
||||
.column(LAST_BOUNCE, Sql.LONG).notNull()
|
||||
.foreignKey(SERVER_ID, ServerTable.TABLE_NAME, ServerTable.ID)
|
||||
.toString();
|
||||
}
|
||||
|
|
|
@ -136,6 +136,7 @@ class AccessControlVisibilityTest {
|
|||
Arguments.arguments(WebPermission.PAGE_SERVER_PLAYER_VERSUS_OVERVIEW, "pvp-pve-as-numbers", "pvppve"),
|
||||
Arguments.arguments(WebPermission.PAGE_SERVER_PLAYER_VERSUS_OVERVIEW, "pvp-pve-insights", "pvppve"),
|
||||
Arguments.arguments(WebPermission.PAGE_SERVER_PLAYER_VERSUS_KILL_LIST, "pvp-kills-table", "pvppve"),
|
||||
Arguments.arguments(WebPermission.PAGE_SERVER_ALLOWLIST_BOUNCE, "allowlist-bounce-table", "allowlist"),
|
||||
Arguments.arguments(WebPermission.PAGE_SERVER_PLAYERBASE_OVERVIEW, "playerbase-trends", "playerbase"),
|
||||
Arguments.arguments(WebPermission.PAGE_SERVER_PLAYERBASE_OVERVIEW, "playerbase-insights", "playerbase"),
|
||||
Arguments.arguments(WebPermission.PAGE_SERVER_PLAYERBASE_GRAPHS, "playerbase-graph", "playerbase"),
|
||||
|
|
|
@ -32,6 +32,7 @@ const ServerOverview = React.lazy(() => import("./views/server/ServerOverview"))
|
|||
const OnlineActivity = React.lazy(() => import("./views/server/OnlineActivity"));
|
||||
const ServerSessions = React.lazy(() => import("./views/server/ServerSessions"));
|
||||
const ServerPvpPve = React.lazy(() => import("./views/server/ServerPvpPve"));
|
||||
const ServerAllowList = React.lazy(() => import("./views/server/ServerAllowList"));
|
||||
const PlayerbaseOverview = React.lazy(() => import("./views/server/PlayerbaseOverview"));
|
||||
const ServerPlayers = React.lazy(() => import("./views/server/ServerPlayers"));
|
||||
const ServerGeolocations = React.lazy(() => import("./views/server/ServerGeolocations"));
|
||||
|
@ -159,6 +160,7 @@ function App() {
|
|||
<Route path="online-activity" element={<Lazy><OnlineActivity/></Lazy>}/>
|
||||
<Route path="sessions" element={<Lazy><ServerSessions/></Lazy>}/>
|
||||
<Route path="pvppve" element={<Lazy><ServerPvpPve/></Lazy>}/>
|
||||
<Route path="allowlist" element={<Lazy><ServerAllowList/></Lazy>}/>
|
||||
<Route path="playerbase" element={<Lazy><PlayerbaseOverview/></Lazy>}/>
|
||||
<Route path="join-addresses" element={<Lazy><ServerJoinAddresses/></Lazy>}/>
|
||||
<Route path="retention" element={<Lazy><ServerPlayerRetention/></Lazy>}/>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import React from "react";
|
||||
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
|
||||
import {faFilterCircleXmark} from "@fortawesome/free-solid-svg-icons";
|
||||
import {Card} from "react-bootstrap";
|
||||
import AllowlistBounceTable from "../../../table/AllowlistBounceTable.jsx";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
const AllowlistBounceTableCard = ({bounces, lastSeen}) => {
|
||||
const {t} = useTranslation();
|
||||
return (
|
||||
<Card id={'allowlist-table'}>
|
||||
<Card.Header>
|
||||
<h6 className="col-black">
|
||||
<Fa icon={faFilterCircleXmark} className="col-orange"/> {t('html.label.allowlistBounces')}
|
||||
</h6>
|
||||
</Card.Header>
|
||||
<AllowlistBounceTable bounces={bounces} lastSeen={lastSeen}/>
|
||||
</Card>
|
||||
)
|
||||
};
|
||||
|
||||
export default AllowlistBounceTableCard;
|
|
@ -0,0 +1,72 @@
|
|||
import React, {useCallback} from "react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
|
||||
import {faDoorOpen, faRepeat, faUser} from "@fortawesome/free-solid-svg-icons";
|
||||
import {usePreferences} from "../../hooks/preferencesHook.jsx";
|
||||
import DataTablesTable from "./DataTablesTable.jsx";
|
||||
import {formatDate, useDatePreferences} from "../text/FormattedDate.jsx";
|
||||
import {faCalendarCheck, faCalendarTimes} from "@fortawesome/free-regular-svg-icons";
|
||||
import {Link} from "react-router-dom";
|
||||
|
||||
const AllowlistBounceTable = ({bounces, lastSeen}) => {
|
||||
const {t} = useTranslation();
|
||||
const {preferencesLoaded} = usePreferences();
|
||||
|
||||
const datePreferences = useDatePreferences();
|
||||
const formatDateEasy = date => {
|
||||
return formatDate(date, datePreferences.offset, datePreferences.pattern, false, datePreferences.recentDaysPattern, t);
|
||||
}
|
||||
|
||||
const columns = [{
|
||||
title: <><Fa icon={faUser}/> {t('html.label.player')}</>,
|
||||
data: {_: "player", display: "link"}
|
||||
}, {
|
||||
title: <><Fa icon={faRepeat}/> {t('html.label.attempts')}</>,
|
||||
data: "attempts"
|
||||
}, {
|
||||
title: <><Fa icon={faDoorOpen}/> {t('html.label.lastKnownAttempt')}</>,
|
||||
data: "lastKnownAttempt"
|
||||
}, {
|
||||
title: <><Fa icon={faCalendarTimes}/> {t('html.label.lastBlocked')}</>,
|
||||
data: {_: "date", display: "dateFormatted"}
|
||||
}, {
|
||||
title: <><Fa icon={faCalendarCheck}/> {t('html.label.lastAllowed')}</>,
|
||||
data: {_: "lastSeen", display: "lastSeenFormatted"}
|
||||
}];
|
||||
|
||||
const rows = bounces.map(bounce => {
|
||||
const seenAfterBounce = bounce.lastBounce < lastSeen[bounce.playerUUID];
|
||||
const playerId = bounce.playerName + ' / ' + bounce.playerUUID;
|
||||
return {
|
||||
player: playerId,
|
||||
link: lastSeen[bounce.playerUUID] ? <Link to={"/player/" + bounce.playerUUID}>{playerId}</Link> : playerId,
|
||||
date: bounce.lastTime,
|
||||
dateFormatted: formatDateEasy(bounce.lastTime),
|
||||
attempts: bounce.count,
|
||||
lastKnownAttempt: seenAfterBounce ? t('html.label.allowed') : t('html.label.blocked'),
|
||||
lastSeen: lastSeen[bounce.playerUUID],
|
||||
lastSeenFormatted: formatDateEasy(lastSeen[bounce.playerUUID])
|
||||
};
|
||||
});
|
||||
const options = {
|
||||
responsive: true,
|
||||
deferRender: true,
|
||||
columns: columns,
|
||||
data: rows,
|
||||
paginationCount: 2,
|
||||
order: [[1, "desc"]]
|
||||
}
|
||||
|
||||
if (!preferencesLoaded) return <></>;
|
||||
|
||||
const rowKeyFunction = useCallback((row, column) => {
|
||||
return row.player + "-" + (column ? JSON.stringify(column.data) : '');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DataTablesTable id={"allowlist-bounce-table"} options={options} colorClass={"bg-orange"}
|
||||
rowKeyFunction={rowKeyFunction}/>
|
||||
)
|
||||
};
|
||||
|
||||
export default AllowlistBounceTable;
|
|
@ -100,6 +100,7 @@ export const fetchPlayersTable = async (timestamp, identifier) => {
|
|||
return await fetchPlayersTableNetwork(timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
const fetchPlayersTableServer = async (timestamp, identifier) => {
|
||||
let url = `/v1/playersTable?server=${identifier}`;
|
||||
if (staticSite) url = `/data/playersTable-${identifier}.json`;
|
||||
|
@ -112,6 +113,12 @@ const fetchPlayersTableNetwork = async (timestamp) => {
|
|||
return doGetRequest(url, timestamp);
|
||||
}
|
||||
|
||||
export const fetchAllowlistBounces = async (timestamp, identifier) => {
|
||||
let url = `/v1/gameAllowlistBounces?server=${identifier}`;
|
||||
if (staticSite) url = `/data/gameAllowlistBounces-${identifier}.json`;
|
||||
return doGetRequest(url, timestamp);
|
||||
}
|
||||
|
||||
export const fetchPingTable = async (timestamp, identifier) => {
|
||||
let url = `/v1/pingTable?server=${identifier}`;
|
||||
if (staticSite) url = `/data/pingTable-${identifier}.json`;
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
faCodeCompare,
|
||||
faCogs,
|
||||
faCubes,
|
||||
faFilterCircleXmark,
|
||||
faGlobe,
|
||||
faInfoCircle,
|
||||
faLocationArrow,
|
||||
|
@ -72,6 +73,12 @@ const ServerSidebar = () => {
|
|||
icon: faCampground,
|
||||
href: "pvppve",
|
||||
permission: 'page.server.player.versus'
|
||||
},
|
||||
{
|
||||
name: 'html.label.allowlist',
|
||||
icon: faFilterCircleXmark,
|
||||
href: "allowlist",
|
||||
permission: 'page.server.allowlist.bounce'
|
||||
}
|
||||
],
|
||||
},
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import React from 'react';
|
||||
import {useDataRequest} from "../../hooks/dataFetchHook";
|
||||
import {useParams} from "react-router-dom";
|
||||
import {fetchAllowlistBounces} from "../../service/serverService";
|
||||
import ErrorView from "../ErrorView";
|
||||
import {Col} from "react-bootstrap";
|
||||
import LoadIn from "../../components/animation/LoadIn";
|
||||
import ExtendableRow from "../../components/layout/extension/ExtendableRow";
|
||||
import {useAuth} from "../../hooks/authenticationHook";
|
||||
import AllowlistBounceTableCard from "../../components/cards/server/tables/AllowlistBounceTableCard.jsx";
|
||||
|
||||
const ServerAllowList = () => {
|
||||
const {hasPermission} = useAuth();
|
||||
const {identifier} = useParams();
|
||||
|
||||
const seeBounce = hasPermission('page.server.allowlist.bounce');
|
||||
const {data, loadingError} = useDataRequest(fetchAllowlistBounces, [identifier], seeBounce);
|
||||
|
||||
if (loadingError) return <ErrorView error={loadingError}/>
|
||||
|
||||
return (
|
||||
<LoadIn>
|
||||
<section className="server-allowlist">
|
||||
{seeBounce && <ExtendableRow id={'row-server-allowlist-0'}>
|
||||
<Col md={12}>
|
||||
<AllowlistBounceTableCard bounces={data?.allowlist_bounces || []}
|
||||
lastSeen={data?.last_seen_by_uuid || {}}/>
|
||||
</Col>
|
||||
</ExtendableRow>}
|
||||
</section>
|
||||
</LoadIn>
|
||||
)
|
||||
};
|
||||
|
||||
export default ServerAllowList
|
Loading…
Reference in New Issue