Add config controller in server

This commit is contained in:
2025-09-09 19:06:03 +02:00
parent 7a7a909440
commit 88e5f3d694
7 changed files with 285 additions and 3 deletions

108
server/src/utils/base62.rs Normal file
View File

@@ -0,0 +1,108 @@
const CHARS: &str = "rYTSJ96O2ntiEBkuwQq0vdslyfI8Ph51bpae3LgHoFZAxj7WmzUNCGXcR4MDKV";
pub struct Base62;
impl Base62 {
pub fn encode(input: &str) -> String {
if input.is_empty() {
return String::new();
}
let bytes = input.as_bytes();
let alphabet_chars: Vec<char> = CHARS.chars().collect();
let mut number = bytes.iter().fold(String::from("0"), |acc, &byte| {
Self::multiply_and_add(&acc, 256, byte as u32)
});
if number == "0" {
return "0".to_string();
}
let mut result = String::new();
while number != "0" {
let (new_number, remainder) = Self::divide_by(&number, 62);
result.push(alphabet_chars[remainder as usize]);
number = new_number;
}
result.chars().rev().collect()
}
pub fn decode(encoded: &str) -> Option<String> {
if encoded.is_empty() {
return Some(String::new());
}
let char_to_value: std::collections::HashMap<char, u32> = CHARS
.chars()
.enumerate()
.map(|(i, c)| (c, i as u32))
.collect();
let mut number = String::from("0");
for c in encoded.chars() {
let value = *char_to_value.get(&c)?;
number = Self::multiply_and_add(&number, 62, value);
}
if number == "0" {
return Some(String::new());
}
let mut bytes = Vec::new();
while number != "0" {
let (new_number, remainder) = Self::divide_by(&number, 256);
bytes.push(remainder as u8);
number = new_number;
}
bytes.reverse();
String::from_utf8(bytes).ok()
}
fn multiply_and_add(num_str: &str, base: u32, add: u32) -> String {
let mut result = Vec::new();
let mut carry = add;
for c in num_str.chars().rev() {
let digit = c.to_digit(10).unwrap_or(0);
let product = digit * base + carry;
result.push((product % 10).to_string());
carry = product / 10;
}
while carry > 0 {
result.push((carry % 10).to_string());
carry /= 10;
}
if result.is_empty() {
"0".to_string()
} else {
result.into_iter().rev().collect()
}
}
fn divide_by(num_str: &str, base: u32) -> (String, u32) {
let mut quotient = String::new();
let mut remainder = 0u32;
for c in num_str.chars() {
let digit = c.to_digit(10).unwrap_or(0);
let current = remainder * 10 + digit;
let q = current / base;
remainder = current % base;
if !quotient.is_empty() || q > 0 {
quotient.push_str(&q.to_string());
}
}
if quotient.is_empty() {
quotient = "0".to_string();
}
(quotient, remainder)
}
}

View File

@@ -0,0 +1,44 @@
use crate::utils::{error::*, DbPool};
use sqlx::Row;
pub struct ConfigManager;
impl ConfigManager {
pub async fn get_config(pool: &DbPool, key: &str) -> AppResult<Option<String>> {
let row = sqlx::query("SELECT value FROM config WHERE key = ?")
.bind(key)
.fetch_optional(pool)
.await?;
if let Some(row) = row {
Ok(Some(row.get("value")))
} else {
Ok(None)
}
}
pub async fn set_config(pool: &DbPool, key: &str, value: &str) -> AppResult<()> {
sqlx::query(
r#"
INSERT INTO config (key, value, updated_at)
VALUES (?, ?, CURRENT_TIMESTAMP)
ON CONFLICT(key) DO UPDATE SET
value = excluded.value,
updated_at = CURRENT_TIMESTAMP
"#,
)
.bind(key)
.bind(value)
.execute(pool)
.await?;
Ok(())
}
pub async fn get_external_url(pool: &DbPool) -> AppResult<String> {
match Self::get_config(pool, "EXTERNAL_URL").await? {
Some(url) => Ok(url),
None => Err(internal_error("EXTERNAL_URL not configured")),
}
}
}

View File

@@ -72,12 +72,12 @@ async fn run_migrations(pool: &DbPool) -> AppResult<()> {
r#"
CREATE TABLE IF NOT EXISTS provisioning_codes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
machine_id INTEGER NOT NULL,
code TEXT UNIQUE NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
expires_at DATETIME NOT NULL,
used BOOLEAN DEFAULT 0,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
FOREIGN KEY(machine_id) REFERENCES machines(id) ON DELETE CASCADE
)
"#,
)
@@ -98,6 +98,19 @@ async fn run_migrations(pool: &DbPool) -> AppResult<()> {
.execute(pool)
.await?;
sqlx::query(
r#"
CREATE TABLE IF NOT EXISTS config (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
"#,
)
.execute(pool)
.await?;
Ok(())
}

View File

@@ -1,4 +1,6 @@
pub mod auth;
pub mod base62;
pub mod config;
pub mod database;
pub mod db_path;
pub mod error;