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