PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
rest_server.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
13// IMPORTANT: Include Crow FIRST before any PACS headers to avoid forward
14// declaration conflicts
15#include "crow.h"
16
40
41#ifdef PACS_WITH_MONITORING
45#endif
46
47#include <atomic>
48#include <mutex>
49#include <sstream>
50#include <thread>
51
52namespace kcenon::pacs::web {
53
54// Forward declare internal registration functions
55namespace endpoints {
56void register_system_endpoints_impl(crow::SimpleApp &app,
57 std::shared_ptr<rest_server_context> ctx);
58void register_patient_endpoints_impl(crow::SimpleApp &app,
59 std::shared_ptr<rest_server_context> ctx);
60void register_study_endpoints_impl(crow::SimpleApp &app,
61 std::shared_ptr<rest_server_context> ctx);
62void register_series_endpoints_impl(crow::SimpleApp &app,
63 std::shared_ptr<rest_server_context> ctx);
64void register_worklist_endpoints_impl(crow::SimpleApp &app,
65 std::shared_ptr<rest_server_context> ctx);
66void register_audit_endpoints_impl(crow::SimpleApp &app,
67 std::shared_ptr<rest_server_context> ctx);
68void register_association_endpoints_impl(crow::SimpleApp &app,
69 std::shared_ptr<rest_server_context> ctx);
70void register_dicomweb_endpoints_impl(crow::SimpleApp &app,
71 std::shared_ptr<rest_server_context> ctx);
72void register_remote_nodes_endpoints_impl(crow::SimpleApp &app,
73 std::shared_ptr<rest_server_context> ctx);
74void register_jobs_endpoints_impl(crow::SimpleApp &app,
75 std::shared_ptr<rest_server_context> ctx);
76void register_routing_endpoints_impl(crow::SimpleApp &app,
77 std::shared_ptr<rest_server_context> ctx);
78void register_thumbnail_endpoints_impl(crow::SimpleApp &app,
79 std::shared_ptr<rest_server_context> ctx);
80void register_metadata_endpoints_impl(crow::SimpleApp &app,
81 std::shared_ptr<rest_server_context> ctx);
82void register_annotation_endpoints_impl(crow::SimpleApp &app,
83 std::shared_ptr<rest_server_context> ctx);
84void register_measurement_endpoints_impl(crow::SimpleApp &app,
85 std::shared_ptr<rest_server_context> ctx);
86void register_key_image_endpoints_impl(crow::SimpleApp &app,
87 std::shared_ptr<rest_server_context> ctx);
88void register_viewer_state_endpoints_impl(crow::SimpleApp &app,
89 std::shared_ptr<rest_server_context> ctx);
90void register_wado_uri_endpoints_impl(crow::SimpleApp &app,
91 std::shared_ptr<rest_server_context> ctx);
93 crow::SimpleApp &app, std::shared_ptr<rest_server_context> ctx);
94#ifdef PACS_WITH_DATABASE_SYSTEM
95void register_metrics_endpoints_impl(crow::SimpleApp &app,
96 std::shared_ptr<rest_server_context> ctx);
97#endif
98} // namespace endpoints
99
105 std::shared_ptr<rest_server_context> context;
106 std::unique_ptr<crow::SimpleApp> app;
107 std::thread server_thread;
108 std::atomic<bool> running{false};
109 std::atomic<std::uint16_t> actual_port{0};
110 std::mutex mutex;
111
112 impl() : context(std::make_shared<rest_server_context>()) {}
113
114 explicit impl(const rest_server_config &cfg)
115 : config(cfg), context(std::make_shared<rest_server_context>()) {
116 context->config = &config;
117 }
118};
119
120rest_server::rest_server() : impl_(std::make_unique<impl>()) {}
121
123 : impl_(std::make_unique<impl>(config)) {}
124
126
127rest_server::rest_server(rest_server &&other) noexcept = default;
128rest_server &rest_server::operator=(rest_server &&other) noexcept = default;
129
131 return impl_->config;
132}
133
135 std::lock_guard<std::mutex> lock(impl_->mutex);
137 impl_->context->config = &impl_->config;
138}
139
141 std::shared_ptr<monitoring::health_checker> checker) {
142 std::lock_guard<std::mutex> lock(impl_->mutex);
143 impl_->context->health_checker = std::move(checker);
144}
145
147 std::shared_ptr<monitoring::pacs_metrics> metrics) {
148 std::lock_guard<std::mutex> lock(impl_->mutex);
149 impl_->context->metrics = std::move(metrics);
150}
151
153 std::shared_ptr<security::access_control_manager> manager) {
154 std::lock_guard<std::mutex> lock(impl_->mutex);
155 impl_->context->security_manager = std::move(manager);
156}
157
159 std::shared_ptr<storage::index_database> database) {
160 std::lock_guard<std::mutex> lock(impl_->mutex);
161 impl_->context->database = std::move(database);
162}
163
165 std::shared_ptr<storage::file_storage> storage) {
166 std::lock_guard<std::mutex> lock(impl_->mutex);
167 impl_->context->file_storage = std::move(storage);
168}
169
171 std::shared_ptr<client::remote_node_manager> manager) {
172 std::lock_guard<std::mutex> lock(impl_->mutex);
173 impl_->context->node_manager = std::move(manager);
174}
175
177 std::shared_ptr<client::job_manager> manager) {
178 std::lock_guard<std::mutex> lock(impl_->mutex);
179 impl_->context->job_manager = std::move(manager);
180}
181
183 std::shared_ptr<client::routing_manager> manager) {
184 std::lock_guard<std::mutex> lock(impl_->mutex);
185 impl_->context->routing_manager = std::move(manager);
186}
187
189 std::shared_ptr<network::dicom_server> server) {
190 std::lock_guard<std::mutex> lock(impl_->mutex);
191 impl_->context->dicom_server = std::move(server);
192}
193
195 std::shared_ptr<auth::oauth2_middleware> middleware) {
196 std::lock_guard<std::mutex> lock(impl_->mutex);
197 impl_->context->oauth2 = std::move(middleware);
198}
199
201 if (impl_->running.exchange(true)) {
202 return; // Already running
203 }
204
205 impl_->app = std::make_unique<crow::SimpleApp>();
206 auto &app = *impl_->app;
207
208 // Register system endpoints
210
211 // Add CORS preflight handler
212 if (impl_->config.enable_cors) {
213 CROW_ROUTE(app, "/api/<path>")
214 .methods(crow::HTTPMethod::OPTIONS)(
215 [this](const crow::request & /*req*/,
216 const std::string & /*path*/) {
217 crow::response res(204);
218 res.add_header("Access-Control-Allow-Origin",
220 res.add_header("Access-Control-Allow-Methods",
221 "GET, POST, PUT, DELETE, OPTIONS");
222 res.add_header("Access-Control-Allow-Headers",
223 "Content-Type, Authorization");
224 res.add_header("Access-Control-Max-Age", "86400");
225 return res;
226 });
227 }
228
229 // Start server
230 app.bindaddr(impl_->config.bind_address)
231 .port(impl_->config.port)
232 .concurrency(static_cast<uint16_t>(impl_->config.concurrency))
233 .run();
234
235 impl_->running = false;
236}
237
239 if (impl_->running.exchange(true)) {
240 return; // Already running
241 }
242
243 impl_->server_thread = std::thread([this]() {
244 impl_->app = std::make_unique<crow::SimpleApp>();
245 auto &app = *impl_->app;
246
247 // Register endpoints
268
269#ifdef PACS_WITH_DATABASE_SYSTEM
270 endpoints::register_metrics_endpoints_impl(app, impl_->context);
271#endif
272
273 // Add CORS preflight handler
274 if (impl_->config.enable_cors) {
275 CROW_ROUTE(app, "/api/<path>")
276 .methods(crow::HTTPMethod::OPTIONS)(
277 [this](const crow::request & /*req*/,
278 const std::string & /*path*/) {
279 crow::response res(204);
280 res.add_header("Access-Control-Allow-Origin",
282 res.add_header("Access-Control-Allow-Methods",
283 "GET, POST, PUT, DELETE, OPTIONS");
284 res.add_header("Access-Control-Allow-Headers",
285 "Content-Type, Authorization");
286 res.add_header("Access-Control-Max-Age", "86400");
287 return res;
288 });
289 }
290
291 // Start server
292 app.bindaddr(impl_->config.bind_address)
293 .port(impl_->config.port)
294 .concurrency(static_cast<uint16_t>(impl_->config.concurrency))
295 .run();
296
297 impl_->running = false;
298 });
299}
300
302 if (!impl_->running) {
303 return;
304 }
305
306 if (impl_->app) {
307 impl_->app->stop();
308 }
309
310 if (impl_->server_thread.joinable()) {
311 impl_->server_thread.join();
312 }
313
314 impl_->running = false;
315}
316
317bool rest_server::is_running() const noexcept { return impl_->running; }
318
320 if (impl_->server_thread.joinable()) {
321 impl_->server_thread.join();
322 }
323}
324
325std::uint16_t rest_server::port() const noexcept {
326 return impl_->running ? impl_->config.port : 0;
327}
328
329} // namespace kcenon::pacs::web
Annotation API endpoints for REST server.
DICOM Association API endpoints for REST server.
Audit log API endpoints for REST server.
REST API server for PACS administration and monitoring.
Definition rest_server.h:85
void start_async()
Start the server (non-blocking)
void set_routing_manager(std::shared_ptr< client::routing_manager > manager)
Set routing manager for auto-forwarding rules.
void set_oauth2_middleware(std::shared_ptr< auth::oauth2_middleware > middleware)
Set OAuth 2.0 middleware for DICOMweb endpoint authorization.
void set_node_manager(std::shared_ptr< client::remote_node_manager > manager)
Set remote node manager for remote PACS node management.
rest_server()
Construct REST server with default configuration.
void set_health_checker(std::shared_ptr< monitoring::health_checker > checker)
Set health checker for /api/v1/system/status endpoint.
bool is_running() const noexcept
Check if server is currently running.
void set_access_control_manager(std::shared_ptr< security::access_control_manager > manager)
Set access control manager for security.
void set_dicom_server(std::shared_ptr< network::dicom_server > server)
Set DICOM server for live association management.
void set_job_manager(std::shared_ptr< client::job_manager > manager)
Set job manager for async DICOM operations.
const rest_server_config & config() const noexcept
Get current configuration.
void wait()
Wait for server to stop.
rest_server & operator=(const rest_server &)=delete
void stop()
Stop the server.
void set_config(const rest_server_config &config)
Update configuration (requires restart to apply)
void start()
Start the server (blocking)
void set_file_storage(std::shared_ptr< storage::file_storage > storage)
Set file storage for DICOM instance persistence (STOW-RS)
void set_database(std::shared_ptr< storage::index_database > database)
Set index database for patient/study/series endpoints.
~rest_server()
Destructor - stops server if running.
void set_metrics_provider(std::shared_ptr< monitoring::pacs_metrics > metrics)
Set metrics provider for /api/v1/system/metrics endpoint.
std::uint16_t port() const noexcept
Get the actual port the server is listening on.
std::unique_ptr< impl > impl_
DICOMweb (WADO-RS) API endpoints for REST server.
Health check service for PACS system components.
JSON serialization for health check data structures.
Job management REST API and WebSocket endpoints.
Key image API endpoints for REST server.
Measurement API endpoints for REST server.
Metadata REST API endpoints.
void register_storage_commitment_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_measurement_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_security_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
Register security endpoints with the Crow app.
void register_metadata_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_key_image_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_audit_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_worklist_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_series_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_remote_nodes_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_viewer_state_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_wado_uri_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_system_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_routing_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_patient_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_dicomweb_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_association_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_study_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_jobs_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_annotation_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
void register_thumbnail_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
OAuth 2.0 middleware for DICOMweb endpoint authorization.
Operation metrics collection for PACS DICOM services.
Patient API endpoints for REST server.
Remote PACS node management REST API endpoints.
Configuration for REST API server.
REST API server for PACS administration.
Common types and utilities for REST API.
Routing rule management REST API endpoints.
Security API endpoints for REST server.
Series API endpoints for REST server.
Implementation details for rest_server.
std::unique_ptr< crow::SimpleApp > app
std::atomic< std::uint16_t > actual_port
std::shared_ptr< rest_server_context > context
impl(const rest_server_config &cfg)
Configuration options for the REST server.
Definition rest_config.h:29
std::string cors_allowed_origins
CORS allowed origins (empty = allow all)
Definition rest_config.h:43
std::string bind_address
Address to bind the server to.
Definition rest_config.h:31
bool enable_cors
Enable CORS (Cross-Origin Resource Sharing) headers.
Definition rest_config.h:40
std::uint16_t port
Port to listen on.
Definition rest_config.h:34
std::size_t concurrency
Number of worker threads for handling requests.
Definition rest_config.h:37
Shared context for REST endpoints.
Study API endpoints for REST server.
System API endpoints for REST server.
Thumbnail REST API endpoints.
Viewer state API endpoints for REST server.
WADO-URI (Web Access to DICOM Objects — URI-based) API endpoints.
Worklist API endpoints for REST server.