1
0

Add option to not create new clients

This commit is contained in:
Rokas Puzonas 2024-06-26 00:22:07 +03:00
parent 1ed97d6b83
commit ed9d885bed
7 changed files with 137 additions and 43 deletions

View File

@ -52,14 +52,22 @@ pub(crate) async fn service(
let mut txn = server_state.storage.txn().map_err(failure_to_ise)?;
// get, or create, the client
let client = match txn.get_client(client_id).map_err(failure_to_ise)? {
Some(client) => client,
None => {
txn.new_client(client_id, NIL_VERSION_ID)
.map_err(failure_to_ise)?;
txn.get_client(client_id).map_err(failure_to_ise)?.unwrap()
}
};
let client;
if server_state.config.create_client_on_request {
client = match txn.get_client(client_id).map_err(failure_to_ise)? {
Some(client) => client,
None => {
txn.new_client(client_id, NIL_VERSION_ID)
.map_err(failure_to_ise)?;
txn.get_client(client_id).map_err(failure_to_ise)?.unwrap()
}
};
} else {
client = txn
.get_client(client_id)
.map_err(failure_to_ise)?
.ok_or_else(|| error::ErrorNotFound("no such client"))?;
}
add_snapshot(
txn,

View File

@ -60,14 +60,22 @@ pub(crate) async fn service(
let mut txn = server_state.storage.txn().map_err(failure_to_ise)?;
// get, or create, the client
let client = match txn.get_client(client_id).map_err(failure_to_ise)? {
Some(client) => client,
None => {
txn.new_client(client_id, NIL_VERSION_ID)
.map_err(failure_to_ise)?;
txn.get_client(client_id).map_err(failure_to_ise)?.unwrap()
}
};
let client;
if server_state.config.create_client_on_request {
client = match txn.get_client(client_id).map_err(failure_to_ise)? {
Some(client) => client,
None => {
txn.new_client(client_id, NIL_VERSION_ID)
.map_err(failure_to_ise)?;
txn.get_client(client_id).map_err(failure_to_ise)?.unwrap()
}
};
} else {
client = txn
.get_client(client_id)
.map_err(failure_to_ise)?
.ok_or_else(|| error::ErrorNotFound("no such client"))?;
}
let (result, snap_urgency) = add_version(
txn,

View File

@ -2,59 +2,102 @@
use actix_web::{middleware::Logger, App, HttpServer};
use clap::{arg, builder::ValueParser, value_parser, Command};
use uuid::Uuid;
use std::ffi::OsString;
use taskchampion_sync_server::storage::SqliteStorage;
use std::path::{Path, PathBuf};
use taskchampion_sync_server::storage::{SqliteStorage, Storage};
use taskchampion_sync_server::{Server, ServerConfig};
#[actix_web::main]
async fn main() -> anyhow::Result<()> {
env_logger::init();
let defaults = ServerConfig::default();
let default_snapshot_versions = defaults.snapshot_versions.to_string();
let default_snapshot_days = defaults.snapshot_days.to_string();
let config_arg = arg!(-c --config <PATH> "Config file")
.default_value(PathBuf::from(&defaults.data_dir).join("config").to_string_lossy().to_string());
let matches = Command::new("taskchampion-sync-server")
.version(env!("CARGO_PKG_VERSION"))
.about("Server for TaskChampion")
.arg(
arg!(-p --port <PORT> "Port on which to serve")
.help("Port on which to serve")
.value_parser(value_parser!(usize))
.default_value("8080"),
)
.arg(&config_arg)
.arg(
arg!(-d --"data-dir" <DIR> "Directory in which to store data")
.value_parser(ValueParser::os_string())
.default_value("/var/lib/taskchampion-sync-server"),
.default_value(&defaults.data_dir),
)
.subcommand(
Command::new("clients")
.subcommand(Command::new("list"))
.subcommand(Command::new("add").arg(arg!(<CLIENT_ID>)))
.subcommand(Command::new("remove").arg(arg!(<CLIENT_ID>)))
)
.arg(
arg!(-p --port <PORT> "Port on which to serve")
.value_parser(value_parser!(u16))
.default_value(defaults.port.to_string()),
)
.arg(
arg!(--"snapshot-versions" <NUM> "Target number of versions between snapshots")
.value_parser(value_parser!(u32))
.default_value(default_snapshot_versions),
.default_value(defaults.snapshot_versions.to_string()),
)
.arg(
arg!(--"snapshot-days" <NUM> "Target number of days between snapshots")
.value_parser(value_parser!(i64))
.default_value(default_snapshot_days),
.default_value(defaults.snapshot_days.to_string()),
)
.get_matches();
let data_dir: &OsString = matches.get_one("data-dir").unwrap();
let port: usize = *matches.get_one("port").unwrap();
let snapshot_versions: u32 = *matches.get_one("snapshot-versions").unwrap();
let snapshot_days: i64 = *matches.get_one("snapshot-days").unwrap();
// TODO: let config_path: &String = matches.get_one("config").unwrap();
let config = ServerConfig::from_args(snapshot_days, snapshot_versions)?;
let server = Server::new(config, Box::new(SqliteStorage::new(data_dir)?));
let storage = SqliteStorage::new(data_dir)?;
match matches.subcommand() {
Some(("clients", sub_matches)) => {
match sub_matches.subcommand() {
Some(("add", sub_matches)) => {
let client_id = sub_matches.get_one::<String>("CLIENT_ID").unwrap();
let client_id = Uuid::parse_str(client_id)?;
let mut t = storage.txn()?;
t.new_client(client_id, Uuid::nil())?
},
Some(("remove", _sub_matches)) => {
todo!()
},
_ => {
let mut t = storage.txn()?;
for client in t.list_clients()? {
print!("{client}\n");
}
}
}
},
None => {
let port: u16 = *matches.get_one("port").unwrap();
let snapshot_versions: u32 = *matches.get_one("snapshot-versions").unwrap();
let snapshot_days: i64 = *matches.get_one("snapshot-days").unwrap();
let data_dir_owned = data_dir.to_string_lossy().into();
let mut config = ServerConfig::from_args(snapshot_days, snapshot_versions, port, data_dir_owned)?;
config.create_client_on_request = false;
let server = Server::new(config, Box::new(storage));
log::warn!("Serving on port {}", port);
HttpServer::new(move || {
App::new()
.wrap(Logger::default())
.configure(|cfg| server.config(cfg))
})
.bind(format!("0.0.0.0:{}", port))?
.run()
.await?;
},
_ => unreachable!()
};
log::warn!("Serving on port {}", port);
HttpServer::new(move || {
App::new()
.wrap(Logger::default())
.configure(|cfg| server.config(cfg))
})
.bind(format!("0.0.0.0:{}", port))?
.run()
.await?;
Ok(())
}

View File

@ -24,6 +24,12 @@ pub struct ServerConfig {
/// Target number of versions between snapshots.
pub snapshot_versions: u32,
pub port: u16,
pub data_dir: String,
pub create_client_on_request: bool,
}
impl Default for ServerConfig {
@ -31,15 +37,21 @@ impl Default for ServerConfig {
ServerConfig {
snapshot_days: 14,
snapshot_versions: 100,
port: 8080,
data_dir: String::from("/var/lib/taskchampion-sync-server"),
create_client_on_request: true
}
}
}
impl ServerConfig {
pub fn from_args(snapshot_days: i64, snapshot_versions: u32) -> anyhow::Result<ServerConfig> {
pub fn from_args(snapshot_days: i64, snapshot_versions: u32, port: u16, data_dir: String) -> anyhow::Result<ServerConfig> {
Ok(ServerConfig {
snapshot_days,
snapshot_versions,
port,
data_dir,
create_client_on_request: false
})
}
}

View File

@ -46,6 +46,10 @@ impl<'a> StorageTxn for InnerTxn<'a> {
Ok(self.0.clients.get(&client_id).cloned())
}
fn list_clients(&mut self) -> anyhow::Result<Vec<Uuid>> {
Ok(self.0.clients.keys().map(|key| key.clone()).collect())
}
fn new_client(&mut self, client_id: Uuid, latest_version_id: Uuid) -> anyhow::Result<()> {
if self.0.clients.get(&client_id).is_some() {
return Err(anyhow::anyhow!("Client {} already exists", client_id));

View File

@ -41,6 +41,9 @@ pub trait StorageTxn {
/// Get information about the given client
fn get_client(&mut self, client_id: Uuid) -> anyhow::Result<Option<Client>>;
/// Get information about the given client
fn list_clients(&mut self) -> anyhow::Result<Vec<Uuid>>;
/// Create a new client with the given latest_version_id
fn new_client(&mut self, client_id: Uuid, latest_version_id: Uuid) -> anyhow::Result<()>;

View File

@ -166,6 +166,22 @@ impl StorageTxn for Txn {
Ok(result)
}
fn list_clients(&mut self) -> anyhow::Result<Vec<Uuid>> {
let t = self.get_txn()?;
let mut stmt = t.prepare("SELECT client_id FROM clients")?;
let rows = stmt
.query_map([], |r| r.get::<usize, StoredUuid>(0))
.context("Error getting clients")?;
let mut result = Vec::new();
for client_id_result in rows {
result.push(client_id_result?.0);
}
Ok(result)
}
fn new_client(&mut self, client_id: Uuid, latest_version_id: Uuid) -> anyhow::Result<()> {
let t = self.get_txn()?;