v3 : socle Rust (moteur pur, accès trustinWallet, serveur) ; maquette Nuxt rangée dans maquette/
- crates/ld-engine : formule inertielle (parité avec la maquette, garde C), coquille / changement de fond, assistant d'édition à règles, composition d'un document sous vote permanent (adoptions, aperçus, rivales). - crates/trustin-access : qui voit, qui ouvre, qui gère les accès. - crates/ld-server : API axum + SQLite, connexion Ğ1 (client OAuth2 de connect.monnaie-libre.fr), invitations, entrée en vigueur automatique, signalements de contradiction, traductions éditoriales, publication git, taille de la toile lue sur la chaîne. - seeds/acte-engagement-certification.json : 33 articles, fr + en + es ; en vigueur : préambule, E1, E2 (seed a minima). - maquette/ : frontend, backend et docker v2 déplacés tels quels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
//! Configuration, from the environment only. Secrets never have a default.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Connect {
|
||||
/// Base URL of the sso-connect instance (no trailing slash).
|
||||
pub base_url: String,
|
||||
pub client_id: String,
|
||||
pub client_secret: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Publish {
|
||||
/// Remote of the texts repository, e.g. `git@git.duniter.org:Yvv/libredecision-textes.git`.
|
||||
pub remote: String,
|
||||
pub branch: String,
|
||||
/// Private SSH key allowed to push there (deploy key).
|
||||
pub key: PathBuf,
|
||||
pub author_name: String,
|
||||
pub author_email: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
pub bind: String,
|
||||
pub data_dir: PathBuf,
|
||||
pub web_dir: PathBuf,
|
||||
pub seed_dir: PathBuf,
|
||||
/// Public origin, e.g. `https://decision.librodrome.org` — callbacks and cookies.
|
||||
pub public_url: String,
|
||||
pub connect: Option<Connect>,
|
||||
pub admin_g1_ids: Vec<String>,
|
||||
pub dev_login: bool,
|
||||
pub bootstrap_admin: bool,
|
||||
pub publish: Option<Publish>,
|
||||
pub rpc_endpoints: Vec<String>,
|
||||
pub indexers: Vec<String>,
|
||||
pub wot_fallback: u32,
|
||||
pub build: String,
|
||||
}
|
||||
|
||||
fn var(name: &str) -> Option<String> {
|
||||
std::env::var(name).ok().map(|v| v.trim().to_owned()).filter(|v| !v.is_empty())
|
||||
}
|
||||
|
||||
fn flag(name: &str) -> bool {
|
||||
var(name).is_some_and(|v| matches!(v.as_str(), "1" | "true" | "yes" | "on"))
|
||||
}
|
||||
|
||||
fn list(name: &str, default: &[&str]) -> Vec<String> {
|
||||
match var(name) {
|
||||
Some(v) => v.split(',').map(|s| s.trim().to_owned()).filter(|s| !s.is_empty()).collect(),
|
||||
None => default.iter().map(|s| (*s).to_owned()).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn from_env() -> Self {
|
||||
let data_dir = PathBuf::from(var("LD_DATA_DIR").unwrap_or_else(|| "data".into()));
|
||||
let connect = match (var("LD_CONNECT_CLIENT_ID"), var("LD_CONNECT_CLIENT_SECRET")) {
|
||||
(Some(client_id), Some(client_secret)) => Some(Connect {
|
||||
base_url: var("LD_CONNECT_URL")
|
||||
.unwrap_or_else(|| "https://connect.monnaie-libre.fr".into())
|
||||
.trim_end_matches('/')
|
||||
.to_owned(),
|
||||
client_id,
|
||||
client_secret,
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
let publish = var("LD_PUBLISH_REMOTE").map(|remote| Publish {
|
||||
remote,
|
||||
branch: var("LD_PUBLISH_BRANCH").unwrap_or_else(|| "main".into()),
|
||||
key: var("LD_PUBLISH_KEY").map(PathBuf::from).unwrap_or_else(|| data_dir.join("keys/publish_ed25519")),
|
||||
author_name: var("LD_PUBLISH_AUTHOR").unwrap_or_else(|| "libreDecision".into()),
|
||||
author_email: var("LD_PUBLISH_EMAIL").unwrap_or_else(|| "decision@librodrome.org".into()),
|
||||
});
|
||||
Self {
|
||||
bind: var("LD_BIND").unwrap_or_else(|| "0.0.0.0:8002".into()),
|
||||
web_dir: PathBuf::from(var("LD_WEB_DIR").unwrap_or_else(|| "web/build".into())),
|
||||
seed_dir: PathBuf::from(var("LD_SEED_DIR").unwrap_or_else(|| "seeds".into())),
|
||||
public_url: var("LD_PUBLIC_URL")
|
||||
.unwrap_or_else(|| "http://127.0.0.1:3002".into())
|
||||
.trim_end_matches('/')
|
||||
.to_owned(),
|
||||
connect,
|
||||
admin_g1_ids: list("LD_ADMIN_G1_IDS", &[]),
|
||||
dev_login: flag("LD_DEV_LOGIN"),
|
||||
bootstrap_admin: flag("LD_BOOTSTRAP_ADMIN"),
|
||||
publish,
|
||||
rpc_endpoints: list(
|
||||
"LD_RPC_ENDPOINTS",
|
||||
&[
|
||||
"https://rpc.duniter.org",
|
||||
"https://g1.axiom-team.fr/ws/",
|
||||
"https://g1.asycn.io/ws/",
|
||||
"https://g1.coinduf.eu",
|
||||
],
|
||||
),
|
||||
indexers: list(
|
||||
"LD_INDEXERS",
|
||||
&["https://indexer.duniter.org/v1/graphql", "https://g1-squid.axiom-team.fr/v1/graphql"],
|
||||
),
|
||||
wot_fallback: var("LD_WOT_FALLBACK").and_then(|v| v.parse().ok()).unwrap_or(5745),
|
||||
build: var("LD_BUILD").unwrap_or_else(|| "dev".into()),
|
||||
data_dir,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cookies are `Secure` as soon as the public origin is https.
|
||||
pub fn secure_cookies(&self) -> bool {
|
||||
self.public_url.starts_with("https://")
|
||||
}
|
||||
|
||||
pub fn connect_callback(&self) -> String {
|
||||
format!("{}/api/auth/g1/callback", self.public_url)
|
||||
}
|
||||
|
||||
/// For tests: an in-memory configuration with dev login.
|
||||
pub fn for_tests() -> Self {
|
||||
Self {
|
||||
bind: "127.0.0.1:0".into(),
|
||||
data_dir: std::env::temp_dir().join("ld-tests"),
|
||||
web_dir: PathBuf::from("web/build"),
|
||||
seed_dir: PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/../../seeds")),
|
||||
public_url: "http://127.0.0.1:3002".into(),
|
||||
connect: None,
|
||||
admin_g1_ids: vec!["idty:8352".into()],
|
||||
dev_login: true,
|
||||
bootstrap_admin: false,
|
||||
publish: None,
|
||||
rpc_endpoints: vec![],
|
||||
indexers: vec![],
|
||||
wot_fallback: 5745,
|
||||
build: "test".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user