Reworked database setup and added pagination

The database tables are now automatically created
The history lookup now uses a view for names (for simplicity and readability)
The all history lookup now uses a view combining all punishment history for efficiency
This commit is contained in:
Teriuihi 2025-04-11 18:22:07 +02:00
parent 3c6141f7ad
commit 4b891dd672
13 changed files with 448 additions and 181 deletions

View File

@ -1,5 +1,8 @@
package com.alttd.altitudeweb;
import com.alttd.altitudeweb.setup.Connection;
import com.alttd.altitudeweb.setup.InitializeLiteBans;
import com.alttd.altitudeweb.setup.InitializeWebDb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -8,6 +11,7 @@ public class AltitudeWebApplication {
public static void main(String[] args) {
SpringApplication.run(AltitudeWebApplication.class, args);
Connection.initDatabases();
}
}

View File

@ -1,7 +1,7 @@
package com.alttd.altitudeweb.controllers.history;
import com.alttd.altitudeweb.api.HistoryApi;
import com.alttd.altitudeweb.database.Connection;
import com.alttd.altitudeweb.setup.Connection;
import com.alttd.altitudeweb.database.Databases;
import com.alttd.altitudeweb.database.litebans.*;
import com.alttd.altitudeweb.model.PunishmentHistoryDto;
@ -19,71 +19,52 @@ import java.util.concurrent.CompletableFuture;
public class HistoryApiController implements HistoryApi {
@Override
public ResponseEntity<PunishmentHistoryDto> getHistoryForUsers(String userType, String type, String user) throws Exception {
UserType userTypeEnum = UserType.getUserType(userType);
HistoryType historyTypeEnum = HistoryType.getHistoryType(type);
PunishmentHistoryDto punishmentHistory = new PunishmentHistoryDto();
CompletableFuture<List<HistoryRecord>> historyRecords = new CompletableFuture<>();
Connection.getConnection(Databases.LITE_BANS, configuration -> configuration.addMapper(NameHistoryMapper.class))
.thenApply(connection -> {
connection.runQuery(sqlSession -> {
log.debug("Loading user names for type {}", type);
List<HistoryRecord> temp = sqlSession.getMapper(NameHistoryMapper.class)
.getRecent(historyTypeEnum, userTypeEnum, user);
historyRecords.complete(temp);
});
return connection;
});
historyRecords.join().forEach(historyRecord -> {
PunishmentHistoryInnerDto innerDto = new PunishmentHistoryInnerDto()
.uuid(historyRecord.getUuid())
.username(historyRecord.getPunishedName())
.reason(historyRecord.getReason())
.punishmentUserUuid(historyRecord.getBannedByUuid())
.punishmentUser(historyRecord.getBannedByName())
.removedBy(historyRecord.getRemovedByName())
.punishmentTime(historyRecord.getTime())
.expiryTime(historyRecord.getUntil())
.removedReason(historyRecord.getRemovedByReason())
.type(historyRecord.getType());
punishmentHistory.add(innerDto);
});
return ResponseEntity.ok().body(punishmentHistory);
public ResponseEntity<PunishmentHistoryDto> getHistoryForAll(String userType, String type, Integer page) {
return getHistoryForUsers(userType, type, "", page);
}
@Override
public ResponseEntity<PunishmentHistoryDto> getHistoryForUuid(String userType, String type, String uuid) throws Exception {
public ResponseEntity<PunishmentHistoryDto> getHistoryForUsers(String userType, String type, String user, Integer page) {
UserType userTypeEnum = UserType.getUserType(userType);
HistoryType historyTypeEnum = HistoryType.getHistoryType(type);
PunishmentHistoryDto punishmentHistory = new PunishmentHistoryDto();
CompletableFuture<List<HistoryRecord>> historyRecords = new CompletableFuture<>();
Connection.getConnection(Databases.LITE_BANS, configuration -> configuration.addMapper(UUIDHistoryMapper.class))
.thenApply(connection -> {
connection.runQuery(sqlSession -> {
log.debug("Loading user names for type {}", type);
List<HistoryRecord> temp = sqlSession.getMapper(UUIDHistoryMapper.class)
.getRecent(historyTypeEnum, userTypeEnum, UUID.fromString(uuid));
historyRecords.complete(temp);
});
return connection;
});
historyRecords.join().forEach(historyRecord -> {
PunishmentHistoryInnerDto innerDto = new PunishmentHistoryInnerDto()
.uuid(historyRecord.getUuid())
.username(historyRecord.getPunishedName())
.reason(historyRecord.getReason())
.punishmentUserUuid(historyRecord.getBannedByUuid())
.punishmentUser(historyRecord.getBannedByName())
.removedBy(historyRecord.getRemovedByName())
.punishmentTime(historyRecord.getTime())
.expiryTime(historyRecord.getUntil())
.removedReason(historyRecord.getRemovedByReason())
.type(historyRecord.getType());
punishmentHistory.add(innerDto);
});
return ResponseEntity.ok().body(punishmentHistory);
Connection.getConnection(Databases.LITE_BANS)
.runQuery(sqlSession -> {
log.debug("Loading history through name for type {}", type);
try {
List<HistoryRecord> temp = sqlSession.getMapper(NameHistoryMapper.class)
.getRecent(historyTypeEnum, userTypeEnum, user, page);
historyRecords.complete(temp);
} catch (Exception e) {
log.error("Failed to load history through name for type {}", type, e);
historyRecords.completeExceptionally(e);
}
});
return mapPunishmentHistory(punishmentHistory, historyRecords);
}
@Override
public ResponseEntity<PunishmentHistoryDto> getHistoryForUuid(String userType, String type, String uuid, Integer page) {
UserType userTypeEnum = UserType.getUserType(userType);
HistoryType historyTypeEnum = HistoryType.getHistoryType(type);
PunishmentHistoryDto punishmentHistory = new PunishmentHistoryDto();
CompletableFuture<List<HistoryRecord>> historyRecords = new CompletableFuture<>();
Connection.getConnection(Databases.LITE_BANS)
.runQuery(sqlSession -> {
log.debug("Loading history through uuid for type {}", type);
try {
List<HistoryRecord> temp = sqlSession.getMapper(UUIDHistoryMapper.class)
.getRecent(historyTypeEnum, userTypeEnum, UUID.fromString(uuid), page);
historyRecords.complete(temp);
} catch (Exception e) {
log.error("Failed to load history through uuid for type {}", type, e);
historyRecords.completeExceptionally(e);
}
});
return mapPunishmentHistory(punishmentHistory, historyRecords);
}
@Override
@ -91,16 +72,37 @@ public class HistoryApiController implements HistoryApi {
UserType userTypeEnum = UserType.getUserType(userType);
HistoryType historyTypeEnum = HistoryType.getHistoryType(type);
CompletableFuture<List<String>> playerGroupFuture = new CompletableFuture<>();
Connection.getConnection(Databases.LITE_BANS, configuration -> configuration.addMapper(RecentNamesMapper.class))
.thenApply(connection -> {
connection.runQuery(sqlSession -> {
log.debug("Loading user names for type {}", type);
Connection.getConnection(Databases.LITE_BANS)
.runQuery(sqlSession -> {
log.debug("Loading user names for type {}", type);
try {
List<String> temp = sqlSession.getMapper(RecentNamesMapper.class)
.getRecent(historyTypeEnum, userTypeEnum);
playerGroupFuture.complete(temp);
});
return connection;
} catch (Exception e) {
log.error("Failed to load user names for type {}", type, e);
playerGroupFuture.completeExceptionally(e);
}
});
return ResponseEntity.ok().body(playerGroupFuture.join());
}
private ResponseEntity<PunishmentHistoryDto> mapPunishmentHistory(PunishmentHistoryDto punishmentHistory, CompletableFuture<List<HistoryRecord>> historyRecords) {
historyRecords.join().forEach(historyRecord -> {
PunishmentHistoryInnerDto innerDto = new PunishmentHistoryInnerDto()
.uuid(historyRecord.getUuid())
.username(historyRecord.getPunishedName())
.reason(historyRecord.getReason())
.punishmentUserUuid(historyRecord.getBannedByUuid())
.punishmentUser(historyRecord.getBannedByName())
.removedBy(historyRecord.getRemovedByName())
.punishmentTime(historyRecord.getTime())
.expiryTime(historyRecord.getUntil())
.removedReason(historyRecord.getRemovedByReason())
.type(historyRecord.getType());
punishmentHistory.add(innerDto);
});
return ResponseEntity.ok().body(punishmentHistory);
}
}

View File

@ -1,7 +1,7 @@
package com.alttd.altitudeweb.controllers.team;
import com.alttd.altitudeweb.api.TeamApi;
import com.alttd.altitudeweb.database.Connection;
import com.alttd.altitudeweb.setup.Connection;
import com.alttd.altitudeweb.database.Databases;
import com.alttd.altitudeweb.database.luckperms.Player;
import com.alttd.altitudeweb.database.luckperms.TeamMemberMapper;
@ -22,14 +22,16 @@ public class TeamApiController implements TeamApi {
public ResponseEntity<TeamMembersDto> getTeamMembers(String group) {
TeamMembersDto teamMemberDtos = new TeamMembersDto();
CompletableFuture<List<Player>> playerGroupFuture = new CompletableFuture<>();
Connection.getConnection(Databases.LUCK_PERMS, configuration -> configuration.addMapper(TeamMemberMapper.class))
.thenApply(connection -> {
connection.runQuery(sqlSession -> {
log.debug("Loading team members for group {}", group);
Connection.getConnection(Databases.LUCK_PERMS)
.runQuery(sqlSession -> {
log.debug("Loading team members for group {}", group);
try {
List<Player> players = sqlSession.getMapper(TeamMemberMapper.class).getTeamMembers("group." + group);
playerGroupFuture.complete(players);
});
return connection;
} catch (Exception e) {
log.error("Failed to load team members for group {}", group, e);
playerGroupFuture.completeExceptionally(e);
}
});
List<Player> join = playerGroupFuture.join();
join.forEach(player -> teamMemberDtos.add(new PlayerDto(player.username(), player.uuid().toString())));

View File

@ -2,5 +2,6 @@ package com.alttd.altitudeweb;
public class Main {
public static void main(String[] args) {
}
}

View File

@ -5,10 +5,64 @@ import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.ArrayList;
import java.util.List;
public interface NameHistoryMapper {
int PAGE_SIZE = 100;
/**
* Retrieves a list of all types of recent punishment history records filtered based on the given parameters sorted
* in descending time order. This result does NOT contain kicks history
*
* @param partialName a partial or complete name of the user to filter the records, case-insensitive
* @param nameColumn the column name in the database indicating the name to use for filtering
* @param limit the maximum number of records to fetch
* @param offset the starting offset position of the result set
*
* @return a list of {@link HistoryRecord} objects that match the specified filters, ordered by time in descending
* order
*/
@Results({
@Result(property = "uuid", column = "uuid"),
@Result(property = "punishedName", column = "punished_name"),
@Result(property = "reason", column = "reason"),
@Result(property = "bannedByUuid", column = "banned_by_uuid"),
@Result(property = "bannedByName", column = "banned_by_name"),
@Result(property = "removedByName", column = "removed_by_name"),
@Result(property = "time", column = "time"),
@Result(property = "until", column = "until"),
@Result(property = "removedByReason", column = "removed_by_reason"),
@Result(property = "type", column = "type")
})
@Select("""
SELECT all_punishments.uuid, user_lookup.name AS punished_name, reason, banned_by_uuid, banned_by_name,
removed_by_name, time, until, removed_by_reason, type
FROM all_punishments
INNER JOIN user_lookup
ON user_lookup.uuid = all_punishments.uuid
WHERE all_punishments.${name_column} LIKE #{partialName}
ORDER BY time DESC
LIMIT #{limit} OFFSET #{offset}
""")
List<HistoryRecord> getRecentAllHistory(@Param("partialName") String partialName,
@Param("name_column") String nameColumn,
@Param("limit") int limit,
@Param("offset") int offset);
/**
* Retrieves a list of a specific type of recent punishment history records filtered based on the provided
* parameters. The records are sorted in descending order by time and limited to a specified range.
*
* @param tableName the name of the database table to query the history records from
* @param partialName a partial or complete name of the user to filter the records, case-insensitive
* @param nameColumn the column name in the database indicating the name to use for filtering
* @param limit the maximum number of records to fetch
* @param offset the starting offset position of the result set
*
* @return a list of {@link HistoryRecord} objects that match the specified filters, sorted by time in descending
* order
*/
@Results({
@Result(property = "uuid", column = "uuid"),
@Result(property = "punishedName", column = "punished_name"),
@ -21,69 +75,67 @@ public interface NameHistoryMapper {
@Result(property = "removedByReason", column = "removed_by_reason")
})
@Select("""
SELECT punishment.uuid, user_table.name AS punished_name, reason, banned_by_uuid, banned_by_name,
SELECT punishment.uuid, user_lookup.name AS punished_name, reason, banned_by_uuid, banned_by_name,
removed_by_name, time, until, removed_by_reason
FROM ${tableName} AS punishment
INNER JOIN (
SELECT history_1.uuid, history_1.name
FROM litebans.litebans_history history_1
INNER JOIN (
SELECT uuid, MAX(id) as max_id
FROM litebans.litebans_history
GROUP BY uuid
) history_2 ON history_1.uuid = history_2.uuid AND history_1.id = history_2.max_id
) AS user_table ON punishment.uuid = user_table.uuid
WHERE LOWER(punishment.${name_column}) LIKE #{partialName}
INNER JOIN user_lookup ON user_lookup.uuid = punishment.uuid
WHERE punishment.${name_column} LIKE #{partialName}
LIMIT #{limit} OFFSET #{offset}
""")
List<HistoryRecord> getRecentHistory(@Param("tableName") String tableName, @Param("partialName") String partialName,
@Param("name_column") String nameColumn);
List<HistoryRecord> getRecentHistory(@Param("tableName") String tableName,
@Param("partialName") String partialName,
@Param("name_column") String nameColumn,
@Param("limit") int limit,
@Param("offset") int offset);
default List<HistoryRecord> getRecent(HistoryType historyType, UserType userType, String partialName) {
default List<HistoryRecord> getRecent(HistoryType historyType, UserType userType, String partialName,
int page) {
return switch (historyType) {
case ALL -> getRecentAll(userType, partialName);
case BANS -> getRecentBans(userType, partialName);
case MUTES -> getRecentMutes(userType, partialName);
case KICKS -> getRecentKicks(userType, partialName);
case WARNS -> getRecentWarns(userType, partialName);
case ALL -> getRecentAll(userType, partialName, page);
case BANS -> getRecentBans(userType, partialName, page);
case MUTES -> getRecentMutes(userType, partialName, page);
case KICKS -> getRecentKicks(userType, partialName, page);
case WARNS -> getRecentWarns(userType, partialName, page);
};
}
private List<HistoryRecord> getRecent(String tableName, UserType userType, String partialName) {
switch (userType) {
case PLAYER -> {
return getRecentHistory(tableName, partialName.toLowerCase() + "%", "name");
}
case STAFF -> {
return getRecentHistory(tableName, partialName.toLowerCase() + "%", "banned_by_name");
}
default -> throw new IllegalArgumentException("Invalid user type");
}
private List<HistoryRecord> getRecent(String tableName, UserType userType, String partialName,
int page) {
int offset = page * PAGE_SIZE;
int limit = PAGE_SIZE;
return switch (userType) {
case PLAYER -> getRecentHistory(tableName, partialName.toLowerCase() + "%", "name",
limit, offset);
case STAFF -> getRecentHistory(tableName, partialName.toLowerCase() + "%", "banned_by_name",
limit, offset);
};
}
private List<HistoryRecord> getRecentBans(UserType userType, String partialName) {
return addType(getRecent("litebans_bans", userType, partialName), "ban");
private List<HistoryRecord> getRecentAll(UserType userType, String partialName, int page) {
int offset = page * PAGE_SIZE;
int limit = PAGE_SIZE;
return switch (userType) {
case PLAYER -> getRecentAllHistory(partialName.toLowerCase() + "%", "name",
limit, offset);
case STAFF -> getRecentAllHistory(partialName.toLowerCase() + "%", "banned_by_name",
limit, offset);
};
}
private List<HistoryRecord> getRecentKicks(UserType userType, String partialName) {
return addType(getRecent("litebans_kicks", userType, partialName), "kick");
private List<HistoryRecord> getRecentBans(UserType userType, String partialName, int page) {
return addType(getRecent("litebans_bans", userType, partialName, page), "ban");
}
private List<HistoryRecord> getRecentMutes(UserType userType, String partialName) {
return addType(getRecent("litebans_mutes", userType, partialName), "mute");
private List<HistoryRecord> getRecentKicks(UserType userType, String partialName, int page) {
return addType(getRecent("litebans_kicks", userType, partialName, page), "kick");
}
private List<HistoryRecord> getRecentWarns(UserType userType, String partialName) {
return addType(getRecent("litebans_warnings", userType, partialName), "warn");
private List<HistoryRecord> getRecentMutes(UserType userType, String partialName, int page) {
return addType(getRecent("litebans_mutes", userType, partialName, page), "mute");
}
private List<HistoryRecord> getRecentAll(UserType userType, String partialName) {
List<HistoryRecord> all = new ArrayList<>();
all.addAll(getRecentBans(userType, partialName));
// Removed recent kicks as we do not show those
// all.addAll(getRecentKicks(userType, partialName));
all.addAll(getRecentMutes(userType, partialName));
all.addAll(getRecentWarns(userType, partialName));
return all;
private List<HistoryRecord> getRecentWarns(UserType userType, String partialName, int page) {
return addType(getRecent("litebans_warnings", userType, partialName, page), "warn");
}
private List<HistoryRecord> addType(List<HistoryRecord> historyRecords, String type) {

View File

@ -8,18 +8,12 @@ import java.util.List;
public interface RecentNamesMapper {
@Select("""
SELECT DISTINCT user_table.name AS punished_name
SELECT DISTINCT user_lookup.name AS punished_name
FROM ${tableName} AS punishment
INNER JOIN (
SELECT history_1.uuid, history_1.name
FROM litebans.litebans_history history_1
INNER JOIN (
SELECT uuid, MAX(id) as max_id
FROM litebans.litebans_history
GROUP BY uuid
) history_2 ON history_1.uuid = history_2.uuid AND history_1.id = history_2.max_id
) AS user_table ON punishment.${uuid_column} = user_table.uuid
INNER JOIN user_lookup
ON user_lookup.uuid = punishment.${uuid_column}
WHERE punishment.time > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 1 YEAR)) * 1000
ORDER BY user_lookup.name
""")
List<String> getRecentHistory(@Param("tableName") String tableName, @Param("uuid_column") String uuidColumn);

View File

@ -6,11 +6,39 @@ import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public interface UUIDHistoryMapper {
int PAGE_SIZE = 100;
@Results({
@Result(property = "uuid", column = "uuid", javaType = UUID.class, typeHandler = UUIDTypeHandler.class),
@Result(property = "punishedName", column = "punished_name"),
@Result(property = "reason", column = "reason"),
@Result(property = "bannedByUuid", column = "banned_by_uuid", javaType = UUID.class, typeHandler = UUIDTypeHandler.class),
@Result(property = "bannedByName", column = "banned_by_name"),
@Result(property = "removedByName", column = "removed_by_name"),
@Result(property = "time", column = "time"),
@Result(property = "until", column = "until"),
@Result(property = "removedByReason", column = "removed_by_reason"),
@Result(property = "type", column = "type")
})
@Select("""
SELECT all_punishments.uuid, user_lookup.name AS punished_name, reason, banned_by_uuid, banned_by_name,
removed_by_name, time, until, removed_by_reason, type
FROM all_punishments
INNER JOIN user_lookup
ON user_lookup.uuid = all_punishments.uuid
WHERE all_punishments.${uuid_column} = #{uuid}
LIMIT #{limit} OFFSET #{offset}
""")
List<HistoryRecord> getRecentAllHistory(@Param("uuid") String uuid,
@Param("uuid_column") String uuidColumn,
@Param("limit") int limit,
@Param("offset") int offset);
@Results({
@Result(property = "uuid", column = "uuid", javaType = UUID.class, typeHandler = UUIDTypeHandler.class),
@Result(property = "punishedName", column = "punished_name"),
@ -23,69 +51,63 @@ public interface UUIDHistoryMapper {
@Result(property = "removedByReason", column = "removed_by_reason")
})
@Select("""
SELECT punishment.uuid, user_table.name AS punished_name, reason, banned_by_uuid, banned_by_name,
SELECT punishment.uuid, user_lookup.name AS punished_name, reason, banned_by_uuid, banned_by_name,
removed_by_name, time, until, removed_by_reason
FROM ${tableName} AS punishment
INNER JOIN (
SELECT history_1.uuid, history_1.name
FROM litebans.litebans_history history_1
INNER JOIN (
SELECT uuid, MAX(id) as max_id
FROM litebans.litebans_history
GROUP BY uuid
) history_2 ON history_1.uuid = history_2.uuid AND history_1.id = history_2.max_id
) AS user_table ON punishment.uuid = user_table.uuid
WHERE punishment.uuid = #{uuid}
INNER JOIN user_lookup ON user_lookup.uuid = punishment.uuid
WHERE punishment.${uuid_column} = #{uuid}
LIMIT #{limit} OFFSET #{offset}
""")
List<HistoryRecord> getRecentHistory(@Param("tableName") String tableName, @Param("uuid") String uuid,
@Param("uuid_column") String uuidColumn);
List<HistoryRecord> getRecentHistory(@Param("tableName") String tableName,
@Param("uuid") String uuid,
@Param("uuid_column") String uuidColumn,
@Param("limit") int limit,
@Param("offset") int offset);
default List<HistoryRecord> getRecent(HistoryType historyType, UserType userType, UUID uuid) {
default List<HistoryRecord> getRecent(HistoryType historyType, UserType userType, UUID uuid, int page) {
return switch (historyType) {
case ALL -> getRecentAll(userType, uuid);
case BANS -> getRecentBans(userType, uuid);
case MUTES -> getRecentMutes(userType, uuid);
case KICKS -> getRecentKicks(userType, uuid);
case WARNS -> getRecentKicks(userType, uuid);
case ALL -> getRecentAll(userType, uuid, page);
case BANS -> getRecentBans(userType, uuid, page);
case MUTES -> getRecentMutes(userType, uuid, page);
case KICKS -> getRecentKicks(userType, uuid, page);
case WARNS -> getRecentWarns(userType, uuid, page);
};
}
private List<HistoryRecord> getRecent(String tableName, UserType userType, UUID uuid) {
switch (userType) {
case PLAYER -> {
return getRecentHistory(tableName, uuid.toString(), "uuid");
}
case STAFF -> {
return getRecentHistory(tableName, uuid.toString(), "banned_by_uuid");
}
default -> throw new IllegalArgumentException("Invalid user type");
}
private List<HistoryRecord> getRecent(String tableName, UserType userType, UUID uuid, int page) {
int offset = page * PAGE_SIZE;
int limit = PAGE_SIZE;
return switch (userType) {
case PLAYER -> getRecentHistory(tableName, uuid.toString(), "uuid", limit, offset);
case STAFF -> getRecentHistory(tableName, uuid.toString(), "banned_by_uuid", limit, offset);
};
}
private List<HistoryRecord> getRecentBans(UserType userType, UUID uuid) {
return addType(getRecent("litebans_bans", userType, uuid), "ban");
private List<HistoryRecord> getRecentAll(UserType userType, UUID uuid, int page) {
int offset = page * PAGE_SIZE;
int limit = PAGE_SIZE;
return switch (userType) {
case PLAYER -> getRecentAllHistory(uuid.toString(), "name",
limit, offset);
case STAFF -> getRecentAllHistory(uuid.toString(), "banned_by_name",
limit, offset);
};
}
private List<HistoryRecord> getRecentKicks(UserType userType, UUID uuid) {
return addType(getRecent("litebans_kicks", userType, uuid), "kick");
private List<HistoryRecord> getRecentBans(UserType userType, UUID uuid, int page) {
return addType(getRecent("litebans_bans", userType, uuid, page), "ban");
}
private List<HistoryRecord> getRecentMutes(UserType userType, UUID uuid) {
return addType(getRecent("litebans_mutes", userType, uuid), "mute");
private List<HistoryRecord> getRecentKicks(UserType userType, UUID uuid, int page) {
return addType(getRecent("litebans_kicks", userType, uuid, page), "kick");
}
private List<HistoryRecord> getRecentWarns(UserType userType, UUID uuid) {
return addType(getRecent("litebans_warnings", userType, uuid), "warn");
private List<HistoryRecord> getRecentMutes(UserType userType, UUID uuid, int page) {
return addType(getRecent("litebans_mutes", userType, uuid, page), "mute");
}
private List<HistoryRecord> getRecentAll(UserType userType, UUID uuid) {
List<HistoryRecord> all = new ArrayList<>();
all.addAll(getRecentBans(userType, uuid));
// Removed recent kicks as we do not show those
// all.addAll(getRecentKicks(userType, uuid));
all.addAll(getRecentMutes(userType, uuid));
all.addAll(getRecentWarns(userType, uuid));
return all;
private List<HistoryRecord> getRecentWarns(UserType userType, UUID uuid, int page) {
return addType(getRecent("litebans_warnings", userType, uuid, page), "warn");
}
private List<HistoryRecord> addType(List<HistoryRecord> historyRecords, String type) {

View File

@ -1,5 +1,6 @@
package com.alttd.altitudeweb.database;
package com.alttd.altitudeweb.setup;
import com.alttd.altitudeweb.database.Databases;
import com.alttd.altitudeweb.database.web_db.DatabaseSettings;
import com.alttd.altitudeweb.database.web_db.SettingsMapper;
import lombok.extern.slf4j.Slf4j;
@ -28,12 +29,25 @@ public class Connection {
this.addMappers = addMappers;
}
public static void initDatabases() {
InitializeWebDb.init();
InitializeLiteBans.init();
InitializeLuckPerms.init();
}
@FunctionalInterface
public interface AddMappers {
void apply(Configuration configuration);
}
public static CompletableFuture<Connection> getConnection(Databases database, AddMappers addMappers) {
public static Connection getConnection(Databases database) {
if (connections.containsKey(database)) {
return connections.get(database);
}
throw new RuntimeException("Database " + database + " has not been initialized");
}
protected static CompletableFuture<Connection> getConnection(Databases database, AddMappers addMappers) {
if (connections.containsKey(database)) {
return CompletableFuture.completedFuture(connections.get(database));
}

View File

@ -0,0 +1,71 @@
package com.alttd.altitudeweb.setup;
import com.alttd.altitudeweb.database.Databases;
import com.alttd.altitudeweb.database.litebans.NameHistoryMapper;
import com.alttd.altitudeweb.database.litebans.RecentNamesMapper;
import com.alttd.altitudeweb.database.litebans.UUIDHistoryMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import java.sql.SQLException;
import java.sql.Statement;
@Slf4j
public class InitializeLiteBans {
protected static void init() {
log.info("Initializing LiteBans");
Connection.getConnection(Databases.LITE_BANS, (configuration) -> {
configuration.addMapper(RecentNamesMapper.class);
configuration.addMapper(NameHistoryMapper.class);
configuration.addMapper(UUIDHistoryMapper.class);
}).join()
.runQuery(sqlSession -> {
createAllPunishmentsView(sqlSession);
createUserLookupView(sqlSession);
});
log.debug("Initialized LiteBans");
}
private static void createAllPunishmentsView(SqlSession sqlSession) {
String query = """
CREATE VIEW IF NOT EXISTS all_punishments AS
SELECT uuid, reason, banned_by_uuid, banned_by_name, removed_by_name, time, until, removed_by_reason,
'ban' as type
FROM litebans_bans
UNION ALL
SELECT uuid, reason, banned_by_uuid, banned_by_name, removed_by_name, time, until, removed_by_reason,
'mute' as type
FROM litebans_mutes
UNION ALL
SELECT uuid, reason, banned_by_uuid, banned_by_name, removed_by_name, time, until, removed_by_reason,
'warn' as type
FROM litebans_warnings
ORDER BY time DESC;
""";
try (Statement statement = sqlSession.getConnection().createStatement()) {
statement.execute(query);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
private static void createUserLookupView(SqlSession sqlSession) {
String query = """
CREATE VIEW IF NOT EXISTS user_lookup AS
SELECT history_1.uuid, history_1.name
FROM litebans.litebans_history history_1
INNER JOIN (
SELECT uuid, MAX(id) as max_id
FROM litebans.litebans_history
GROUP BY uuid
) history_2 ON history_1.uuid = history_2.uuid AND history_1.id = history_2.max_id
""";
try (Statement statement = sqlSession.getConnection().createStatement()) {
statement.execute(query);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,18 @@
package com.alttd.altitudeweb.setup;
import com.alttd.altitudeweb.database.Databases;
import com.alttd.altitudeweb.database.luckperms.TeamMemberMapper;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class InitializeLuckPerms {
protected static void init() {
log.info("Initializing LuckPerms");
Connection.getConnection(Databases.LUCK_PERMS, (configuration) -> {
configuration.addMapper(TeamMemberMapper.class);
}).join();
log.debug("Initialized LuckPerms");
}
}

View File

@ -0,0 +1,43 @@
package com.alttd.altitudeweb.setup;
import com.alttd.altitudeweb.database.Databases;
import com.alttd.altitudeweb.database.web_db.SettingsMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import java.sql.SQLException;
import java.sql.Statement;
@Slf4j
public class InitializeWebDb {
protected static void init() {
log.info("Initializing LiteBans");
Connection.getConnection(Databases.DEFAULT, (configuration) -> {
configuration.addMapper(SettingsMapper.class);
}).join()
.runQuery(InitializeWebDb::createSettingsTable);
log.debug("Initialized LuckPerms");
}
private static void createSettingsTable(SqlSession sqlSession) {
String query = """
CREATE TABLE IF NOT EXISTS db_connection_settings
(
internal_name VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
host VARCHAR(255) NOT NULL,
port INT NOT NULL,
CONSTRAINT pk_internal_name PRIMARY KEY (internal_name)
);
""";
try (Statement statement = sqlSession.getConnection().createStatement()) {
statement.execute(query);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -16,7 +16,9 @@ paths:
$ref: './schemas/team/team.yml#/getTeam'
/history/{userType}/search/{type}:
$ref: './schemas/bans/bans.yml#/getUserNames'
/history/{userType}/name/{type}/{user}:
/history/{userType}/name/{type}/{user}/{page}:
$ref: './schemas/bans/bans.yml#/getHistoryForUsers'
/history/{userType}/uuid/{type}/{uuid}:
/history/{userType}/name/{type}/{page}:
$ref: './schemas/bans/bans.yml#/getHistoryForAll'
/history/{userType}/uuid/{type}/{uuid}/{page}:
$ref: './schemas/bans/bans.yml#/getHistoryForUuid'

View File

@ -29,14 +29,41 @@ getHistoryForUsers:
tags:
- history
summary: Get history for users
description: >
description: >
Get all users with punishment history of the specified type,
where the user name starts with the search query
where the user name starts with the search query, 100 records at a time
operationId: getHistoryForUsers
parameters:
- $ref: '#/components/parameters/UserType'
- $ref: '#/components/parameters/HistoryType'
- $ref: '#/components/parameters/User'
- $ref: '#/components/parameters/Page'
responses:
'200':
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/PunishmentHistory'
default:
description: Unexpected error
content:
application/json:
schema:
$ref: "../generic/errors.yml#/components/schemas/ApiError"
getHistoryForAll:
get:
tags:
- history
summary: Get history for users
description: >
Get all users with punishment history of the specified type,
where the user name starts with the search query, 100 records at a time
operationId: getHistoryForAll
parameters:
- $ref: '#/components/parameters/UserType'
- $ref: '#/components/parameters/HistoryType'
- $ref: '#/components/parameters/Page'
responses:
'200':
description: Successful operation
@ -55,14 +82,15 @@ getHistoryForUuid:
tags:
- history
summary: Get user
description: >
description: >
Get all users with punishment history of the specified type,
where the user uuid matches
where the user uuid matches, 100 records at a time
operationId: getHistoryForUuid
parameters:
- $ref: '#/components/parameters/UserType'
- $ref: '#/components/parameters/HistoryType'
- $ref: '#/components/parameters/Uuid'
- $ref: '#/components/parameters/Page'
responses:
'200':
description: Successful operation
@ -108,11 +136,25 @@ components:
type: string
enum: [ player, staff ]
description: Indicates if this is a staff history or a player history look up
Page:
name: page
in: path
required: true
schema:
type: integer
description: The page that should be retrieved
schemas:
PunishmentHistory:
type: array
items:
type: object
required:
- username
- uuid
- reason
- type
- punishmentTime
- punishmentUserUuid
properties:
username:
type: string