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

# User Rev Content
1 yuzu 13 mod context;
2 yuzu 11 mod endpoints;
3    
4 yuzu 9 use clap::Parser;
5 yuzu 14 use dropshot::{ApiDescription, ConfigDropshot, ConfigLogging, ConfigLoggingLevel, ServerBuilder};
6 yuzu 12 use salaryman::service::{Service, ServiceConf};
7 yuzu 13 use schemars::JsonSchema;
8 yuzu 9 use serde::{Deserialize, Serialize};
9 yuzu 15 use tokio::{fs::read_to_string, sync::RwLock};
10 yuzu 9
11 yuzu 14 use std::{
12     net::{IpAddr, SocketAddr},
13     path::PathBuf,
14     sync::Arc,
15     };
16 yuzu 9
17 yuzu 14 use crate::context::{SalarymanDContext, SalarymanService};
18     use crate::endpoints::{
19 yuzu 15 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 yuzu 14 endpoint_start_service, endpoint_stop_service,
22     };
23 yuzu 13
24 yuzu 9 #[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 yuzu 13 #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)]
54     pub struct User {
55     pub username: String,
56     pub token: String,
57 yuzu 9 }
58    
59 yuzu 13 #[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 yuzu 11 async fn load_config(file: &PathBuf) -> Result<Config, Box<dyn std::error::Error>> {
78 yuzu 9 let s: String = match read_to_string(file).await {
79     Ok(s) => s,
80 yuzu 12 Err(_) => {
81     return Err(Box::new(std::io::Error::new(
82     std::io::ErrorKind::NotFound,
83     "cannot find config file",
84     )));
85     }
86 yuzu 9 };
87     match toml::from_str(s.as_str()) {
88 yuzu 11 Ok(c) => Ok(c),
89 yuzu 12 Err(_) => Err(Box::new(std::io::Error::new(
90     std::io::ErrorKind::Other,
91     "unable to parse config file",
92     ))),
93 yuzu 9 }
94     }
95    
96     #[tokio::main]
97     async fn main() -> Result<(), Box<dyn std::error::Error>> {
98     let args = Args::parse();
99 yuzu 11 let conf: Config = load_config(&args.config).await?;
100 yuzu 14 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 yuzu 15 let services: RwLock<Vec<Arc<SalarymanService>>> = RwLock::new(Vec::new());
112 yuzu 9 for i in 0..conf.service.len() {
113 yuzu 15 let mut lock = services.write().await;
114     lock.push(Arc::new(SalarymanService::from_parts(
115 yuzu 14 conf.service[i].clone(),
116 yuzu 15 Arc::new(RwLock::new(Service::from_conf(&conf.service[i]))),
117 yuzu 14 )));
118 yuzu 15 drop(lock);
119 yuzu 14 }
120 yuzu 15 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 yuzu 9 }
129     }
130 yuzu 15 drop(lock);
131 yuzu 13 let log_conf = ConfigLogging::StderrTerminal {
132     level: ConfigLoggingLevel::Info,
133     };
134     let log = log_conf.to_logger("smd")?;
135 yuzu 15 let ctx = Arc::new(SalarymanDContext::from_parts(
136     services,
137     args.config,
138     Arc::new(RwLock::new(conf)),
139     ));
140 yuzu 14 let config = ConfigDropshot {
141     bind_address: bind,
142     ..Default::default()
143     };
144 yuzu 13 let mut api = ApiDescription::new();
145 yuzu 14 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 yuzu 15 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 yuzu 14 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 yuzu 13 server.await?;
161 yuzu 9 Ok(())
162     }