WIP
This commit is contained in:
parent
abdcc6cbc4
commit
310a62cb72
4 changed files with 233 additions and 47 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -429,6 +429,7 @@ dependencies = [
|
|||
"thiserror",
|
||||
"tokio",
|
||||
"toml",
|
||||
"url",
|
||||
"validator",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
|
|
@ -13,6 +13,7 @@ serde = { version = "1.0", features = ["derive"] }
|
|||
sqlx = { version = "0.7", features = ["runtime-tokio", "postgres"] }
|
||||
tokio = { version = "1.38", features = ["full"] }
|
||||
toml = "0.8"
|
||||
url = "2.5"
|
||||
validator = { version = "0.18", features = ["derive"] }
|
||||
yaml-rust = "0.4"
|
||||
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
#![allow(clippy::type_complexity)]
|
||||
|
||||
use crate::config::{client, server};
|
||||
use sqlx::{postgres::PgConnectOptions, ConnectOptions};
|
||||
use std::{collections::HashMap, env, fs, io::Read};
|
||||
use url::Url;
|
||||
use yaml_rust::{Yaml, YamlLoader};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
@ -12,6 +14,10 @@ pub(crate) enum Error {
|
|||
ReadYaml(#[from] ReadYamlConfigError),
|
||||
#[error("failed to read the meta table")]
|
||||
ReadMeta(#[from] sqlx::Error),
|
||||
#[error("invalid config ({0})")]
|
||||
InvalidConfig(&'static str),
|
||||
#[error("failed to parse server URL")]
|
||||
InvalidUrl(#[from] url::ParseError),
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
@ -20,7 +26,7 @@ pub(crate) enum ReadYamlConfigError {
|
|||
ReadFile(#[from] std::io::Error),
|
||||
#[error(transparent)]
|
||||
Yaml(#[from] yaml_rust::ScanError),
|
||||
#[error("invalid config file ({0})")]
|
||||
#[error("invalid config ({0})")]
|
||||
InvalidConfig(String),
|
||||
}
|
||||
|
||||
|
@ -56,7 +62,7 @@ fn read_default_yml() -> Result<HashMap<String, Yaml>, ReadYamlConfigError> {
|
|||
for (key, val) in content {
|
||||
let Some(key) = key.as_str() else {
|
||||
return Err(ReadYamlConfigError::InvalidConfig(format!(
|
||||
"non-string key found: {:?}",
|
||||
"non-string key: {:?}",
|
||||
key
|
||||
)));
|
||||
};
|
||||
|
@ -66,7 +72,7 @@ fn read_default_yml() -> Result<HashMap<String, Yaml>, ReadYamlConfigError> {
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow, Debug)]
|
||||
#[derive(sqlx::FromRow)]
|
||||
#[sqlx(rename_all = "camelCase")]
|
||||
struct Meta {
|
||||
name: Option<String>,
|
||||
|
@ -172,23 +178,199 @@ async fn read_meta_table(
|
|||
Ok(meta)
|
||||
}
|
||||
|
||||
pub(super) async fn run() -> Result<(), Error> {
|
||||
let old_config = read_default_yml()?;
|
||||
|
||||
for (k, v) in &old_config {
|
||||
println!("{}:\n {:?}", k, v);
|
||||
}
|
||||
|
||||
async fn read_old_config() -> Result<(HashMap<String, Yaml>, Meta), Error> {
|
||||
let default_yml = read_default_yml()?;
|
||||
let db = default_yml
|
||||
.get("db")
|
||||
.ok_or(Error::InvalidConfig("`db` is missing"))?;
|
||||
let meta = read_meta_table(
|
||||
old_config["db"]["host"].as_str().unwrap(),
|
||||
old_config["db"]["port"].as_i64().unwrap() as u16,
|
||||
old_config["db"]["user"].as_str().unwrap(),
|
||||
old_config["db"]["pass"].as_str().unwrap(),
|
||||
old_config["db"]["db"].as_str().unwrap(),
|
||||
db["host"].as_str().unwrap(),
|
||||
db["port"].as_i64().unwrap() as u16,
|
||||
db["user"].as_str().unwrap(),
|
||||
db["pass"].as_str().unwrap(),
|
||||
db["db"].as_str().unwrap(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Meta: {:#?}", meta);
|
||||
Ok((default_yml, meta))
|
||||
}
|
||||
|
||||
async fn create_new_config(
|
||||
default_yml: HashMap<String, Yaml>,
|
||||
meta: Meta,
|
||||
) -> Result<(server::Config, client::Config), Error> {
|
||||
let db = default_yml
|
||||
.get("db")
|
||||
.map(|db| db.as_hash())
|
||||
.flatten()
|
||||
.ok_or(Error::InvalidConfig("`db` is missing"))?;
|
||||
|
||||
let redis = default_yml
|
||||
.get("redis")
|
||||
.map(|redis| redis.as_hash())
|
||||
.flatten()
|
||||
.ok_or(Error::InvalidConfig("`redis` is missing"))?;
|
||||
|
||||
let server_url = default_yml
|
||||
.get("url")
|
||||
.map(|url| url.as_str())
|
||||
.flatten()
|
||||
.ok_or(Error::InvalidConfig("`url` is missing"))?;
|
||||
|
||||
let parsed_server_url = Url::parse(server_url)?;
|
||||
let protocol = parsed_server_url.scheme().to_owned();
|
||||
|
||||
if protocol != "https" && protocol != "http" {
|
||||
return Err(Error::InvalidConfig(
|
||||
"server URL must start with http:// or https://",
|
||||
));
|
||||
}
|
||||
|
||||
let hostname = parsed_server_url.host_str().ok_or(Error::InvalidConfig(
|
||||
"hostname is missing in the server url",
|
||||
))?;
|
||||
let host = match parsed_server_url.port() {
|
||||
Some(port) => format!("{}:{}", hostname, port),
|
||||
None => hostname.to_owned(),
|
||||
};
|
||||
|
||||
let repository_url = match meta.repository_url.as_ref() {
|
||||
"https://codeberg.org/firefish/firefish"
|
||||
| "https://git.joinfirefish.org/firefish/firefish" => {
|
||||
"https://firefish.dev/firefish/firefish".to_owned()
|
||||
}
|
||||
url => url.to_owned(),
|
||||
};
|
||||
|
||||
let mut server_config = server::Config {
|
||||
info: Some(server::Info {
|
||||
name: meta.name,
|
||||
description: meta.description,
|
||||
maintainer_name: meta.maintainer_name,
|
||||
contact_info: meta.maintainer_email,
|
||||
open_registrations: !meta.disable_registration,
|
||||
repository_url: Some(repository_url),
|
||||
}),
|
||||
timelines: Some(server::Timelines {
|
||||
local: !meta.disable_local_timeline,
|
||||
global: !meta.disable_global_timeline,
|
||||
recommended: !meta.disable_recommended_timeline,
|
||||
guest: meta.enable_guest_timeline,
|
||||
}),
|
||||
network: server::Network {
|
||||
protocol: match protocol.as_str() {
|
||||
"http" => Some(server::HttpProtocol::Http),
|
||||
_ => Some(server::HttpProtocol::Https),
|
||||
},
|
||||
host,
|
||||
port: default_yml
|
||||
.get("port")
|
||||
.map(|v| v.as_i64())
|
||||
.flatten()
|
||||
.ok_or(Error::InvalidConfig("port"))? as u16,
|
||||
},
|
||||
database: server::Database {
|
||||
host: db
|
||||
.get(&Yaml::String("host".to_string()))
|
||||
.map(|v| v.as_str())
|
||||
.flatten()
|
||||
.ok_or(Error::InvalidConfig("db.host"))?
|
||||
.to_string(),
|
||||
port: db
|
||||
.get(&Yaml::String("port".to_string()))
|
||||
.map(|v| v.as_i64())
|
||||
.flatten()
|
||||
.ok_or(Error::InvalidConfig("db.port"))? as u16,
|
||||
user: db
|
||||
.get(&Yaml::String("user".to_string()))
|
||||
.map(|v| v.as_str())
|
||||
.flatten()
|
||||
.ok_or(Error::InvalidConfig("db.user"))?
|
||||
.to_string(),
|
||||
password: db
|
||||
.get(&Yaml::String("pass".to_string()))
|
||||
.map(|v| v.as_str())
|
||||
.flatten()
|
||||
.ok_or(Error::InvalidConfig("db.pass"))?
|
||||
.to_string(),
|
||||
name: db
|
||||
.get(&Yaml::String("db".to_string()))
|
||||
.map(|v| v.as_str())
|
||||
.flatten()
|
||||
.ok_or(Error::InvalidConfig("db.db"))?
|
||||
.to_string(),
|
||||
},
|
||||
cache_server: server::CacheServer {
|
||||
host: redis
|
||||
.get(&Yaml::String("host".to_string()))
|
||||
.map(|v| v.as_str())
|
||||
.flatten()
|
||||
.ok_or(Error::InvalidConfig("redis.host"))?
|
||||
.to_string(),
|
||||
port: redis
|
||||
.get(&Yaml::String("port".to_string()))
|
||||
.map(|v| v.as_i64())
|
||||
.flatten()
|
||||
.ok_or(Error::InvalidConfig("redis.port"))? as u16,
|
||||
user: match redis.get(&Yaml::String("user".to_string())) {
|
||||
Some(user) => Some(
|
||||
user.as_str()
|
||||
.ok_or(Error::InvalidConfig("redis.user"))?
|
||||
.to_string(),
|
||||
),
|
||||
None => None,
|
||||
},
|
||||
password: match redis.get(&Yaml::String("pass".to_string())) {
|
||||
Some(user) => Some(
|
||||
user.as_str()
|
||||
.ok_or(Error::InvalidConfig("redis.pass"))?
|
||||
.to_string(),
|
||||
),
|
||||
None => None,
|
||||
},
|
||||
index: match redis.get(&Yaml::String("db".to_string())) {
|
||||
Some(user) => Some(user.as_i64().ok_or(Error::InvalidConfig("redis.db"))? as u8),
|
||||
None => None,
|
||||
},
|
||||
prefix: match redis.get(&Yaml::String("prefix".to_string())) {
|
||||
Some(user) => Some(
|
||||
user.as_str()
|
||||
.ok_or(Error::InvalidConfig("redis.prefix"))?
|
||||
.to_string(),
|
||||
),
|
||||
None => None,
|
||||
},
|
||||
},
|
||||
id: None,
|
||||
};
|
||||
|
||||
if let Some(id) = default_yml.get("cuid") {
|
||||
let id = id.as_hash().ok_or(Error::InvalidConfig("cuid"))?;
|
||||
server_config.id = Some(server::Id {
|
||||
length: match id.get(&Yaml::String("length".to_string())) {
|
||||
Some(length) => {
|
||||
Some(length.as_i64().ok_or(Error::InvalidConfig("cuid.length"))? as u8)
|
||||
}
|
||||
None => None,
|
||||
},
|
||||
fingerprint: match id.get(&Yaml::String("fingerprint".to_string())) {
|
||||
Some(length) => Some(
|
||||
length
|
||||
.as_str()
|
||||
.ok_or(Error::InvalidConfig("cuid.fingerpring"))?
|
||||
.to_string(),
|
||||
),
|
||||
None => None,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Ok((server_config, todo!()))
|
||||
}
|
||||
|
||||
pub(super) async fn run() -> Result<(), Error> {
|
||||
let (default_yml, meta) = read_old_config().await?;
|
||||
let (server_config, client_config) = create_new_config(default_yml, meta).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -10,45 +10,47 @@ use validator::Validate;
|
|||
|
||||
#[derive(Deserialize, Serialize, Validate, Debug)]
|
||||
pub struct Config {
|
||||
info: Option<Info>,
|
||||
timelines: Option<Timelines>,
|
||||
network: Network,
|
||||
database: Database,
|
||||
cache_server: CacheServer,
|
||||
id: Option<Id>,
|
||||
pub info: Option<Info>,
|
||||
pub timelines: Option<Timelines>,
|
||||
pub network: Network,
|
||||
pub database: Database,
|
||||
pub cache_server: CacheServer,
|
||||
pub id: Option<Id>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Validate, Debug)]
|
||||
pub struct Info {
|
||||
/// Server name
|
||||
name: Option<String>,
|
||||
pub name: Option<String>,
|
||||
/// Server description
|
||||
description: Option<String>,
|
||||
pub description: Option<String>,
|
||||
/// Name/handle of the server maintainer
|
||||
maintainer_name: Option<String>,
|
||||
pub maintainer_name: Option<String>,
|
||||
/// Contact info (e.g., email) of the server maintainer
|
||||
contact_info: Option<String>,
|
||||
pub contact_info: Option<String>,
|
||||
/// Whether the server allows open self-registration
|
||||
open_registrations: bool,
|
||||
pub open_registrations: bool,
|
||||
/// Repository URL
|
||||
repository_url: Option<String>,
|
||||
pub repository_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Validate, Debug)]
|
||||
pub struct Timelines {
|
||||
/// Whether to enable the local timeline
|
||||
local: bool,
|
||||
pub local: bool,
|
||||
/// Whether to enable the global timeline
|
||||
global: bool,
|
||||
pub global: bool,
|
||||
/// Whether to enable the recommended timeline
|
||||
pub recommended: bool,
|
||||
/// Whether to publish the local/global timelines to signed out users
|
||||
guest: bool,
|
||||
pub guest: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Validate, Debug)]
|
||||
pub struct Network {
|
||||
protocol: Option<HttpProtocol>,
|
||||
host: String,
|
||||
port: u16,
|
||||
pub protocol: Option<HttpProtocol>,
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
|
@ -59,26 +61,26 @@ pub enum HttpProtocol {
|
|||
|
||||
#[derive(Deserialize, Serialize, Validate, Debug)]
|
||||
pub struct Database {
|
||||
host: String,
|
||||
port: u16,
|
||||
user: String,
|
||||
password: String,
|
||||
name: String,
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub user: String,
|
||||
pub password: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Validate, Debug)]
|
||||
pub struct CacheServer {
|
||||
host: String,
|
||||
port: u16,
|
||||
user: Option<String>,
|
||||
password: Option<String>,
|
||||
index: Option<String>,
|
||||
prefix: Option<String>,
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub user: Option<String>,
|
||||
pub password: Option<String>,
|
||||
pub index: Option<u8>,
|
||||
pub prefix: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Validate, Debug)]
|
||||
pub struct Id {
|
||||
#[validate(range(min = 16, max = 24))]
|
||||
length: Option<u8>,
|
||||
fingerprint: Option<String>,
|
||||
pub length: Option<u8>,
|
||||
pub fingerprint: Option<String>,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue