1use crate::log_store::LogEntry;
2use serde::Serialize;
3
4#[derive(Serialize)]
5pub struct JsonListEntry {
6 pub id: String,
7 pub namespace: String,
8 pub name: String,
9 pub pid: Option<u32>,
10 pub status: String,
11 pub oneshot: bool,
15 pub disabled: bool,
16 pub available: bool,
17 pub proxy_url: Option<String>,
19 pub url: Option<String>,
21 #[serde(skip_serializing_if = "Option::is_none")]
24 pub proxy_tls: Option<String>,
25 pub error: Option<String>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub active_port: Option<u16>,
28 pub port: Vec<u16>,
29}
30
31#[derive(Serialize)]
32pub struct JsonStatusEntry {
33 pub id: String,
34 pub namespace: String,
35 pub name: String,
36 pub pid: Option<u32>,
37 pub status: String,
38 pub oneshot: bool,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub active_port: Option<u16>,
42 pub port: Vec<u16>,
43 pub proxy_url: Option<String>,
45 pub url: Option<String>,
47 #[serde(skip_serializing_if = "Option::is_none")]
50 pub proxy_tls: Option<String>,
51}
52
53#[derive(Serialize)]
54pub struct JsonLogEntry {
55 #[serde(skip_serializing_if = "is_zero_id")]
59 pub id: i64,
60 pub timestamp: String,
61 pub daemon_id: String,
62 pub message: String,
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub level: Option<String>,
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub msg: Option<String>,
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub logger: Option<String>,
69 #[serde(skip_serializing_if = "Option::is_none")]
72 pub fields: Option<serde_json::Value>,
73}
74
75fn is_zero_id(id: &i64) -> bool {
76 *id == 0
77}
78
79impl From<LogEntry> for JsonLogEntry {
80 fn from(e: LogEntry) -> Self {
81 let fields = e
82 .fields_json
83 .as_deref()
84 .and_then(|s| serde_json::from_str(s).ok());
85 JsonLogEntry {
86 id: e.id,
87 timestamp: e.timestamp.format("%Y-%m-%d %H:%M:%S").to_string(),
88 daemon_id: e.daemon_id,
89 message: console::strip_ansi_codes(&e.message).to_string(),
90 level: e.level,
91 msg: e.msg,
92 logger: e.logger,
93 fields,
94 }
95 }
96}
97
98#[derive(Serialize)]
99pub struct JsonDaemonConfigEntry {
100 pub id: String,
101 pub run: String,
102}
103
104#[derive(Serialize)]
105pub struct JsonProxyStatus {
106 pub enabled: bool,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub scheme: Option<String>,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub tld: Option<String>,
111 #[serde(skip_serializing_if = "Option::is_none")]
112 pub port: Option<u16>,
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub lan: Option<JsonLanInfo>,
115 #[serde(skip_serializing_if = "Option::is_none")]
116 pub tls_cert: Option<String>,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub trusted: Option<bool>,
119 pub slugs: Vec<JsonSlugEntry>,
120 #[serde(default, skip_serializing_if = "Vec::is_empty")]
122 pub projects: Vec<JsonProxyProject>,
123 #[serde(default, skip_serializing_if = "Vec::is_empty")]
126 pub conflicts: Vec<String>,
127}
128
129#[derive(Serialize)]
130pub struct JsonProxyProject {
131 pub project: String,
132 pub dir: String,
133 pub url: String,
135 pub daemons: Vec<JsonProxyHost>,
136 #[serde(skip_serializing_if = "Vec::is_empty")]
137 pub worktrees: Vec<JsonProxyWorktree>,
138}
139
140#[derive(Serialize)]
141pub struct JsonProxyWorktree {
142 pub worktree: String,
143 pub dir: String,
144 pub url: String,
146 pub daemons: Vec<JsonProxyHost>,
147}
148
149#[derive(Serialize)]
150pub struct JsonProxyHost {
151 pub daemon: String,
152 pub host: String,
154 pub url: String,
155 pub status: String,
156 #[serde(skip_serializing_if = "Option::is_none")]
157 pub port: Option<u16>,
158}
159
160#[derive(Serialize)]
161pub struct JsonLanInfo {
162 pub enabled: bool,
163 pub ip: String,
164}
165
166#[derive(Serialize)]
167pub struct JsonSlugEntry {
168 pub slug: String,
169 #[serde(skip_serializing_if = "Option::is_none")]
171 pub url: Option<String>,
172 pub dir: String,
173 pub daemon: String,
174 pub status: String,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub port: Option<u16>,
177}
178
179#[derive(Serialize)]
180pub struct JsonSupervisorStatus {
181 pub status: &'static str,
182 #[serde(skip_serializing_if = "Option::is_none")]
183 pub web_ui: Option<String>,
184 #[serde(skip_serializing_if = "Option::is_none")]
185 pub error: Option<String>,
186}
187
188#[derive(Serialize)]
189pub struct JsonSettingEntry {
190 pub key: String,
191 pub value: String,
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub default: Option<String>,
194 #[serde(skip_serializing_if = "Option::is_none")]
195 pub r#type: Option<&'static str>,
196 #[serde(skip_serializing_if = "Option::is_none")]
197 pub env_var: Option<&'static str>,
198 #[serde(skip_serializing_if = "Option::is_none")]
203 pub origin: Option<String>,
204}
205
206pub fn print_json<T: Serialize>(value: &T) -> crate::Result<()> {
207 let json = serde_json::to_string_pretty(value)
208 .map_err(|e| miette::miette!("failed to serialize JSON: {e}"))?;
209 println!("{json}");
210 Ok(())
211}