Compare commits

...

7 Commits

Author SHA1 Message Date
Charles GTE 254253d3a4 feat: separate mysql and mariadb 2026-03-29 09:27:36 +02:00
Charles GTE a728ba0d13 feat: separate mysql and mariadb 2026-03-28 18:54:24 +01:00
github-actions[bot] 8bf6503f1f chore: release 1.7.0 2026-03-28 15:47:43 +00:00
Charles GTE fdf35228d4 feat: add-heathcheck-database (#48)
feat: add-heathcheck-database
2026-03-28 16:45:13 +01:00
Charles GTE 1855c5120a fix: docker-compose.yml 2026-03-28 16:44:07 +01:00
Charles GTE 3d5a7eeded feat: add health check ping for databases 2026-03-28 16:07:37 +01:00
Charles GTE 517f3779cd feat: sending database ping result for each databases to portabase server 2026-03-27 23:07:06 +01:00
23 changed files with 448 additions and 116 deletions
+1 -1
View File
@@ -27,5 +27,5 @@ keywords:
- self-hosted
- portabase
license: Apache-2.0
version: 1.6.3
version: 1.7.0
date-released: '2026-02-24'
Generated
+1 -1
View File
@@ -3038,7 +3038,7 @@ dependencies = [
[[package]]
name = "portabase-agent"
version = "1.6.3"
version = "1.7.0"
dependencies = [
"aes",
"aes-gcm",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "portabase-agent"
version = "1.6.3"
version = "1.7.0"
edition = "2024"
[dependencies]
+11 -1
View File
@@ -13,7 +13,7 @@
{
"name": "Test database 2 - MariaDB",
"database": "mariadb",
"type": "mysql",
"type": "mariadb",
"username": "mariadb",
"password": "changeme",
"port": 3306,
@@ -81,6 +81,16 @@
"username": "default",
"host": "db-valkey-auth",
"generated_id": "16678561-ff7e-4c97-8c83-0adeff214681"
},
{
"name": "Test database 12 - Mysql",
"database": "mysqldb",
"type": "mysql",
"username": "mysqldb",
"password": "changeme",
"port": 3306,
"host": "db-mysql",
"generated_id": "16656124-ff7e-4c97-8c83-0adeff214681"
}
]
}
+2 -5
View File
@@ -5,23 +5,20 @@ services:
# dockerfile: docker/Dockerfile
# target: prod
image: portabase/agent:latest
# platform: linux/arm64
container_name: rust-prod
volumes:
- ./databases.json:/config/config.json
environment:
# APP_ENV: production
LOG: info
TZ: "Europe/Paris"
# DATABASES_CONFIG_FILE: "config.toml"
EDGE_KEY: "eyJzZXJ2ZXJVcmwiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODciLCJhZ2VudElkIjoiNWU1OGU2MGEtODhiMy00YTBjLWI0NDktNTQ3OWZhOTQzZDBkIiwibWFzdGVyS2V5QjY0IjoiQlhWM1hvbEM2NTZTVjdkTmdjV1BHUWxrKytycExJNmxHRGk3Q1BCNWllbz0ifQ=="
#DATABASES_CONFIG_FILE: "config.toml"
EDGE_KEY: "<to complete>"
extra_hosts:
- "localhost:host-gateway"
networks:
- portabase
db-mongodb-auth:
container_name: db-mongodb-auth
image: mongo:latest
+97 -82
View File
@@ -11,8 +11,7 @@ services:
# - ./databases.toml:/config/config.toml
- cargo-registry:/usr/local/cargo/registry
- cargo-git:/usr/local/cargo/git
- /var/run/docker.sock:/var/run/docker.sock
# - /var/run/docker.sock:/var/run/docker.sock
# - cargo-target:/app/target
# - sqlite-data:/sqlite-data/workspace/data
# - ./scripts/sqlite/test-db:/sqlite-data-2/workspace/data
@@ -28,34 +27,49 @@ services:
networks:
- portabase
db-postgres:
container_name: db-postgres
image: postgres:17-alpine
# db-postgres:
# container_name: db-postgres
# image: postgres:17-alpine
# ports:
# - "5436:5432"
# volumes:
# - postgres-data:/var/lib/postgresql/data
# environment:
# - POSTGRES_DB=devdb
# - POSTGRES_USER=devuser
# - POSTGRES_PASSWORD=changeme
# networks:
# - portabase
db-mariadb:
container_name: db-mariadb
image: mariadb:latest
ports:
- "5436:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
- "3311:3306"
environment:
- POSTGRES_DB=devdb
- POSTGRES_USER=devuser
- POSTGRES_PASSWORD=changeme
- MYSQL_DATABASE=mariadb
- MYSQL_USER=mariadb
- MYSQL_PASSWORD=changeme
- MYSQL_RANDOM_ROOT_PASSWORD=yes
volumes:
- mariadb-data:/var/lib/mysql
networks:
- portabase
db-mysql:
container_name: db-mysql
image: mysql:9.5
ports:
- "3312:3306"
environment:
- MYSQL_DATABASE=mysqldb
- MYSQL_USER=mysqldb
- MYSQL_PASSWORD=changeme
- MYSQL_RANDOM_ROOT_PASSWORD=yes
volumes:
- mysql-data:/var/lib/mysql
networks:
- portabase
#
# db-mariadb:
# container_name: db-mariadb
# image: mariadb:latest
# ports:
# - "3311:3306"
# environment:
# - MYSQL_DATABASE=mariadb
# - MYSQL_USER=mariadb
# - MYSQL_PASSWORD=changeme
# - MYSQL_RANDOM_ROOT_PASSWORD=yes
# volumes:
# - mariadb-data:/var/lib/mysql
# networks:
# - portabase
#
#
# db-mongodb-auth:
@@ -105,67 +119,68 @@ services:
# stdin_open: true
# tty: true
db-redis:
image: redis:latest
container_name: db-redis
ports:
- "6379:6379"
volumes:
- redis-data:/data
command: [ "redis-server", "--appendonly", "yes" ]
networks:
- portabase
db-redis-auth:
image: redis:latest
container_name: db-redis-auth
ports:
- "6380:6379"
volumes:
- redis-data-auth:/data
environment:
- REDIS_PASSWORD=supersecurepassword
command: [ "redis-server", "--requirepass", "supersecurepassword", "--appendonly", "yes" ]
networks:
- portabase
db-valkey:
image: valkey/valkey
container_name: db-valkey
environment:
- ALLOW_EMPTY_PASSWORD=yes
ports:
- '6381:6379'
volumes:
- valkey-data:/data
networks:
- portabase
db-valkey-auth:
image: valkey/valkey
container_name: db-valkey-auth
command: >
--requirepass "supersecurepassword"
ports:
- '6382:6379'
volumes:
- valkey-data-auth:/data
networks:
- portabase
# db-redis:
# image: redis:latest
# container_name: db-redis
# ports:
# - "6379:6379"
# volumes:
# - redis-data:/data
# command: [ "redis-server", "--appendonly", "yes" ]
# networks:
# - portabase
#
# db-redis-auth:
# image: redis:latest
# container_name: db-redis-auth
# ports:
# - "6380:6379"
# volumes:
# - redis-data-auth:/data
# environment:
# - REDIS_PASSWORD=supersecurepassword
# command: [ "redis-server", "--requirepass", "supersecurepassword", "--appendonly", "yes" ]
# networks:
# - portabase
#
# db-valkey:
# image: valkey/valkey
# container_name: db-valkey
# environment:
# - ALLOW_EMPTY_PASSWORD=yes
# ports:
# - '6381:6379'
# volumes:
# - valkey-data:/data
# networks:
# - portabase
#
# db-valkey-auth:
# image: valkey/valkey
# container_name: db-valkey-auth
# command: >
# --requirepass "supersecurepassword"
# ports:
# - '6382:6379'
# volumes:
# - valkey-data-auth:/data
# networks:
# - portabase
volumes:
cargo-registry:
cargo-git:
# cargo-target:
postgres-data:
# mariadb-data:
# mongodb-data:
# mongodb-data-auth:
# sqlite-data:
redis-data:
redis-data-auth:
valkey-data:
valkey-data-auth:
# postgres-data:
mariadb-data:
mysql-data:
# mongodb-data:
# mongodb-data-auth:
# sqlite-data:
# redis-data:
# redis-data-auth:
# valkey-data:
# valkey-data-auth:
networks:
portabase:
+1
View File
@@ -46,6 +46,7 @@ RUN if [ "$TARGETARCH" = "amd64" ]; then \
rm -rf /tmp/pg-x64 /tmp/pg-arm && \
chmod +x /usr/lib/postgresql/*/bin/*
# =========================
# MongoDB client binaries
# =========================
+3 -2
View File
@@ -9,6 +9,7 @@ use crate::services::config::{DatabaseConfig, DbType};
use anyhow::Result;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use crate::domain::mariadb::database::MariaDBDatabase;
#[async_trait::async_trait]
pub trait Database: Send + Sync {
@@ -28,7 +29,7 @@ impl DatabaseFactory {
Arc::new(PostgresDatabase::new(cfg, format))
}
DbType::Mysql => Arc::new(MySQLDatabase::new(cfg)),
DbType::Mariadb => Arc::new(MySQLDatabase::new(cfg)),
DbType::Mariadb => Arc::new(MariaDBDatabase::new(cfg)),
DbType::MongoDB => Arc::new(MongoDatabase::new(cfg)),
DbType::Sqlite => Arc::new(SqliteDatabase::new(cfg)),
DbType::Redis => Arc::new(RedisDatabase::new(cfg)),
@@ -43,7 +44,7 @@ impl DatabaseFactory {
Arc::new(PostgresDatabase::new(cfg, format))
}
DbType::Mysql => Arc::new(MySQLDatabase::new(cfg)),
DbType::Mariadb => Arc::new(MySQLDatabase::new(cfg)),
DbType::Mariadb => Arc::new(MariaDBDatabase::new(cfg)),
DbType::MongoDB => Arc::new(MongoDatabase::new(cfg)),
DbType::Sqlite => Arc::new(SqliteDatabase::new(cfg)),
DbType::Redis => Arc::new(RedisDatabase::new(cfg)),
+66
View File
@@ -0,0 +1,66 @@
use crate::domain::mariadb::connection::{select_mariadb_path, server_version};
use crate::services::config::DatabaseConfig;
use anyhow::{Context, Result};
use std::collections::HashMap;
use std::path::PathBuf;
use std::process::Command;
use tracing::{debug, error, info};
pub async fn run(
cfg: DatabaseConfig,
backup_dir: PathBuf,
env: HashMap<String, String>,
file_extension: &'static str,
) -> Result<PathBuf> {
tokio::task::spawn_blocking(move || -> Result<PathBuf> {
debug!("Starting backup for database {}", cfg.name);
let version = match futures::executor::block_on(server_version(&cfg)) {
Ok(v) => {
debug!("Mariadb version detected: {}", v);
v
}
Err(e) => {
error!("Failed to get server version for {}: {:?}", cfg.name, e);
return Err(e.into());
}
};
info!("Mariadb version found: {}", version);
let file_path = backup_dir.join(format!("{}{}", cfg.generated_id, file_extension));
let mariadb_dump = select_mariadb_path(&version).join("mariadb-dump");
info!("Mariadb dump found: {}", mariadb_dump.display());
let output = Command::new("mariadb-dump")
.arg("--host").arg(&cfg.host)
.arg("--port").arg(cfg.port.to_string())
.arg("--user").arg(&cfg.username)
.arg("--routines")
.arg("--events")
.arg("--triggers")
.arg("--single-transaction")
.arg("--quick")
.arg("--skip-lock-tables")
.arg("--add-drop-database")
.arg("--databases").arg(&cfg.database)
.arg("--compress")
.arg("--max-allowed-packet=512M")
.arg("--net-buffer-length=16K")
.arg("--default-character-set=utf8mb4")
.arg("-r").arg(&file_path)
.envs(env)
.output()
.with_context(|| format!("Failed to run mariadb-dump for {}", cfg.name))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!("Mariadb backup failed for {}: {}", cfg.name, stderr);
}
Ok(file_path)
})
.await?
}
+45
View File
@@ -0,0 +1,45 @@
use std::path::PathBuf;
use crate::services::config::DatabaseConfig;
use anyhow::Result;
use std::process::Command;
pub async fn server_version(cfg: &DatabaseConfig) -> Result<String> {
let output = Command::new("mariadb")
.arg("--host")
.arg(&cfg.host)
.arg("--port")
.arg(cfg.port.to_string())
.arg("--user")
.arg(&cfg.username)
.arg("-e")
.arg("SELECT VERSION();")
.env("MYSQL_PWD", &cfg.password)
.output()?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!("Version query failed: {}", stderr);
}
let version = String::from_utf8_lossy(&output.stdout)
.lines()
.nth(1)
.unwrap_or_default()
.trim()
.to_string();
Ok(version)
}
pub fn select_mariadb_path(version: &str) -> PathBuf {
let mut parts = version.split('.');
let major = parts.next().and_then(|v| v.parse::<u32>().ok()).unwrap_or(10);
let minor = parts.next().and_then(|v| v.parse::<u32>().ok()).unwrap_or(0);
if major < 10 || (major == 10 && minor <= 6) {
"/usr/local/mariadb-10.6/bin".into()
} else {
"/usr/local/mariadb-12.1/bin".into()
}
}
+55
View File
@@ -0,0 +1,55 @@
use super::{backup, ping, restore};
use crate::domain::factory::Database;
use crate::services::config::DatabaseConfig;
use crate::utils::locks::{DbOpLock, FileLock};
use anyhow::Result;
use async_trait::async_trait;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
pub struct MariaDBDatabase {
cfg: DatabaseConfig,
}
impl MariaDBDatabase {
pub fn new(cfg: DatabaseConfig) -> Self {
Self { cfg }
}
fn build_env(&self) -> HashMap<String, String> {
let mut envs = std::env::vars().collect::<HashMap<_, _>>();
envs.insert("MYSQL_PWD".to_string(), self.cfg.password.to_string());
envs
}
}
#[async_trait]
impl Database for MariaDBDatabase {
fn file_extension(&self) -> &'static str {
".sql"
}
async fn ping(&self) -> Result<bool> {
ping::run(self.cfg.clone(), self.build_env().clone()).await
}
async fn backup(&self, dir: &Path) -> Result<PathBuf> {
FileLock::acquire(&self.cfg.generated_id, DbOpLock::Backup.as_str()).await?;
let res = backup::run(
self.cfg.clone(),
dir.to_path_buf(),
self.build_env().clone(),
self.file_extension(),
)
.await;
FileLock::release(&self.cfg.generated_id).await?;
res
}
async fn restore(&self, file: &Path) -> Result<()> {
FileLock::acquire(&self.cfg.generated_id, DbOpLock::Restore.as_str()).await?;
let res = restore::run(self.cfg.clone(), file.to_path_buf()).await;
FileLock::release(&self.cfg.generated_id).await?;
res
}
}
+5
View File
@@ -0,0 +1,5 @@
pub mod backup;
mod connection;
pub mod database;
mod ping;
mod restore;
+28
View File
@@ -0,0 +1,28 @@
use crate::services::config::DatabaseConfig;
use std::collections::HashMap;
use tokio::process::Command;
use tokio::time::{Duration, timeout};
pub async fn run(cfg: DatabaseConfig, env: HashMap<String, String>) -> anyhow::Result<bool> {
let mut cmd = Command::new("mysqladmin");
cmd.arg("--host")
.arg(cfg.host)
.arg("--port")
.arg(cfg.port.to_string())
.arg("--user")
.arg(cfg.username)
.arg("ping")
.envs(env);
let result = timeout(Duration::from_secs(10), cmd.output()).await;
match result {
Ok(output) => {
let output = output?;
Ok(output.status.success())
}
Err(_) => Ok(false),
}
}
+80
View File
@@ -0,0 +1,80 @@
use crate::services::config::DatabaseConfig;
use anyhow::{Context, Result};
use std::fs::File;
use std::io::{Read, Write};
use std::path::PathBuf;
use std::process::Command;
use tracing::{debug, error, info};
pub async fn run(cfg: DatabaseConfig, restore_file: PathBuf) -> Result<()> {
let handle = tokio::task::spawn_blocking(move || -> Result<()> {
debug!("Starting restore for database {}", cfg.name);
let mut sql_content = String::new();
let mut file = File::open(&restore_file)
.with_context(|| format!("Failed to open restore file {}", restore_file.display()))?;
file.read_to_string(&mut sql_content)
.with_context(|| format!("Failed to read restore file {}", restore_file.display()))?;
let drop_create_cmd = format!(
"DROP DATABASE IF EXISTS {0}; CREATE DATABASE {0};",
cfg.database
);
let drop_status = Command::new("mariadb")
.arg("--host")
.arg(&cfg.host)
.arg("--port")
.arg(cfg.port.to_string())
.arg("--user")
.arg(&cfg.username)
.arg("-e")
.arg(&drop_create_cmd)
.env("MYSQL_PWD", &cfg.password)
.status()
.with_context(|| format!("Failed to drop/recreate database {}", cfg.name))?;
if !drop_status.success() {
error!("Drop/create database failed for {}", cfg.name);
anyhow::bail!("Failed to drop/recreate database {}", cfg.name);
}
info!("Database {} dropped and recreated", cfg.name);
let mut child = Command::new("mariadb")
.arg("--host")
.arg(&cfg.host)
.arg("--port")
.arg(cfg.port.to_string())
.arg("--user")
.arg(&cfg.username)
.arg(&cfg.database)
.env("MYSQL_PWD", &cfg.password)
.stdin(std::process::Stdio::piped())
.spawn()
.with_context(|| format!("Failed to start MariaDB restore for {}", cfg.name))?;
let mut stdin = child.stdin.take().context("Failed to open child stdin")?;
stdin
.write_all(sql_content.as_bytes())
.context("Failed to write SQL content to MariaDB stdin")?;
stdin.flush()?;
drop(stdin);
let output = child
.wait_with_output()
.with_context(|| format!("Failed to complete MariaDB restore for {}", cfg.name))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
error!("MariaDB restore failed for {}: {}", cfg.name, stderr);
anyhow::bail!("MariaDB restore failed for {}", cfg.name);
}
info!("Restore finished successfully for database {}", cfg.name);
Ok(())
});
handle.await??;
Ok(())
}
+1
View File
@@ -5,3 +5,4 @@ pub mod postgres;
mod redis;
mod sqlite;
mod valkey;
mod mariadb;
+6
View File
@@ -30,6 +30,9 @@ pub async fn run(
let file_path = backup_dir.join(format!("{}{}", cfg.generated_id, file_extension));
// let mysql_dump = select_mysql_path(&version).join("mysqldump");
// info!("MySQL dump found: {}", mysql_dump.display());
let output = Command::new("mysqldump")
.arg("--host")
.arg(cfg.host)
@@ -54,9 +57,12 @@ pub async fn run(
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
info!("mysqldump stderr: {}", stderr);
anyhow::bail!("MySQL backup failed for {}: {}", cfg.name, stderr);
}
info!("Output {}", String::from_utf8_lossy(&output.stdout));
Ok(file_path)
})
.await?
+1 -1
View File
@@ -4,7 +4,7 @@ use tokio::process::Command;
use tokio::time::{Duration, timeout};
pub async fn run(cfg: DatabaseConfig, env: HashMap<String, String>) -> anyhow::Result<bool> {
let mut cmd = Command::new("mysqladmin");
let mut cmd = Command::new("mariadb-admin");
cmd.arg("--host")
.arg(cfg.host)
.arg("--port")
+7 -6
View File
@@ -2,7 +2,7 @@ use crate::services::config::DatabaseConfig;
use anyhow::{Context, Result};
use tokio::process::Command;
use tokio::time::{Duration, timeout};
use tracing::{debug, info};
use tracing::{debug, error, info};
pub async fn run(cfg: DatabaseConfig) -> Result<bool> {
let mut cmd = Command::new("redis-cli");
@@ -21,7 +21,7 @@ pub async fn run(cfg: DatabaseConfig) -> Result<bool> {
cmd.arg("PING");
debug!("Command Ping: {:?}", cmd);
debug!("Command Ping Redis: {:?}", cmd);
let result = timeout(Duration::from_secs(10), cmd.output()).await;
@@ -31,16 +31,17 @@ pub async fn run(cfg: DatabaseConfig) -> Result<bool> {
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
info!("Redis stdout: {}", stdout);
info!("Redis stderr: {}", stderr);
if !stdout.is_empty() {
error!("Redis stderr: {}", stderr);
}
if stderr.contains("NOAUTH") {
info!("Redis authentication failed (NOAUTH required)");
error!("Redis authentication failed (NOAUTH required)");
return Ok(false);
}
if !output.status.success() {
info!("Redis command failed with status: {:?}", output.status);
error!("Redis command failed with status: {:?}", output.status);
return Ok(false);
}
+9 -8
View File
@@ -2,7 +2,7 @@ use crate::services::config::DatabaseConfig;
use anyhow::{Context, Result};
use tokio::process::Command;
use tokio::time::{Duration, timeout};
use tracing::{debug, info};
use tracing::{debug, info, error};
pub async fn run(cfg: DatabaseConfig) -> Result<bool> {
let mut cmd = Command::new("valkey-cli");
@@ -21,33 +21,34 @@ pub async fn run(cfg: DatabaseConfig) -> Result<bool> {
cmd.arg("PING");
debug!("Command Ping: {:?}", cmd);
debug!("Command Ping Valkey: {:?}", cmd);
let result = timeout(Duration::from_secs(10), cmd.output()).await;
match result {
Ok(output) => {
let output = output.context("Failed to execute redis-cli")?;
let output = output.context("Failed to execute valkey-cli")?;
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
info!("Redis stdout: {}", stdout);
info!("Redis stderr: {}", stderr);
if !stdout.is_empty() {
error!("Valkey stderr: {}", stderr);
}
if stderr.contains("NOAUTH") {
info!("Redis authentication failed (NOAUTH required)");
error!("Valkey authentication failed (NOAUTH required)");
return Ok(false);
}
if !output.status.success() {
info!("Redis command failed with status: {:?}", output.status);
error!("Valkey command failed with status: {:?}", output.status);
return Ok(false);
}
Ok(stdout.contains("PONG"))
}
Err(_) => {
info!("Timeout connecting to Redis at {}:{}", cfg.host, cfg.port);
info!("Timeout connecting to Valkey at {}:{}", cfg.host, cfg.port);
Ok(false)
}
}
@@ -11,6 +11,8 @@ pub struct DatabasePayload<'a> {
pub dbms: &'a str,
#[serde(rename = "generatedId")]
pub generated_id: &'a str,
#[serde(rename = "pingStatus")]
pub ping_status: bool
}
#[derive(Serialize)]
+1 -1
View File
@@ -27,7 +27,7 @@ impl DbType {
pub fn as_str(&self) -> &'static str {
match self {
DbType::Mysql => "mysql",
DbType::Mariadb => "mysql",
DbType::Mariadb => "mariadb",
DbType::Postgresql => "postgresql",
DbType::MongoDB => "mongodb",
DbType::Sqlite => "sqlite",
+15 -7
View File
@@ -8,6 +8,8 @@ use crate::settings::CONFIG;
use reqwest::Client;
use std::error::Error;
use std::sync::Arc;
use futures_util::future::try_join_all;
use crate::domain::factory::DatabaseFactory;
pub struct StatusService {
ctx: Arc<Context>,
@@ -25,14 +27,20 @@ impl StatusService {
pub async fn ping(&self, databases: &[DatabaseConfig]) -> Result<PingResult, Box<dyn Error>> {
let edge_key = &self.ctx.edge_key;
let databases_payload: Vec<DatabasePayload> = databases
.iter()
.map(|db| DatabasePayload {
name: &db.name,
dbms: &db.db_type.as_str(),
generated_id: &db.generated_id,
let databases_payload: Vec<DatabasePayload> = try_join_all(
databases.into_iter().map(|db| async move {
let db_engine = DatabaseFactory::create_for_backup(db.clone()).await;
let reachable = db_engine.ping().await?;
Ok::<DatabasePayload, anyhow::Error>(DatabasePayload {
name: &db.name,
dbms: &db.db_type.as_str(),
generated_id: &db.generated_id,
ping_status: reachable,
})
})
.collect();
).await?;
let version_str = CONFIG.app_version.as_str();
let result = self
+10
View File
@@ -25,6 +25,16 @@ impl Settings {
.parse::<usize>()
.expect("POOLING must be a valid positive integer");
if pooling_seconds < 1 || pooling_seconds > 600 {
panic!("POOLING must be between 1 second and 600 seconds (10 minutes)");
}
if pooling_seconds < 5 {
eprintln!(
"[WARNING] POOLING is set to {}s. Values under 5s are not recommended for production.",
pooling_seconds
);
}
let tz = env::var("TZ").unwrap_or_else(|_| "UTC".to_string());
Self {