ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/salaryman/trunk/src/server/main.rs
Revision: 14
Committed: Sat Jul 12 06:17:38 2025 UTC (2 months, 4 weeks ago) by yuzu
Original Path: trunk/src/smd/main.rs
File size: 4110 byte(s)
Log Message:
add start, stop, restart endpoints

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