81 lines
2.6 KiB
Rust
81 lines
2.6 KiB
Rust
//! `figcraft-server`: FigCraft's standalone collaboration server (milestone M9, step 5 of
|
|
//! `docs/collab-protocol.md`; running it: `docs/server.md`).
|
|
//!
|
|
//! It keeps many files in a data directory ([`store`]), each with share links that let people
|
|
//! edit or only view, and serves their live sessions through the same hub the desktop app hosts
|
|
//! with ([`figcraft_collab_net::hub`]). Files persist as a snapshot plus an append-only log of what
|
|
//! changed ([`disk`]), compacted into a new snapshot from time to time, so a restart loses nothing
|
|
//! that was confirmed and clients resume where they were. There are no user accounts yet: share
|
|
//! links are the access, and the server key manages files.
|
|
|
|
pub mod cli;
|
|
pub mod config;
|
|
pub mod disk;
|
|
pub mod store;
|
|
|
|
use std::net::{SocketAddr, TcpListener};
|
|
|
|
use figcraft_collab_net::hub::Hosting;
|
|
|
|
use crate::config::Config;
|
|
use crate::store::Store;
|
|
|
|
/// A running server. Dropping it stops it: open files are saved, everyone is told the server is
|
|
/// restarting (their apps reconnect once it is back).
|
|
pub struct Running {
|
|
hosting: Hosting,
|
|
}
|
|
|
|
impl Running {
|
|
/// Where it listens.
|
|
pub fn addr(&self) -> SocketAddr {
|
|
self.hosting.addr()
|
|
}
|
|
|
|
/// Open connections.
|
|
pub fn connections(&self) -> usize {
|
|
self.hosting.connections()
|
|
}
|
|
|
|
/// Stop (see the type's docs).
|
|
pub fn stop(&mut self) {
|
|
self.hosting.stop();
|
|
}
|
|
}
|
|
|
|
/// Start serving `cfg.data` on `cfg.listen`. Files that can't be read are skipped and logged.
|
|
pub fn start(cfg: &Config) -> Result<Running, String> {
|
|
cfg.check()?;
|
|
let store = Store::open(&cfg.data, cfg.store())?;
|
|
for p in store.problems() {
|
|
log::warn!("{p}");
|
|
}
|
|
let files = store.len();
|
|
let addr = cfg.addr()?;
|
|
let listener = TcpListener::bind(addr).map_err(|e| format!("can't listen on {addr}: {e}"))?;
|
|
let addr = listener.local_addr().map_err(|e| e.to_string())?;
|
|
cli::write_running(&cfg.data, addr)?;
|
|
let public = cli::public_endpoint(cfg, addr)?;
|
|
let hosting = Hosting::serve(listener, store, cfg.net()).map_err(|e| format!("can't start serving: {e}"))?;
|
|
log::info!(
|
|
"figcraft-server {}: {files} file{} in {}; listening on {addr}; links name {}",
|
|
env!("CARGO_PKG_VERSION"),
|
|
if files == 1 { "" } else { "s" },
|
|
cfg.data.display(),
|
|
public.url()
|
|
);
|
|
if addr.ip().is_loopback() {
|
|
log::info!("only this machine can connect: listen on 0.0.0.0:{} (or behind a reverse proxy) to share", addr.port());
|
|
}
|
|
Ok(Running { hosting })
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod cli_tests;
|
|
#[cfg(test)]
|
|
mod server_tests;
|
|
#[cfg(test)]
|
|
mod store_tests;
|
|
#[cfg(test)]
|
|
mod test_util;
|