diff --git a/application/src/main/java/org/togetherjava/tjbot/logging/FlaggedFilter.java b/application/src/main/java/org/togetherjava/tjbot/logging/FlaggedFilter.java deleted file mode 100644 index 844c90a24f..0000000000 --- a/application/src/main/java/org/togetherjava/tjbot/logging/FlaggedFilter.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.togetherjava.tjbot.logging; - -import org.apache.logging.log4j.core.Core; -import org.apache.logging.log4j.core.Filter; -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.config.plugins.Plugin; -import org.apache.logging.log4j.core.config.plugins.PluginAttribute; -import org.apache.logging.log4j.core.config.plugins.PluginFactory; -import org.apache.logging.log4j.core.filter.AbstractFilter; - -/** - * A custom Filter for Log4j2, which only lets an event pass through if a Logging Flag is set in the - * environment. Intended to be used for local development for devs do not want to also run the - * logviewer project. No errors in console or Log should appear, if the Flag is not set and the - * logviewer is not running. - */ -@Plugin(name = "FlaggedFilter", category = Core.CATEGORY_NAME, elementType = Filter.ELEMENT_TYPE) -public class FlaggedFilter extends AbstractFilter { - - /** - * The environment Variable that needs to bet set in order for this Filter to let events through - */ - public static final String LOGGING_FLAG = "TJ_APPENDER"; - - /** - * Create a FlaggedFilter. - * - * @param onMatch The action to take on a match. - * @param onMismatch The action to take on a mismatch. - */ - public FlaggedFilter(Result onMatch, Result onMismatch) { - super(onMatch, onMismatch); - } - - /** - * The actual filtering occurs here. If the Flag {@link #LOGGING_FLAG} is not set returns - * {@link Result#DENY} so nothing goes through. If the Flag is set it returns - * {@link Result#NEUTRAL} so other configured Filter still work. - * - * @param event The Event to log. - * @return {@link Result#DENY} if the Flag is not set, else {@link Result#NEUTRAL} - */ - @Override - public Result filter(LogEvent event) { - return isLoggingEnabled() ? Result.NEUTRAL : Result.DENY; - } - - boolean isLoggingEnabled() { - return System.getenv().containsKey(LOGGING_FLAG); - } - - /** - * Required by the Log4j2 - Plugin framework in order to create an instance of this Filter. - * - * @param onMatch The action to take on a match. - * @param onMismatch The action to take on a mismatch. - * @return The created FlaggedFilter. - */ - @PluginFactory - public static FlaggedFilter createFilter( - @PluginAttribute(value = "onMatch", defaultString = "NEUTRAL") Result onMatch, - - @PluginAttribute(value = "onMismatch", defaultString = "DENY") Result onMismatch) { - return new FlaggedFilter(onMatch, onMismatch); - } -} diff --git a/application/src/main/resources/log4j2.xml b/application/src/main/resources/log4j2.xml index 4f68a45e5f..3de21e9d65 100644 --- a/application/src/main/resources/log4j2.xml +++ b/application/src/main/resources/log4j2.xml @@ -13,13 +13,6 @@ - - - - - - - @@ -29,7 +22,6 @@ - diff --git a/application/src/test/java/org/togetherjava/tjbot/logging/FilterTest.java b/application/src/test/java/org/togetherjava/tjbot/logging/FilterTest.java deleted file mode 100644 index cda30d60fa..0000000000 --- a/application/src/test/java/org/togetherjava/tjbot/logging/FilterTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.togetherjava.tjbot.logging; - -import org.apache.logging.log4j.core.Filter; -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.impl.Log4jLogEvent; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -final class FilterTest { - - private FlaggedFilter filter; - private LogEvent event; - - @BeforeEach - void setUp() { - this.filter = FlaggedFilter.createFilter(Filter.Result.NEUTRAL, Filter.Result.DENY); - this.event = Log4jLogEvent.newBuilder().build(); - } - - @Test - void shouldPassFilter() { - FlaggedFilter spy = Mockito.spy(this.filter); - Mockito.when(spy.isLoggingEnabled()).thenReturn(true); - Assertions.assertEquals(Filter.Result.NEUTRAL, spy.filter(this.event)); - } - - @Test - void shouldNotPassFilter() { - FlaggedFilter spy = Mockito.spy(this.filter); - Mockito.when(spy.isLoggingEnabled()).thenReturn(false); - Assertions.assertEquals(Filter.Result.DENY, spy.filter(this.event)); - } -} diff --git a/docker-compose.yml.example b/docker-compose.yml.example index 74465edf74..362b1aa334 100644 --- a/docker-compose.yml.example +++ b/docker-compose.yml.example @@ -1,20 +1,27 @@ version: "3.9" services: - bot: - image: togetherjava/tjbot:latest + tjbot-develop: + image: "togetherjava.duckdns.org:5001/togetherjava/tjbot:develop" + command: ["/home/bot/config/config.json"] volumes: - - "./application/config.json:/config.json" - - "./application/logs/:/logs" - links: - - logs - environment: - - TJ_LOG_TARGET=logs - logs: - image: togetherjava/tjlogs:latest + - database:/home/bot/database + - ./config:/home/bot/config + restart: always + networks: + - develop-bot + + tj-develop-website: + image: "togetherjava.duckdns.org:5001/togetherjava/website:develop" ports: - - "5050:5050" - volumes: - - "./logviewer/config.json:/logviewer/config.json" - - "./application/logs:/application/logs" - - "./logviewer/logs:/logviewer/logs" - - "./logviewer/db:/logviewer/db" + - "127.0.0.1:5051:5051" + networks: + - develop-bot + restart: always + +volumes: + database: + name: "tj-bot-develop-database" + +networks: + develop-bot: + name: "develop-bot" diff --git a/logviewer/build.gradle b/logviewer/build.gradle deleted file mode 100644 index 2086804732..0000000000 --- a/logviewer/build.gradle +++ /dev/null @@ -1,103 +0,0 @@ -plugins { - id "com.google.cloud.tools.jib" version "3.1.4" - id "org.springframework.boot" version "2.6.1" - id "io.spring.dependency-management" version "1.0.11.RELEASE" - id "com.vaadin" version "21.0.2" - id 'java' - id 'database-settings' -} - -defaultTasks("clean", "bootRun") - -repositories { - mavenCentral() - maven { - url = uri("https://maven.vaadin.com/vaadin-addons") - } -} - -configurations { - developmentOnly - runtimeClasspath.extendsFrom(developmentOnly) -} - -jooq { - configurations { - main { - generationTool { - generator { - target { - packageName = 'org.togetherjava.tjbot.db.generated' - } - } - } - } - } -} - -dependencies { - implementation('org.apache.logging.log4j:log4j-api') { - version { - require '2.16.0' - } - because 'Log4Shell happened' - } - runtimeOnly('org.apache.logging.log4j:log4j-core') { - version { - require '2.16.0' - } - because 'Log4Shell happened' - } - - runtimeOnly 'org.apache.logging.log4j:log4j-jul:2.16.0' - runtimeOnly 'org.apache.logging.log4j:log4j-slf4j-impl:2.16.0' - - - implementation(project(":database")) - implementation 'org.jooq:jooq:3.15.3' - - implementation 'com.vaadin:vaadin-core:21.0.2' - implementation('com.vaadin:vaadin-spring:18.0.0') - implementation 'org.vaadin.artur:a-vaadin-helper:1.7.2' - implementation 'org.vaadin.crudui:crudui:4.6.0' - implementation 'com.vaadin.componentfactory:enhanced-dialog:21.0.0' - - - implementation('org.springframework.boot:spring-boot-starter-web:2.6.1') { - exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' - } - implementation('org.springframework.boot:spring-boot-starter-security:2.6.1') { - exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' - } - implementation('org.springframework.boot:spring-boot-starter-oauth2-client:2.6.1') { - exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' - } - - developmentOnly('org.springframework.boot:spring-boot-starter-actuator:2.6.1') { - exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' - } - developmentOnly('org.springframework.boot:spring-boot-devtools:2.6.1') { - exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' - } -} - -tasks.jib.dependsOn(tasks.vaadinBuildFrontend) -jib { - from.image = 'eclipse-temurin:17' - to { - image = 'togetherjava.duckdns.org:5001/togetherjava/tjlogs:' + System.getenv('BRANCH_NAME') ?: 'latest' - auth { - username = System.getenv('REGISTRY_USER') ?: '' - password = System.getenv('REGISTRY_PASSWORD') ?: '' - } - } - container { - setPorts(["5050"].asList()) - setCreationTime(Instant.now().toString()) - } -} - -vaadin { - pnpmEnable = true - productionMode = true -} diff --git a/logviewer/frontend/index.html b/logviewer/frontend/index.html deleted file mode 100644 index 627308e8d4..0000000000 --- a/logviewer/frontend/index.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - -
- - diff --git a/logviewer/frontend/themes/logviewer/main-layout.css b/logviewer/frontend/themes/logviewer/main-layout.css deleted file mode 100644 index 3477da5822..0000000000 --- a/logviewer/frontend/themes/logviewer/main-layout.css +++ /dev/null @@ -1,36 +0,0 @@ -[slot='drawer'] { - background-image: linear-gradient(0deg, var(--lumo-shade-5pct), var(--lumo-shade-5pct)); -} - -[slot='drawer'] nav a { - text-decoration: none; - transition: color 140ms; -} - -[slot='drawer'] nav a .la { - margin-top: calc(var(--lumo-space-xs) * 0.5); -} - -[slot='drawer'] nav a::before { - border-radius: var(--lumo-border-radius); - bottom: calc(var(--lumo-space-xs) * 0.5); - content: ''; - left: 0; - position: absolute; - right: 0; - top: calc(var(--lumo-space-xs) * 0.5); - transition: background-color 140ms; -} - -[slot='drawer'] nav a[highlight] { - color: var(--lumo-primary-text-color); -} - -[slot='drawer'] nav a[highlight]::before { - background-color: var(--lumo-primary-color-10pct); -} - -[slot='drawer'] footer vaadin-context-menu { - align-items: center; - display: flex; -} diff --git a/logviewer/frontend/themes/logviewer/styles.css b/logviewer/frontend/themes/logviewer/styles.css deleted file mode 100644 index f7dfadb7ed..0000000000 --- a/logviewer/frontend/themes/logviewer/styles.css +++ /dev/null @@ -1,4 +0,0 @@ -/* Import your application global css files here or add the styles directly to this file */ -@import url('./main-layout.css'); -@import url('./views/logs-view.css'); -@import url('line-awesome/dist/line-awesome/css/line-awesome.min.css'); diff --git a/logviewer/frontend/themes/logviewer/theme.json b/logviewer/frontend/themes/logviewer/theme.json deleted file mode 100644 index 7ccf9d1c46..0000000000 --- a/logviewer/frontend/themes/logviewer/theme.json +++ /dev/null @@ -1 +0,0 @@ -{"lumoImports":["typography","color","spacing","badge","utility"]} diff --git a/logviewer/frontend/themes/logviewer/views/logs-view.css b/logviewer/frontend/themes/logviewer/views/logs-view.css deleted file mode 100644 index aa8f478e95..0000000000 --- a/logviewer/frontend/themes/logviewer/views/logs-view.css +++ /dev/null @@ -1,39 +0,0 @@ -.logs-view { - display: block; - padding: 1em; -} - -:root { - --color-info: rgba(120, 107, 213, 0.5); - --color-warn: rgba(170, 187, 97, 0.5); - --color-error: rgba(255, 75, 75, 0.5); - --color-debug: rgba(127, 255, 212, 0.5); - --color-trace: rgba(58, 118, 58, 0.5); -} - -p { - - line-height: normal; - margin-top: 0.1em; - margin-bottom: 0.1em; -} - -.error { - background-color: var(--color-error); -} - -.warn { - background-color: var(--color-warn); -} - -.info { - background-color: var(--color-info); -} - -.debug { - background-color: var(--color-debug); -} - -.trace { - background-color: var(--color-trace); -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/Application.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/Application.java deleted file mode 100644 index b8661bd91d..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/Application.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.togetherjava.tjbot.logwatcher; - -import com.vaadin.flow.component.dependency.NpmPackage; -import com.vaadin.flow.component.page.AppShellConfigurator; -import com.vaadin.flow.component.page.Push; -import com.vaadin.flow.server.PWA; -import com.vaadin.flow.theme.Theme; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration; -import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; -import org.togetherjava.tjbot.logwatcher.config.Config; -import org.togetherjava.tjbot.logwatcher.logs.LogREST; -import org.togetherjava.tjbot.logwatcher.oauth.OAuth2LoginConfig; -import org.vaadin.artur.helpers.LaunchUtil; - -/** - * This is a small Spring-Vaadin Webserver, which main purpose is to show Log-Event's generated from - * the Bot. - *

- * You can see the Views you can see in your Browser at {@link org.togetherjava.logwatcher.views} - *

- * Log-Event's are captured by the REST-API at {@link LogREST} - *

- * Security/OAuth2 Config at {@link OAuth2LoginConfig} - *

- * And the initial Config at {@link Config} - */ -@SpringBootApplication(exclude = {R2dbcAutoConfiguration.class}) -@Theme(value = "logviewer") -@PWA(name = "LogViewer", shortName = "Logs", offlineResources = {"images/logo.png"}) -@NpmPackage(value = "line-awesome", version = "1.3.0") -@Push -public class Application extends SpringBootServletInitializer implements AppShellConfigurator { - - private static final Logger applicationLogger = LoggerFactory.getLogger(Application.class); - - public static void main(String[] args) { - if (args.length > 1) { - applicationLogger - .error("Usage: Provide a single Argument, containing the Path to the Config-File"); - System.exit(1); - } - - Config.init(args.length == 1 ? args[0] : "./logviewer/config.json"); - LaunchUtil.launchBrowserInDevelopmentMode(SpringApplication.run(Application.class, args)); - } - -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/DatabaseProvider.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/DatabaseProvider.java deleted file mode 100644 index 914687850d..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/DatabaseProvider.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.togetherjava.tjbot.logwatcher; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.FatalBeanException; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Scope; -import org.togetherjava.tjbot.logwatcher.config.Config; -import org.togetherjava.tjbot.db.Database; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.sql.SQLException; - -@Configuration -@Scope(BeanDefinition.SCOPE_SINGLETON) -public class DatabaseProvider { - - private final Database db; - private final Config config; - - private static final Logger logger = LoggerFactory.getLogger(DatabaseProvider.class); - - public DatabaseProvider(final Config config) { - this.config = config; - this.db = createDB(); - } - - @SuppressWarnings({"java:S2139"}) // At this point there is nothing we can do about a - // SQLException - private Database createDB() { - final Path dbPath = getDBPath(); - - try { - return new Database("jdbc:sqlite:%s".formatted(dbPath.toAbsolutePath())); - } catch (final SQLException e) { - logger.error("Exception while creating Database.", e); - throw new FatalBeanException("Could not create Database.", e); - } - } - - private Path getDBPath() { - final Path dbPath = Path.of(this.config.getDatabasePath()); - - try { - Files.createDirectories(dbPath.getParent()); - } catch (final IOException e) { - logger.error("Exception while creating Database-Path.", e); - } - - return dbPath; - } - - - @Bean - public Database getDb() { - return this.db; - } - - -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/accesscontrol/AllowedRoles.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/accesscontrol/AllowedRoles.java deleted file mode 100644 index 4b3ce22ece..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/accesscontrol/AllowedRoles.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.accesscontrol; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - - -/** - * Annotation for handling Access Control on Views - */ -@Retention(RUNTIME) -@Target({TYPE}) -public @interface AllowedRoles { - - - /** - * Roles to permit access to the View {@link Role} - */ - Role[] roles(); -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/accesscontrol/Role.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/accesscontrol/Role.java deleted file mode 100644 index 364474fbf3..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/accesscontrol/Role.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.accesscontrol; - -import java.util.Arrays; -import java.util.EnumSet; -import java.util.Set; - -/** - * Basic Roles for Access Control on Views - */ -public enum Role { - /** - * Role for when Stuff goes wrong - */ - UNKNOWN(0, "unknown"), - - /** - * Base Role - */ - USER(1, "user"), - - /** - * Role for Views that should require more permissions - */ - ADMIN(2, "admin"); - - private final int id; - private final String roleName; - - Role(int id, String roleName) { - this.id = id; - this.roleName = roleName; - } - - public int getId() { - return id; - } - - public String getRoleName() { - return roleName; - } - - public static Role forID(final int id) { - return Arrays.stream(values()) - .filter(r -> r.id == id) - .findAny() - .orElseThrow(() -> new IllegalArgumentException("Unknown RoleID: %d".formatted(id))); - } - - public static Set getDisplayableRoles() { - return EnumSet.of(USER, ADMIN); - } - -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/config/Config.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/config/Config.java deleted file mode 100644 index f19cad897e..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/config/Config.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.config; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.context.annotation.Configuration; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.file.Path; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; - -/** - * Basic Class for accumulating Information which is required to start this application - */ -@Configuration -public class Config { - - private static final AtomicReference CONFIG_PATH = new AtomicReference<>(); - - public static void init(final String pathToConfig) { - if (!CONFIG_PATH.compareAndSet(null, pathToConfig)) { - throw new IllegalStateException( - "Config Path already set to %s".formatted(CONFIG_PATH.get())); - } - } - - - /** - * Client-Name of the OAuth2-Application - */ - private final String clientName; - - /** - * Client-ID of the OAuth2-Application - */ - private final String clientId; - - /** - * Client-Secret of the OAuth2-Application - */ - private final String clientSecret; - - /** - * Discord-Username of the User to add as 1. User - */ - private final String rootUserName; - - /** - * Discord-ID of the User to add as 1. User - */ - private final String rootDiscordID; - - /** - * Path of the Log directory - */ - private final String logPath; - - /** - * Redirect Path for OAuth2 - */ - private final String redirectPath; - - /** - * Path for this Database - */ - private final String databasePath; - - public Config(final ObjectMapper mapper) { - final JsonNode jsonNode = getJsonNode(mapper); - - this.clientName = jsonNode.get("clientName").asText(); - this.clientId = jsonNode.get("clientId").asText(); - this.clientSecret = jsonNode.get("clientSecret").asText(); - this.rootUserName = jsonNode.get("rootUserName").asText(); - this.rootDiscordID = jsonNode.get("rootDiscordID").asText(); - this.logPath = jsonNode.get("logPath").asText(); - this.redirectPath = jsonNode.get("redirectPath").asText(); - this.databasePath = jsonNode.get("databasePath").asText(); - } - - private JsonNode getJsonNode(final ObjectMapper mapper) { - final String pathToConfig = Objects.requireNonNull(CONFIG_PATH.get(), "Path not Set"); - try { - return mapper.readTree(Path.of(pathToConfig).toFile()); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - public String getRedirectPath() { - return redirectPath; - } - - public String getLogPath() { - return logPath; - } - - public String getRootUserName() { - return rootUserName; - } - - public String getRootDiscordID() { - return rootDiscordID; - } - - public String getClientName() { - return clientName; - } - - public String getClientId() { - return clientId; - } - - public String getClientSecret() { - return clientSecret; - } - - public String getDatabasePath() { - return this.databasePath; - } -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/constants/LogEventsConstants.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/constants/LogEventsConstants.java deleted file mode 100644 index 45e46e4730..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/constants/LogEventsConstants.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.constants; - -/** - * Constant Class for Logevents - */ -public final class LogEventsConstants { - - public static final String FIELD_INSTANT = "time"; - public static final String FIELD_END_OF_BATCH = "endOfbatch"; - public static final String FIELD_LOGGER_NAME = "loggername"; - public static final String FIELD_LOGGER_LEVEL = "level"; - public static final String FIELD_LOGGER_FQCN = "loggerfqcn"; - public static final String FIELD_MESSAGE = "message"; - public static final String FIELD_THREAD = "thread"; - public static final String FIELD_THREAD_ID = "threadid"; - public static final String FIELD_THREAD_PRIORITY = "threadpriority"; - - - - /** - * Contestants class, nothing to instantiate - */ - private LogEventsConstants() {} -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/constants/UserConstants.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/constants/UserConstants.java deleted file mode 100644 index 0532c11229..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/constants/UserConstants.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.constants; - -/** - * Gathers all static Values regarding Users - */ -public final class UserConstants { - - public static final String FIELD_USERNAME = "username"; - public static final String FIELD_DISCORD_ID = "discordid"; - - - private UserConstants() {} -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/entities/InstantWrapper.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/entities/InstantWrapper.java deleted file mode 100644 index 4754ad9b32..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/entities/InstantWrapper.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.entities; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.text.SimpleDateFormat; -import java.time.Instant; -import java.util.Date; - -public class InstantWrapper { - - private long epochSecond; - private long nanoOfSecond; - - /** - * @param epochSecond - * @param nanoOfSecond - */ - @JsonCreator - public InstantWrapper(@JsonProperty("epochSecond") long epochSecond, - @JsonProperty("nanoOfSecond") long nanoOfSecond) { - super(); - this.epochSecond = epochSecond; - this.nanoOfSecond = nanoOfSecond; - } - - public long getEpochSecond() { - return epochSecond; - } - - public void setEpochSecond(long epochSecond) { - this.epochSecond = epochSecond; - } - - public long getNanoOfSecond() { - return nanoOfSecond; - } - - public void setNanoOfSecond(long nanoOfSecond) { - this.nanoOfSecond = nanoOfSecond; - } - - @Override - public String toString() { - final var instant = toInstant(); - - return new SimpleDateFormat("yyy-MM-dd HH:mm:ss.SSS").format(Date.from(instant)); - } - - public Instant toInstant() { - return Instant.ofEpochSecond(this.epochSecond, this.nanoOfSecond); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof InstantWrapper other)) { - return false; - } - - return epochSecond == other.epochSecond && nanoOfSecond != other.nanoOfSecond; - } - - @Override - public int hashCode() { - int result = (int) (epochSecond ^ (epochSecond >>> 32)); - result = 31 * result + (int) (nanoOfSecond ^ (nanoOfSecond >>> 32)); - return result; - } -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/entities/LogEvent.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/entities/LogEvent.java deleted file mode 100644 index fcf80c671d..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/entities/LogEvent.java +++ /dev/null @@ -1,131 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.entities; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.time.Instant; - - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class LogEvent { - - private Instant instant; - - private String thread; - - private String level; - - private String loggerName; - - private String message; - - private Boolean endOfBatch; - - private String loggerFqcn; - - private Integer threadId; - - private Integer threadPriority; - - @JsonCreator - public LogEvent(@JsonProperty("instant") InstantWrapper wrapper, - @JsonProperty("thread") String thread, @JsonProperty("level") String level, - @JsonProperty("loggerName") String loggerName, @JsonProperty("message") String message, - @JsonProperty("endOfBatch") Boolean endOfBatch, - @JsonProperty("loggerFqcn") String loggerFqcn, - @JsonProperty("threadId") Integer threadId, - @JsonProperty("threadPriority") Integer threadPriority) { - this.instant = wrapper.toInstant(); - this.thread = thread; - this.level = level; - this.loggerName = loggerName; - this.message = message; - this.endOfBatch = endOfBatch; - this.loggerFqcn = loggerFqcn; - this.threadId = threadId; - this.threadPriority = threadPriority; - } - - - public Instant getInstant() { - return instant; - } - - public void setInstant(Instant instant) { - this.instant = instant; - } - - public String getThread() { - return thread; - } - - public void setThread(String thread) { - this.thread = thread; - } - - public String getLevel() { - return level; - } - - public void setLevel(String level) { - this.level = level; - } - - public String getLoggerName() { - return loggerName; - } - - public void setLoggerName(String loggerName) { - this.loggerName = loggerName; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public Boolean getEndOfBatch() { - return endOfBatch; - } - - public void setEndOfBatch(Boolean endOfBatch) { - this.endOfBatch = endOfBatch; - } - - public String getLoggerFqcn() { - return loggerFqcn; - } - - public void setLoggerFqcn(String loggerFqcn) { - this.loggerFqcn = loggerFqcn; - } - - public Integer getThreadId() { - return threadId; - } - - public void setThreadId(Integer threadId) { - this.threadId = threadId; - } - - public Integer getThreadPriority() { - return threadPriority; - } - - public void setThreadPriority(Integer threadPriority) { - this.threadPriority = threadPriority; - } - - @Override - public String toString() { - return "LogDTO{" + "instant=" + instant + ", thread='" + thread + '\'' + ", level='" + level - + '\'' + ", loggerName='" + loggerName + '\'' + ", message='" + message + '\'' - + ", endOfBatch=" + endOfBatch + ", loggerFqcn='" + loggerFqcn + '\'' - + ", threadId=" + threadId + ", threadPriority=" + threadPriority + '}'; - } - -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/entities/UserWrapper.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/entities/UserWrapper.java deleted file mode 100644 index 8bd55eefda..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/entities/UserWrapper.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.entities; - -import org.togetherjava.tjbot.logwatcher.accesscontrol.Role; - -import java.io.Serial; -import java.io.Serializable; -import java.util.Collections; -import java.util.Objects; -import java.util.Set; - -public final class UserWrapper implements Serializable { - @Serial - private static final long serialVersionUID = -3701246411434315431L; - - private long discordID; - private String userName; - private Set roles = Collections.emptySet(); - - public UserWrapper() {} - - public UserWrapper(long discordID, String userName, Set roles) { - this.discordID = discordID; - this.userName = userName; - this.roles = roles; - } - - public void setDiscordID(long discordID) { - this.discordID = discordID; - } - - public void setUserName(String userName) { - this.userName = userName; - } - - public void setRoles(Set roles) { - this.roles = roles; - } - - public long getDiscordID() { - return discordID; - } - - public String getUserName() { - return userName; - } - - public Set getRoles() { - return roles; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof UserWrapper other)) { - return true; - } - return this.discordID == other.discordID && Objects.equals(this.userName, other.userName) - && Objects.equals(this.roles, other.roles); - } - - @Override - public int hashCode() { - return Objects.hash(discordID, userName, roles); - } - - @Override - public String toString() { - return "UserDTO[" + "discordID=" + discordID + ", " + "userName=" + userName + ", " - + "roles=" + roles + ']'; - } - - -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/logs/LogREST.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/logs/LogREST.java deleted file mode 100644 index c1d2103a6f..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/logs/LogREST.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.logs; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; -import org.togetherjava.tjbot.logwatcher.entities.LogEvent; -import org.togetherjava.tjbot.logwatcher.watcher.StreamWatcher; -import org.togetherjava.tjbot.db.generated.tables.pojos.Logevents; - -@RestController -public class LogREST { - - private final LogRepository logs; - - public LogREST(final LogRepository logs) { - this.logs = logs; - } - - @PostMapping(path = "/rest/api/logs", consumes = "application/json") - public ResponseEntity logEvent(@RequestBody final LogEvent body) { - this.logs.save(mapToLogevents(body)); - StreamWatcher.notifyOfEvent(); - return ResponseEntity.ok().build(); - } - - private Logevents mapToLogevents(final LogEvent body) { - return new Logevents(Integer.MIN_VALUE, body.getInstant(), body.getThread(), - body.getLevel(), body.getLoggerName(), body.getMessage(), body.getEndOfBatch(), - body.getLoggerFqcn(), body.getThreadId(), body.getThreadPriority()); - } -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/logs/LogRepository.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/logs/LogRepository.java deleted file mode 100644 index be2ae85de5..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/logs/LogRepository.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.logs; - -import org.togetherjava.tjbot.db.generated.tables.pojos.Logevents; - -import java.util.Collection; -import java.util.List; - -public interface LogRepository { - - /** - * Saves the given event to the DB, does not update or merge - * - * @param event Event to Insert - */ - void save(Logevents event); - - /** - * Fetches all Events from the DB - * - * @return List of LogEvents - */ - List findAll(); - - /** - * Fetches all Events, which LogLevel matches the given Collection, from the DB - * - * @return List of LogEvents - */ - List findWithLevelMatching(Collection logLevels); - -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/logs/LogRepositoryImpl.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/logs/LogRepositoryImpl.java deleted file mode 100644 index 865b3efd72..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/logs/LogRepositoryImpl.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.logs; - -import org.springframework.stereotype.Repository; -import org.togetherjava.tjbot.db.Database; -import org.togetherjava.tjbot.db.generated.tables.pojos.Logevents; -import org.togetherjava.tjbot.db.generated.tables.records.LogeventsRecord; - -import java.util.Collection; -import java.util.List; - -import static org.togetherjava.tjbot.db.generated.tables.Logevents.LOGEVENTS; - -@Repository -public class LogRepositoryImpl implements LogRepository { - - private final Database db; - - public LogRepositoryImpl(final Database db) { - this.db = db; - } - - - @Override - public void save(Logevents event) { - this.db.writeTransaction(ctx -> { - LogeventsRecord toInsert = ctx.newRecord(LOGEVENTS) - .setEndofbatch(event.getEndofbatch()) - .setLevel(event.getLevel()) - .setLoggername(event.getLoggername()) - .setLoggerfqcn(event.getLoggerfqcn()) - .setMessage(event.getMessage()) - .setTime(event.getTime()) - .setThread(event.getThread()) - .setThreadid(event.getThreadid()) - .setThreadpriority(event.getThreadpriority()); - - if (event.getId() != Integer.MIN_VALUE) { - toInsert.setId(event.getId()); - } - - // No merge or Update here, Logs are not supposed to be updated - toInsert.insert(); - }); - } - - @Override - public List findAll() { - return this.db.read(ctx -> ctx.selectFrom(LOGEVENTS).fetch(this::recordToPojo)); - } - - - @Override - public List findWithLevelMatching(Collection logLevels) { - return this.db.read(ctx -> ctx.selectFrom(LOGEVENTS) - .where(LOGEVENTS.LEVEL.in(logLevels)) - .fetch(this::recordToPojo)); - } - - private Logevents recordToPojo(final LogeventsRecord logRecord) { - return new Logevents(logRecord.getId(), logRecord.getTime(), logRecord.getThread(), - logRecord.getLevel(), logRecord.getLoggername(), logRecord.getMessage(), - logRecord.getEndofbatch(), logRecord.getLoggerfqcn(), logRecord.getThreadid(), - logRecord.getThreadpriority()); - } - - -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/oauth/OAuth2LoginConfig.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/oauth/OAuth2LoginConfig.java deleted file mode 100644 index eb9bfc92a7..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/oauth/OAuth2LoginConfig.java +++ /dev/null @@ -1,91 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.oauth; - -import com.vaadin.flow.spring.security.VaadinWebSecurityConfigurerAdapter; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.builders.WebSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; -import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; -import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository; -import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; -import org.springframework.security.oauth2.core.AuthorizationGrantType; -import org.springframework.security.oauth2.core.ClientAuthenticationMethod; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.togetherjava.tjbot.logwatcher.config.Config; - -/** - * Configures Spring Security so that we use Discord-OAuth2 as the identity provider - */ -@Configuration -public class OAuth2LoginConfig { - - @Bean - public ClientRegistrationRepository clientRegistrationRepository(Config config) { - return new InMemoryClientRegistrationRepository(googleClientRegistration(config)); - } - - @Bean - public OAuth2AuthorizedClientService authorizedClientService( - ClientRegistrationRepository clientRegistrationRepository) { - return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository); - } - - @Bean - public OAuth2AuthorizedClientRepository authorizedClientRepository( - OAuth2AuthorizedClientService authorizedClientService) { - return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService); - } - - /** - * This is where we actually configure the Security-API to talk to Discord - * - * @return The ClientRegistration for Discord - */ - private ClientRegistration googleClientRegistration(Config config) { - return ClientRegistration.withRegistrationId("Discord") - .clientName(config.getClientName()) - .clientId(config.getClientId()) - .clientSecret(config.getClientSecret()) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .scope("identify") - .authorizationUri("https://discord.com/api/oauth2/authorize") - .userInfoUri("https://discordapp.com/api/users/@me") - .userNameAttributeName("username") - .tokenUri("https://discordapp.com/api/oauth2/token") - .redirectUri(config.getRedirectPath()) - .build(); - } - - /** - * Configures the Security-API which path's should be protected and generally what to do - */ - @EnableWebSecurity - public static class OAuth2LoginSecurityConfig extends VaadinWebSecurityConfigurerAdapter { - - @Override - public void configure(WebSecurity web) throws Exception { - super.configure(web); - web.ignoring().antMatchers("/rest/api/**"); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http.authorizeRequests().antMatchers("/rest/api/**").anonymous(); - http.oauth2Login(); - http.logout() - .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) - .deleteCookies("JSESSIONID") - .invalidateHttpSession(true) - .clearAuthentication(true); - - // Enables Vaadin to load Server Resources - super.configure(http); - } - } -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/users/AuthenticatedUser.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/users/AuthenticatedUser.java deleted file mode 100644 index eaf128785d..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/users/AuthenticatedUser.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.users; - -import com.vaadin.flow.component.UI; -import com.vaadin.flow.server.VaadinServletRequest; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; -import org.springframework.stereotype.Component; -import org.togetherjava.tjbot.logwatcher.accesscontrol.Role; -import org.togetherjava.tjbot.db.generated.tables.pojos.Users; - -import java.util.Objects; -import java.util.Optional; -import java.util.Set; - -/** - * Wrapper for accessing the current User - */ -@Component -public class AuthenticatedUser { - - private final UserRepository userRepository; - - public AuthenticatedUser(UserRepository userRepository) { - this.userRepository = userRepository; - } - - /** - * Check's if you could register the current User - * - * @return true, if the Current user is Authenticated - */ - public boolean canRegister() { - return getAuthenticatedUser().isPresent(); - } - - /** - * Check's if the current User is already registered - * - * @return true, if the User is already in the repository - */ - public boolean isRegistered() { - if (!canRegister()) { - return false; - } - - return getAuthenticatedUser().map(this::extractID) - .map(userRepository::findByDiscordID) - .isPresent(); - } - - /** - * Attempts to register the current User, if he is not yet registered - */ - public void register() { - if (!canRegister() || isRegistered()) { - throw new IllegalStateException("Can not register an already registered User"); - } - - getAuthenticatedUser().map(this::toUser).ifPresent(userRepository::save); - } - - /** - * Get's the current User Object from the repository - * - * @return The Optional User, should in most cases not be empty - */ - public Users get() { - return getAuthenticatedUser().map(this::extractID) - .map(userRepository::findByDiscordID) - .orElseThrow(() -> new IllegalArgumentException("No authenticated User present.")); - } - - public Set getRoles() { - return this.userRepository.fetchRolesForUser(get()); - } - - /** - * Performs a logout on the current User - */ - public void logout() { - UI.getCurrent().getPage().setLocation("/"); - SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler(); - logoutHandler.logout(VaadinServletRequest.getCurrent().getHttpServletRequest(), null, null); - } - - /** - * Extracts the principal from the current SessionContext of the user - * - * @return The Optional Principal of the current User - */ - private Optional getAuthenticatedUser() { - SecurityContext context = SecurityContextHolder.getContext(); - Object principal = context.getAuthentication().getPrincipal(); - - return principal instanceof OAuth2User ? Optional.of(principal).map(OAuth2User.class::cast) - : Optional.empty(); - } - - /** - * Maps the principal to the User object - * - * @param oAuth2User Principal to map - * @return User-Object derived from the Principal - */ - private Users toUser(OAuth2User oAuth2User) { - return new Users(extractID(oAuth2User), oAuth2User.getName()); - } - - /** - * Extracts the discord-ID from the Principal - * - * @param oAuth2User Principal with the ID - * @return Discord-ID from the given Principal - */ - private long extractID(OAuth2User oAuth2User) { - final String id = oAuth2User.getAttribute("id"); - return Long.parseLong( - Objects.requireNonNull(id, "ID from OAuth-User is null, this should never happen")); - } - -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/users/UserDetailsServiceImpl.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/users/UserDetailsServiceImpl.java deleted file mode 100644 index ad8a7e6b8d..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/users/UserDetailsServiceImpl.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.users; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Service; -import org.togetherjava.tjbot.logwatcher.accesscontrol.Role; -import org.togetherjava.tjbot.logwatcher.config.Config; -import org.togetherjava.tjbot.db.generated.tables.pojos.Users; - -import java.util.List; -import java.util.Set; - -/** - * Service to load a spring UserDetail-Object from the userRepository, currently only used for - * db-initialisation - */ -@Service -public class UserDetailsServiceImpl implements UserDetailsService { - - private final UserRepository userRepository; - - public UserDetailsServiceImpl(UserRepository userRepository, Config config) { - this.userRepository = userRepository; - - if (this.userRepository.count() == 0) { - final Users defaultUser = - new Users(Long.parseLong(config.getRootDiscordID()), config.getRootUserName()); - - this.userRepository.save(defaultUser); - this.userRepository.saveRolesForUser(defaultUser, Set.of(Role.ADMIN, Role.USER)); - } - } - - private List getAuthorities(Users user) { - return this.userRepository.fetchRolesForUser(user) - .stream() - .map(Role::getRoleName) - .map(name -> "ROLE_" + name) - .map(SimpleGrantedAuthority::new) - .map(GrantedAuthority.class::cast) - .toList(); - - } - - /** - * Loads the user from the userRepository and maps it to the Spring-Object UserDetails - * - * @param username Username of the User to Load - * @return The UserDetail-Object that is associated with the discordID, or else throws an - * {@link UsernameNotFoundException} - */ - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - Users user = userRepository.findByUsername(username); - if (user == null) { - throw new UsernameNotFoundException("No user present with username: " + username); - } else { - return new org.springframework.security.core.userdetails.User(user.getUsername(), null, - getAuthorities(user)); - } - } -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/users/UserRepository.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/users/UserRepository.java deleted file mode 100644 index de78e71d8a..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/users/UserRepository.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.users; - -import org.togetherjava.tjbot.logwatcher.accesscontrol.Role; -import org.togetherjava.tjbot.db.generated.tables.pojos.Users; - -import java.util.List; -import java.util.Set; - -/** - * Basic JPA-Repository for loading Users from the DB - */ -public interface UserRepository { - - /** - * Load's the User from the DB, that matches the discordID - * - * @param discordID Discord-ID of the User - * @return User-Object of the user where the discordID matches, else null - */ - Users findByDiscordID(long discordID); - - /** - * Load's the User from the DB, that matches the username - * - * @param username Username of the User to load - * @return User-Object of the user where the username matches, else null - */ - Users findByUsername(final String username); - - /** - * Fetches all saved User - * - * @return List of Users from the DB, never null - */ - List findAll(); - - /** - * Counts the amount of Users saved in the DB - * - * @return Count of Users in the db >=0 - */ - int count(); - - /** - * Merges the given user in the DB - * - * @param user User to Save in the DB - */ - void save(Users user); - - /** - * Removes this User and all referencing Entities - */ - void delete(Users user); - - /** - * Fetches the Roles the User has, see {@link Role} - * - * @param user User to fetch the Roles for - * @return Set of Roles, never null - */ - Set fetchRolesForUser(Users user); - - /** - * Updates/Saves the Roles for the User - * - * @param user User to update the Role's - * @param roles All Roles the User should have - */ - void saveRolesForUser(Users user, Set roles); -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/users/UserRepositoryImpl.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/users/UserRepositoryImpl.java deleted file mode 100644 index 792a4c9984..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/users/UserRepositoryImpl.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.users; - -import org.springframework.stereotype.Component; -import org.togetherjava.tjbot.logwatcher.accesscontrol.Role; -import org.togetherjava.tjbot.db.Database; -import org.togetherjava.tjbot.db.generated.tables.Userroles; -import org.togetherjava.tjbot.db.generated.tables.pojos.Users; -import org.togetherjava.tjbot.db.generated.tables.records.UserrolesRecord; -import org.togetherjava.tjbot.db.generated.tables.records.UsersRecord; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static org.togetherjava.tjbot.db.generated.tables.Users.USERS; - -@Component -public class UserRepositoryImpl implements UserRepository { - - private final Database db; - - public UserRepositoryImpl(Database db) { - this.db = db; - } - - @Override - public Users findByDiscordID(long discordID) { - return this.db.readTransaction(ctx -> ctx.selectFrom(USERS) - .where(USERS.DISCORDID.eq(discordID)) - .fetchOne(this::recordToRole)); - } - - @Override - public Users findByUsername(String username) { - return this.db.readTransaction(ctx -> ctx.selectFrom(USERS) - .where(USERS.USERNAME.eq(username)) - .fetchOne(this::recordToRole)); - } - - @Override - public List findAll() { - return this.db.readTransaction(ctx -> ctx.selectFrom(USERS).fetch(this::recordToRole)); - } - - @Override - public int count() { - return this.db.readTransaction(ctx -> ctx.fetchCount(USERS)); - } - - @Override - public void save(Users user) { - this.db.writeTransaction(ctx -> ctx.newRecord(USERS) - .setDiscordid(user.getDiscordid()) - .setUsername(user.getUsername()) - .merge()); - } - - @Override - public void delete(Users user) { - this.db.writeTransaction(ctx -> ctx.deleteFrom(USERS) - .where(USERS.DISCORDID.eq(user.getDiscordid())) - .execute()); - } - - @Override - public Set fetchRolesForUser(Users user) { - return new HashSet<>(this.db.readTransaction(ctx -> ctx.selectFrom(Userroles.USERROLES) - .where(Userroles.USERROLES.USERID.eq(user.getDiscordid())) - .fetch(this::recordToRole))); - } - - @Override - public void saveRolesForUser(Users user, Set roles) { - this.db.writeTransaction(ctx -> { - ctx.deleteFrom(Userroles.USERROLES) - .where(Userroles.USERROLES.USERID.eq(user.getDiscordid())) - .execute(); - - for (final Role role : roles) { - ctx.newRecord(Userroles.USERROLES) - .setRoleid(role.getId()) - .setUserid(user.getDiscordid()) - .insert(); - } - }); - } - - private Users recordToRole(UsersRecord usersRecord) { - return new Users(usersRecord.getDiscordid(), usersRecord.getUsername()); - } - - private Role recordToRole(UserrolesRecord rolesRecord) { - return Role.forID(rolesRecord.getRoleid()); - } -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/util/LogReader.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/util/LogReader.java deleted file mode 100644 index 9aadc236af..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/util/LogReader.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.util; - -import org.springframework.stereotype.Component; -import org.togetherjava.tjbot.logwatcher.config.Config; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.stream.Stream; - -@Component -public final class LogReader { - - private final Path logPath; - - public LogReader(Config config) { - this.logPath = Path.of(config.getLogPath()); - } - - /** - * Returns all log Files in the configured Path {@link Config#logPath} - * - * @return Names of the Logfiles - */ - public List getLogs() { - try (final Stream stream = Files.list(this.logPath)) { - return stream.filter(Files::isRegularFile) - .filter(s -> s.toString().endsWith(".log") || s.toString().endsWith(".log.gz")) - .toList(); - } catch (final IOException e) { - throw new UncheckedIOException(e); - } - } - - /** - * Read's the content of the given Logfile in the configured Logging path - * - * @param log Name of the Logfile - * @return The Content of the Log - */ - public List readLog(final Path log) { - try { - return Files.readAllLines(log); - } catch (final IOException e) { - throw new UncheckedIOException(e); - } - } - -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/util/LogUtils.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/util/LogUtils.java deleted file mode 100644 index 719fb20223..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/util/LogUtils.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.util; - -import org.togetherjava.tjbot.db.generated.tables.pojos.Logevents; - -import java.util.Arrays; -import java.util.EnumSet; -import java.util.Locale; -import java.util.Set; -import java.util.stream.Collectors; - -public final class LogUtils { - - /** - * Loglevel of all Configurable Levels in the logger - */ - public enum LogLevel { - INFO, - WARN, - ERROR, - DEBUG, - TRACE; - - /** - * Collects all LogLevel in a Set - * - * @return A Set containing every Loglevel - */ - public static Set getAll() { - return EnumSet.allOf(LogLevel.class); - } - - /** - * Maps the LogLevel to their name and collects it in a Set - * - * @return A Set containing every LogLevel as String - */ - public static Set getAllNames() { - return Arrays.stream(values()).map(Enum::name).collect(Collectors.toUnmodifiableSet()); - } - } - - /** - * Maps a Logevent to the color-coded css-Class (css class has i.e. red as background-color for - * ERROR events) - * - * @param event Logevent from the DB with a specific Logevent - * @return The name of the CSS class to use with this Logevent. - */ - public static String logLevelToCssClass(final Logevents event) { - return event.getLevel().toLowerCase(Locale.ENGLISH); - } - - - - private LogUtils() {} -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/util/NotificationUtils.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/util/NotificationUtils.java deleted file mode 100644 index e2968b1165..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/util/NotificationUtils.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.util; - -import com.vaadin.flow.component.notification.Notification; -import com.vaadin.flow.component.notification.NotificationVariant; - -import java.util.concurrent.TimeUnit; - -/** - * Util class for handy Methods regarding Notifications for the Client - */ -public final class NotificationUtils { - - - private NotificationUtils() {} - - /** - * Prepares a little Notification to display an Errormessage, in case anything goes wrong - * - * @param e Exception that occurred - * @return Notification for the user - */ - public static Notification getNotificationForError(final Exception e) { - final Notification not = new Notification(); - not.setDuration((int) TimeUnit.SECONDS.toMillis(6)); - not.setPosition(Notification.Position.MIDDLE); - not.setText("Exception occurred while saving. %s".formatted(e.getMessage())); - not.addThemeVariants(NotificationVariant.LUMO_ERROR); - return not; - } -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/views/MainLayout.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/views/MainLayout.java deleted file mode 100644 index d3f60008d7..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/views/MainLayout.java +++ /dev/null @@ -1,173 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.views; - -import com.google.common.collect.Sets; -import com.vaadin.flow.component.Component; -import com.vaadin.flow.component.applayout.AppLayout; -import com.vaadin.flow.component.applayout.DrawerToggle; -import com.vaadin.flow.component.avatar.Avatar; -import com.vaadin.flow.component.button.ButtonVariant; -import com.vaadin.flow.component.contextmenu.ContextMenu; -import com.vaadin.flow.component.html.*; -import com.vaadin.flow.router.PageTitle; -import com.vaadin.flow.router.RouterLink; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.togetherjava.tjbot.logwatcher.accesscontrol.AllowedRoles; -import org.togetherjava.tjbot.logwatcher.accesscontrol.Role; -import org.togetherjava.tjbot.logwatcher.users.AuthenticatedUser; -import org.togetherjava.tjbot.logwatcher.views.logs.LogsView; -import org.togetherjava.tjbot.logwatcher.views.logs.StreamedView; -import org.togetherjava.tjbot.logwatcher.views.usermanagement.UserManagement; -import org.togetherjava.tjbot.db.generated.tables.pojos.Users; - -import java.util.List; -import java.util.Set; -import java.util.stream.Stream; - -/** - * The main view is a top-level placeholder for other views. - */ -@PageTitle("Main") -@SuppressWarnings({"java:S1192"}) -// Those are css names, and those don't need an extra Constants Class -public class MainLayout extends AppLayout { - - private final transient AuthenticatedUser authenticatedUser; - private H1 viewTitle; - private static final Logger logger = LoggerFactory.getLogger(MainLayout.class); - - public MainLayout(AuthenticatedUser authUser) { - this.authenticatedUser = authUser; - setPrimarySection(Section.DRAWER); - addToNavbar(true, createHeaderContent()); - addToDrawer(createDrawerContent()); - - if (authUser.canRegister() && !authUser.isRegistered()) { - authUser.register(); - } - - - if (this.authenticatedUser.getRoles().isEmpty()) { - authUser.logout(); - } - - } - - private static RouterLink createLink(MenuItemInfo menuItemInfo) { - RouterLink link = new RouterLink(); - link.addClassNames("flex", "mx-s", "p-s", "relative", "text-secondary"); - link.setRoute(menuItemInfo.view()); - - Span icon = new Span(); - icon.addClassNames("me-s", "text-l"); - if (!menuItemInfo.iconClass().isEmpty()) { - icon.addClassNames(menuItemInfo.iconClass()); - } - - Span text = new Span(menuItemInfo.text()); - text.addClassNames("font-medium", "text-s"); - - link.add(icon, text); - return link; - } - - private Component createHeaderContent() { - DrawerToggle toggle = new DrawerToggle(); - toggle.addClassName("text-secondary"); - toggle.addThemeVariants(ButtonVariant.LUMO_CONTRAST); - toggle.getElement().setAttribute("aria-label", "Menu toggle"); - - viewTitle = new H1(); - viewTitle.addClassNames("m-0", "text-l"); - - Header header = new Header(toggle, viewTitle); - header.addClassNames("bg-base", "border-b", "border-contrast-10", "box-border", "flex", - "h-xl", "items-center", "w-full"); - return header; - } - - private Component createDrawerContent() { - H2 appName = new H2("Logviewer"); - appName.addClassNames("flex", "items-center", "h-xl", "m-0", "px-m", "text-m"); - - com.vaadin.flow.component.html.Section section = new com.vaadin.flow.component.html.Section( - appName, createNavigation(), createFooter()); - section.addClassNames("flex", "flex-col", "items-stretch", "max-h-full", "min-h-full"); - return section; - } - - private Nav createNavigation() { - Nav nav = new Nav(); - nav.addClassNames("border-b", "border-contrast-10", "flex-grow", "overflow-auto"); - nav.getElement().setAttribute("aria-labelledby", "views"); - - H3 views = new H3("Views"); - views.addClassNames("flex", "h-m", "items-center", "mx-m", "my-0", "text-s", - "text-tertiary"); - views.setId("views"); - - createLinks().forEach(nav::add); - - return nav; - } - - private List createLinks() { - return Stream - .of(new MenuItemInfo("Logs", "la la-globe", LogsView.class), - new MenuItemInfo("Streamed", "la la-globe", StreamedView.class), - new MenuItemInfo("User Management", "la la-file", UserManagement.class)) - .filter(this::checkAccess) - .map(MainLayout::createLink) - .toList(); - } - - private boolean checkAccess(MenuItemInfo menuItemInfo) { - final Class view = menuItemInfo.view; - final AllowedRoles annotation = view.getAnnotation(AllowedRoles.class); - - if (annotation == null) { - logger.warn("Class {} not properly secured with Annotation", view); - return false; - } - - final Set roles = Set.of(annotation.roles()); - - return !Sets.intersection(this.authenticatedUser.getRoles(), roles).isEmpty(); - } - - private Footer createFooter() { - Footer layout = new Footer(); - layout.addClassNames("flex", "items-center", "my-s", "px-m", "py-xs"); - - Users user = this.authenticatedUser.get(); - - Avatar avatar = new Avatar(user.getUsername()); - avatar.addClassNames("me-xs"); - - ContextMenu userMenu = new ContextMenu(avatar); - userMenu.setOpenOnClick(true); - userMenu.addItem("Logout", e -> authenticatedUser.logout()); - - Span name = new Span(user.getUsername()); - name.addClassNames("font-medium", "text-s", "text-secondary"); - - layout.add(avatar, name); - - return layout; - } - - @Override - protected void afterNavigation() { - super.afterNavigation(); - viewTitle.setText(getCurrentPageTitle()); - } - - private String getCurrentPageTitle() { - PageTitle title = getContent().getClass().getAnnotation(PageTitle.class); - return title == null ? "" : title.value(); - } - - - private record MenuItemInfo(String text, String iconClass, Class view) { - } -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/views/logs/LogsView.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/views/logs/LogsView.java deleted file mode 100644 index 97e23e70e5..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/views/logs/LogsView.java +++ /dev/null @@ -1,183 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.views.logs; - -import com.vaadin.flow.component.AbstractField; -import com.vaadin.flow.component.HasValue; -import com.vaadin.flow.component.checkbox.Checkbox; -import com.vaadin.flow.component.combobox.ComboBox; -import com.vaadin.flow.component.html.Paragraph; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.orderedlayout.Scroller; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.data.provider.DataProvider; -import com.vaadin.flow.data.renderer.TextRenderer; -import com.vaadin.flow.router.PageTitle; -import com.vaadin.flow.router.Route; -import com.vaadin.flow.router.RouteAlias; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.togetherjava.tjbot.logwatcher.accesscontrol.AllowedRoles; -import org.togetherjava.tjbot.logwatcher.accesscontrol.Role; -import org.togetherjava.tjbot.logwatcher.util.LogReader; -import org.togetherjava.tjbot.logwatcher.util.LogUtils; -import org.togetherjava.tjbot.logwatcher.util.NotificationUtils; -import org.togetherjava.tjbot.logwatcher.views.MainLayout; - -import javax.annotation.security.PermitAll; -import java.io.UncheckedIOException; -import java.nio.file.Path; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - - -/** - * The Logs View in the Browser - */ - -@PageTitle("Logs") -@Route(value = "logs", layout = MainLayout.class) -@RouteAlias(value = "", layout = MainLayout.class) -@AllowedRoles(roles = {Role.USER}) -@PermitAll -public class LogsView extends VerticalLayout { - - private static final Logger logger = LoggerFactory.getLogger(LogsView.class); - - private static final Pattern LOGLEVEL_MATCHER = - Pattern.compile("(%s)".formatted(String.join("|", LogUtils.LogLevel.getAllNames()))); - - /** - * Field where the events are displayed - */ - private final VerticalLayout events = new VerticalLayout(); - - private final transient LogReader watcher; - - public LogsView(LogReader watcher) { - this.watcher = watcher; - this.events.setWidthFull(); - - addClassName("logs-view"); - - HorizontalLayout options = new HorizontalLayout(); - options.setAlignItems(Alignment.START); - - for (final String level : LogUtils.LogLevel.getAllNames()) { - final Checkbox ch = new Checkbox(level); - ch.setValue(true); - ch.addValueChangeListener(this::onLogLevelCheckbox); - options.add(ch); - } - - ComboBox logs = createComboBox(); - logs.getOptionalValue().ifPresent(this::fillTextField); - - add(logs, options, new Scroller(this.events, Scroller.ScrollDirection.VERTICAL)); - } - - private void onLogLevelCheckbox( - AbstractField.ComponentValueChangeEvent event) { - if (!event.isFromClient()) { - return; - } - - final String level = event.getSource().getLabel().toLowerCase(Locale.ENGLISH); - - this.events.getChildren() - .filter(Paragraph.class::isInstance) - .map(Paragraph.class::cast) - .filter(c -> c.getClassName().equals(level)) - .forEach(c -> c.setVisible(event.getValue())); - } - - /** - * Creates the Combobox of Logfile-Names where the User can choose what File to view - * - * @return The created Combobox - */ - private ComboBox createComboBox() { - final ComboBox logs = new ComboBox<>("Log-Files"); - logs.setAllowCustomValue(false); - logs.setRenderer(new TextRenderer<>(p -> p.getFileName().toString())); - - final List logFiles = getLogFiles(); - logs.setItems(DataProvider.ofCollection(logFiles)); - logFiles.stream().findFirst().ifPresent(logs::setValue); - - logs.addValueChangeListener(this::onLogFileCombobox); - - return logs; - } - - /** - * When User chooses another Logfile, reload the Log and set it in the textField - * - * @param event Generated Event, containing old and new Value - */ - private void onLogFileCombobox(HasValue.ValueChangeEvent event) { - if (Objects.equals(event.getOldValue(), event.getValue())) { - return; - } - - fillTextField(event.getValue()); - } - - /** - * Reload the Log and set it in the textField - * - * @param logFileName Name of the Logfile - */ - private void fillTextField(final Path logFileName) { - this.events.removeAll(); - - final List logEntries = getLogEntries(logFileName); - - String previousGroup = "unknown"; - for (final String event : logEntries) { - final String trimmed = event.trim(); - final Paragraph text = new Paragraph(trimmed); - - final Matcher matcher = LOGLEVEL_MATCHER.matcher(trimmed); - if (matcher.find()) { - previousGroup = matcher.group().toLowerCase(Locale.ENGLISH); - } - - text.addClassName(previousGroup); - this.events.add(text); - } - } - - /** - * Gathers all available Logfiles - * - * @return Names of available Logfiles - */ - private List getLogFiles() { - try { - return this.watcher.getLogs(); - } catch (final UncheckedIOException e) { - logger.error("Exception while gathering LogFiles", e); - NotificationUtils.getNotificationForError(e).open(); - return Collections.emptyList(); - } - } - - /** - * Reads the log for the given Logfile - * - * @param logFile Name of the log to read - * @return Contents of the LogFile - */ - private List getLogEntries(final Path logFile) { - try { - return this.watcher.readLog(logFile); - } catch (final UncheckedIOException e) { - logger.error("Exception while gathering LogFiles", e); - NotificationUtils.getNotificationForError(e).open(); - return Collections.emptyList(); - } - } -} diff --git a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/views/logs/StreamedView.java b/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/views/logs/StreamedView.java deleted file mode 100644 index 2d15ac9cb1..0000000000 --- a/logviewer/src/main/java/org/togetherjava/tjbot/logwatcher/views/logs/StreamedView.java +++ /dev/null @@ -1,189 +0,0 @@ -package org.togetherjava.tjbot.logwatcher.views.logs; - -import com.vaadin.componentfactory.EnhancedDialog; -import com.vaadin.flow.component.*; -import com.vaadin.flow.component.button.Button; -import com.vaadin.flow.component.checkbox.Checkbox; -import com.vaadin.flow.component.dependency.CssImport; -import com.vaadin.flow.component.grid.Grid; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.data.renderer.LocalDateTimeRenderer; -import com.vaadin.flow.router.PageTitle; -import com.vaadin.flow.router.Route; -import com.vaadin.flow.server.SessionDestroyEvent; -import com.vaadin.flow.server.VaadinService; -import org.togetherjava.tjbot.db.generated.tables.pojos.Logevents; -import org.togetherjava.tjbot.logwatcher.accesscontrol.AllowedRoles; -import org.togetherjava.tjbot.logwatcher.accesscontrol.Role; -import org.togetherjava.tjbot.logwatcher.constants.LogEventsConstants; -import org.togetherjava.tjbot.logwatcher.logs.LogRepository; -import org.togetherjava.tjbot.logwatcher.util.LogUtils; -import org.togetherjava.tjbot.logwatcher.views.MainLayout; -import org.togetherjava.tjbot.logwatcher.watcher.StreamWatcher; -import org.vaadin.crudui.crud.impl.GridCrud; - -import javax.annotation.security.PermitAll; -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; -import java.util.*; -import java.util.stream.Collectors; - -/** - * The Logs View in the Browser - */ - -@PageTitle("Streamed") -@Route(value = "streamed", layout = MainLayout.class) -@AllowedRoles(roles = {Role.USER}) -@PermitAll -@CssImport(themeFor = "vaadin-grid", value = "./themes/logviewer/views/logs-view.css") -public class StreamedView extends VerticalLayout { - - private final GridCrud grid = new GridCrud<>(Logevents.class); - private final UUID uuid = UUID.randomUUID(); - private final Set enabledLogLevel = new HashSet<>(LogUtils.LogLevel.getAllNames()); - - public StreamedView(LogRepository logs) { - addClassName("logs-view"); - - final HorizontalLayout buttonPanel = - new HorizontalLayout(new Button("Change Columns", this::onChangeColumns)); - - for (final String level : this.enabledLogLevel) { - final Checkbox ch = new Checkbox(level); - ch.setValue(true); - ch.addValueChangeListener(this::onLogLevelCheckbox); - buttonPanel.add(ch); - } - - add(buttonPanel, this.grid); - - this.grid.setOperations(() -> logs.findWithLevelMatching(this.enabledLogLevel), null, null, - null); - this.grid.setAddOperationVisible(false); - this.grid.setDeleteOperationVisible(false); - this.grid.setUpdateOperationVisible(false); - - - final Grid baseGrid = this.grid.getGrid(); - baseGrid.setColumns(LogEventsConstants.FIELD_INSTANT, LogEventsConstants.FIELD_LOGGER_NAME, - LogEventsConstants.FIELD_MESSAGE); - setInstantFormatter(); - baseGrid.getColumns().forEach(c -> c.setAutoWidth(true)); - baseGrid.getColumns().forEach(c -> c.setResizable(true)); - baseGrid.setClassNameGenerator(LogUtils::logLevelToCssClass); - baseGrid.recalculateColumnWidths(); - baseGrid.setColumnReorderingAllowed(true); - - VaadinService.getCurrent().addSessionDestroyListener(this::onDestroy); - final UI ui = UI.getCurrent(); - StreamWatcher.addSubscription(this.uuid, () -> ui.access(this.grid::refreshGrid)); - } - - private void onLogLevelCheckbox( - AbstractField.ComponentValueChangeEvent event) { - if (!event.isFromClient()) { - return; - } - - final String logLevel = event.getSource().getLabel(); - - final boolean isChecked = event.getValue();// don't inline this, else SonarLint is crying - if (isChecked) { - this.enabledLogLevel.add(logLevel); - } else { - this.enabledLogLevel.remove(logLevel); - } - this.grid.refreshGrid(); - } - - private void onDestroy(SessionDestroyEvent event) { - removeHook(); - } - - @Override - protected void onDetach(DetachEvent detachEvent) { - removeHook(); - super.onDetach(detachEvent); - } - - private void removeHook() { - StreamWatcher.removeSubscription(this.uuid); - } - - private void setInstantFormatter() { - final Grid innerGrid = this.grid.getGrid(); - final Optional> column = - Optional.ofNullable(innerGrid.getColumnByKey(LogEventsConstants.FIELD_INSTANT)); - if (column.isEmpty()) { - return; - } - - final Grid.Column instant = column.orElseThrow(); - innerGrid.removeColumn(instant); - - final String[] keys = - innerGrid.getColumns().stream().map(Grid.Column::getKey).toArray(String[]::new); - innerGrid.removeAllColumns(); - - - innerGrid - .addColumn(new LocalDateTimeRenderer<>( - logEvents -> LocalDateTime.ofInstant(logEvents.getTime(), ZoneOffset.UTC), - DateTimeFormatter.ofPattern("yyy-MM-dd HH:mm:ss.SSS"))) - .setHeader("Instant") - .setComparator(Comparator.comparing(Logevents::getTime)) - .setKey(LogEventsConstants.FIELD_INSTANT); - - innerGrid.addColumns(keys); - } - - private void onChangeColumns(ClickEvent