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 #[serde(skip_serializing_if = "Option::is_none")]
54 pub cron_schedule: Option<String>,
55 #[serde(skip_serializing_if = "Option::is_none")]
59 pub cron_last_run: Option<String>,
60 #[serde(skip_serializing_if = "Option::is_none")]
64 pub cron_next_run: Option<String>,
65}
66
67#[derive(Serialize)]
68pub struct JsonLogEntry {
69 #[serde(skip_serializing_if = "is_zero_id")]
73 pub id: i64,
74 pub timestamp: String,
75 pub daemon_id: String,
76 pub message: String,
77 #[serde(skip_serializing_if = "Option::is_none")]
78 pub level: Option<String>,
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub msg: Option<String>,
81 #[serde(skip_serializing_if = "Option::is_none")]
82 pub logger: Option<String>,
83 #[serde(skip_serializing_if = "Option::is_none")]
86 pub fields: Option<serde_json::Value>,
87}
88
89fn is_zero_id(id: &i64) -> bool {
90 *id == 0
91}
92
93impl From<LogEntry> for JsonLogEntry {
94 fn from(e: LogEntry) -> Self {
95 let fields = e
96 .fields_json
97 .as_deref()
98 .and_then(|s| serde_json::from_str(s).ok());
99 JsonLogEntry {
100 id: e.id,
101 timestamp: e.timestamp.format("%Y-%m-%d %H:%M:%S").to_string(),
102 daemon_id: e.daemon_id,
103 message: console::strip_ansi_codes(&e.message).to_string(),
104 level: e.level,
105 msg: e.msg,
106 logger: e.logger,
107 fields,
108 }
109 }
110}
111
112#[derive(Serialize)]
113pub struct JsonDaemonConfigEntry {
114 pub id: String,
115 pub run: crate::pitchfork_toml::RunCommand,
117}
118
119#[derive(Serialize)]
120pub struct JsonProxyStatus {
121 pub enabled: bool,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub scheme: Option<String>,
124 #[serde(skip_serializing_if = "Option::is_none")]
125 pub tld: Option<String>,
126 #[serde(skip_serializing_if = "Option::is_none")]
127 pub port: Option<u16>,
128 #[serde(skip_serializing_if = "Option::is_none")]
129 pub lan: Option<JsonLanInfo>,
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub tls_cert: Option<String>,
132 #[serde(skip_serializing_if = "Option::is_none")]
133 pub trusted: Option<bool>,
134 pub slugs: Vec<JsonSlugEntry>,
135 #[serde(default, skip_serializing_if = "Vec::is_empty")]
137 pub projects: Vec<JsonProxyProject>,
138 #[serde(default, skip_serializing_if = "Vec::is_empty")]
141 pub conflicts: Vec<String>,
142}
143
144#[derive(Serialize)]
145pub struct JsonProxyProject {
146 pub project: String,
147 pub dir: String,
148 pub url: String,
150 pub daemons: Vec<JsonProxyHost>,
151 #[serde(skip_serializing_if = "Vec::is_empty")]
152 pub worktrees: Vec<JsonProxyWorktree>,
153}
154
155#[derive(Serialize)]
156pub struct JsonProxyWorktree {
157 pub worktree: String,
158 pub dir: String,
159 pub url: String,
161 pub daemons: Vec<JsonProxyHost>,
162}
163
164#[derive(Serialize)]
165pub struct JsonProxyHost {
166 pub daemon: String,
167 pub host: String,
169 pub url: String,
170 pub status: String,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 pub port: Option<u16>,
173}
174
175#[derive(Serialize)]
176pub struct JsonLanInfo {
177 pub enabled: bool,
178 pub ip: String,
179}
180
181#[derive(Serialize)]
182pub struct JsonSlugEntry {
183 pub slug: String,
184 #[serde(skip_serializing_if = "Option::is_none")]
186 pub url: Option<String>,
187 pub dir: String,
188 pub daemon: String,
189 pub status: String,
190 #[serde(skip_serializing_if = "Option::is_none")]
191 pub port: Option<u16>,
192}
193
194#[derive(Serialize)]
195pub struct JsonSupervisorStatus {
196 pub status: &'static str,
197 #[serde(skip_serializing_if = "Option::is_none")]
198 pub web_ui: Option<String>,
199 #[serde(skip_serializing_if = "Option::is_none")]
200 pub error: Option<String>,
201}
202
203#[derive(Serialize)]
204pub struct JsonSettingEntry {
205 pub key: String,
206 pub value: String,
207 #[serde(skip_serializing_if = "Option::is_none")]
208 pub default: Option<String>,
209 #[serde(skip_serializing_if = "Option::is_none")]
210 pub r#type: Option<&'static str>,
211 #[serde(skip_serializing_if = "Option::is_none")]
212 pub env_var: Option<&'static str>,
213 #[serde(skip_serializing_if = "Option::is_none")]
218 pub origin: Option<String>,
219}
220
221pub fn print_json<T: Serialize>(value: &T) -> crate::Result<()> {
222 let json = serde_json::to_string_pretty(value)
223 .map_err(|e| miette::miette!("failed to serialize JSON: {e}"))?;
224 println!("{json}");
225 Ok(())
226}