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