1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
use crate::server::{
OrdinaryApiServerState, ProvisionMode, Server, WrappedOrdinaryAppServer,
WrappedOrdinaryProxyServer, ops,
};
use anyhow::bail;
use ordinary_auth::OsRng;
use ordinary_config::OrdinaryConfig;
use std::fs;
use std::path::Path;
use std::sync::Arc;
use tracing::{Instrument, Span};
use x25519_dalek::{PublicKey, StaticSecret};
#[allow(clippy::too_many_lines)]
pub(crate) async fn start(
apps_dir: &Path,
server_span: &Span,
state: &Arc<OrdinaryApiServerState>,
) -> anyhow::Result<()> {
let state_clone = state.clone();
let Ok(shared_rt) = tokio::runtime::Handle::try_current() else {
bail!("failed to get shared runtime");
};
Box::pin(async {
// todo: sleep this for 2 seconds or something to wait for the server to get started up
let mut servers = state_clone.servers.write().await;
for entry in fs::read_dir(apps_dir)?.flatten() {
let config_path = entry.path().join("ordinary.json");
let killed_path = entry.path().join("killed");
match fs::read(&config_path) {
Ok(mut config_bytes) => match simd_json::from_slice::<OrdinaryConfig>(&mut config_bytes[..]) {
Ok(config) => {
let domain = config.domain.clone();
if state.stored_logs
&& let Some(monitor) = &*state.monitor
&& let Err(err) = monitor
.add(
config.domain.as_str(),
&config.cnames.clone().unwrap_or_default(),
)
{
tracing::error!(%err, "failed to add domain to log manager");
}
let app_span = Some(state.server_span.clone().in_scope(|| {
tracing::info_span!("app", domain = %domain)
}));
match ops::app::dbs(&state_clone, &config, &entry.path(), app_span.clone()).await {
Ok((auth, storage)) => {
match ops::app::start(
&state_clone,
&config,
auth,
storage,
state_clone
.provision_mode
.as_ref()
.unwrap_or(&ProvisionMode::Localhost).clone(),
entry.path().join("certs"),
shared_rt.clone(),
app_span.clone(),
).await {
Ok((ports_res, app, terminate_tx, terminate_rx, stream_tx, configs)) => {
for (domain, router) in &*app.proxies {
servers.insert(
domain.clone(),
Server::Proxy(Arc::new(WrappedOrdinaryProxyServer {
service: router.0.clone(),
terminate_rx: terminate_rx.clone(),
configs: configs.clone(),
})),
);
}
let cnames = app.config.cnames.clone();
let static_secret = StaticSecret::random_from_rng(OsRng);
let public_key = PublicKey::from(&static_secret);
let app = Arc::new(WrappedOrdinaryAppServer {
port: ports_res.app.unwrap_or_default(),
app,
terminate_tx: terminate_tx.clone(),
stream_tx,
dh_keypair: (static_secret, public_key),
});
if let Some(cnames) = cnames {
for cname in cnames {
servers.insert(cname, Server::App(app.clone()));
}
}
servers.insert(
domain,
Server::App(app),
);
if killed_path.exists() && let Some(app_span) = app_span {
async {
if let Ok(since) = tokio::fs::read_to_string(killed_path).await {
tracing::info!(%since, "killed");
}
}.instrument(app_span).await;
}
}
Err(err) => {
if let Some(app_span) = app_span {
app_span.in_scope(|| {
tracing::error!(%err, "failed to start app");
});
}
}
}
}
Err(err) => {
if let Some(app_span) = app_span {
app_span.in_scope(|| {
tracing::error!(%err, "failed to init dbs");
});
}
}
}
}
Err(err) => {
if let Some(path_str) = config_path.to_str() {
tracing::error!(%err, path = path_str, "failed to parse config file");
} else {
tracing::error!(%err, "failed to parse config file");
}
}
},
Err(err) => {
if !entry.path().join("erased").exists() {
if let Some(path_str) = config_path.to_str() {
tracing::warn!(%err, path = path_str, "failed to read config file");
} else {
tracing::warn!(%err, "failed to read config file");
}
}
}
}
}
drop(servers);
anyhow::Ok(())
}.instrument(server_span.clone())).await
}