A PLC Mirror written in Rust
1use deadpool_postgres::{Manager, Object, Pool};
2use dropshot::{ApiDescription, ConfigLogging, ConfigLoggingLevel, HttpError};
3use eyre::Context;
4use slog::Logger;
5use std::env::var;
6use std::str::FromStr;
7use tokio_postgres::{Config, NoTls};
8
9mod api;
10mod db;
11pub mod import;
12mod types;
13mod utils;
14
15#[derive(Clone)]
16pub struct ApiContext {
17 pub pool: Pool,
18}
19
20impl ApiContext {
21 pub async fn get_conn(&self) -> Result<Object, HttpError> {
22 self.pool
23 .get()
24 .await
25 .map_err(|err| HttpError::for_internal_error(err.to_string()))
26 }
27}
28
29pub fn create_logger() -> eyre::Result<Logger> {
30 let log = ConfigLogging::StderrTerminal {
31 level: ConfigLoggingLevel::Info,
32 }
33 .to_logger("plc-mirror")?;
34
35 Ok(log)
36}
37
38pub fn create_api() -> eyre::Result<ApiDescription<ApiContext>> {
39 let mut api_desc = ApiDescription::new();
40
41 api_desc.register(api::get_plc_op_log)?;
42 api_desc.register(api::get_plc_audit_log)?;
43 api_desc.register(api::get_last_op)?;
44 api_desc.register(api::resolve_did)?;
45 api_desc.register(api::index)?;
46
47 Ok(api_desc)
48}
49
50pub async fn connect_db() -> eyre::Result<Pool> {
51 let db_uri = var("PLC_DB_URI").wrap_err("PLC_DB_URI missing")?;
52
53 let cfg = Config::from_str(&db_uri)?;
54 let mgr = Manager::from_config(cfg, NoTls, Default::default());
55 let pool = Pool::builder(mgr).build()?;
56
57 // run the init script
58 let init_conn = pool.get().await?;
59 init_conn.simple_query(include_str!("sql/init.sql")).await?;
60
61 Ok(pool)
62}
63
64pub async fn get_start_after(pool: &Pool) -> eyre::Result<Option<String>> {
65 if let Some(env_start_after) = var("PLC_START_AFTER").ok() {
66 return Ok(Some(env_start_after));
67 }
68
69 let obj = pool.get().await?;
70 let last_ts = db::get_last_operation_ts(&obj)
71 .await?
72 .map(|v| v.to_rfc3339_opts(chrono::SecondsFormat::Millis, true));
73
74 Ok(last_ts)
75}