mirror of
https://git.alemi.dev/guestbook.rs.git
synced 2024-12-19 02:54:52 +01:00
feat: default config action, pass more options
This commit is contained in:
parent
d8fa2173cb
commit
9659bee4e1
4 changed files with 67 additions and 27 deletions
|
@ -22,6 +22,7 @@ uuid = { version = "1.6.1", features = ["v4", "fast-rng"] }
|
||||||
md-5 = "0.10.6"
|
md-5 = "0.10.6"
|
||||||
# telegram provider
|
# telegram provider
|
||||||
teloxide = { version = "0.12.2", features = ["macros"], optional = true }
|
teloxide = { version = "0.12.2", features = ["macros"], optional = true }
|
||||||
|
toml = "0.8.8"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|
68
src/main.rs
68
src/main.rs
|
@ -1,13 +1,14 @@
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
use crate::{storage::JsonFileStorageStrategy, routes::Context, notifications::console::ConsoleTracingNotifier};
|
use crate::{storage::JsonFileStorageStrategy, routes::Context, notifications::console::ConsoleTracingNotifier, config::{Config, ConfigNotifier}};
|
||||||
|
|
||||||
mod notifications;
|
mod notifications;
|
||||||
|
|
||||||
mod routes;
|
mod routes;
|
||||||
mod model;
|
mod model;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
mod config;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Parser)]
|
#[derive(Debug, Clone, Parser)]
|
||||||
|
@ -18,6 +19,10 @@ struct CliArgs {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
action: CliAction,
|
action: CliAction,
|
||||||
|
|
||||||
|
/// connection string of storage database
|
||||||
|
#[arg(long, default_value = "./storage.json")] // file://./guestbook.db
|
||||||
|
db: String,
|
||||||
|
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
/// increase log verbosity to DEBUG level
|
/// increase log verbosity to DEBUG level
|
||||||
debug: bool,
|
debug: bool,
|
||||||
|
@ -25,24 +30,19 @@ struct CliArgs {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Subcommand)]
|
#[derive(Debug, Clone, Subcommand)]
|
||||||
enum CliAction {
|
enum CliAction {
|
||||||
|
/// serve guestbook api
|
||||||
Serve {
|
Serve {
|
||||||
#[arg(long, short, default_value = "127.0.0.1:37812")]
|
#[arg(default_value = "127.0.0.1:37812")]
|
||||||
/// host to bind onto
|
/// host to bind onto
|
||||||
addr: String,
|
addr: String,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long, short)]
|
||||||
/// force public field content
|
/// path to config file for overrides and notifiers
|
||||||
public: Option<bool>,
|
config: Option<String>,
|
||||||
|
},
|
||||||
|
|
||||||
#[arg(long)]
|
/// print a sample configuration, redirect to file and customize
|
||||||
/// force author field content
|
Default,
|
||||||
author: Option<String>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CliServeOverrides {
|
|
||||||
author: Option<String>,
|
|
||||||
public: Option<bool>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -51,20 +51,46 @@ async fn main() {
|
||||||
|
|
||||||
tracing_subscriber::fmt::fmt()
|
tracing_subscriber::fmt::fmt()
|
||||||
.with_max_level(if args.debug { tracing::Level::DEBUG } else { tracing::Level::INFO })
|
.with_max_level(if args.debug { tracing::Level::DEBUG } else { tracing::Level::INFO })
|
||||||
.pretty()
|
.init();
|
||||||
.finish();
|
|
||||||
|
// TODO more (and better) storage solutions! sqlx to the rescue...
|
||||||
|
let storage = Box::new(JsonFileStorageStrategy::new(&args.db));
|
||||||
|
|
||||||
match args.action {
|
match args.action {
|
||||||
CliAction::Serve { addr, public, author } => {
|
CliAction::Default => {
|
||||||
|
let mut cfg = Config::default();
|
||||||
|
cfg.notifiers.push(ConfigNotifier::ConsoleNotifier);
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
|
cfg.notifiers.push(ConfigNotifier::TelegramNotifier { token: "asd".into(), chat_id: -1 });
|
||||||
|
println!("{}", toml::to_string(&cfg).unwrap());
|
||||||
|
},
|
||||||
|
CliAction::Serve { addr, config } => {
|
||||||
let addr : SocketAddr = addr.parse().expect("invalid host provided");
|
let addr : SocketAddr = addr.parse().expect("invalid host provided");
|
||||||
|
|
||||||
let storage = Box::new(JsonFileStorageStrategy::new("./storage.json"));
|
let config = match config {
|
||||||
|
None => Config::default(),
|
||||||
|
Some(path) => {
|
||||||
|
let cfg_file = std::fs::read_to_string(path).unwrap();
|
||||||
|
toml::from_str(&cfg_file).unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let overrides = CliServeOverrides { author, public };
|
let mut state = Context::new(storage, config.overrides);
|
||||||
|
|
||||||
let mut state = Context::new(storage, overrides);
|
|
||||||
|
|
||||||
|
for notifier in config.notifiers {
|
||||||
|
match notifier {
|
||||||
|
ConfigNotifier::ConsoleNotifier => {
|
||||||
state.register(Box::new(ConsoleTracingNotifier {}));
|
state.register(Box::new(ConsoleTracingNotifier {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
#[cfg(feature = "telegram")]
|
||||||
|
ConfigNotifier::TelegramNotifier { token, chat_id } => {
|
||||||
|
state.register(Box::new(
|
||||||
|
notifications::telegram::TGNotifier::new(&token, chat_id)
|
||||||
|
));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let router = routes::create_router_with_app_routes(state);
|
let router = routes::create_router_with_app_routes(state);
|
||||||
|
|
||||||
|
|
19
src/model.rs
19
src/model.rs
|
@ -3,6 +3,8 @@ use serde::{Serialize, Deserialize};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::config::ConfigOverrides;
|
||||||
|
|
||||||
const AUTHOR_MAX_CHARS: usize = 25;
|
const AUTHOR_MAX_CHARS: usize = 25;
|
||||||
const CONTACT_MAX_CHARS: usize = 50;
|
const CONTACT_MAX_CHARS: usize = 50;
|
||||||
const BODY_MAX_CHARS: usize = 4096;
|
const BODY_MAX_CHARS: usize = 4096;
|
||||||
|
@ -70,6 +72,10 @@ pub struct PageInsertion {
|
||||||
pub contact: Option<String>,
|
pub contact: Option<String>,
|
||||||
|
|
||||||
pub body: String,
|
pub body: String,
|
||||||
|
|
||||||
|
pub public: Option<bool>,
|
||||||
|
|
||||||
|
pub date: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PageInsertion {
|
impl PageInsertion {
|
||||||
|
@ -79,15 +85,15 @@ impl PageInsertion {
|
||||||
self.body = html_escape::encode_safe(&self.body.chars().take(BODY_MAX_CHARS).collect::<String>()).to_string();
|
self.body = html_escape::encode_safe(&self.body.chars().take(BODY_MAX_CHARS).collect::<String>()).to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert(mut self, overrides: &crate::CliServeOverrides) -> Page {
|
pub fn convert(mut self, overrides: &ConfigOverrides) -> Page {
|
||||||
self.sanitize();
|
self.sanitize();
|
||||||
|
|
||||||
let mut page = Page {
|
let mut page = Page {
|
||||||
author: self.author.unwrap_or("".into()),
|
author: self.author.unwrap_or("".into()),
|
||||||
contact: self.contact,
|
contact: self.contact,
|
||||||
body: self.body,
|
body: self.body,
|
||||||
date: Utc::now(),
|
date: self.date.unwrap_or(Utc::now()),
|
||||||
public: true,
|
public: self.public.unwrap_or(true),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(author) = &overrides.author {
|
if let Some(author) = &overrides.author {
|
||||||
|
@ -96,6 +102,13 @@ impl PageInsertion {
|
||||||
if let Some(public) = overrides.public {
|
if let Some(public) = overrides.public {
|
||||||
page.public = public;
|
page.public = public;
|
||||||
}
|
}
|
||||||
|
if let Some(date) = &overrides.date {
|
||||||
|
if date.to_lowercase() == "now" {
|
||||||
|
page.date = Utc::now();
|
||||||
|
} else {
|
||||||
|
page.date = DateTime::parse_from_rfc3339(date).unwrap().into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
page
|
page
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use axum::{Json, Form, Router, routing::{put, post, get}, extract::{State, Query}, response::Redirect};
|
use axum::{Json, Form, Router, routing::{put, post, get}, extract::{State, Query}, response::Redirect};
|
||||||
|
|
||||||
use crate::{notifications::NotificationProcessor, model::{Page, PageOptions, PageInsertion}, storage::StorageStrategy, CliServeOverrides};
|
use crate::{notifications::NotificationProcessor, model::{Page, PageOptions, PageInsertion}, storage::StorageStrategy, config::ConfigOverrides};
|
||||||
|
|
||||||
pub fn create_router_with_app_routes(state: Context) -> Router {
|
pub fn create_router_with_app_routes(state: Context) -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
|
@ -15,11 +15,11 @@ pub fn create_router_with_app_routes(state: Context) -> Router {
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
providers: Vec<Box<dyn NotificationProcessor<Page>>>,
|
providers: Vec<Box<dyn NotificationProcessor<Page>>>,
|
||||||
storage: Box<dyn StorageStrategy<Page>>,
|
storage: Box<dyn StorageStrategy<Page>>,
|
||||||
overrides: CliServeOverrides,
|
overrides: ConfigOverrides,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
pub fn new(storage: Box<dyn StorageStrategy<Page>>, overrides: CliServeOverrides) -> Self {
|
pub fn new(storage: Box<dyn StorageStrategy<Page>>, overrides: ConfigOverrides) -> Self {
|
||||||
Context { providers: Vec::new(), storage, overrides }
|
Context { providers: Vec::new(), storage, overrides }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue