From b8417b156472db84d3048798da8824fd118b98c2 Mon Sep 17 00:00:00 2001 From: Yaroslav Svitlytskyi <53532703+YarikRevich@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:16:24 +0100 Subject: [PATCH] Feature: add postgres support (#21) * feature: added postgres support * fix: fixed bugs --- README.md | 28 +++- api-server/pom.xml | 4 + .../entity/common/ConfigEntity.java | 49 +++++++ .../entity/common/PropertiesEntity.java | 9 ++ ...figDatabasePropertiesMissingException.java | 21 +++ .../repository/ConfigRepository.java | 121 ---------------- .../repository/ContentRepository.java | 33 +++-- .../repository/ProviderRepository.java | 52 +++++-- .../repository/SecretRepository.java | 51 ++++--- .../repository/TemporateRepository.java | 121 ++++++++++++---- .../executor/RepositoryExecutor.java | 40 ++++-- .../integration/backup/BackupService.java | 2 - .../diagnostics/DiagnosticsConfigService.java | 36 +---- .../DatabasePropertiesConfigService.java | 130 +++++++++++++++++ .../SecurityPropertiesConfigService.java | 6 +- .../service/state/watcher/WatcherService.java | 7 +- .../io.smallrye.config.ConfigSourceFactory | 3 +- .../src/main/resources/application.properties | 13 +- .../resources/liquibase/postgres/config.yaml | 134 ++++++++++++++++++ .../liquibase/{ => postgres}/data/data.csv | 0 .../liquibase/{ => sqlite3}/config.yaml | 31 ---- .../resources/liquibase/sqlite3/data/data.csv | 3 + config/grafana/dashboards/diagnostics.tmpl | 118 +++++---------- docs/internal-database-design.md | 13 +- docs/internal-database-design.png | Bin 37487 -> 32543 bytes docs/internal-storage-design.md | 4 +- docs/internal-storage-design.png | Bin 8203 -> 8867 bytes pom.xml | 5 + samples/config/api-server/api-server.yaml | 14 ++ 29 files changed, 672 insertions(+), 376 deletions(-) create mode 100644 api-server/src/main/java/com/objectstorage/exception/ConfigDatabasePropertiesMissingException.java delete mode 100644 api-server/src/main/java/com/objectstorage/repository/ConfigRepository.java create mode 100644 api-server/src/main/java/com/objectstorage/service/integration/properties/database/DatabasePropertiesConfigService.java create mode 100644 api-server/src/main/resources/liquibase/postgres/config.yaml rename api-server/src/main/resources/liquibase/{ => postgres}/data/data.csv (100%) rename api-server/src/main/resources/liquibase/{ => sqlite3}/config.yaml (80%) create mode 100644 api-server/src/main/resources/liquibase/sqlite3/data/data.csv diff --git a/README.md b/README.md index 559538a..2b6c80f 100644 --- a/README.md +++ b/README.md @@ -102,11 +102,23 @@ connection: # Represents password, which will be used to decode operations. password: "test123" -# Represents section used for ObjectStorage API Server temporate storage configuration. -temporate-storage: - # Represents format used for content to be saved. - format: "zip" +# Represents section used for ObjectStorage API Server internal database configuration. +internal-storage: + # Represents provider selected for ObjectStorage internal database. Supported providers are "sqlite3" and "postgres" only. + provider: "sqlite3" + + # Represents host for the previously selected ObjectStorage internal database provider, works only for "postgres". + # host: "localhost:5432" + + # Represents username for the previously selected ObjectStorage internal database provider. + username: "objectstorage_user" + + # Represents password for the previously selected ObjectStorage internal database provider. + password: "objectstorage_password" +# Represents section used for ObjectStorage API Server temporate storage configuration. Same compression will be +# used to upload files to the configured cloud providers. +temporate-storage: # Represents frequency of scheduled operations processing. frequency: "*/5 * * * * ?" @@ -118,10 +130,13 @@ backup: # Represents frequency of backup operation for selected provider. frequency: "0 */5 * * * ?" + # Represents the highest amount of downloaded backup content versions per each workspace. + max-versions: 5 + # Represents section used for ObjectStorage API Server diagnostics configuration. diagnostics: # Enables diagnostics functionality. - enabled: false + enabled: true # Represents section used for ObjectStorage diagnostics metrics configuration. metrics: @@ -144,6 +159,9 @@ diagnostics: port: 8121 ``` +In the **~/.objectstorage/internal/database** directory there will be located internal database data, if **sqlite3** +option is selected as target database. + ### Diagnostics dashboard For **ObjectStorage API Server** configuration the following section should be modified: \ No newline at end of file diff --git a/api-server/pom.xml b/api-server/pom.xml index 6a79f68..2f99a8e 100644 --- a/api-server/pom.xml +++ b/api-server/pom.xml @@ -100,6 +100,10 @@ io.quarkiverse.jdbc quarkus-jdbc-sqlite + + io.quarkus + quarkus-jdbc-postgresql + io.quarkus quarkus-smallrye-jwt diff --git a/api-server/src/main/java/com/objectstorage/entity/common/ConfigEntity.java b/api-server/src/main/java/com/objectstorage/entity/common/ConfigEntity.java index 361f673..347d4f3 100644 --- a/api-server/src/main/java/com/objectstorage/entity/common/ConfigEntity.java +++ b/api-server/src/main/java/com/objectstorage/entity/common/ConfigEntity.java @@ -51,6 +51,55 @@ public static class Security { @JsonProperty("connection") public Connection connection; + /** + * Represents ObjectStorage internal storage configuration used for internal database setup. + */ + @Getter + public static class InternalStorage { + /** + * Represents all supported providers, which can be used by ObjectStorage internal storage. + */ + @Getter + public enum Provider { + @JsonProperty("sqlite3") + SQLITE3("sqlite3"), + + @JsonProperty("postgres") + POSTGRES("postgres"); + + private final String value; + + Provider(String value) { + this.value = value; + } + + public String toString() { + return value; + } + } + + @Valid + @NotNull + @JsonProperty("provider") + public Provider provider; + + @JsonProperty("host") + public String host; + + @NotNull + @JsonProperty("username") + public String username; + + @NotNull + @JsonProperty("password") + public String password; + } + + @Valid + @NotNull + @JsonProperty("internal-storage") + public InternalStorage internalStorage; + /** * Represents ObjectStorage API Server configuration used for temporate storage setup. */ diff --git a/api-server/src/main/java/com/objectstorage/entity/common/PropertiesEntity.java b/api-server/src/main/java/com/objectstorage/entity/common/PropertiesEntity.java index ec3e7a9..cbc7502 100644 --- a/api-server/src/main/java/com/objectstorage/entity/common/PropertiesEntity.java +++ b/api-server/src/main/java/com/objectstorage/entity/common/PropertiesEntity.java @@ -20,6 +20,15 @@ public class PropertiesEntity { @ConfigProperty(name = "quarkus.http.port") Integer applicationPort; + @ConfigProperty(name = "database.name") + String databaseName; + + @ConfigProperty(name = "liquibase.sqlite3.config") + String liquibaseSqlite3Config; + + @ConfigProperty(name = "liquibase.postgres.config") + String liquibasePostgresConfig; + @ConfigProperty(name = "content.root.notation") String contentRootNotation; diff --git a/api-server/src/main/java/com/objectstorage/exception/ConfigDatabasePropertiesMissingException.java b/api-server/src/main/java/com/objectstorage/exception/ConfigDatabasePropertiesMissingException.java new file mode 100644 index 0000000..76007d9 --- /dev/null +++ b/api-server/src/main/java/com/objectstorage/exception/ConfigDatabasePropertiesMissingException.java @@ -0,0 +1,21 @@ +package com.objectstorage.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when configuration file database properties are missing. + */ +public class ConfigDatabasePropertiesMissingException extends IOException { + public ConfigDatabasePropertiesMissingException() { + this(""); + } + + public ConfigDatabasePropertiesMissingException(Object... message) { + super( + new Formatter() + .format("Config file database properties are missing: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/objectstorage/repository/ConfigRepository.java b/api-server/src/main/java/com/objectstorage/repository/ConfigRepository.java deleted file mode 100644 index cce2352..0000000 --- a/api-server/src/main/java/com/objectstorage/repository/ConfigRepository.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.objectstorage.repository; - -import com.objectstorage.entity.common.PropertiesEntity; -import com.objectstorage.entity.repository.ConfigEntity; -import com.objectstorage.exception.QueryEmptyResultException; -import com.objectstorage.exception.QueryExecutionFailureException; -import com.objectstorage.exception.RepositoryOperationFailureException; -import com.objectstorage.repository.executor.RepositoryExecutor; -import io.quarkus.runtime.annotations.RegisterForReflection; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -/** - * Represents repository implementation to handle config table. - */ -@ApplicationScoped -@RegisterForReflection -public class ConfigRepository { - @Inject - PropertiesEntity properties; - - @Inject - RepositoryExecutor repositoryExecutor; - - /** - * Inserts given values into the config table. - * - * @param provider given provider. - * @param secret given secret. - * @param hash given configuration file hash. - * @throws RepositoryOperationFailureException if operation execution fails. - */ - public void insert(Integer provider, Integer secret, String hash) throws RepositoryOperationFailureException { - try { - repositoryExecutor.performQuery( - String.format( - "INSERT INTO %s (provider, secret, hash) VALUES (%d, %d, '%s')", - properties.getDatabaseConfigTableName(), - provider, - secret, - hash)); - - } catch (QueryExecutionFailureException | QueryEmptyResultException e) { - throw new RepositoryOperationFailureException(e.getMessage()); - } - } - - /** - * Retrieves all the persisted temporate entities with the given provider and secret. - * - * @return retrieved temporate entities. - * @throws RepositoryOperationFailureException if repository operation fails. - */ - public List findByProviderAndSecret(Integer provider, Integer secret) throws - RepositoryOperationFailureException { - ResultSet resultSet; - - try { - resultSet = - repositoryExecutor.performQueryWithResult( - String.format( - "SELECT t.id, t.hash FROM %s as t WHERE t.provider = %d AND t.secret = %d", - properties.getDatabaseConfigTableName(), - provider, - secret)); - - } catch (QueryExecutionFailureException | QueryEmptyResultException e) { - throw new RepositoryOperationFailureException(e.getMessage()); - } - - List result = new ArrayList<>(); - - Integer id; - String hash; - - try { - while (resultSet.next()) { - id = resultSet.getInt("id"); - hash = resultSet.getString("hash"); - - result.add(ConfigEntity.of(id, provider, secret, hash)); - } - } catch (SQLException e) { - throw new RepositoryOperationFailureException(e.getMessage()); - } - - try { - resultSet.close(); - } catch (SQLException e) { - throw new RepositoryOperationFailureException(e.getMessage()); - } - - return result; - } - - /** - * Deletes all entities with the given provider and secret from config table. - * - * @param provider given provider. - * @param secret given secret. - * @throws RepositoryOperationFailureException if operation execution fails. - */ - public void deleteByProviderAndSecret(Integer provider, Integer secret) throws RepositoryOperationFailureException { - try { - repositoryExecutor.performQuery( - String.format( - "DELETE FROM %s as t WHERE t.provider = %d AND t.secret = %d", - properties.getDatabaseConfigTableName(), - provider, - secret)); - - } catch (QueryExecutionFailureException | QueryEmptyResultException e) { - throw new RepositoryOperationFailureException(e.getMessage()); - } - } -} diff --git a/api-server/src/main/java/com/objectstorage/repository/ContentRepository.java b/api-server/src/main/java/com/objectstorage/repository/ContentRepository.java index 32412d8..dab78ca 100644 --- a/api-server/src/main/java/com/objectstorage/repository/ContentRepository.java +++ b/api-server/src/main/java/com/objectstorage/repository/ContentRepository.java @@ -49,7 +49,7 @@ public void insert(Integer provider, Integer secret, String root) throws Reposit try { repositoryExecutor.performQuery(query); - } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + } catch (QueryExecutionFailureException e) { throw new RepositoryOperationFailureException(e.getMessage()); } } @@ -77,12 +77,29 @@ public ContentEntity findByProviderAndSecret(Integer provider, Integer secret) t throw new RepositoryOperationFailureException(e.getMessage()); } - Integer id; - String root; - try { - id = resultSet.getInt("id"); - root = resultSet.getString("root"); + if (resultSet.next()) { + try { + Integer id = resultSet.getInt("id"); + String root = resultSet.getString("root"); + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return ContentEntity.of(id, provider, secret, root); + } catch (SQLException e1) { + try { + resultSet.close(); + } catch (SQLException e2) { + throw new RepositoryOperationFailureException(e2.getMessage()); + } + + throw new RepositoryOperationFailureException(e1.getMessage()); + } + } } catch (SQLException e1) { try { resultSet.close(); @@ -99,7 +116,7 @@ public ContentEntity findByProviderAndSecret(Integer provider, Integer secret) t throw new RepositoryOperationFailureException(e.getMessage()); } - return ContentEntity.of(id, provider, secret, root); + return null; } /** @@ -167,7 +184,7 @@ public void deleteByProviderAndSecret(Integer provider, Integer secret) throws R provider, secret)); - } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + } catch (QueryExecutionFailureException e) { throw new RepositoryOperationFailureException(e.getMessage()); } } diff --git a/api-server/src/main/java/com/objectstorage/repository/ProviderRepository.java b/api-server/src/main/java/com/objectstorage/repository/ProviderRepository.java index 6e8ad7c..566bea4 100644 --- a/api-server/src/main/java/com/objectstorage/repository/ProviderRepository.java +++ b/api-server/src/main/java/com/objectstorage/repository/ProviderRepository.java @@ -49,10 +49,28 @@ public ProviderEntity findByName(String name) throws RepositoryOperationFailureE throw new RepositoryOperationFailureException(e.getMessage()); } - Integer id; - try { - id = resultSet.getInt("id"); + if (resultSet.next()) { + try { + Integer id = resultSet.getInt("id"); + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return ProviderEntity.of(id, name); + } catch (SQLException e1) { + try { + resultSet.close(); + } catch (SQLException e2) { + throw new RepositoryOperationFailureException(e2.getMessage()); + } + + throw new RepositoryOperationFailureException(e1.getMessage()); + } + } } catch (SQLException e1) { try { resultSet.close(); @@ -69,7 +87,7 @@ public ProviderEntity findByName(String name) throws RepositoryOperationFailureE throw new RepositoryOperationFailureException(e.getMessage()); } - return ProviderEntity.of(id, name); + return null; } /** @@ -94,10 +112,28 @@ public ProviderEntity findById(Integer id) throws RepositoryOperationFailureExce throw new RepositoryOperationFailureException(e.getMessage()); } - String name; - try { - name = resultSet.getString("name"); + if (resultSet.next()) { + try { + String name = resultSet.getString("name"); + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return ProviderEntity.of(id, name); + } catch (SQLException e1) { + try { + resultSet.close(); + } catch (SQLException e2) { + throw new RepositoryOperationFailureException(e2.getMessage()); + } + + throw new RepositoryOperationFailureException(e1.getMessage()); + } + } } catch (SQLException e1) { try { resultSet.close(); @@ -114,6 +150,6 @@ public ProviderEntity findById(Integer id) throws RepositoryOperationFailureExce throw new RepositoryOperationFailureException(e.getMessage()); } - return ProviderEntity.of(id, name); + return null; } } diff --git a/api-server/src/main/java/com/objectstorage/repository/SecretRepository.java b/api-server/src/main/java/com/objectstorage/repository/SecretRepository.java index 0e0508b..af076da 100644 --- a/api-server/src/main/java/com/objectstorage/repository/SecretRepository.java +++ b/api-server/src/main/java/com/objectstorage/repository/SecretRepository.java @@ -45,7 +45,7 @@ public void insert(Integer session, String credentials) throws RepositoryOperati try { repositoryExecutor.performQuery(query); - } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + } catch (QueryExecutionFailureException e) { throw new RepositoryOperationFailureException(e.getMessage()); } } @@ -107,10 +107,28 @@ public SecretEntity findBySessionAndCredentials(Integer session, String credenti throw new RepositoryOperationFailureException(e.getMessage()); } - Integer id; - try { - id = resultSet.getInt("id"); + if (resultSet.next()) { + try { + Integer id = resultSet.getInt("id"); + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return SecretEntity.of(id, session, credentials); + } catch (SQLException e1) { + try { + resultSet.close(); + } catch (SQLException e2) { + throw new RepositoryOperationFailureException(e2.getMessage()); + } + + throw new RepositoryOperationFailureException(e1.getMessage()); + } + } } catch (SQLException e1) { try { resultSet.close(); @@ -127,7 +145,7 @@ public SecretEntity findBySessionAndCredentials(Integer session, String credenti throw new RepositoryOperationFailureException(e.getMessage()); } - return SecretEntity.of(id, session, credentials); + return null; } /** @@ -150,18 +168,19 @@ public SecretEntity findById(Integer id) throws RepositoryOperationFailureExcept throw new RepositoryOperationFailureException(e.getMessage()); } - Integer session; - try { - session = resultSet.getInt("session"); - } catch (SQLException e) { - throw new RepositoryOperationFailureException(e.getMessage()); - } + if (resultSet.next()) { + Integer session = resultSet.getInt("session"); + String credentials = resultSet.getString("credentials"); - String credentials; + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } - try { - credentials = resultSet.getString("credentials"); + return SecretEntity.of(id, session, credentials); + } } catch (SQLException e1) { try { resultSet.close(); @@ -178,7 +197,7 @@ public SecretEntity findById(Integer id) throws RepositoryOperationFailureExcept throw new RepositoryOperationFailureException(e.getMessage()); } - return SecretEntity.of(id, session, credentials); + return null; } /** @@ -195,7 +214,7 @@ public void deleteById(Integer id) throws RepositoryOperationFailureException { properties.getDatabaseSecretTableName(), id)); - } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + } catch (QueryExecutionFailureException e) { throw new RepositoryOperationFailureException(e.getMessage()); } } diff --git a/api-server/src/main/java/com/objectstorage/repository/TemporateRepository.java b/api-server/src/main/java/com/objectstorage/repository/TemporateRepository.java index 5069417..1a7a5a3 100644 --- a/api-server/src/main/java/com/objectstorage/repository/TemporateRepository.java +++ b/api-server/src/main/java/com/objectstorage/repository/TemporateRepository.java @@ -7,6 +7,7 @@ import com.objectstorage.exception.QueryExecutionFailureException; import com.objectstorage.exception.RepositoryOperationFailureException; import com.objectstorage.repository.executor.RepositoryExecutor; +import com.objectstorage.service.config.ConfigService; import io.quarkus.runtime.annotations.RegisterForReflection; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -27,6 +28,9 @@ public class TemporateRepository { @Inject PropertiesEntity properties; + @Inject + ConfigService configService; + @Inject RepositoryExecutor repositoryExecutor; @@ -54,7 +58,7 @@ public void insert(Integer provider, Integer secret, String location, String has try { repositoryExecutor.performQuery(query); - } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + } catch (QueryExecutionFailureException e) { throw new RepositoryOperationFailureException(e.getMessage()); } } @@ -71,16 +75,45 @@ public Integer count() throws RepositoryOperationFailureException { try { resultSet = repositoryExecutor.performQueryWithResult( - String.format("SELECT COUNT(1) as 'result' FROM %s", properties.getDatabaseTemporateTableName())); + String.format("SELECT COUNT(1) as result FROM %s", properties.getDatabaseTemporateTableName())); } catch (QueryExecutionFailureException | QueryEmptyResultException e) { throw new RepositoryOperationFailureException(e.getMessage()); } - Integer count; + Integer count = 0; try { - count = resultSet.getInt("result"); + if (resultSet.next()) { + switch (configService.getConfig().getInternalStorage().getProvider()) { + case SQLITE3 -> { + try { + count = resultSet.getInt("result"); + } catch (SQLException e1) { + try { + resultSet.close(); + } catch (SQLException e2) { + throw new RepositoryOperationFailureException(e2.getMessage()); + } + + throw new RepositoryOperationFailureException(e1.getMessage()); + } + } + case POSTGRES -> { + try { + count = (int) resultSet.getLong("result"); + } catch (SQLException e1) { + try { + resultSet.close(); + } catch (SQLException e2) { + throw new RepositoryOperationFailureException(e2.getMessage()); + } + + throw new RepositoryOperationFailureException(e1.getMessage()); + } + } + } + } } catch (SQLException e1) { try { resultSet.close(); @@ -120,20 +153,33 @@ public TemporateEntity findEarliest() throws RepositoryOperationFailureException throw new RepositoryOperationFailureException(e.getMessage()); } - Integer id; - Integer provider; - Integer secret; - String location; - String hash; - Long createdAt; - try { - id = resultSet.getInt("id"); - provider = resultSet.getInt("provider"); - secret = resultSet.getInt("secret"); - location = resultSet.getString("location"); - hash = resultSet.getString("hash"); - createdAt = resultSet.getLong("created_at"); + if (resultSet.next()) { + try { + Integer id = resultSet.getInt("id"); + Integer provider = resultSet.getInt("provider"); + Integer secret = resultSet.getInt("secret"); + String location = resultSet.getString("location"); + String hash = resultSet.getString("hash"); + Long createdAt = resultSet.getLong("created_at"); + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return TemporateEntity.of(id, provider, secret, location, hash, createdAt); + } catch (SQLException e1) { + try { + resultSet.close(); + } catch (SQLException e2) { + throw new RepositoryOperationFailureException(e2.getMessage()); + } + + throw new RepositoryOperationFailureException(e1.getMessage()); + } + } } catch (SQLException e1) { try { resultSet.close(); @@ -150,7 +196,7 @@ public TemporateEntity findEarliest() throws RepositoryOperationFailureException throw new RepositoryOperationFailureException(e.getMessage()); } - return TemporateEntity.of(id, provider, secret, location, hash, createdAt); + return null; } /** @@ -240,15 +286,30 @@ public TemporateEntity findEarliestByLocationProviderAndSecret( throw new RepositoryOperationFailureException(e.getMessage()); } - Integer id; - String hash; - Long createdAt; - try { - id = resultSet.getInt("id"); - hash = resultSet.getString("hash"); - createdAt = resultSet.getLong("created_at"); - + if (resultSet.next()) { + try { + Integer id = resultSet.getInt("id"); + String hash = resultSet.getString("hash"); + Long createdAt = resultSet.getLong("created_at"); + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return TemporateEntity.of(id, provider, secret, location, hash, createdAt); + } catch (SQLException e1) { + try { + resultSet.close(); + } catch (SQLException e2) { + throw new RepositoryOperationFailureException(e2.getMessage()); + } + + throw new RepositoryOperationFailureException(e1.getMessage()); + } + } } catch (SQLException e1) { try { resultSet.close(); @@ -265,7 +326,7 @@ public TemporateEntity findEarliestByLocationProviderAndSecret( throw new RepositoryOperationFailureException(e.getMessage()); } - return TemporateEntity.of(id, provider, secret, location, hash, createdAt); + return null; } /** @@ -347,7 +408,7 @@ public void deleteByLocationProviderAndSecret(String location, Integer provider, provider, secret)); - } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + } catch (QueryExecutionFailureException e) { throw new RepositoryOperationFailureException(e.getMessage()); } } @@ -366,7 +427,7 @@ public void deleteByHash(String hash) throws RepositoryOperationFailureException properties.getDatabaseTemporateTableName(), hash)); - } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + } catch (QueryExecutionFailureException e) { throw new RepositoryOperationFailureException(e.getMessage()); } } @@ -387,7 +448,7 @@ public void deleteByProviderAndSecret(Integer provider, Integer secret) throws R provider, secret)); - } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + } catch (QueryExecutionFailureException e) { throw new RepositoryOperationFailureException(e.getMessage()); } } diff --git a/api-server/src/main/java/com/objectstorage/repository/executor/RepositoryExecutor.java b/api-server/src/main/java/com/objectstorage/repository/executor/RepositoryExecutor.java index 18ab7ee..7848137 100644 --- a/api-server/src/main/java/com/objectstorage/repository/executor/RepositoryExecutor.java +++ b/api-server/src/main/java/com/objectstorage/repository/executor/RepositoryExecutor.java @@ -1,8 +1,10 @@ package com.objectstorage.repository.executor; +import com.objectstorage.entity.common.ConfigEntity; import com.objectstorage.entity.common.PropertiesEntity; import com.objectstorage.exception.*; import com.objectstorage.repository.common.RepositoryConfigurationHelper; +import com.objectstorage.service.config.ConfigService; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import jakarta.enterprise.context.ApplicationScoped; @@ -29,16 +31,16 @@ public class RepositoryExecutor { PropertiesEntity properties; @Inject - DataSource dataSource; + ConfigService configService; @Inject - RepositoryConfigurationHelper repositoryConfigurationHelper; + DataSource dataSource; private Connection connection; - private final List statements = new ArrayList<>(); + private final static List statements = new ArrayList<>(); - private final ScheduledExecutorService scheduledExecutorService = + private final static ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); /** @@ -56,36 +58,48 @@ private void configure() throws QueryExecutionFailureException { } /** - * Performs given SQL query without result. + * Performs given SQL query via given connection without result. * + * @param connection given SQL connection. * @param query given SQL query to be executed. + * @param databaseStatementCloseDelay given database statement close delay. * @throws QueryExecutionFailureException if query execution is interrupted by failure. - * @throws QueryEmptyResultException if result is empty. */ - public void performQuery(String query) throws QueryExecutionFailureException, QueryEmptyResultException { + public static void performQuery(Connection connection, String query, Integer databaseStatementCloseDelay) + throws QueryExecutionFailureException { Statement statement; try { - statement = this.connection.createStatement(); + statement = connection.createStatement(); } catch (SQLException e) { throw new QueryExecutionFailureException(e.getMessage()); } + statements.add(statement); + try { statement.executeUpdate(query); } catch (SQLException e) { throw new QueryExecutionFailureException(e.getMessage()); } - statements.add(statement); - scheduledExecutorService.schedule(() -> { try { statement.close(); } catch (SQLException e) { logger.fatal(new QueryExecutionFailureException(e.getMessage()).getMessage()); } - }, properties.getDatabaseStatementCloseDelay(), TimeUnit.MILLISECONDS); + }, databaseStatementCloseDelay, TimeUnit.MILLISECONDS); + } + + /** + * Performs given SQL query without result. + * + * @param query given SQL query to be executed. + * @throws QueryExecutionFailureException if query execution is interrupted by failure. + */ + public void performQuery(String query) throws QueryExecutionFailureException { + performQuery(this.connection, query, properties.getDatabaseStatementCloseDelay()); } /** @@ -105,6 +119,8 @@ public ResultSet performQueryWithResult(String query) throws QueryExecutionFailu throw new QueryExecutionFailureException(e.getMessage()); } + statements.add(statement); + ResultSet resultSet; try { @@ -113,8 +129,6 @@ public ResultSet performQueryWithResult(String query) throws QueryExecutionFailu throw new QueryExecutionFailureException(e.getMessage()); } - statements.add(statement); - scheduledExecutorService.schedule(() -> { try { statement.close(); diff --git a/api-server/src/main/java/com/objectstorage/service/integration/backup/BackupService.java b/api-server/src/main/java/com/objectstorage/service/integration/backup/BackupService.java index 453fc2a..1eb6e40 100644 --- a/api-server/src/main/java/com/objectstorage/service/integration/backup/BackupService.java +++ b/api-server/src/main/java/com/objectstorage/service/integration/backup/BackupService.java @@ -85,8 +85,6 @@ public void process() throws BackupPeriodRetrievalFailureException { } catch (ContentApplicationRetrievalFailureException e) { StateService.getBackupProcessorGuard().unlock(); - logger.error(e.getMessage()); - return; } diff --git a/api-server/src/main/java/com/objectstorage/service/integration/diagnostics/DiagnosticsConfigService.java b/api-server/src/main/java/com/objectstorage/service/integration/diagnostics/DiagnosticsConfigService.java index 662f996..61c5f33 100644 --- a/api-server/src/main/java/com/objectstorage/service/integration/diagnostics/DiagnosticsConfigService.java +++ b/api-server/src/main/java/com/objectstorage/service/integration/diagnostics/DiagnosticsConfigService.java @@ -159,22 +159,12 @@ private void process() throws configService.getConfig().getDiagnostics().getNodeExporter().getPort(), properties.getDiagnosticsCommonDockerNetworkName()); - CommandExecutorOutputDto nodeExporterDeployCommandOutput; - try { - nodeExporterDeployCommandOutput = - commandExecutorService.executeCommand(nodeExporterDeployCommandService); + commandExecutorService.executeCommand(nodeExporterDeployCommandService); } catch (CommandExecutorException e) { throw new NodeExporterDeploymentFailureException(e.getMessage()); } - String nodeExporterDeployCommandErrorOutput = nodeExporterDeployCommandOutput.getErrorOutput(); - - if (Objects.nonNull(nodeExporterDeployCommandErrorOutput) && - !nodeExporterDeployCommandErrorOutput.isEmpty()) { - throw new NodeExporterDeploymentFailureException(nodeExporterDeployCommandErrorOutput); - } - PrometheusDeployCommandService prometheusDeployCommandService = new PrometheusDeployCommandService( properties.getDiagnosticsPrometheusDockerName(), @@ -184,22 +174,12 @@ private void process() throws properties.getDiagnosticsPrometheusConfigLocation(), properties.getDiagnosticsPrometheusInternalLocation()); - CommandExecutorOutputDto prometheusDeployCommandOutput; - try { - prometheusDeployCommandOutput = - commandExecutorService.executeCommand(prometheusDeployCommandService); + commandExecutorService.executeCommand(prometheusDeployCommandService); } catch (CommandExecutorException e) { throw new PrometheusDeploymentFailureException(e.getMessage()); } - String prometheusDeployCommandErrorOutput = prometheusDeployCommandOutput.getErrorOutput(); - - if (Objects.nonNull(prometheusDeployCommandErrorOutput) && - !prometheusDeployCommandErrorOutput.isEmpty()) { - throw new PrometheusDeploymentFailureException(prometheusDeployCommandErrorOutput); - } - GrafanaDeployCommandService grafanaDeployCommandService = new GrafanaDeployCommandService( properties.getDiagnosticsGrafanaDockerName(), @@ -209,21 +189,11 @@ private void process() throws properties.getDiagnosticsGrafanaConfigLocation(), properties.getDiagnosticsGrafanaInternalLocation()); - CommandExecutorOutputDto grafanaDeployCommandOutput; - try { - grafanaDeployCommandOutput = - commandExecutorService.executeCommand(grafanaDeployCommandService); + commandExecutorService.executeCommand(grafanaDeployCommandService); } catch (CommandExecutorException e) { throw new GrafanaDeploymentFailureException(e.getMessage()); } - - String grafanaDeployCommandErrorOutput = grafanaDeployCommandOutput.getErrorOutput(); - - if (Objects.nonNull(grafanaDeployCommandErrorOutput) && - !grafanaDeployCommandErrorOutput.isEmpty()) { - throw new GrafanaDeploymentFailureException(grafanaDeployCommandErrorOutput); - } } } diff --git a/api-server/src/main/java/com/objectstorage/service/integration/properties/database/DatabasePropertiesConfigService.java b/api-server/src/main/java/com/objectstorage/service/integration/properties/database/DatabasePropertiesConfigService.java new file mode 100644 index 0000000..2154704 --- /dev/null +++ b/api-server/src/main/java/com/objectstorage/service/integration/properties/database/DatabasePropertiesConfigService.java @@ -0,0 +1,130 @@ +package com.objectstorage.service.integration.properties.database; + +import com.objectstorage.exception.ConfigDatabasePropertiesMissingException; +import com.objectstorage.exception.QueryExecutionFailureException; +import com.objectstorage.repository.executor.RepositoryExecutor; +import com.objectstorage.service.config.common.ConfigConfigurationHelper; +import io.quarkus.runtime.annotations.StaticInitSafe; +import io.smallrye.config.ConfigSourceContext; +import io.smallrye.config.ConfigSourceFactory; +import io.smallrye.config.ConfigValue; +import io.smallrye.config.PropertiesConfigSource; +import lombok.SneakyThrows; +import org.eclipse.microprofile.config.spi.ConfigSource; +import com.objectstorage.entity.common.ConfigEntity; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.util.Collections; +import java.util.Objects; +import java.util.OptionalInt; +import java.util.Properties; + +/** + * Service used to perform security properties configuration operations. + */ +@StaticInitSafe +public class DatabasePropertiesConfigService implements ConfigSourceFactory { + @Override + @SneakyThrows + public Iterable getConfigSources(final ConfigSourceContext context) { + final ConfigValue configLocation = context.getValue("config.location"); + + if (Objects.isNull(configLocation) || Objects.isNull(configLocation.getValue())) { + return Collections.emptyList(); + } + + final ConfigValue databaseName = context.getValue("database.name"); + if (Objects.isNull(databaseName) || Objects.isNull(databaseName.getValue())) { + return Collections.emptyList(); + } + + final ConfigValue liquibaseSqlite3Config = context.getValue("liquibase.sqlite3.config"); + if (Objects.isNull(liquibaseSqlite3Config) || Objects.isNull(liquibaseSqlite3Config.getValue())) { + return Collections.emptyList(); + } + + final ConfigValue liquibasePostgresConfig = context.getValue("liquibase.postgres.config"); + if (Objects.isNull(liquibasePostgresConfig) || Objects.isNull(liquibasePostgresConfig.getValue())) { + return Collections.emptyList(); + } + + final ConfigValue databaseStatementCloseDelay = context.getValue("database.statement.close-delay"); + if (Objects.isNull(databaseStatementCloseDelay) || Objects.isNull(databaseStatementCloseDelay.getValue())) { + return Collections.emptyList(); + } + + Properties properties = new Properties(); + + ConfigEntity config = ConfigConfigurationHelper.readConfig(configLocation.getValue(), false); + if (Objects.isNull(config)) { + return Collections.emptyList(); + } + + if (Objects.isNull(config.getInternalStorage()) || + Objects.isNull(config.getInternalStorage().getProvider()) || + Objects.isNull(config.getInternalStorage().getUsername()) || + Objects.isNull(config.getInternalStorage().getPassword())) { + throw new ConfigDatabasePropertiesMissingException(); + } + + if (config.getInternalStorage().getProvider() == ConfigEntity.InternalStorage.Provider.POSTGRES && + Objects.isNull(config.getInternalStorage().getHost())) { + throw new ConfigDatabasePropertiesMissingException(); + } + + switch (config.getInternalStorage().getProvider()) { + case SQLITE3 -> { + properties.put("quarkus.liquibase.change-log", liquibaseSqlite3Config.getValue()); + + properties.put("quarkus.datasource.jdbc.driver", "org.sqlite.JDBC"); + properties.put("quarkus.datasource.db-kind", "other"); + properties.put( + "quarkus.datasource.jdbc.url", + String.format( + "jdbc:sqlite:%s/.%s/internal/database/data.db", + System.getProperty("user.home"), + databaseName.getValue())); + } + case POSTGRES -> { + properties.put("quarkus.liquibase.change-log", liquibasePostgresConfig.getValue()); + + properties.put("quarkus.datasource.db-kind", "postgresql"); + properties.put( + "quarkus.datasource.jdbc.url", + String.format("jdbc:postgresql://%s/%s", + config.getInternalStorage().getHost(), + databaseName.getValue())); + + Connection connection = DriverManager.getConnection( + String.format("jdbc:postgresql://%s/postgres", config.getInternalStorage().getHost()), + config.getInternalStorage().getUsername(), + config.getInternalStorage().getPassword()); + + try { + RepositoryExecutor.performQuery( + connection, + String.format("CREATE DATABASE %s", databaseName.getValue()), + Integer.valueOf(databaseStatementCloseDelay.getValue())); + } catch (QueryExecutionFailureException ignore) { + } + + connection.close(); + } + } + + properties.put("quarkus.datasource.username", config.getInternalStorage().getUsername()); + properties.put("quarkus.datasource.password", config.getInternalStorage().getPassword()); + + return Collections.singletonList( + new PropertiesConfigSource( + properties, + com.objectstorage.service.integration.properties.security.SecurityPropertiesConfigService.class.getSimpleName(), + 290)); + } + + @Override + public OptionalInt getPriority() { + return OptionalInt.of(290); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/objectstorage/service/integration/properties/security/SecurityPropertiesConfigService.java b/api-server/src/main/java/com/objectstorage/service/integration/properties/security/SecurityPropertiesConfigService.java index ad66940..b7084c2 100644 --- a/api-server/src/main/java/com/objectstorage/service/integration/properties/security/SecurityPropertiesConfigService.java +++ b/api-server/src/main/java/com/objectstorage/service/integration/properties/security/SecurityPropertiesConfigService.java @@ -24,14 +24,14 @@ public class SecurityPropertiesConfigService implements ConfigSourceFactory { @Override @SneakyThrows public Iterable getConfigSources(final ConfigSourceContext context) { - final ConfigValue value = context.getValue("config.location"); - if (value == null || value.getValue() == null) { + final ConfigValue configLocation = context.getValue("config.location"); + if (Objects.isNull(configLocation) || Objects.isNull(configLocation.getValue())) { return Collections.emptyList(); } Properties properties = new Properties(); - ConfigEntity config = ConfigConfigurationHelper.readConfig(value.getValue(), false); + ConfigEntity config = ConfigConfigurationHelper.readConfig(configLocation.getValue(), false); if (Objects.isNull(config)) { return Collections.emptyList(); } diff --git a/api-server/src/main/java/com/objectstorage/service/state/watcher/WatcherService.java b/api-server/src/main/java/com/objectstorage/service/state/watcher/WatcherService.java index bf3a21e..0d215c3 100644 --- a/api-server/src/main/java/com/objectstorage/service/state/watcher/WatcherService.java +++ b/api-server/src/main/java/com/objectstorage/service/state/watcher/WatcherService.java @@ -1,7 +1,5 @@ package com.objectstorage.service.state.watcher; -import org.apache.commons.io.FileUtils; - /** * Service used to track state metrics for the current session in the application. */ @@ -39,7 +37,10 @@ public void increaseUploadedFilesSize(Integer value) { */ public Double getAverageFileSize() { if (filesUploadCounter > 0) { - return Double.valueOf(uploadedFilesSize) / Double.valueOf(filesUploadCounter) / 1024 / 1024; + return (Double.valueOf(uploadedFilesSize) / + Double.valueOf(filesUploadCounter)) / + (double) 1024 / + (double) 1024; } return (double) 0; diff --git a/api-server/src/main/resources/META-INF/services/io.smallrye.config.ConfigSourceFactory b/api-server/src/main/resources/META-INF/services/io.smallrye.config.ConfigSourceFactory index 282b490..f12dc8a 100644 --- a/api-server/src/main/resources/META-INF/services/io.smallrye.config.ConfigSourceFactory +++ b/api-server/src/main/resources/META-INF/services/io.smallrye.config.ConfigSourceFactory @@ -1 +1,2 @@ -com.objectstorage.service.integration.properties.security.SecurityPropertiesConfigService \ No newline at end of file +com.objectstorage.service.integration.properties.database.DatabasePropertiesConfigService +com.objectstorage.service.integration.properties.security.SecurityPropertiesConfigService diff --git a/api-server/src/main/resources/application.properties b/api-server/src/main/resources/application.properties index d9213f4..0eb7d0c 100644 --- a/api-server/src/main/resources/application.properties +++ b/api-server/src/main/resources/application.properties @@ -6,18 +6,15 @@ quarkus.swagger-ui.always-include=true quarkus.native.builder-image=graalvm quarkus.banner.path=banner.txt +# Describes database Quarkus configuration. +database.name=objectstorage + # Describes security Quarkus configuration. quarkus.rest-client.alpn=false -# Describes database Quarkus configuration. -quarkus.datasource.jdbc.driver=org.sqlite.JDBC -quarkus.datasource.db-kind=other -quarkus.datasource.jdbc.url=jdbc:sqlite:${user.home}/.objectstorage/internal/database/data.db -quarkus.datasource.username=objectstorage_user -quarkus.datasource.password=objectstorage_password - # Describes LiquiBase Quarkus configuration. -quarkus.liquibase.change-log=liquibase/config.yaml +liquibase.sqlite3.config=liquibase/sqlite3/config.yaml +liquibase.postgres.config=liquibase/postgres/config.yaml quarkus.liquibase.migrate-at-start=true # Describes internal healthcheck client configuration. diff --git a/api-server/src/main/resources/liquibase/postgres/config.yaml b/api-server/src/main/resources/liquibase/postgres/config.yaml new file mode 100644 index 0000000..affff68 --- /dev/null +++ b/api-server/src/main/resources/liquibase/postgres/config.yaml @@ -0,0 +1,134 @@ +databaseChangeLog: + - changeSet: + id: 1 + author: YarikRevich + changes: + - createTable: + tableName: secret + columns: + - column: + name: id + type: INT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: session + type: INT + constraints: + nullable: false + - column: + name: credentials + type: TEXT + constraints: + nullable: true + - createTable: + tableName: provider + columns: + - column: + name: id + type: INT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: name + type: TEXT + constraints: + unique: true + nullable: false + - createTable: + tableName: content + columns: + - column: + name: id + type: INT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: provider + type: INT + constraints: + foreignKeyName: provider_fk + references: provider(id) + nullable: false + deleteCascade: true + - column: + name: secret + type: INT + constraints: + foreignKeyName: secret_fk + references: secret(id) + nullable: false + unique: true + deleteCascade: true + - column: + name: root + type: TEXT + constraints: + nullable: false + - createTable: + tableName: temporate + columns: + - column: + name: id + type: INT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: provider + type: INT + constraints: + foreignKeyName: provider_fk + references: provider(id) + nullable: false + deleteCascade: true + - column: + name: secret + type: INT + constraints: + foreignKeyName: secret_fk + references: secret(id) + nullable: false + deleteCascade: true + - column: + name: location + type: TEXT + constraints: + nullable: false + - column: + name: hash + type: TEXT + constraints: + nullable: false + unique: true + - column: + name: created_at + type: BIGINT + constraints: + nullable: false + - createIndex: + columns: + - column: + name: name + indexName: idx_provider_name + tableName: provider + - loadData: + tableName: provider + usePreparedStatements: false + separator: ; + relativeToChangelogFile: true + file: data/data.csv + encoding: UTF-8 + quotchar: '''' + columns: + - column: + header: Name + name: name + type: STRING \ No newline at end of file diff --git a/api-server/src/main/resources/liquibase/data/data.csv b/api-server/src/main/resources/liquibase/postgres/data/data.csv similarity index 100% rename from api-server/src/main/resources/liquibase/data/data.csv rename to api-server/src/main/resources/liquibase/postgres/data/data.csv diff --git a/api-server/src/main/resources/liquibase/config.yaml b/api-server/src/main/resources/liquibase/sqlite3/config.yaml similarity index 80% rename from api-server/src/main/resources/liquibase/config.yaml rename to api-server/src/main/resources/liquibase/sqlite3/config.yaml index 75f23cd..2443eb8 100644 --- a/api-server/src/main/resources/liquibase/config.yaml +++ b/api-server/src/main/resources/liquibase/sqlite3/config.yaml @@ -3,37 +3,6 @@ databaseChangeLog: id: 1 author: YarikRevich changes: - - createTable: - tableName: config - columns: - - column: - name: id - type: INT - autoIncrement: true - constraints: - primaryKey: true - nullable: false - - column: - name: provider - type: INT - constraints: - foreignKeyName: provider_fk - references: provider(id) - nullable: false - deleteCascade: true - - column: - name: secret - type: INT - constraints: - foreignKeyName: secret_fk - references: secret(id) - nullable: false - deleteCascade: true - - column: - name: hash - type: VARCHAR - constraints: - nullable: false - createTable: tableName: secret columns: diff --git a/api-server/src/main/resources/liquibase/sqlite3/data/data.csv b/api-server/src/main/resources/liquibase/sqlite3/data/data.csv new file mode 100644 index 0000000..49f7e28 --- /dev/null +++ b/api-server/src/main/resources/liquibase/sqlite3/data/data.csv @@ -0,0 +1,3 @@ +Name +s3 +gcs \ No newline at end of file diff --git a/config/grafana/dashboards/diagnostics.tmpl b/config/grafana/dashboards/diagnostics.tmpl index a9e7da5..c7ebb33 100644 --- a/config/grafana/dashboards/diagnostics.tmpl +++ b/config/grafana/dashboards/diagnostics.tmpl @@ -18,8 +18,8 @@ "description": "ObjectStorage API Server: ${(info.version)}", "editable": true, "fiscalYearStartMonth": 0, - "gnetId": 179, "graphTooltip": 1, + "id": 1, "links": [], "panels": [ { @@ -52,6 +52,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -115,6 +116,7 @@ "sort": "none" } }, + "pluginVersion": "11.3.1", "targets": [ { "datasource": { @@ -153,6 +155,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -216,6 +219,7 @@ "sort": "none" } }, + "pluginVersion": "11.3.1", "targets": [ { "datasource": { @@ -254,6 +258,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -317,6 +322,7 @@ "sort": "none" } }, + "pluginVersion": "11.3.1", "targets": [ { "datasource": { @@ -355,6 +361,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -419,6 +426,7 @@ "sort": "none" } }, + "pluginVersion": "11.3.1", "targets": [ { "datasource": { @@ -457,6 +465,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -497,7 +506,7 @@ } ] }, - "unit": "decmbytes" + "unit": "none" }, "overrides": [] }, @@ -521,6 +530,7 @@ "sort": "none" } }, + "pluginVersion": "11.3.1", "targets": [ { "datasource": { @@ -578,6 +588,7 @@ "graphMode": "none", "justifyMode": "auto", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { "calcs": [ "lastNotNull" @@ -589,7 +600,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "10.4.2", + "pluginVersion": "11.3.1", "targets": [ { "datasource": { @@ -649,6 +660,7 @@ "graphMode": "none", "justifyMode": "auto", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { "calcs": [ "lastNotNull" @@ -660,7 +672,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "10.4.2", + "pluginVersion": "11.3.1", "targets": [ { "datasource": { @@ -747,6 +759,7 @@ "graphMode": "none", "justifyMode": "auto", "orientation": "horizontal", + "percentChangeColorMode": "standard", "reduceOptions": { "calcs": [ "lastNotNull" @@ -758,7 +771,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "10.4.2", + "pluginVersion": "11.3.1", "targets": [ { "datasource": { @@ -842,7 +855,7 @@ "showThresholdMarkers": true, "sizing": "auto" }, - "pluginVersion": "10.4.2", + "pluginVersion": "11.3.1", "targets": [ { "datasource": { @@ -928,7 +941,7 @@ "showThresholdMarkers": true, "sizing": "auto" }, - "pluginVersion": "10.4.2", + "pluginVersion": "11.3.1", "targets": [ { "datasource": { @@ -947,8 +960,9 @@ "type": "gauge" } ], - "refresh": "", - "schemaVersion": 39, + "preload": false, + "refresh": "auto", + "schemaVersion": 40, "tags": [ "docker", "prometheus, ", @@ -962,11 +976,9 @@ "auto_count": 30, "auto_min": "10s", "current": { - "selected": false, "text": "1h", "value": "1h" }, - "hide": 0, "label": "interval", "name": "interval", "options": [ @@ -1022,27 +1034,19 @@ } ], "query": "1m,10m,30m,1h,6h,12h,1d,7d,14d,30d", - "queryValue": "", "refresh": 2, - "skipUrlSync": false, "type": "interval" }, { "current": { - "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": "All", + "value": "$__all" }, "datasource": { "type": "prometheus", "uid": "P21B111CBFE6E8FCA" }, "definition": "label_values(node_exporter_build_info{name=~'$name'},instance)", - "hide": 0, "includeAll": true, "label": "IP", "multi": true, @@ -1051,29 +1055,19 @@ "query": "label_values(node_exporter_build_info{name=~'$name'},instance)", "refresh": 2, "regex": "", - "skipUrlSync": false, "sort": 1, - "tagValuesQuery": "", - "tagsQuery": "", - "type": "query", - "useTags": false + "type": "query" }, { "current": { - "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": "All", + "value": "$__all" }, "datasource": { "type": "prometheus", "uid": "P21B111CBFE6E8FCA" }, "definition": "label_values(node_exporter_build_info,env)", - "hide": 0, "includeAll": true, "label": "Env", "multi": true, @@ -1082,29 +1076,18 @@ "query": "label_values(node_exporter_build_info,env)", "refresh": 2, "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tagsQuery": "", - "type": "query", - "useTags": false + "type": "query" }, { "current": { - "selected": true, - "text": [ - "All" - ], - "value": [ - "$__all" - ] + "text": "All", + "value": "$__all" }, "datasource": { "type": "prometheus", "uid": "P21B111CBFE6E8FCA" }, "definition": "label_values(node_exporter_build_info{env=~'$env'},name)", - "hide": 0, "includeAll": true, "label": "CPU Name", "multi": true, @@ -1113,47 +1096,18 @@ "query": "label_values(node_exporter_build_info{env=~'$env'},name)", "refresh": 2, "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tagsQuery": "", - "type": "query", - "useTags": false + "type": "query" } ] }, "time": { - "from": "2024-11-29T14:23:10.911Z", - "to": "2024-11-29T14:25:03.115Z" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ], - "time_options": [ - "5m", - "15m", - "1h", - "6h", - "12h", - "24h", - "2d", - "7d", - "30d" - ] + "from": "now-5m", + "to": "now" }, + "timepicker": {}, "timezone": "browser", "title": "ObjectStorage Diagnostics", "uid": "64nrElFmk", - "version": 10, + "version": 2, "weekStart": "" } \ No newline at end of file diff --git a/docs/internal-database-design.md b/docs/internal-database-design.md index 09c31c2..03568f5 100644 --- a/docs/internal-database-design.md +++ b/docs/internal-database-design.md @@ -7,14 +7,6 @@ Internal database design of "ObjectStorage" end title -entity "config" { - *id : number <> - *provider : number <> # provider(id) - *secret: number <> # secret(id) - -- - hash : varchar -} - entity "secret" { *id : number <> -- @@ -44,9 +36,8 @@ entity "temporate" { hash : varchar } -config ||...|| secret #magenta : attached to content ||...|| secret #magenta : attached to content }|...|| provider #magenta : configures -temporate ||...|| secret #magenta : created with -temporate }|...|| provider #magenta : created with +temporate }|...|| secret #magenta : created with +temporate }|...|| provider #magenta : created for ``` \ No newline at end of file diff --git a/docs/internal-database-design.png b/docs/internal-database-design.png index 79e56b19230479f9e12931aab67f9d07e0b47f71..c733a54e9007e1a6ab81501be449e3b8f24d75d6 100644 GIT binary patch literal 32543 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 literal 37487 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 L+V?A!tit{WS@@Ic literal 8203 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 DPLgp> diff --git a/pom.xml b/pom.xml index 18ebd18..bb5e9de 100644 --- a/pom.xml +++ b/pom.xml @@ -243,6 +243,11 @@ quarkus-jdbc-sqlite 3.0.11 + + io.quarkus + quarkus-jdbc-postgresql + 3.17.2 + io.quarkus quarkus-smallrye-jwt diff --git a/samples/config/api-server/api-server.yaml b/samples/config/api-server/api-server.yaml index 4022b0a..1bf5e43 100644 --- a/samples/config/api-server/api-server.yaml +++ b/samples/config/api-server/api-server.yaml @@ -14,6 +14,20 @@ connection: # Represents password, which will be used to decode operations. password: "test123" +# Represents section used for ObjectStorage API Server internal database configuration. +internal-storage: + # Represents provider selected for ObjectStorage internal database. Supported providers are "sqlite3" and "postgres" only. + provider: "sqlite3" + + # Represents host for the previously selected ObjectStorage internal database provider, works only for "postgres". + # host: "localhost:5432" + + # Represents username for the previously selected ObjectStorage internal database provider. + username: "objectstorage_user" + + # Represents password for the previously selected ObjectStorage internal database provider. + password: "objectstorage_password" + # Represents section used for ObjectStorage API Server temporate storage configuration. Same compression will be # used to upload files to the configured cloud providers. temporate-storage: