mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2025-01-18 14:11:32 +01:00
Whitelist bounce gathering (#3511)
* Store bounced whitelist logins * Add allowlist bounce endpoint * Restore locale file indent from master branch * Add UI for allowlist * Update locale * Fix sonar detected bug and implement database tests Affects issues: - Close #2233
This commit is contained in:
parent
24e6af2d03
commit
8116063e62
@ -28,6 +28,7 @@ import com.djrapitops.plan.identification.ServerUUID;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.BanStatusTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.KickStoreTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.StoreAllowlistBounceTransaction;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorContext;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@ -82,6 +83,11 @@ public class PlayerOnlineListener implements Listener {
|
||||
UUID playerUUID = event.getPlayer().getUniqueId();
|
||||
ServerUUID serverUUID = serverInfo.getServerUUID();
|
||||
boolean banned = PlayerLoginEvent.Result.KICK_BANNED == event.getResult();
|
||||
boolean notWhitelisted = PlayerLoginEvent.Result.KICK_WHITELIST == event.getResult();
|
||||
|
||||
if (notWhitelisted) {
|
||||
dbSystem.getDatabase().executeTransaction(new StoreAllowlistBounceTransaction(playerUUID, event.getPlayer().getName(), serverUUID, System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
String address = event.getHostname();
|
||||
if (!address.isEmpty()) {
|
||||
|
@ -93,6 +93,7 @@ public enum WebPermission implements Supplier<String>, Lang {
|
||||
PAGE_SERVER_PERFORMANCE_OVERVIEW("See Performance numbers"),
|
||||
PAGE_SERVER_PLUGIN_HISTORY("See Plugin History"),
|
||||
PAGE_SERVER_PLUGINS("See Plugins -tabs of servers"),
|
||||
PAGE_SERVER_ALLOWLIST_BOUNCE("See list of Game allowlist bounces"),
|
||||
|
||||
PAGE_PLAYER("See all of player page"),
|
||||
PAGE_PLAYER_OVERVIEW("See Player Overview -tab"),
|
||||
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.delivery.domain.datatransfer;
|
||||
|
||||
import com.djrapitops.plan.utilities.dev.Untrusted;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents an event where player bounced off the whitelist.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
*/
|
||||
public class AllowlistBounce {
|
||||
|
||||
private final UUID playerUUID;
|
||||
@Untrusted
|
||||
private final String playerName;
|
||||
private final int count;
|
||||
private final long lastTime;
|
||||
|
||||
public AllowlistBounce(UUID playerUUID, String playerName, int count, long lastTime) {
|
||||
this.playerUUID = playerUUID;
|
||||
this.playerName = playerName;
|
||||
this.count = count;
|
||||
this.lastTime = lastTime;
|
||||
}
|
||||
|
||||
public UUID getPlayerUUID() {
|
||||
return playerUUID;
|
||||
}
|
||||
|
||||
public String getPlayerName() {
|
||||
return playerName;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public long getLastTime() {
|
||||
return lastTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
AllowlistBounce bounce = (AllowlistBounce) o;
|
||||
return getCount() == bounce.getCount() && getLastTime() == bounce.getLastTime() && Objects.equals(getPlayerUUID(), bounce.getPlayerUUID()) && Objects.equals(getPlayerName(), bounce.getPlayerName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getPlayerUUID(), getPlayerName(), getCount(), getLastTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AllowlistBounce{" +
|
||||
"playerUUID=" + playerUUID +
|
||||
", playerName='" + playerName + '\'' +
|
||||
", count=" + count +
|
||||
", lastTime=" + lastTime +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -57,6 +57,7 @@ public enum DataID {
|
||||
JOIN_ADDRESSES_BY_DAY,
|
||||
PLAYER_RETENTION,
|
||||
PLAYER_JOIN_ADDRESSES,
|
||||
PLAYER_ALLOWLIST_BOUNCES,
|
||||
;
|
||||
|
||||
public String of(ServerUUID serverUUID) {
|
||||
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.delivery.webserver.resolver.json;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.auth.WebPermission;
|
||||
import com.djrapitops.plan.delivery.formatting.Formatter;
|
||||
import com.djrapitops.plan.delivery.web.resolver.MimeType;
|
||||
import com.djrapitops.plan.delivery.web.resolver.Response;
|
||||
import com.djrapitops.plan.delivery.web.resolver.request.Request;
|
||||
import com.djrapitops.plan.delivery.web.resolver.request.WebUser;
|
||||
import com.djrapitops.plan.delivery.webserver.cache.AsyncJSONResolverService;
|
||||
import com.djrapitops.plan.delivery.webserver.cache.DataID;
|
||||
import com.djrapitops.plan.delivery.webserver.cache.JSONStorage;
|
||||
import com.djrapitops.plan.identification.Identifiers;
|
||||
import com.djrapitops.plan.identification.ServerUUID;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.Database;
|
||||
import com.djrapitops.plan.storage.database.queries.objects.AllowlistQueries;
|
||||
import com.djrapitops.plan.storage.database.queries.objects.SessionQueries;
|
||||
import com.djrapitops.plan.utilities.dev.Untrusted;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.ExampleObject;
|
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Response resolver to get game allowlist bounces.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
*/
|
||||
@Singleton
|
||||
@Path("/v1/gameAllowlistBounces")
|
||||
public class AllowlistJSONResolver extends JSONResolver {
|
||||
|
||||
private final DBSystem dbSystem;
|
||||
private final Identifiers identifiers;
|
||||
private final AsyncJSONResolverService jsonResolverService;
|
||||
|
||||
@Inject
|
||||
public AllowlistJSONResolver(DBSystem dbSystem, Identifiers identifiers, AsyncJSONResolverService jsonResolverService) {
|
||||
this.dbSystem = dbSystem;
|
||||
this.identifiers = identifiers;
|
||||
this.jsonResolverService = jsonResolverService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Formatter<Long> getHttpLastModifiedFormatter() {return jsonResolverService.getHttpLastModifiedFormatter();}
|
||||
|
||||
@Override
|
||||
public boolean canAccess(@Untrusted Request request) {
|
||||
WebUser user = request.getUser().orElse(new WebUser(""));
|
||||
|
||||
return user.hasPermission(WebPermission.PAGE_SERVER_ALLOWLIST_BOUNCE);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Operation(
|
||||
description = "Get allowlist bounce data for server",
|
||||
responses = {
|
||||
@ApiResponse(responseCode = "200", content = @Content(mediaType = MimeType.JSON)),
|
||||
@ApiResponse(responseCode = "400", description = "If 'server' parameter is not an existing server")
|
||||
},
|
||||
parameters = @Parameter(in = ParameterIn.QUERY, name = "server", description = "Server identifier to get data for (optional)", examples = {
|
||||
@ExampleObject("Server 1"),
|
||||
@ExampleObject("1"),
|
||||
@ExampleObject("1fb39d2a-eb82-4868-b245-1fad17d823b3"),
|
||||
}),
|
||||
requestBody = @RequestBody(content = @Content(examples = @ExampleObject()))
|
||||
)
|
||||
@Override
|
||||
public Optional<Response> resolve(@Untrusted Request request) {
|
||||
return Optional.of(getResponse(request));
|
||||
}
|
||||
|
||||
private Response getResponse(@Untrusted Request request) {
|
||||
JSONStorage.StoredJSON result = getStoredJSON(request);
|
||||
return getCachedOrNewResponse(request, result);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JSONStorage.StoredJSON getStoredJSON(Request request) {
|
||||
Optional<Long> timestamp = Identifiers.getTimestamp(request);
|
||||
|
||||
ServerUUID serverUUID = identifiers.getServerUUID(request);
|
||||
Database database = dbSystem.getDatabase();
|
||||
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.lastSeen(serverUUID))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -90,6 +90,7 @@ public class RootJSONResolver {
|
||||
RetentionJSONResolver retentionJSONResolver,
|
||||
PlayerJoinAddressJSONResolver playerJoinAddressJSONResolver,
|
||||
PluginHistoryJSONResolver pluginHistoryJSONResolver,
|
||||
AllowlistJSONResolver allowlistJSONResolver,
|
||||
|
||||
PreferencesJSONResolver preferencesJSONResolver,
|
||||
StorePreferencesJSONResolver storePreferencesJSONResolver,
|
||||
@ -129,7 +130,8 @@ public class RootJSONResolver {
|
||||
.add("extensionData", extensionJSONResolver)
|
||||
.add("retention", retentionJSONResolver)
|
||||
.add("joinAddresses", playerJoinAddressJSONResolver)
|
||||
.add("preferences", preferencesJSONResolver);
|
||||
.add("preferences", preferencesJSONResolver)
|
||||
.add("gameAllowlistBounces", allowlistJSONResolver);
|
||||
|
||||
this.webServer = webServer;
|
||||
// These endpoints require authentication to be enabled.
|
||||
|
@ -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"),
|
||||
|
@ -147,6 +147,10 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
public static ThreadLocal<StackTraceElement[]> getTransactionOrigin() {
|
||||
return TRANSACTION_ORIGIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
List<Runnable> unfinishedTransactions = forceCloseTransactionExecutor();
|
||||
@ -187,22 +191,6 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected List<Runnable> forceCloseTransactionExecutor() {
|
||||
if (transactionExecutor == null || transactionExecutor.isShutdown() || transactionExecutor.isTerminated()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
List<Runnable> unfinished = transactionExecutor.shutdownNow();
|
||||
int unfinishedCount = unfinished.size();
|
||||
if (unfinishedCount > 0) {
|
||||
logger.warn(unfinishedCount + " unfinished database transactions were not executed.");
|
||||
}
|
||||
return unfinished;
|
||||
} finally {
|
||||
logger.info(locale.getString(PluginLang.DISABLED_WAITING_TRANSACTIONS_COMPLETE));
|
||||
}
|
||||
}
|
||||
|
||||
Patch[] patches() {
|
||||
return new Patch[]{
|
||||
new Version10Patch(),
|
||||
@ -313,6 +301,22 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
*/
|
||||
public abstract void setupDataSource();
|
||||
|
||||
protected List<Runnable> forceCloseTransactionExecutor() {
|
||||
if (transactionExecutor == null || transactionExecutor.isShutdown() || transactionExecutor.isTerminated()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
List<Runnable> unfinished = transactionExecutor.shutdownNow();
|
||||
int unfinishedCount = unfinished.size();
|
||||
if (unfinishedCount > 0) {
|
||||
logger.warn(unfinishedCount + " unfinished database transactions were not executed.");
|
||||
}
|
||||
return unfinished;
|
||||
} finally {
|
||||
logger.info(locale.getString(PluginLang.DISABLED_WAITING_TRANSACTIONS_COMPLETE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// SQLiteDB Overrides this, so any additions to this should also be reflected there.
|
||||
@ -326,13 +330,6 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
setState(State.CLOSED);
|
||||
}
|
||||
|
||||
protected void unloadDriverClassloader() {
|
||||
// Unloading class loader using close() causes issues when reloading.
|
||||
// It is better to leak this memory than crash the plugin on reload.
|
||||
|
||||
driverClassLoader = null;
|
||||
}
|
||||
|
||||
public abstract Connection getConnection() throws SQLException;
|
||||
|
||||
public abstract void returnToPool(Connection connection);
|
||||
@ -346,8 +343,11 @@ public abstract class SQLDB extends AbstractDatabase {
|
||||
return accessLock.performDatabaseOperation(() -> query.executeQuery(this), transaction);
|
||||
}
|
||||
|
||||
public static ThreadLocal<StackTraceElement[]> getTransactionOrigin() {
|
||||
return TRANSACTION_ORIGIN;
|
||||
protected void unloadDriverClassloader() {
|
||||
// Unloading class loader using close() causes issues when reloading.
|
||||
// It is better to leak this memory than crash the plugin on reload.
|
||||
|
||||
driverClassLoader = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.storage.database.queries.objects;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.datatransfer.AllowlistBounce;
|
||||
import com.djrapitops.plan.identification.ServerUUID;
|
||||
import com.djrapitops.plan.storage.database.queries.Query;
|
||||
import com.djrapitops.plan.storage.database.sql.tables.AllowlistBounceTable;
|
||||
import com.djrapitops.plan.storage.database.sql.tables.ServerTable;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
|
||||
|
||||
/**
|
||||
* Query against {@link AllowlistBounceTable}.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
*/
|
||||
public class AllowlistQueries {
|
||||
|
||||
private AllowlistQueries() {
|
||||
/* Static method class */
|
||||
}
|
||||
|
||||
public static Query<List<AllowlistBounce>> getBounces(ServerUUID serverUUID) {
|
||||
@Language("SQL") String sql = SELECT +
|
||||
AllowlistBounceTable.UUID + ',' +
|
||||
AllowlistBounceTable.USER_NAME + ',' +
|
||||
AllowlistBounceTable.TIMES + ',' +
|
||||
AllowlistBounceTable.LAST_BOUNCE +
|
||||
FROM + AllowlistBounceTable.TABLE_NAME +
|
||||
WHERE + AllowlistBounceTable.SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID;
|
||||
return db -> db.queryList(sql, AllowlistQueries::extract, serverUUID);
|
||||
}
|
||||
|
||||
private static AllowlistBounce extract(ResultSet set) throws SQLException {
|
||||
return new AllowlistBounce(
|
||||
UUID.fromString(set.getString(AllowlistBounceTable.UUID)),
|
||||
set.getString(AllowlistBounceTable.USER_NAME),
|
||||
set.getInt(AllowlistBounceTable.TIMES),
|
||||
set.getLong(AllowlistBounceTable.LAST_BOUNCE)
|
||||
);
|
||||
}
|
||||
}
|
@ -1014,4 +1014,16 @@ public class SessionQueries {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Query<Map<UUID, Long>> lastSeen(ServerUUID serverUUID) {
|
||||
String sql = SELECT + UsersTable.USER_UUID + ", MAX(" + SessionsTable.SESSION_END + ") as last_seen" +
|
||||
FROM + SessionsTable.TABLE_NAME + " s" +
|
||||
INNER_JOIN + UsersTable.TABLE_NAME + " u ON u." + UsersTable.ID + "=s." + SessionsTable.USER_ID +
|
||||
WHERE + SessionsTable.SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID +
|
||||
GROUP_BY + UsersTable.USER_UUID;
|
||||
return db -> db.queryMap(sql, (set, to) -> to.put(
|
||||
UUID.fromString(set.getString(UsersTable.USER_UUID)),
|
||||
set.getLong("last_seen")
|
||||
), serverUUID);
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.storage.database.sql.tables;
|
||||
|
||||
import com.djrapitops.plan.storage.database.DBType;
|
||||
import com.djrapitops.plan.storage.database.sql.building.CreateTableBuilder;
|
||||
import com.djrapitops.plan.storage.database.sql.building.Sql;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
/**
|
||||
* Represents plan_allowlist_bounce table.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
*/
|
||||
public class AllowlistBounceTable {
|
||||
|
||||
public static final String TABLE_NAME = "plan_allowlist_bounce";
|
||||
|
||||
public static final String ID = "id";
|
||||
public static final String UUID = "uuid";
|
||||
public static final String USER_NAME = "name";
|
||||
public static final String SERVER_ID = "server_id";
|
||||
public static final String TIMES = "times";
|
||||
public static final String LAST_BOUNCE = "last_bounce";
|
||||
|
||||
@Language("SQL")
|
||||
public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" +
|
||||
UUID + ',' +
|
||||
USER_NAME + ',' +
|
||||
SERVER_ID + ',' +
|
||||
TIMES + ',' +
|
||||
LAST_BOUNCE +
|
||||
") VALUES (?,?," + ServerTable.SELECT_SERVER_ID + ",?,?)";
|
||||
|
||||
@Language("SQL")
|
||||
public static final String INCREMENT_TIMES_STATEMENT = "UPDATE " + TABLE_NAME +
|
||||
" SET " + TIMES + "=" + TIMES + "+1, " + LAST_BOUNCE + "=?" +
|
||||
" WHERE " + UUID + "=?" +
|
||||
" AND " + SERVER_ID + "=" + ServerTable.SELECT_SERVER_ID;
|
||||
|
||||
private AllowlistBounceTable() {
|
||||
/* Static information class */
|
||||
}
|
||||
|
||||
public static String createTableSQL(DBType dbType) {
|
||||
return CreateTableBuilder.create(TABLE_NAME, dbType)
|
||||
.column(ID, Sql.INT).primaryKey()
|
||||
.column(UUID, Sql.varchar(36)).notNull().unique()
|
||||
.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();
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ import com.djrapitops.plan.storage.database.sql.building.Insert;
|
||||
import com.djrapitops.plan.storage.database.sql.building.Sql;
|
||||
import com.djrapitops.plan.storage.database.sql.building.Update;
|
||||
import org.apache.commons.text.TextStringBuilder;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@ -61,6 +62,7 @@ public class ServerTable {
|
||||
.where(SERVER_UUID + "=?")
|
||||
.toString();
|
||||
|
||||
@Language("SQL")
|
||||
public static final String SELECT_SERVER_ID =
|
||||
'(' + SELECT + TABLE_NAME + '.' + ID +
|
||||
FROM + TABLE_NAME +
|
||||
|
@ -43,6 +43,7 @@ public class RemoveEverythingTransaction extends Patch {
|
||||
clearTable(WorldTimesTable.TABLE_NAME);
|
||||
clearTable(SessionsTable.TABLE_NAME);
|
||||
clearTable(JoinAddressTable.TABLE_NAME);
|
||||
clearTable(AllowlistBounceTable.TABLE_NAME);
|
||||
clearTable(WorldTable.TABLE_NAME);
|
||||
clearTable(PingTable.TABLE_NAME);
|
||||
clearTable(UserInfoTable.TABLE_NAME);
|
||||
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.storage.database.transactions.events;
|
||||
|
||||
import com.djrapitops.plan.identification.ServerUUID;
|
||||
import com.djrapitops.plan.storage.database.sql.tables.AllowlistBounceTable;
|
||||
import com.djrapitops.plan.storage.database.transactions.ExecStatement;
|
||||
import com.djrapitops.plan.storage.database.transactions.Transaction;
|
||||
import com.djrapitops.plan.utilities.dev.Untrusted;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Stores a bounced allowlist login.
|
||||
*
|
||||
* @author AuroraLS3
|
||||
*/
|
||||
public class StoreAllowlistBounceTransaction extends Transaction {
|
||||
|
||||
private final UUID playerUUID;
|
||||
@Untrusted
|
||||
private final String playerName;
|
||||
private final ServerUUID serverUUID;
|
||||
private final long time;
|
||||
|
||||
public StoreAllowlistBounceTransaction(UUID playerUUID, @Untrusted String playerName, ServerUUID serverUUID, long time) {
|
||||
this.playerUUID = playerUUID;
|
||||
this.playerName = playerName;
|
||||
this.serverUUID = serverUUID;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performOperations() {
|
||||
boolean updated = execute(new ExecStatement(AllowlistBounceTable.INCREMENT_TIMES_STATEMENT) {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) throws SQLException {
|
||||
statement.setLong(1, time);
|
||||
statement.setString(2, playerUUID.toString());
|
||||
statement.setString(3, serverUUID.toString());
|
||||
}
|
||||
});
|
||||
if (!updated) {
|
||||
execute(new ExecStatement(AllowlistBounceTable.INSERT_STATEMENT) {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) throws SQLException {
|
||||
statement.setString(1, playerUUID.toString());
|
||||
statement.setString(2, playerName);
|
||||
statement.setString(3, serverUUID.toString());
|
||||
statement.setInt(4, 1);
|
||||
statement.setLong(5, time);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -59,6 +59,7 @@ public class CreateTablesTransaction extends OperationCriticalTransaction {
|
||||
executeOther(new SecurityTableIdPatch());
|
||||
execute(WebUserPreferencesTable.createTableSQL(dbType));
|
||||
execute(PluginVersionTable.createTableSQL(dbType));
|
||||
execute(AllowlistBounceTable.createTableSQL(dbType));
|
||||
|
||||
// DataExtension tables
|
||||
execute(ExtensionIconTable.createTableSQL(dbType));
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "挂机时间"
|
||||
all: "全部"
|
||||
allTime: "所有时间"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "按字母顺序"
|
||||
apply: "应用"
|
||||
asNumbers: "数据"
|
||||
attempts: "Attempts"
|
||||
average: "平均"
|
||||
averageActivePlaytime: "平均活跃时间"
|
||||
averageAfkTime: "平均挂机时间"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "已被封禁"
|
||||
bestPeak: "历史最高峰值"
|
||||
bestPing: "最低延迟"
|
||||
blocked: "Blocked"
|
||||
calendar: " 日历"
|
||||
comparing7days: "对比 7 天的情况"
|
||||
connectionInfo: "连接信息"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "过去 24 小时"
|
||||
last30days: "过去 30 天"
|
||||
last7days: "过去 7 天"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "最后连接时间"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "上次在线峰值"
|
||||
lastSeen: "最后在线时间"
|
||||
latestJoinAddresses: "上一次加入地址"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "查看玩家会话 - 选项卡"
|
||||
page_player_versus: "查看PvP和PvE - 选项卡"
|
||||
page_server: "查看所有服务器页面"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "查看服务器地理位置 - 选项卡"
|
||||
page_server_geolocations_map: "查看服务器地理位置地图"
|
||||
page_server_geolocations_ping_per_country: "查看按国家划分的延迟表"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "AFK čas"
|
||||
all: "Vše"
|
||||
allTime: "Celkově"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "Abecední řazení"
|
||||
apply: "Apply"
|
||||
asNumbers: "statistiky"
|
||||
attempts: "Attempts"
|
||||
average: "Průměrná délka prvního připojení"
|
||||
averageActivePlaytime: "Průměrná herní aktivita"
|
||||
averageAfkTime: "Průměrný AFK čas"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "Zabanován"
|
||||
bestPeak: "Nejvíce hráčů"
|
||||
bestPing: "Nejlepší ping"
|
||||
blocked: "Blocked"
|
||||
calendar: " Kalendář"
|
||||
comparing7days: "Srovnání posledních 7 dní"
|
||||
connectionInfo: "Informace o připojení"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "Posledních 24 hodin"
|
||||
last30days: "Posledních 30 dní"
|
||||
last7days: "Posledních 7 dní"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "Poslední připojení"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "Naposledy nejvíce hráčů"
|
||||
lastSeen: "Naposledy viděn"
|
||||
latestJoinAddresses: "Poslední adresy pro připojení"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "See Player Sessions -tab"
|
||||
page_player_versus: "See PvP & PvE -tab"
|
||||
page_server: "See all of server page"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "See Geolocations tab"
|
||||
page_server_geolocations_map: "See Geolocations Map"
|
||||
page_server_geolocations_ping_per_country: "See Ping Per Country table"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "AFK Zeit"
|
||||
all: "Gesamt"
|
||||
allTime: "Gesamte zeit"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "als Zahlen"
|
||||
attempts: "Attempts"
|
||||
average: "Average first session length"
|
||||
averageActivePlaytime: "Durchschnittliche aktive Spielzeit"
|
||||
averageAfkTime: "Durchschnittliche AFK Zeit"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "Gebannt"
|
||||
bestPeak: "Rekord"
|
||||
bestPing: "Bester Ping"
|
||||
blocked: "Blocked"
|
||||
calendar: " Kalender"
|
||||
comparing7days: "Vergleiche 7 Tage"
|
||||
connectionInfo: "Verbindungsinformationen"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "Letzte 24 Stunden"
|
||||
last30days: "Letzte 30 Tage"
|
||||
last7days: "Letzte 7 Tage"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "Letzte Verbindung"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "Letzter Höchststand"
|
||||
lastSeen: "Zuletzt gesehen"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "See Player Sessions -tab"
|
||||
page_player_versus: "See PvP & PvE -tab"
|
||||
page_server: "See all of server page"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "See Geolocations tab"
|
||||
page_server_geolocations_map: "See Geolocations Map"
|
||||
page_server_geolocations_ping_per_country: "See Ping Per Country table"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "AFK Time"
|
||||
all: "All"
|
||||
allTime: "All Time"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "as Numbers"
|
||||
attempts: "Attempts"
|
||||
average: "Average first session length"
|
||||
averageActivePlaytime: "Average Active Playtime"
|
||||
averageAfkTime: "Average AFK Time"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "Banned"
|
||||
bestPeak: "All Time Peak"
|
||||
bestPing: "Best Ping"
|
||||
blocked: "Blocked"
|
||||
calendar: " Calendar"
|
||||
comparing7days: "Comparing 7 days"
|
||||
connectionInfo: "Connection Information"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "Last 24 hours"
|
||||
last30days: "Last 30 days"
|
||||
last7days: "Last 7 days"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "Last Connected"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "Last Peak"
|
||||
lastSeen: "Last Seen"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "See Player Sessions -tab"
|
||||
page_player_versus: "See PvP & PvE -tab"
|
||||
page_server: "See all of server page"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "See Geolocations tab"
|
||||
page_server_geolocations_map: "See Geolocations Map"
|
||||
page_server_geolocations_ping_per_country: "See Ping Per Country table"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "Tiempo AFK"
|
||||
all: "Todo"
|
||||
allTime: "Todo el tiempo"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "como números"
|
||||
attempts: "Attempts"
|
||||
average: "Average first session length"
|
||||
averageActivePlaytime: "Tiempo de juego activo promedio"
|
||||
averageAfkTime: "Tiempo AFK promedio"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "Baneado"
|
||||
bestPeak: "Mejor pico"
|
||||
bestPing: "Mejor Ping"
|
||||
blocked: "Blocked"
|
||||
calendar: " Calendario"
|
||||
comparing7days: "Comparando 7 dias"
|
||||
connectionInfo: "Información de conexión"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "Últimas 24 horas"
|
||||
last30days: "Últimos 30 dias"
|
||||
last7days: "Últimos 7 días"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "Última vez conectado"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "Último pico"
|
||||
lastSeen: "Última vez visto"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "See Player Sessions -tab"
|
||||
page_player_versus: "See PvP & PvE -tab"
|
||||
page_server: "See all of server page"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "See Geolocations tab"
|
||||
page_server_geolocations_map: "See Geolocations Map"
|
||||
page_server_geolocations_ping_per_country: "See Ping Per Country table"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "Aika AFK:ina"
|
||||
all: "Kaikki"
|
||||
allTime: "Kaikkien aikojen"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "Aakkosjärjestys"
|
||||
apply: "Käytä"
|
||||
asNumbers: "Numeroina"
|
||||
attempts: "Attempts"
|
||||
average: "Keskimäräinen"
|
||||
averageActivePlaytime: "Keskimäräinen Aktiivinen peliaika"
|
||||
averageAfkTime: "Keskimäräinen AFK aika"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "Pannassa"
|
||||
bestPeak: "Paras Huippu"
|
||||
bestPing: "Paras Vasteaika"
|
||||
blocked: "Blocked"
|
||||
calendar: " Kalenteri"
|
||||
comparing7days: "Verrataan 7 päivää"
|
||||
connectionInfo: "Yhteyksien tiedot"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "Viimeiset 24 tuntia"
|
||||
last30days: "Viimeiset 30 päivää"
|
||||
last7days: "Viimeiset 7 päivää"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "Viimeisin yhteys"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "Viimeisin huippu"
|
||||
lastSeen: "Nähty Viimeksi"
|
||||
latestJoinAddresses: "Viimeisimmät Liittymisosoitteet"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "Näkee Pelaajan Istunnot osion"
|
||||
page_player_versus: "Näkee PvP & PvE osion"
|
||||
page_server: "Näkee koko palvelin sivun"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "Näkee Geolokaatio osion"
|
||||
page_server_geolocations_map: "Näkee Geolokaatio kartan"
|
||||
page_server_geolocations_ping_per_country: "Näkee Viive per Maa -taulun"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "Temps AFK"
|
||||
all: "Tout"
|
||||
allTime: "Tout le Temps"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "en Chiffres"
|
||||
attempts: "Attempts"
|
||||
average: "Average first session length"
|
||||
averageActivePlaytime: "Temps Actif moyen"
|
||||
averageAfkTime: "Temps AFK moyen"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "Banni(e)"
|
||||
bestPeak: "Pic maximal de Joueurs en Ligne"
|
||||
bestPing: "Meilleure Latence"
|
||||
blocked: "Blocked"
|
||||
calendar: " Calendrier"
|
||||
comparing7days: "Comparaison des 7 derniers Jours"
|
||||
connectionInfo: "Renseignements sur la Connexion"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "24 Dernières heures"
|
||||
last30days: "30 Derniers jours"
|
||||
last7days: "7 Derniers jours"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "Dernier Connecté"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "Dernier pic de Joueurs en Ligne"
|
||||
lastSeen: "Dernière Connexion"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "See Player Sessions -tab"
|
||||
page_player_versus: "See PvP & PvE -tab"
|
||||
page_server: "See all of server page"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "See Geolocations tab"
|
||||
page_server_geolocations_map: "See Geolocations Map"
|
||||
page_server_geolocations_ping_per_country: "See Ping Per Country table"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "Tempo AFK"
|
||||
all: "Tutto"
|
||||
allTime: "Tutto il Tempo"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "Statistiche"
|
||||
attempts: "Attempts"
|
||||
average: "Average first session length"
|
||||
averageActivePlaytime: "Average Active Playtime"
|
||||
averageAfkTime: "Average AFK Time"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "Bannato"
|
||||
bestPeak: "Record Migliore"
|
||||
bestPing: "Ping Migliore"
|
||||
blocked: "Blocked"
|
||||
calendar: " Calendario"
|
||||
comparing7days: "Comparazione di 7 giorni"
|
||||
connectionInfo: "Informazioni sulla Connessione"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "Ultime 24 ore"
|
||||
last30days: "Ultimi 30 giorni"
|
||||
last7days: "Ultimi 7 giorni"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "Ultima connessione"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "Record Settimanale"
|
||||
lastSeen: "Ultima Visita"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "See Player Sessions -tab"
|
||||
page_player_versus: "See PvP & PvE -tab"
|
||||
page_server: "See all of server page"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "See Geolocations tab"
|
||||
page_server_geolocations_map: "See Geolocations Map"
|
||||
page_server_geolocations_ping_per_country: "See Ping Per Country table"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "離席時間"
|
||||
all: "全て"
|
||||
allTime: "全体"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "アルファベット順"
|
||||
apply: "適用"
|
||||
asNumbers: "の情報"
|
||||
attempts: "Attempts"
|
||||
average: "平均の初回セッション時間"
|
||||
averageActivePlaytime: "平均アクティブプレイ時間"
|
||||
averageAfkTime: "平均AFK時間"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "BAN履歴"
|
||||
bestPeak: "全体のピークタイム"
|
||||
bestPing: "最高Ping値"
|
||||
blocked: "Blocked"
|
||||
calendar: "カレンダー"
|
||||
comparing7days: "直近1週間との比較"
|
||||
connectionInfo: "接続情報"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "24時間"
|
||||
last30days: "1ヶ月"
|
||||
last7days: "1週間"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "直近の接続"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "直近のピークタイム"
|
||||
lastSeen: "直近のオンライン"
|
||||
latestJoinAddresses: "最後に参加したサーバーのアドレス"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "プレイヤーセッションタブを表示"
|
||||
page_player_versus: "PvP & PvEタブを表示"
|
||||
page_server: "全てのサーバーページを表示"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "ジオロケーションタブを表示"
|
||||
page_server_geolocations_map: "ジオロケーションマップを表示"
|
||||
page_server_geolocations_ping_per_country: "国ごとのPing表を表示"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "AFK 시간"
|
||||
all: "모두"
|
||||
allTime: "모든 시간"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "숫자로"
|
||||
attempts: "Attempts"
|
||||
average: "Average first session length"
|
||||
averageActivePlaytime: "Average Active Playtime"
|
||||
averageAfkTime: "Average AFK Time"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "Banned"
|
||||
bestPeak: "최고의 피크"
|
||||
bestPing: "최고 Ping"
|
||||
blocked: "Blocked"
|
||||
calendar: " 달력"
|
||||
comparing7days: "지난 7일 비교"
|
||||
connectionInfo: "연결 정보"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "지난 24시간"
|
||||
last30days: "지난 30일"
|
||||
last7days: "지난 7일"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "마지막 연결"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "마지막 피크"
|
||||
lastSeen: "마지막으로 본"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "See Player Sessions -tab"
|
||||
page_player_versus: "See PvP & PvE -tab"
|
||||
page_server: "See all of server page"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "See Geolocations tab"
|
||||
page_server_geolocations_map: "See Geolocations Map"
|
||||
page_server_geolocations_ping_per_country: "See Ping Per Country table"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "AFK Tijd"
|
||||
all: "Alle"
|
||||
allTime: "Alle Tijd"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "als nummers"
|
||||
attempts: "Attempts"
|
||||
average: "Average first session length"
|
||||
averageActivePlaytime: "Gemiddelde Actieve Speeltijd"
|
||||
averageAfkTime: "Gemiddelde AFK Tijd"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "Verbannen"
|
||||
bestPeak: "Piek aller tijden"
|
||||
bestPing: "Beste ping"
|
||||
blocked: "Blocked"
|
||||
calendar: " Kalender"
|
||||
comparing7days: "7 dagen vergelijken"
|
||||
connectionInfo: "Verbindingsinformatie"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "Afgelopen 24 uur"
|
||||
last30days: "Afgelopen 30 dagen"
|
||||
last7days: "Afgelopen 7 dagen"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "Laatst verbonden"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "Laatste piek"
|
||||
lastSeen: "Laatste gezien"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "See Player Sessions -tab"
|
||||
page_player_versus: "See PvP & PvE -tab"
|
||||
page_server: "See all of server page"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "See Geolocations tab"
|
||||
page_server_geolocations_map: "See Geolocations Map"
|
||||
page_server_geolocations_ping_per_country: "See Ping Per Country table"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "AFK Time"
|
||||
all: "Todos"
|
||||
allTime: "All Time"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "as Numbers"
|
||||
attempts: "Attempts"
|
||||
average: "Average first session length"
|
||||
averageActivePlaytime: "Average Active Playtime"
|
||||
averageAfkTime: "Average AFK Time"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "Banido"
|
||||
bestPeak: "Pico Máximo"
|
||||
bestPing: "Best Ping"
|
||||
blocked: "Blocked"
|
||||
calendar: " Calendário"
|
||||
comparing7days: "Comparing 7 days"
|
||||
connectionInfo: "Connection Information"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "Últimas 24 horas"
|
||||
last30days: "Últimos 30 dias"
|
||||
last7days: "Últimos 7 dias"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "Última Conexão"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "Último Pico"
|
||||
lastSeen: "Última Vez Visto"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "See Player Sessions -tab"
|
||||
page_player_versus: "See PvP & PvE -tab"
|
||||
page_server: "See all of server page"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "See Geolocations tab"
|
||||
page_server_geolocations_map: "See Geolocations Map"
|
||||
page_server_geolocations_ping_per_country: "See Ping Per Country table"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "Время AFK"
|
||||
all: "Все"
|
||||
allTime: "Все время"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "В числах"
|
||||
attempts: "Attempts"
|
||||
average: "Средняя продолжительность первого сеанса"
|
||||
averageActivePlaytime: "Среднее время активной игры"
|
||||
averageAfkTime: "Среднее время AFK"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "Забанен"
|
||||
bestPeak: "Максимальный Пик"
|
||||
bestPing: "Наилучший пинг"
|
||||
blocked: "Blocked"
|
||||
calendar: " Календарь"
|
||||
comparing7days: "Сравнение 7 дней"
|
||||
connectionInfo: "Информация о соединении"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "Последние 24 часа"
|
||||
last30days: "Последние 30 дней"
|
||||
last7days: "Последние 7 дней"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "Последнее подключение"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "Последний Пик"
|
||||
lastSeen: "Последнее посещение"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "See Player Sessions -tab"
|
||||
page_player_versus: "See PvP & PvE -tab"
|
||||
page_server: "See all of server page"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "See Geolocations tab"
|
||||
page_server_geolocations_map: "See Geolocations Map"
|
||||
page_server_geolocations_ping_per_country: "See Ping Per Country table"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "AFK Süresi"
|
||||
all: "Tamamı"
|
||||
allTime: "Tüm zamanlar"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "Alphabetical"
|
||||
apply: "Apply"
|
||||
asNumbers: "Sayılar olarak"
|
||||
attempts: "Attempts"
|
||||
average: "Average first session length"
|
||||
averageActivePlaytime: "Ortalama Aktif Oyun Süresi"
|
||||
averageAfkTime: "Ortalama AFK Süresi"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "Yasaklanmış"
|
||||
bestPeak: "Tüm Zamanların Zirvesi"
|
||||
bestPing: "En iyi Ping"
|
||||
blocked: "Blocked"
|
||||
calendar: " Takvim"
|
||||
comparing7days: "7 gün karşılaştırılıyor"
|
||||
connectionInfo: "Bağlantı Bilgisi"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "Son 24 saat"
|
||||
last30days: "Son 30 gün"
|
||||
last7days: "Son 7 gün"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "Son bağlantı"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "Son Zirve"
|
||||
lastSeen: "Son Görülme"
|
||||
latestJoinAddresses: "Latest Join Addresses"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "See Player Sessions -tab"
|
||||
page_player_versus: "See PvP & PvE -tab"
|
||||
page_server: "See all of server page"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "See Geolocations tab"
|
||||
page_server_geolocations_map: "See Geolocations Map"
|
||||
page_server_geolocations_ping_per_country: "See Ping Per Country table"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "Час AFK"
|
||||
all: "Всі"
|
||||
allTime: "Весь час"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "За алфавітом"
|
||||
apply: "Застосувати"
|
||||
asNumbers: "В числах"
|
||||
attempts: "Attempts"
|
||||
average: "Середня тривалість першого сеансу"
|
||||
averageActivePlaytime: "Середній час активної гри"
|
||||
averageAfkTime: "Середній час AFK"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "Заблокований"
|
||||
bestPeak: "Максимальний Пік"
|
||||
bestPing: "Найкращий пінг"
|
||||
blocked: "Blocked"
|
||||
calendar: "Календар"
|
||||
comparing7days: "Порівняння 7 днів"
|
||||
connectionInfo: "Інформація про з`єднання"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "Останні 24 години"
|
||||
last30days: "Останні 30 днів"
|
||||
last7days: "Останні 7 днів"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "Останнє підключення"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "Останній Пік"
|
||||
lastSeen: "Останнє відвідування"
|
||||
latestJoinAddresses: "Останні адреси приєднання"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "See Player Sessions -tab"
|
||||
page_player_versus: "See PvP & PvE -tab"
|
||||
page_server: "See all of server page"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "See Geolocations tab"
|
||||
page_server_geolocations_map: "See Geolocations Map"
|
||||
page_server_geolocations_ping_per_country: "See Ping Per Country table"
|
||||
|
@ -294,9 +294,13 @@ html:
|
||||
afkTime: "掛機時間"
|
||||
all: "全部"
|
||||
allTime: "所有時間"
|
||||
allowed: "Allowed"
|
||||
allowlist: "Allowlist"
|
||||
allowlistBounces: "Allowlist Bounces"
|
||||
alphabetical: "按字母順序"
|
||||
apply: "確定"
|
||||
asNumbers: "統計"
|
||||
attempts: "Attempts"
|
||||
average: "Average first session length"
|
||||
averageActivePlaytime: "平均活躍時間"
|
||||
averageAfkTime: "平均掛機時間"
|
||||
@ -317,6 +321,7 @@ html:
|
||||
banned: "已被封鎖"
|
||||
bestPeak: "所有時間峰值"
|
||||
bestPing: "最低延遲"
|
||||
blocked: "Blocked"
|
||||
calendar: " 日誌"
|
||||
comparing7days: "對比 7 天的情況"
|
||||
connectionInfo: "連接訊息"
|
||||
@ -430,7 +435,10 @@ html:
|
||||
last24hours: "過去 24 小時"
|
||||
last30days: "過去 30 天"
|
||||
last7days: "過去 7 天"
|
||||
lastAllowed: "Last Allowed"
|
||||
lastBlocked: "Last Blocked"
|
||||
lastConnected: "最後連接時間"
|
||||
lastKnownAttempt: "Last Known Attempt"
|
||||
lastPeak: "上次線上峰值"
|
||||
lastSeen: "最後線上時間"
|
||||
latestJoinAddresses: "最後加入位址"
|
||||
@ -686,6 +694,7 @@ html:
|
||||
page_player_sessions: "See Player Sessions -tab"
|
||||
page_player_versus: "See PvP & PvE -tab"
|
||||
page_server: "See all of server page"
|
||||
page_server_allowlist_bounce: "See list of Game allowlist bounces"
|
||||
page_server_geolocations: "See Geolocations tab"
|
||||
page_server_geolocations_map: "See Geolocations Map"
|
||||
page_server_geolocations_ping_per_country: "See Ping Per Country table"
|
||||
|
@ -163,7 +163,8 @@ class AccessControlTest {
|
||||
Arguments.of("/v1/preferences", WebPermission.ACCESS, 200, 200),
|
||||
Arguments.of("/v1/storePreferences", WebPermission.ACCESS, 400, 400),
|
||||
Arguments.of("/v1/pluginHistory?server=" + TestConstants.SERVER_UUID_STRING, WebPermission.PAGE_NETWORK_PLUGIN_HISTORY, 200, 403),
|
||||
Arguments.of("/v1/pluginHistory?server=" + TestConstants.SERVER_UUID_STRING, WebPermission.PAGE_SERVER_PLUGIN_HISTORY, 200, 403)
|
||||
Arguments.of("/v1/pluginHistory?server=" + TestConstants.SERVER_UUID_STRING, WebPermission.PAGE_SERVER_PLUGIN_HISTORY, 200, 403),
|
||||
Arguments.of("/v1/gameAllowlistBounces?server=" + TestConstants.SERVER_UUID_STRING, WebPermission.PAGE_SERVER_ALLOWLIST_BOUNCE, 200, 403)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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"),
|
||||
|
@ -28,6 +28,7 @@ import com.djrapitops.plan.storage.database.transactions.patches.BadJoinAddressD
|
||||
|
||||
public interface DatabaseTestAggregate extends
|
||||
ActivityIndexQueriesTest,
|
||||
AllowlistQueriesTest,
|
||||
DatabaseBackupTest,
|
||||
ExtensionsDatabaseTest,
|
||||
GeolocationQueriesTest,
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.storage.database.queries;
|
||||
|
||||
import com.djrapitops.plan.delivery.domain.datatransfer.AllowlistBounce;
|
||||
import com.djrapitops.plan.storage.database.DatabaseTestPreparer;
|
||||
import com.djrapitops.plan.storage.database.queries.objects.AllowlistQueries;
|
||||
import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.StoreAllowlistBounceTransaction;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import utilities.TestConstants;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public interface AllowlistQueriesTest extends DatabaseTestPreparer {
|
||||
|
||||
@Test
|
||||
@DisplayName("plan_allowlist_bounce is empty")
|
||||
default void allowListTableIsEmpty() {
|
||||
List<AllowlistBounce> expected = List.of();
|
||||
List<AllowlistBounce> result = db().query(AllowlistQueries.getBounces(serverUUID()));
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("plan_allowlist_bounce is cleared by RemoveEverythingTransaction")
|
||||
default void allowListTableIsEmptyAfterClear() throws ExecutionException, InterruptedException {
|
||||
allowListBounceIsStored();
|
||||
db().executeTransaction(new RemoveEverythingTransaction());
|
||||
allowListTableIsEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Allowlist bounce is stored")
|
||||
default void allowListBounceIsStored() throws ExecutionException, InterruptedException {
|
||||
AllowlistBounce bounce = new AllowlistBounce(TestConstants.PLAYER_ONE_UUID, TestConstants.PLAYER_ONE_NAME, 1, System.currentTimeMillis());
|
||||
db().executeTransaction(new StoreAllowlistBounceTransaction(bounce.getPlayerUUID(), bounce.getPlayerName(), serverUUID(), bounce.getLastTime()))
|
||||
.get();
|
||||
|
||||
List<AllowlistBounce> expected = List.of(bounce);
|
||||
List<AllowlistBounce> result = db().query(AllowlistQueries.getBounces(serverUUID()));
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
}
|
@ -37,6 +37,7 @@ import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythi
|
||||
import com.djrapitops.plan.storage.database.transactions.events.*;
|
||||
import com.djrapitops.plan.utilities.java.Maps;
|
||||
import net.playeranalytics.plugin.scheduling.TimeAmount;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.RepeatedTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import utilities.RandomData;
|
||||
@ -485,4 +486,21 @@ public interface SessionQueriesTest extends DatabaseTestPreparer {
|
||||
Map<String, Long> results = db().query(SessionQueries.playtimePerServer(Long.MIN_VALUE, Long.MAX_VALUE));
|
||||
assertEquals(expected, results);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Last seen query by server uuid groups last seen by player")
|
||||
default void lastSeenByServerIsGroupedByPlayer() {
|
||||
prepareForSessionSave();
|
||||
List<FinishedSession> player1Sessions = RandomData.randomSessions(serverUUID(), worlds, playerUUID, player2UUID);
|
||||
List<FinishedSession> player2Sessions = RandomData.randomSessions(serverUUID(), worlds, player2UUID, playerUUID);
|
||||
player1Sessions.forEach(session -> db().executeTransaction(new StoreSessionTransaction(session)));
|
||||
player2Sessions.forEach(session -> db().executeTransaction(new StoreSessionTransaction(session)));
|
||||
|
||||
long lastSeenP1 = new SessionsMutator(player1Sessions).toLastSeen();
|
||||
long lastSeenP2 = new SessionsMutator(player2Sessions).toLastSeen();
|
||||
|
||||
Map<UUID, Long> expected = Map.of(playerUUID, lastSeenP1, player2UUID, lastSeenP2);
|
||||
Map<UUID, Long> result = db().query(SessionQueries.lastSeen(serverUUID()));
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import com.djrapitops.plan.identification.ServerUUID;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.BanStatusTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.KickStoreTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.StoreAllowlistBounceTransaction;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorContext;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
|
||||
@ -95,6 +96,13 @@ public class PlayerOnlineListener implements Listener {
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerKick(PlayerKickEvent event) {
|
||||
try {
|
||||
if (event.getReasonEnum() == PlayerKickEvent.Reason.NOT_WHITELISTED) {
|
||||
dbSystem.getDatabase().executeTransaction(new StoreAllowlistBounceTransaction(
|
||||
event.getPlayer().getUniqueId(),
|
||||
event.getPlayer().getName(),
|
||||
serverInfo.getServerUUID(), System.currentTimeMillis())
|
||||
);
|
||||
}
|
||||
if (status.areKicksNotCounted() || event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
@ -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;
|
@ -25,7 +25,8 @@ const QueryPlayerListModal = ({open, toggle, queryData, title}) => {
|
||||
<PlayerTable data={queryData?.data?.players || {players: [], extensionDescriptors: []}}
|
||||
orderBy={2}/>}
|
||||
<Modal.Footer>
|
||||
{hasPermission('access.query') && Boolean(queryData?.data?.players.players.length) && <Link className="btn bg-theme"
|
||||
{hasPermission('access.query') && Boolean(queryData?.data?.players.players.length) &&
|
||||
<Link className="btn bg-theme"
|
||||
to={"/query/result?timestamp=" + queryData?.timestamp}>
|
||||
{t('html.query.label.showFullQuery')} <Fa icon={faArrowRight}/>
|
||||
</Link>}
|
||||
|
@ -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"]]
|
||||
}
|
||||
|
||||
const rowKeyFunction = useCallback((row, column) => {
|
||||
return row.player + "-" + (column ? JSON.stringify(column.data) : '');
|
||||
}, []);
|
||||
|
||||
if (!preferencesLoaded) return <></>;
|
||||
|
||||
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'
|
||||
}
|
||||
],
|
||||
},
|
||||
|
35
Plan/react/dashboard/src/views/server/ServerAllowList.jsx
Normal file
35
Plan/react/dashboard/src/views/server/ServerAllowList.jsx
Normal file
@ -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
|
@ -27,8 +27,10 @@ import com.djrapitops.plan.identification.ServerUUID;
|
||||
import com.djrapitops.plan.storage.database.DBSystem;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.BanStatusTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.KickStoreTransaction;
|
||||
import com.djrapitops.plan.storage.database.transactions.events.StoreAllowlistBounceTransaction;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorContext;
|
||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||
import org.spongepowered.api.Game;
|
||||
import org.spongepowered.api.Sponge;
|
||||
import org.spongepowered.api.entity.living.player.Player;
|
||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
||||
@ -54,6 +56,7 @@ public class PlayerOnlineListener {
|
||||
private final PlayerJoinEventConsumer joinEventConsumer;
|
||||
private final PlayerLeaveEventConsumer leaveEventConsumer;
|
||||
|
||||
private final Game game;
|
||||
private final ServerInfo serverInfo;
|
||||
private final DBSystem dbSystem;
|
||||
private final Status status;
|
||||
@ -63,13 +66,14 @@ public class PlayerOnlineListener {
|
||||
public PlayerOnlineListener(
|
||||
PlayerJoinEventConsumer joinEventConsumer,
|
||||
PlayerLeaveEventConsumer leaveEventConsumer,
|
||||
ServerInfo serverInfo,
|
||||
Game game, ServerInfo serverInfo,
|
||||
DBSystem dbSystem,
|
||||
Status status,
|
||||
ErrorLogger errorLogger
|
||||
) {
|
||||
this.joinEventConsumer = joinEventConsumer;
|
||||
this.leaveEventConsumer = leaveEventConsumer;
|
||||
this.game = game;
|
||||
this.serverInfo = serverInfo;
|
||||
this.dbSystem = dbSystem;
|
||||
this.status = status;
|
||||
@ -89,6 +93,18 @@ public class PlayerOnlineListener {
|
||||
GameProfile profile = event.profile();
|
||||
UUID playerUUID = profile.uniqueId();
|
||||
ServerUUID serverUUID = serverInfo.getServerUUID();
|
||||
if (game.server().isWhitelistEnabled()) {
|
||||
game.server().serviceProvider().whitelistService().isWhitelisted(profile)
|
||||
.thenAccept(whitelisted -> {
|
||||
if (Boolean.FALSE.equals(whitelisted)) {
|
||||
dbSystem.getDatabase().executeTransaction(new StoreAllowlistBounceTransaction(
|
||||
playerUUID,
|
||||
event.profile().name().orElse(event.user().uniqueId().toString()),
|
||||
serverUUID,
|
||||
System.currentTimeMillis()));
|
||||
}
|
||||
});
|
||||
}
|
||||
dbSystem.getDatabase().executeTransaction(new BanStatusTransaction(playerUUID, serverUUID, () -> isBanned(profile)));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user