Add config controller in server
This commit is contained in:
108
server/src/utils/base62.rs
Normal file
108
server/src/utils/base62.rs
Normal 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)
|
||||
}
|
||||
}
|
44
server/src/utils/config.rs
Normal file
44
server/src/utils/config.rs
Normal 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")),
|
||||
}
|
||||
}
|
||||
}
|
@@ -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(())
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,6 @@
|
||||
pub mod auth;
|
||||
pub mod base62;
|
||||
pub mod config;
|
||||
pub mod database;
|
||||
pub mod db_path;
|
||||
pub mod error;
|
||||
|
Reference in New Issue
Block a user