1 use std
::collections
::HashMap
;
2 use std
::path
::PathBuf
;
3 use std
::time
::SystemTime
;
5 use std
::sync
::{Arc, Mutex, RwLock}
;
7 use anyhow
::{bail, Error, format_err}
;
9 use handlebars
::Handlebars
;
12 use proxmox
::api
::{ApiMethod, Router, RpcEnvironmentType}
;
13 use proxmox
::tools
::fs
::{create_path, CreateOptions}
;
15 use crate::{ApiAuth, FileLogger, FileLogOptions, CommandoSocket}
;
17 pub struct ApiConfig
{
19 router
: &'
static Router
,
20 aliases
: HashMap
<String
, PathBuf
>,
21 env_type
: RpcEnvironmentType
,
22 templates
: RwLock
<Handlebars
<'
static>>,
23 template_files
: RwLock
<HashMap
<String
, (SystemTime
, PathBuf
)>>,
24 request_log
: Option
<Arc
<Mutex
<FileLogger
>>>,
25 pub api_auth
: Arc
<dyn ApiAuth
+ Send
+ Sync
>,
29 pub fn new
<B
: Into
<PathBuf
>>(
31 router
: &'
static Router
,
32 env_type
: RpcEnvironmentType
,
33 api_auth
: Arc
<dyn ApiAuth
+ Send
+ Sync
>,
34 ) -> Result
<Self, Error
> {
36 basedir
: basedir
.into(),
38 aliases
: HashMap
::new(),
40 templates
: RwLock
::new(Handlebars
::new()),
41 template_files
: RwLock
::new(HashMap
::new()),
51 uri_param
: &mut HashMap
<String
, String
>,
52 ) -> Option
<&'
static ApiMethod
> {
54 self.router
.find_method(components
, method
, uri_param
)
57 pub fn find_alias(&self, components
: &[&str]) -> PathBuf
{
59 let mut prefix
= String
::new();
60 let mut filename
= self.basedir
.clone();
61 let comp_len
= components
.len();
63 prefix
.push_str(components
[0]);
64 if let Some(subdir
) = self.aliases
.get(&prefix
) {
65 filename
.push(subdir
);
66 components
.iter().skip(1).for_each(|comp
| filename
.push(comp
));
68 components
.iter().for_each(|comp
| filename
.push(comp
));
74 pub fn add_alias
<S
, P
>(&mut self, alias
: S
, path
: P
)
75 where S
: Into
<String
>,
78 self.aliases
.insert(alias
.into(), path
.into());
81 pub fn env_type(&self) -> RpcEnvironmentType
{
85 pub fn register_template
<P
>(&self, name
: &str, path
: P
) -> Result
<(), Error
>
89 if self.template_files
.read().unwrap().contains_key(name
) {
90 bail
!("template already registered");
93 let path
: PathBuf
= path
.into();
94 let metadata
= metadata(&path
)?
;
95 let mtime
= metadata
.modified()?
;
97 self.templates
.write().unwrap().register_template_file(name
, &path
)?
;
98 self.template_files
.write().unwrap().insert(name
.to_string(), (mtime
, path
));
103 /// Checks if the template was modified since the last rendering
104 /// if yes, it loads a the new version of the template
105 pub fn render_template
<T
>(&self, name
: &str, data
: &T
) -> Result
<String
, Error
>
112 let template_files
= self.template_files
.read().unwrap();
113 let (old_mtime
, old_path
) = template_files
.get(name
).ok_or_else(|| format_err
!("template not found"))?
;
115 mtime
= metadata(old_path
)?
.modified()?
;
116 if mtime
<= *old_mtime
{
117 return self.templates
.read().unwrap().render(name
, data
).map_err(|err
| format_err
!("{}", err
));
119 path
= old_path
.to_path_buf();
123 let mut template_files
= self.template_files
.write().unwrap();
124 let mut templates
= self.templates
.write().unwrap();
126 templates
.register_template_file(name
, &path
)?
;
127 template_files
.insert(name
.to_string(), (mtime
, path
));
129 templates
.render(name
, data
).map_err(|err
| format_err
!("{}", err
))
133 pub fn enable_file_log
<P
>(
136 dir_opts
: Option
<CreateOptions
>,
137 file_opts
: Option
<CreateOptions
>,
138 commando_sock
: &mut CommandoSocket
,
139 ) -> Result
<(), Error
>
143 let path
: PathBuf
= path
.into();
144 if let Some(base
) = path
.parent() {
146 create_path(base
, None
, dir_opts
).map_err(|err
| format_err
!("{}", err
))?
;
150 let logger_options
= FileLogOptions
{
152 file_opts
: file_opts
.unwrap_or(CreateOptions
::default()),
155 let request_log
= Arc
::new(Mutex
::new(FileLogger
::new(&path
, logger_options
)?
));
156 self.request_log
= Some(Arc
::clone(&request_log
));
158 commando_sock
.register_command("api-access-log-reopen".into(), move |_args
| {
159 println
!("re-opening log file");
160 request_log
.lock().unwrap().reopen()?
;
161 Ok(serde_json
::Value
::Null
)
167 pub fn get_file_log(&self) -> Option
<&Arc
<Mutex
<FileLogger
>>> {
168 self.request_log
.as_ref()