PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
series_endpoints.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
24
25#include <sstream>
26
28
29namespace {
30
34void add_cors_headers(crow::response &res, const rest_server_context &ctx) {
35 if (ctx.config && !ctx.config->cors_allowed_origins.empty()) {
36 res.add_header("Access-Control-Allow-Origin",
37 ctx.config->cors_allowed_origins);
38 }
39}
40
44std::string series_to_json(const storage::series_record &series) {
45 std::ostringstream oss;
46 oss << R"({"pk":)" << series.pk << R"(,"study_pk":)" << series.study_pk
47 << R"(,"series_instance_uid":")" << json_escape(series.series_uid)
48 << R"(","modality":")" << json_escape(series.modality)
49 << R"(","series_number":)";
50 if (series.series_number) {
51 oss << *series.series_number;
52 } else {
53 oss << "null";
54 }
55 oss << R"(,"series_description":")" << json_escape(series.series_description)
56 << R"(","body_part_examined":")" << json_escape(series.body_part_examined)
57 << R"(","station_name":")" << json_escape(series.station_name)
58 << R"(","num_instances":)" << series.num_instances << "}";
59 return oss.str();
60}
61
65std::string instance_to_json(const storage::instance_record &instance) {
66 std::ostringstream oss;
67 oss << R"({"pk":)" << instance.pk << R"(,"series_pk":)" << instance.series_pk
68 << R"(,"sop_instance_uid":")" << json_escape(instance.sop_uid)
69 << R"(","sop_class_uid":")" << json_escape(instance.sop_class_uid)
70 << R"(","transfer_syntax":")" << json_escape(instance.transfer_syntax)
71 << R"(","instance_number":)";
72 if (instance.instance_number) {
73 oss << *instance.instance_number;
74 } else {
75 oss << "null";
76 }
77 oss << R"(,"file_size":)" << instance.file_size << "}";
78 return oss.str();
79}
80
84std::string instances_to_json(const std::vector<storage::instance_record> &instances) {
85 std::ostringstream oss;
86 oss << R"({"data":[)";
87 for (size_t i = 0; i < instances.size(); ++i) {
88 if (i > 0) {
89 oss << ",";
90 }
91 oss << instance_to_json(instances[i]);
92 }
93 oss << R"(],"count":)" << instances.size() << "}";
94 return oss.str();
95}
96
97} // namespace
98
99// Internal implementation function called from rest_server.cpp
100void register_series_endpoints_impl(crow::SimpleApp &app,
101 std::shared_ptr<rest_server_context> ctx) {
102 // GET /api/v1/series/:uid - Get series details
103 CROW_ROUTE(app, "/api/v1/series/<string>")
104 .methods(crow::HTTPMethod::GET)(
105 [ctx](const crow::request & /*req*/, const std::string &series_uid) {
106 crow::response res;
107 res.add_header("Content-Type", "application/json");
108 add_cors_headers(res, *ctx);
109
110 if (!ctx->database) {
111 res.code = 503;
112 res.body = make_error_json("DATABASE_UNAVAILABLE",
113 "Database not configured");
114 return res;
115 }
116
117 auto series = ctx->database->find_series(series_uid);
118 if (!series) {
119 res.code = 404;
120 res.body = make_error_json("NOT_FOUND", "Series not found");
121 return res;
122 }
123
124 res.code = 200;
125 res.body = series_to_json(*series);
126 return res;
127 });
128
129 // GET /api/v1/series/:uid/instances - Get series instances
130 CROW_ROUTE(app, "/api/v1/series/<string>/instances")
131 .methods(crow::HTTPMethod::GET)(
132 [ctx](const crow::request & /*req*/, const std::string &series_uid) {
133 crow::response res;
134 res.add_header("Content-Type", "application/json");
135 add_cors_headers(res, *ctx);
136
137 if (!ctx->database) {
138 res.code = 503;
139 res.body = make_error_json("DATABASE_UNAVAILABLE",
140 "Database not configured");
141 return res;
142 }
143
144 // Verify series exists
145 auto series = ctx->database->find_series(series_uid);
146 if (!series) {
147 res.code = 404;
148 res.body = make_error_json("NOT_FOUND", "Series not found");
149 return res;
150 }
151
152 auto instances_result = ctx->database->list_instances(series_uid);
153 if (!instances_result.is_ok()) {
154 res.code = 500;
155 res.body = make_error_json("QUERY_ERROR",
156 instances_result.error().message);
157 return res;
158 }
159
160 res.code = 200;
161 res.body = instances_to_json(instances_result.value());
162 return res;
163 });
164}
165
166} // namespace kcenon::pacs::web::endpoints
PACS index database for metadata storage and retrieval.
Instance record data structures for database operations.
void register_series_endpoints_impl(crow::SimpleApp &app, std::shared_ptr< rest_server_context > ctx)
std::string make_error_json(std::string_view code, std::string_view message)
Create JSON error response body with details.
Definition rest_types.h:79
std::string json_escape(std::string_view s)
Escape a string for JSON.
Definition rest_types.h:101
Configuration for REST API server.
Common types and utilities for REST API.
Series API endpoints for REST server.
Series record data structures for database operations.
System API endpoints for REST server.