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
|
//! "splorm, i wake and breathe"
//!
//! Genevieve Raine's webserver for her website, <dreamy.place>. Are you genny?
//! One of us? I will write with this assumption.
//!
//! `splorm` is the main webserver, but it uses several subcrates to complete it's mission.
//!
//! [`gatherer_client`] manages a Unix Socket that communicates with gatherer over it's API.
//!
//! [`knowing`] does authentication. It manages it's own database tables and provides an
//! API that makes user and session management easier.
//!
//! [`paige`] is a very basic page template library-thing. all it does is parse frontmatter
//! from the page content itself.
//!
//! [`sessionhash`] has axum extractors that take the client's IP address, user-agent, and
//! the current date to form truncated sha256 hash identifying it for the day.
//!
//! [`somethings`] holds structures common to subcrates of `splorm`, as well a structure
//! holding a `rusqlite` database `Connection`
//!
//! [`timeparse`] does parsing of dates/times using the `time` crate. It also provides some
//! formatting methods and a few methods to get the current time, so I don't have to depend
//! on the `time` crate in `splorm`, too.
//!
//! [`wikarden`] is my digital garden/wiki. It manages a git repository of the pages,
//! providing an API for all the CRUD operations. Does not do any authentication, that's
//! handled within splorm using `knowing`, as mentioned in previous.
use std::sync::Arc;
use axum::{Extension, Router, middleware::from_fn, routing::get};
use tokio::{net::TcpListener, runtime::Builder};
use wikarden::Wikarden;
use crate::{
config::{CommandLine, Config},
connector::GathererConnector,
state::{ConnectorsState, SplormState},
};
mod config;
pub mod connector;
mod extractor;
pub mod fs;
mod handler;
mod query;
mod runtime_error;
mod state;
pub mod templated;
#[doc(hidden)]
pub use query::{Parameter, Query, QueryParseError};
#[doc(hidden)]
pub use runtime_error::{InvalidPathReason, RuntimeError};
/// entrypoint
///
/// We all know what `main()` is :)
/// - creates and registeres the tracing subscriber
/// - gets config: [CommandLine] and [Config] are created here.
/// - Creates [GathererThread] and spawns it's thread
/// - Creates [SplormState]
/// - Starts the tokio multi-threaded runtime and blocks on [`async_main()`]
fn main() {
config::create_tracing_subscriber();
let cli = CommandLine::get();
let conf = Config::get(cli.conf_path());
tracing::trace!(?cli);
tracing::trace!(?conf);
let gatherer = GathererConnector::spawn(conf.settings().connectors().gatherer_path()).unwrap();
let state = SplormState::new(ConnectorsState::new(gatherer), conf.settings());
// Create and spawn the tokio multithreaded runtime. Execution stays
// here until async_main() returns.
let rt = Builder::new_multi_thread().enable_all().build().unwrap();
rt.block_on(async_main(cli, conf, state))
}
/// async entrypoint
///
/// This is where the application lives for 99% of it's life.
/// - Creates wikarden (it's constructor requires async)
/// - Makes sure the index for the wikarden exists, creating it if not
/// - Sets up all our routes
/// - Applies extensiosn and gives the router our state ([SplormState])
/// - Starts axum listening and serving routes
async fn async_main(cli: CommandLine, conf: Config, state: SplormState) {
state.init_database().await;
let wikarden = Wikarden::new(
conf.settings().wikarden_root(),
wikarden::Settings::default()
.commit_name(conf.settings().wikarden_gitname().to_owned())
.commit_email(conf.settings().wikarden_gitemail().to_owned()),
)
.await
.unwrap();
if cli.refresh_sections() {
tracing::warn!("refreshing wikarden's section table. This might take awhile..");
wikarden.refresh_sections().await.unwrap();
tracing::info!("sections refreshed!");
}
let wikarden_index = conf.settings().wikarden_index();
if !wikarden.page_exists(wikarden_index).await.unwrap() {
wikarden
.create(
wikarden_index,
"index",
Some("splorm: automatic index creation"),
)
.await
.unwrap();
}
// Setup the routing
let bare_routes = Router::new()
.route("/", get(handler::index))
.nest("/.me", handler::me::routes())
.nest("/api/", handler::api::routes())
.route("/quark", get(handler::quark::redirect_index_not_directory))
.nest("/quark/", handler::quark::routes())
.route("/{*path}", get(handler::static_path));
// Apply Extensions and state
let arc_wikarden = Arc::new(wikarden);
let routes = bare_routes
.layer(from_fn(handler::log_errors))
.layer(Extension(conf.settings().clone()))
.layer(Extension(arc_wikarden))
.with_state(state);
// Create our listener and start axum serving and routing
let addr = conf.listen_address();
tracing::info!("listening on {addr}");
let listener = TcpListener::bind(addr).await.unwrap();
axum::serve(listener, routes).await.unwrap();
}
|