ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/salaryman/trunk/src/server/main.rs
Revision: 15
Committed: Sat Jul 12 22:17:26 2025 UTC (2 months, 3 weeks ago) by yuzu
File size: 4582 byte(s)
Log Message:
add additional endpoints; change out mutexes for rwlocks

File Contents

# Content
1 mod context;
2 mod endpoints;
3
4 use clap::Parser;
5 use dropshot::{ApiDescription, ConfigDropshot, ConfigLogging, ConfigLoggingLevel, ServerBuilder};
6 use salaryman::service::{Service, ServiceConf};
7 use schemars::JsonSchema;
8 use serde::{Deserialize, Serialize};
9 use tokio::{fs::read_to_string, sync::RwLock};
10
11 use std::{
12 net::{IpAddr, SocketAddr},
13 path::PathBuf,
14 sync::Arc,
15 };
16
17 use crate::context::{SalarymanDContext, SalarymanService};
18 use crate::endpoints::{
19 endpoint_get_config, endpoint_get_config_save, endpoint_get_service, endpoint_get_services,
20 endpoint_post_service, endpoint_post_stdin, endpoint_put_config, endpoint_restart_service,
21 endpoint_start_service, endpoint_stop_service,
22 };
23
24 #[derive(Parser, Debug)]
25 #[command(version, about, long_about = None)]
26 struct Args {
27 #[arg(
28 short,
29 long,
30 value_name = "FILE",
31 help = "config file override",
32 default_value = "salaryman.toml"
33 )]
34 config: PathBuf,
35 #[arg(
36 short,
37 long,
38 value_name = "ADDR",
39 help = "IP address to bind API to",
40 default_value = "127.0.0.1"
41 )]
42 address: IpAddr,
43 #[arg(
44 short,
45 long,
46 value_name = "PORT",
47 help = "TCP Port to bind API to",
48 default_value = "3080"
49 )]
50 port: u16,
51 }
52
53 #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)]
54 pub struct User {
55 pub username: String,
56 pub token: String,
57 }
58
59 #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)]
60 pub struct Config {
61 pub address: Option<IpAddr>,
62 pub port: Option<u16>,
63 pub user: Vec<User>,
64 pub service: Vec<ServiceConf>,
65 }
66 impl Config {
67 pub fn new() -> Self {
68 Self {
69 address: None,
70 port: None,
71 user: Vec::new(),
72 service: Vec::new(),
73 }
74 }
75 }
76
77 async fn load_config(file: &PathBuf) -> Result<Config, Box<dyn std::error::Error>> {
78 let s: String = match read_to_string(file).await {
79 Ok(s) => s,
80 Err(_) => {
81 return Err(Box::new(std::io::Error::new(
82 std::io::ErrorKind::NotFound,
83 "cannot find config file",
84 )));
85 }
86 };
87 match toml::from_str(s.as_str()) {
88 Ok(c) => Ok(c),
89 Err(_) => Err(Box::new(std::io::Error::new(
90 std::io::ErrorKind::Other,
91 "unable to parse config file",
92 ))),
93 }
94 }
95
96 #[tokio::main]
97 async fn main() -> Result<(), Box<dyn std::error::Error>> {
98 let args = Args::parse();
99 let conf: Config = load_config(&args.config).await?;
100 let addr = if let Some(addr) = conf.address {
101 addr
102 } else {
103 args.address
104 };
105 let port = if let Some(port) = conf.port {
106 port
107 } else {
108 args.port
109 };
110 let bind = SocketAddr::new(addr, port);
111 let services: RwLock<Vec<Arc<SalarymanService>>> = RwLock::new(Vec::new());
112 for i in 0..conf.service.len() {
113 let mut lock = services.write().await;
114 lock.push(Arc::new(SalarymanService::from_parts(
115 conf.service[i].clone(),
116 Arc::new(RwLock::new(Service::from_conf(&conf.service[i]))),
117 )));
118 drop(lock);
119 }
120 let lock = services.write().await;
121 for i in 0..lock.len() {
122 if lock[i].config.autostart {
123 let mut l = lock[i].service.write().await;
124 l.start().await?;
125 l.scan_stdout().await?;
126 l.scan_stderr().await?;
127 drop(l);
128 }
129 }
130 drop(lock);
131 let log_conf = ConfigLogging::StderrTerminal {
132 level: ConfigLoggingLevel::Info,
133 };
134 let log = log_conf.to_logger("smd")?;
135 let ctx = Arc::new(SalarymanDContext::from_parts(
136 services,
137 args.config,
138 Arc::new(RwLock::new(conf)),
139 ));
140 let config = ConfigDropshot {
141 bind_address: bind,
142 ..Default::default()
143 };
144 let mut api = ApiDescription::new();
145 api.register(endpoint_get_services)?;
146 api.register(endpoint_get_service)?;
147 api.register(endpoint_start_service)?;
148 api.register(endpoint_stop_service)?;
149 api.register(endpoint_restart_service)?;
150 api.register(endpoint_post_stdin)?;
151 api.register(endpoint_post_service)?;
152 api.register(endpoint_get_config)?;
153 api.register(endpoint_put_config)?;
154 api.register(endpoint_get_config_save)?;
155 api.openapi("Salaryman", semver::Version::new(1, 0, 0))
156 .write(&mut std::io::stdout())?;
157 let server = ServerBuilder::new(api, ctx.clone(), log)
158 .config(config)
159 .start()?;
160 server.await?;
161 Ok(())
162 }