1use crate::auth::TokenManager;
2use crate::database::DB;
3use crate::handlers::*;
4pub mod auth;
5mod database;
6mod handlers;
7pub use httpageboy::{Request, Response, Rt, Server, StatusCode, handler};
8use std::sync::OnceLock;
9use tokio::time::{self, Duration};
10
11pub mod test_utils {
12 pub use httpageboy::test_utils::{run_test, setup_test_server};
13}
14
15pub fn active_test_server_url() -> &'static str {
16 httpageboy::test_utils::active_test_server_url()
17}
18
19fn build_cors_policy() -> httpageboy::CorsPolicy {
20 let mut policy = httpageboy::CorsPolicy::default();
21
22 if let Ok(origins) = std::env::var("CORS") {
23 if !origins.trim().is_empty() {
24 policy.allow_origin = origins;
25 }
26 }
27
28 let mut headers: Vec<String> = policy
29 .allow_headers
30 .split(',')
31 .map(|h| h.trim().to_string())
32 .filter(|h| !h.is_empty())
33 .collect();
34
35 let mut push_unique = |value: &str| {
36 if !headers.iter().any(|h| h.eq_ignore_ascii_case(value)) {
37 headers.push(value.to_string());
38 }
39 };
40
41 push_unique("token");
42 if let Ok(extra) = std::env::var("CORS_HEADERS") {
43 for header in extra.split(',').map(|h| h.trim()).filter(|h| !h.is_empty()) {
44 push_unique(header);
45 }
46 }
47
48 policy.allow_headers = headers.join(", ");
49 policy
50}
51
52static CLEANUP_JOB_STARTED: OnceLock<()> = OnceLock::new();
53
54fn spawn_cleanup_job() {
55 if CLEANUP_JOB_STARTED.set(()).is_err() {
56 return;
57 }
58 tokio::spawn(async move {
59 let mut ticker = time::interval(Duration::from_secs(60));
60 loop {
61 ticker.tick().await;
62 match DB::new().await {
63 Ok(db) => {
64 let manager = TokenManager::new(db.pool());
65 if let Err(err) = manager.cleanup_expired().await {
66 eprintln!("[cleanup] token cleanup failed: {}", err);
67 }
68 }
69 Err(err) => eprintln!("[cleanup] db unavailable: {}", err),
70 }
71 }
72 });
73}
74
75pub async fn create_server(server_url: &str) -> Server {
76 let mut server = Server::new(server_url, None)
77 .await
78 .expect("Failed to create server");
79
80 server.set_cors(build_cors_policy());
81 spawn_cleanup_job();
82
83 server.add_route("/", Rt::GET, handler!(home));
84
85 server.add_route("/auth/login", Rt::POST, handler!(login));
87 server.add_route("/auth/logout", Rt::POST, handler!(logout));
88 server.add_route("/auth/profile", Rt::GET, handler!(profile));
89 server.add_route("/check-token", Rt::POST, handler!(check_token));
90
91 server.add_route("/users", Rt::GET, handler!(list_people));
93 server.add_route("/users", Rt::POST, handler!(create_user));
94 server.add_route("/users/{id}", Rt::GET, handler!(get_user));
95 server.add_route("/users/{id}", Rt::PUT, handler!(update_user));
96 server.add_route("/users/{id}", Rt::DELETE, handler!(delete_user));
97
98 server.add_route("/services", Rt::GET, handler!(list_services));
100 server.add_route("/services", Rt::POST, handler!(create_service));
101 server.add_route("/services/{id}", Rt::PUT, handler!(update_service));
102 server.add_route("/services/{id}", Rt::DELETE, handler!(delete_service));
103
104 server.add_route("/roles", Rt::GET, handler!(list_roles));
106 server.add_route("/roles", Rt::POST, handler!(create_role));
107 server.add_route("/roles/{id}", Rt::GET, handler!(get_role));
108 server.add_route("/roles/{id}", Rt::PUT, handler!(update_role));
109 server.add_route("/roles/{id}", Rt::DELETE, handler!(delete_role));
110
111 server.add_route("/permissions", Rt::GET, handler!(list_permissions));
113 server.add_route("/permissions", Rt::POST, handler!(create_permission));
114 server.add_route("/permissions/{id}", Rt::PUT, handler!(update_permission));
115 server.add_route("/permissions/{id}", Rt::DELETE, handler!(delete_permission));
116
117 server.add_route(
119 "/role-permissions",
120 Rt::POST,
121 handler!(assign_permission_to_role),
122 );
123 server.add_route(
124 "/role-permissions",
125 Rt::DELETE,
126 handler!(remove_permission_from_role),
127 );
128 server.add_route(
129 "/roles/{id}/permissions",
130 Rt::GET,
131 handler!(list_role_permissions),
132 );
133
134 server.add_route("/service-roles", Rt::POST, handler!(assign_role_to_service));
136 server.add_route(
137 "/service-roles",
138 Rt::DELETE,
139 handler!(remove_role_from_service),
140 );
141 server.add_route(
142 "/services/{id}/roles",
143 Rt::GET,
144 handler!(list_service_roles),
145 );
146
147 server.add_route(
149 "/person-service-roles",
150 Rt::POST,
151 handler!(assign_role_to_person_in_service),
152 );
153 server.add_route(
154 "/person-service-roles",
155 Rt::DELETE,
156 handler!(remove_role_from_person_in_service),
157 );
158 server.add_route(
159 "/person-service-permissions",
160 Rt::POST,
161 handler!(grant_permission_to_person_in_service),
162 );
163 server.add_route(
164 "/people/{person_id}/services/{service_id}/roles",
165 Rt::GET,
166 handler!(list_person_roles_in_service),
167 );
168 server.add_route(
169 "/services/{service_id}/roles/{role_id}/people",
170 Rt::GET,
171 handler!(list_persons_with_role_in_service),
172 );
173 server.add_route(
174 "/people/{person_id}/services",
175 Rt::GET,
176 handler!(list_services_of_person),
177 );
178 server.add_route(
179 "/people/{person_id}/services/{service_id}",
180 Rt::GET,
181 handler!(get_person_service_info),
182 );
183
184 server
185}