1use std::sync::Arc;
2
3use axum::{
4 Router, middleware,
5 routing::{get, post},
6};
7
8#[cfg(feature = "scripting")]
9use crate::pages::script;
10#[cfg(feature = "scripting")]
11use crate::pages::template;
12use crate::{
13 AppState,
14 files::download_file,
15 handler::{
16 get_home_link, get_logout_link, get_me_handler, get_version, login_form_handler,
17 login_user_handler, logout_handler, refresh_access_token_handler, register_user_handler,
18 },
19 jwt_auth::{admin, auth},
20 pages::{account, commodity, file_table, report, tag},
21 redirect_middleware::redirect_on_auth_error,
22};
23
24pub fn create_pages_router(app_state: Arc<AppState>) -> Router<Arc<AppState>> {
25 Router::new()
26 .route(
27 "/commodity/create",
28 get(crate::pages::commodity::create::submit::commodity_create_page),
29 )
30 .route(
31 "/commodity/list",
32 get(crate::pages::commodity::list::commodity_list_page),
33 )
34 .route_layer(middleware::from_fn_with_state(app_state.clone(), auth))
35 .route_layer(middleware::from_fn(redirect_on_auth_error))
36 .with_state(app_state.clone())
37}
38
39pub fn create_accounts_router(app_state: Arc<AppState>) -> Router<Arc<AppState>> {
40 Router::new()
41 .route(
42 "/account/create",
43 get(crate::pages::account::create::submit::account_create_page),
44 )
45 .route(
46 "/account/list",
47 get(crate::pages::account::list::account_list_page),
48 )
49 .route(
50 "/account/manage",
51 get(crate::pages::account::manage::account_manage_page),
52 )
53 .route(
54 "/account/ssh-key",
55 get(crate::pages::account::ssh_key::ssh_key_page),
56 )
57 .route_layer(middleware::from_fn_with_state(app_state.clone(), auth))
58 .route_layer(middleware::from_fn(redirect_on_auth_error))
59 .with_state(app_state.clone())
60}
61
62pub fn create_transactions_router(app_state: Arc<AppState>) -> Router<Arc<AppState>> {
63 Router::new()
64 .route(
65 "/transaction/create",
66 get(crate::pages::transaction::create::submit::transaction_create_page),
67 )
68 .route(
69 "/transaction/list",
70 get(crate::pages::transaction::list::transaction_list_page),
71 )
72 .route(
73 "/transaction/edit/{id}",
74 get(crate::pages::transaction::edit::submit::transaction_edit_page),
75 )
76 .route_layer(middleware::from_fn_with_state(app_state.clone(), auth))
77 .route_layer(middleware::from_fn(redirect_on_auth_error))
78 .with_state(app_state.clone())
79}
80
81pub fn create_tags_router(app_state: Arc<AppState>) -> Router<Arc<AppState>> {
82 Router::new()
83 .route(
84 "/tag/create",
85 get(crate::pages::tag::create::tag_create_page),
86 )
87 .route("/tag/list", get(crate::pages::tag::list::tag_list_page))
88 .route(
89 "/tag/edit/{id}",
90 get(crate::pages::tag::edit::tag_edit_page),
91 )
92 .route_layer(middleware::from_fn_with_state(app_state.clone(), auth))
93 .route_layer(middleware::from_fn(redirect_on_auth_error))
94 .with_state(app_state.clone())
95}
96
97#[cfg(feature = "scripting")]
98pub fn create_scripts_router(app_state: Arc<AppState>) -> Router<Arc<AppState>> {
99 Router::new()
100 .route("/script/create", get(script::edit::script_create_page))
101 .route("/script/list", get(script::list::script_list_page))
102 .route("/script/edit/{id}", get(script::edit::script_edit_page))
103 .route(
104 "/template/create",
105 get(template::edit::template_create_page),
106 )
107 .route("/template/list", get(template::list::template_list_page))
108 .route(
109 "/template/edit/{id}",
110 get(template::edit::template_edit_page),
111 )
112 .route_layer(middleware::from_fn_with_state(app_state.clone(), auth))
113 .route_layer(middleware::from_fn(redirect_on_auth_error))
114 .with_state(app_state.clone())
115}
116
117pub fn create_reports_router(app_state: Arc<AppState>) -> Router<Arc<AppState>> {
118 Router::new()
119 .route("/report/balance", get(report::balance::balance_report_page))
120 .route(
121 "/report/activity",
122 get(report::activity::activity_report_page),
123 )
124 .route(
125 "/report/category-breakdown",
126 get(report::category_breakdown::category_breakdown_report_page),
127 )
128 .route_layer(middleware::from_fn_with_state(app_state.clone(), auth))
129 .route_layer(middleware::from_fn(redirect_on_auth_error))
130 .with_state(app_state.clone())
131}
132
133pub fn create_api_router(app_state: Arc<AppState>) -> Router<Arc<AppState>> {
134 let public_router = Router::new()
135 .route("/auth/login", post(login_form_handler))
136 .route("/auth/login/json", post(login_user_handler))
137 .route("/version", get(get_version));
138
139 let auth_router = Router::new()
140 .route("/auth/refresh", get(refresh_access_token_handler))
141 .route("/files/list", get(file_table))
142 .route("/files/download/{file_name}", get(download_file))
143 .route("/auth/logout", get(logout_handler))
144 .route("/users/me", get(get_me_handler))
145 .route("/htmx/logoutlink", get(get_logout_link))
146 .route_layer(middleware::from_fn_with_state(app_state.clone(), auth))
147 .with_state(app_state.clone());
148
149 let account_router = Router::new()
150 .route("/account/list", get(account::list::account_table))
151 .route("/account/info", get(account::list::account_info))
152 .route("/account/tree", get(account::manage::account_tree))
153 .route(
154 "/account/manage/view",
155 get(account::manage::account_manage_view),
156 )
157 .route(
158 "/account/manage/details",
159 get(account::manage::account_manage_details),
160 )
161 .route(
162 "/account/manage/tag",
163 post(account::manage::account_manage_set_tag),
164 )
165 .route(
166 "/account/manage/tag/submit",
167 post(account::manage::account_manage_set_tag_submit),
168 )
169 .route(
170 "/account/manage/tag/delete/{tag_id}",
171 post(account::manage::account_manage_delete_tag_submit),
172 )
173 .route("/account/search", post(account::search::search_accounts))
174 .route(
175 "/account/create/submit",
176 post(account::create::submit::create_account),
177 )
178 .route(
179 "/account/create/validate/name",
180 post(account::create::validate::validate_name),
181 )
182 .route("/account/rename", post(account::edit::rename_account))
183 .route(
184 "/account/{id}/tags/submit",
185 post(account::edit::account_tags_submit),
186 )
187 .route("/account/ssh-key/add", post(account::ssh_key::ssh_key_add))
188 .route(
189 "/account/ssh-key/remove",
190 post(account::ssh_key::ssh_key_remove),
191 )
192 .route_layer(middleware::from_fn_with_state(app_state.clone(), auth))
193 .with_state(app_state.clone());
194
195 let nav_router = Router::new()
196 .route("/nav/home", get(get_home_link))
197 .route_layer(middleware::from_fn_with_state(app_state.clone(), auth))
198 .with_state(app_state.clone());
199
200 let commodity_router = Router::new()
201 .route("/commodity/list", get(commodity::list::commodity_table))
202 .route(
203 "/commodity/search",
204 post(commodity::search::search_commodities),
205 )
206 .route(
207 "/commodity/create/submit",
208 post(commodity::create::submit::commodity_submit),
209 )
210 .route(
211 "/commodity/create/validate/symbol",
212 post(commodity::create::validate::validate_symbol),
213 )
214 .route(
215 "/commodity/create/validate/name",
216 post(commodity::create::validate::validate_name),
217 )
218 .route_layer(middleware::from_fn_with_state(app_state.clone(), auth))
219 .with_state(app_state.clone());
220
221 let transaction_router = Router::new()
222 .route(
223 "/transaction/list",
224 get(crate::pages::transaction::list::transaction_table),
225 )
226 .route(
227 "/transaction/split/create",
228 get(crate::pages::transaction::create::split::split_create_handler),
229 )
230 .route(
231 "/transaction/create/submit",
232 post(crate::pages::transaction::create::submit::transaction_submit),
233 )
234 .route(
235 "/transaction/create/validate/amount",
236 post(crate::pages::transaction::validate::validate_amount_html),
237 )
238 .route(
239 "/transaction/create/validate/from_account",
240 post(crate::pages::transaction::validate::validate_from_account_html),
241 )
242 .route(
243 "/transaction/create/validate/to_account",
244 post(crate::pages::transaction::validate::validate_to_account_html),
245 )
246 .route(
247 "/transaction/create/validate/note",
248 post(crate::pages::transaction::validate::validate_note_html),
249 )
250 .route(
251 "/transaction/edit/submit",
252 post(crate::pages::transaction::edit::submit::transaction_edit_submit),
253 )
254 .route(
255 "/transaction/edit/validate/amount",
256 post(crate::pages::transaction::validate::validate_amount_json),
257 )
258 .route(
259 "/transaction/edit/validate/from_account",
260 post(crate::pages::transaction::validate::validate_from_account_json),
261 )
262 .route(
263 "/transaction/edit/validate/to_account",
264 post(crate::pages::transaction::validate::validate_to_account_json),
265 )
266 .route(
267 "/transaction/edit/validate/note",
268 post(crate::pages::transaction::validate::validate_note_json),
269 )
270 .route(
271 "/transaction/delete/{id}",
272 post(crate::pages::transaction::delete::submit::transaction_delete_submit),
273 )
274 .route_layer(middleware::from_fn_with_state(app_state.clone(), auth))
275 .with_state(app_state.clone());
276
277 let tag_router = Router::new()
278 .route("/tag/list", get(tag::list::tag_table))
279 .route("/tag/create/submit", post(tag::create::create_tag))
280 .route(
281 "/transaction/{id}/tag/create/submit",
282 post(tag::create::create_transaction_tag),
283 )
284 .route(
285 "/split/{id}/tag/create/submit",
286 post(tag::create::create_split_tag),
287 )
288 .route(
289 "/account/{id}/tag/create/submit",
290 post(tag::create::create_account_tag),
291 )
292 .route("/tag/edit/submit", post(tag::edit::edit_tag))
293 .route("/tag/delete/{id}", post(tag::edit::delete_tag))
294 .route(
295 "/tags/transaction/names",
296 get(tag::autocomplete::transaction_tag_names),
297 )
298 .route(
299 "/tags/transaction/values",
300 get(tag::autocomplete::transaction_tag_values),
301 )
302 .route("/tags/split/names", get(tag::autocomplete::split_tag_names))
303 .route(
304 "/tags/split/values",
305 get(tag::autocomplete::split_tag_values),
306 )
307 .route(
308 "/tags/account/names",
309 get(tag::autocomplete::account_tag_names),
310 )
311 .route(
312 "/tags/account/values",
313 get(tag::autocomplete::account_tag_values),
314 )
315 .route_layer(middleware::from_fn_with_state(app_state.clone(), auth))
316 .with_state(app_state.clone());
317
318 #[cfg(feature = "scripting")]
319 let script_router = Router::new()
320 .route("/script/list", get(script::list::script_table))
321 .route("/script/create/submit", post(script::edit::create_script))
322 .route("/script/edit/submit", post(script::edit::edit_script))
323 .route(
324 "/script/bytecode/{id}",
325 post(script::edit::upload_script_bytecode),
326 )
327 .route("/script/delete/{id}", post(script::edit::delete_script))
328 .route(
329 "/account/{account_id}/script/run/{script_id}",
330 post(account::edit::run_account_script),
331 )
332 .route("/template/list", get(template::list::template_table))
333 .route(
334 "/template/create/submit",
335 post(template::edit::create_template),
336 )
337 .route("/template/test", post(template::edit::test_template))
338 .route("/template/edit/submit", post(template::edit::edit_template))
339 .route(
340 "/template/delete/{id}",
341 post(template::edit::delete_template),
342 )
343 .route_layer(middleware::from_fn_with_state(app_state.clone(), auth))
344 .with_state(app_state.clone());
345
346 let report_router = Router::new()
347 .route(
348 "/report/balance",
349 get(report::balance::balance_report_table),
350 )
351 .route(
352 "/report/balance/chart.svg",
353 get(report::balance::balance_report_chart_svg),
354 )
355 .route(
356 "/report/balance/chart.json",
357 get(report::balance::balance_report_chart_json),
358 )
359 .route(
360 "/report/activity",
361 get(report::activity::activity_report_table),
362 )
363 .route(
364 "/report/activity/chart.svg",
365 get(report::activity::activity_report_chart_svg),
366 )
367 .route(
368 "/report/activity/chart.json",
369 get(report::activity::activity_report_chart_json),
370 )
371 .route(
372 "/report/category-breakdown",
373 get(report::category_breakdown::category_breakdown_report_table),
374 )
375 .route(
376 "/report/category-breakdown/chart.svg",
377 get(report::category_breakdown::category_breakdown_report_chart_svg),
378 )
379 .route(
380 "/report/category-breakdown/chart.json",
381 get(report::category_breakdown::category_breakdown_report_chart_json),
382 )
383 .route_layer(middleware::from_fn_with_state(app_state.clone(), auth))
384 .with_state(app_state.clone());
385
386 let admin_router = Router::new()
387 .route("/auth/register", post(register_user_handler))
388 .route_layer(middleware::from_fn_with_state(app_state.clone(), admin))
389 .route_layer(middleware::from_fn_with_state(app_state, auth));
390
391 let router = Router::new()
392 .merge(public_router)
393 .merge(nav_router)
394 .merge(auth_router)
395 .merge(account_router)
396 .merge(commodity_router)
397 .merge(transaction_router)
398 .merge(tag_router)
399 .merge(report_router)
400 .merge(admin_router);
401
402 #[cfg(feature = "scripting")]
403 let router = router.merge(script_router);
404
405 router
406}