#include "http_server.h" #include #include #include #include #include #include #include namespace jtjai_media { HttpServer::HttpServer(const std::string& output_directory, int port) : output_directory_(output_directory) , port_(port) , is_running_(false) { } HttpServer::~HttpServer() { stop(); } bool HttpServer::start() { if (is_running_.load()) { std::cerr << "HTTP服务器已经在运行中" << std::endl; return false; } is_running_.store(true); server_thread_ = std::make_unique(&HttpServer::server_main_loop, this); std::cout << "HTTP服务器已启动,监听端口: " << port_ << std::endl; std::cout << "API端点:" << std::endl; std::cout << " GET /api/videos - 列出所有视频" << std::endl; std::cout << " GET /api/timestamps - 列出所有时间戳目录" << std::endl; std::cout << " GET /api/videos/{timestamp} - 列出指定时间戳目录的视频" << std::endl; std::cout << " GET /api/reports - 列出所有轮询报告" << std::endl; std::cout << " GET /api/report/{timestamp} - 获取指定时间戳的轮询报告" << std::endl; std::cout << " DELETE /api/video?path={path} - 删除指定视频文件" << std::endl; std::cout << " DELETE /api/timestamp/{timestamp} - 删除指定时间戳目录及其所有文件" << std::endl; std::cout << "Web界面:" << std::endl; std::cout << " GET / - 主页" << std::endl; std::cout << " GET /manager - 视频管理界面" << std::endl; return true; } void HttpServer::stop() { if (!is_running_.load()) { return; } std::cout << "正在停止HTTP服务器..." << std::endl; is_running_.store(false); if (server_thread_ && server_thread_->joinable()) { server_thread_->join(); } std::cout << "HTTP服务器已停止" << std::endl; } void HttpServer::server_main_loop() { try { boost::asio::io_context io_context; boost::asio::ip::tcp::acceptor acceptor( io_context, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port_) ); acceptor.listen(); while (is_running_.load()) { boost::asio::ip::tcp::socket socket(io_context); // 设置接受超时 acceptor.non_blocking(true); boost::system::error_code ec; acceptor.accept(socket, ec); if (!ec) { // 在新线程中处理请求 std::thread([this](boost::asio::ip::tcp::socket sock) { handle_client(std::move(sock)); }, std::move(socket)).detach(); } else if (ec != boost::asio::error::would_block) { std::cerr << "接受连接错误: " << ec.message() << std::endl; } // 短暂休眠避免CPU占用过高 std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } catch (const std::exception& e) { std::cerr << "HTTP服务器异常: " << e.what() << std::endl; } } void HttpServer::handle_client(boost::asio::ip::tcp::socket socket) { try { // 读取请求 boost::asio::streambuf buffer; boost::system::error_code ec; // 读取直到双换行(HTTP请求结束标志) boost::asio::read_until(socket, buffer, "\r\n\r\n", ec); if (ec && ec != boost::asio::error::eof) { std::cerr << "读取请求错误: " << ec.message() << std::endl; return; } // 转换为字符串 std::string request_data( boost::asio::buffers_begin(buffer.data()), boost::asio::buffers_begin(buffer.data()) + buffer.size() ); // 解析请求 HttpRequest request = parse_request(request_data); // 路由处理 HttpResponse response = route_request(request); // 构建响应 std::string response_data = build_response(response); // 发送响应 boost::asio::write(socket, boost::asio::buffer(response_data), ec); if (ec) { std::cerr << "发送响应错误: " << ec.message() << std::endl; } // 关闭连接 socket.close(); } catch (const std::exception& e) { std::cerr << "处理客户端请求异常: " << e.what() << std::endl; } } HttpRequest HttpServer::parse_request(const std::string& request_data) { HttpRequest request; std::istringstream stream(request_data); std::string line; // 解析请求行 if (std::getline(stream, line)) { std::istringstream request_line(line); std::string uri; request_line >> request.method >> uri; // 分离路径和查询字符串 size_t query_pos = uri.find('?'); if (query_pos != std::string::npos) { request.path = uri.substr(0, query_pos); request.query_string = uri.substr(query_pos + 1); } else { request.path = uri; } } // 解析请求头 while (std::getline(stream, line) && line != "\r") { size_t colon_pos = line.find(':'); if (colon_pos != std::string::npos) { std::string key = line.substr(0, colon_pos); std::string value = line.substr(colon_pos + 1); // 去除首尾空格 value.erase(0, value.find_first_not_of(" \t\r\n")); value.erase(value.find_last_not_of(" \t\r\n") + 1); request.headers[key] = value; } } return request; } std::string HttpServer::build_response(const HttpResponse& response) { std::ostringstream oss; // 状态行 oss << "HTTP/1.1 " << response.status_code << " " << response.status_message << "\r\n"; // 响应头 oss << "Content-Type: " << response.content_type << "\r\n"; oss << "Content-Length: " << response.body.length() << "\r\n"; oss << "Access-Control-Allow-Origin: *\r\n"; oss << "Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS\r\n"; oss << "Accept-Ranges: bytes\r\n"; // 支持Range请求 oss << "Connection: close\r\n"; oss << "\r\n"; // 响应体 oss << response.body; return oss.str(); } HttpResponse HttpServer::route_request(const HttpRequest& request) { std::cout << request.method << " " << request.path; if (!request.query_string.empty()) { std::cout << "?" << request.query_string; } std::cout << std::endl; // OPTIONS请求处理(CORS预检) if (request.method == "OPTIONS") { return HttpResponse(200, "OK", ""); } // 路由分发 if (request.method == "GET" || request.method == "HEAD") { if (request.path == "/api/videos") { return handle_list_videos(request); } else if (request.path == "/api/timestamps") { return handle_list_timestamps(request); } else if (request.path == "/api/reports") { return handle_list_reports(request); } else if (request.path.find("/api/report/") == 0) { return handle_get_report(request); } else if (request.path.find("/api/videos/") == 0) { return handle_get_video_info(request); } else if (request.path.find("/videos/") == 0) { // 处理视频文件访问 auto response = handle_video_file(request); // 如果是HEAD请求,只返回头部,不返回体 if (request.method == "HEAD") { response.body.clear(); } return response; } else { // 处理静态文件访问(HTML、CSS、JS等) return handle_static_file(request); } } else if (request.method == "DELETE") { if (request.path == "/api/video") { return handle_delete_video(request); } else if (request.path.find("/api/timestamp/") == 0) { return handle_delete_timestamp(request); } } // 404未找到 boost::json::object error_obj; error_obj["error"] = "Not Found"; error_obj["message"] = "请求的资源不存在"; return HttpResponse(404, "Not Found", boost::json::serialize(error_obj)); } HttpResponse HttpServer::handle_list_videos(const HttpRequest& request) { try { auto query_params = parse_query_string(request.query_string); std::vector videos; // 检查是否指定了时间戳 if (query_params.count("timestamp")) { std::string timestamp = query_params["timestamp"]; std::string dir_path = output_directory_ + "/" + timestamp; videos = list_videos_in_directory(dir_path); } else { videos = list_all_videos(); } // 构建JSON响应 boost::json::array videos_array; for (const auto& video : videos) { boost::json::object video_obj; video_obj["filename"] = video.filename; video_obj["full_path"] = video.full_path; video_obj["timestamp_dir"] = video.timestamp_dir; video_obj["file_size"] = video.file_size; video_obj["created_time"] = video.created_time; video_obj["stream_index"] = video.stream_index; videos_array.push_back(video_obj); } boost::json::object result; result["count"] = videos.size(); result["videos"] = videos_array; return HttpResponse(200, "OK", boost::json::serialize(result)); } catch (const std::exception& e) { boost::json::object error_obj; error_obj["error"] = "Internal Server Error"; error_obj["message"] = e.what(); return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj)); } } HttpResponse HttpServer::handle_list_timestamps(const HttpRequest& request) { try { auto timestamps = list_timestamp_directories(); // 构建JSON响应 boost::json::array timestamps_array; for (const auto& timestamp : timestamps) { boost::json::object ts_obj; ts_obj["timestamp"] = timestamp; // 获取该目录下的视频数量 std::string dir_path = output_directory_ + "/" + timestamp; auto videos = list_videos_in_directory(dir_path); ts_obj["video_count"] = videos.size(); // 计算总大小 int64_t total_size = 0; for (const auto& video : videos) { total_size += video.file_size; } ts_obj["total_size"] = total_size; timestamps_array.push_back(ts_obj); } boost::json::object result; result["count"] = timestamps.size(); result["timestamps"] = timestamps_array; return HttpResponse(200, "OK", boost::json::serialize(result)); } catch (const std::exception& e) { boost::json::object error_obj; error_obj["error"] = "Internal Server Error"; error_obj["message"] = e.what(); return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj)); } } HttpResponse HttpServer::handle_delete_video(const HttpRequest& request) { try { auto query_params = parse_query_string(request.query_string); if (!query_params.count("path")) { boost::json::object error_obj; error_obj["error"] = "Bad Request"; error_obj["message"] = "缺少path参数"; return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj)); } std::string file_path = query_params["path"]; // 安全检查:确保文件路径在输出目录内 std::filesystem::path abs_path = std::filesystem::absolute(file_path); std::filesystem::path abs_output = std::filesystem::absolute(output_directory_); if (abs_path.string().find(abs_output.string()) != 0) { boost::json::object error_obj; error_obj["error"] = "Forbidden"; error_obj["message"] = "无权删除该文件"; return HttpResponse(403, "Forbidden", boost::json::serialize(error_obj)); } bool success = delete_file(file_path); boost::json::object result; result["success"] = success; result["path"] = file_path; if (success) { result["message"] = "文件删除成功"; return HttpResponse(200, "OK", boost::json::serialize(result)); } else { result["message"] = "文件删除失败"; return HttpResponse(500, "Internal Server Error", boost::json::serialize(result)); } } catch (const std::exception& e) { boost::json::object error_obj; error_obj["error"] = "Internal Server Error"; error_obj["message"] = e.what(); return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj)); } } HttpResponse HttpServer::handle_delete_timestamp(const HttpRequest& request) { try { // 从路径中提取时间戳 /api/timestamp/{timestamp} std::string path = request.path; size_t pos = path.find_last_of('/'); if (pos == std::string::npos) { boost::json::object error_obj; error_obj["error"] = "Bad Request"; error_obj["message"] = "无效的请求路径"; return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj)); } std::string timestamp = path.substr(pos + 1); std::string dir_path = output_directory_ + "/" + timestamp; // 安全检查 std::filesystem::path abs_path = std::filesystem::absolute(dir_path); std::filesystem::path abs_output = std::filesystem::absolute(output_directory_); if (abs_path.string().find(abs_output.string()) != 0) { boost::json::object error_obj; error_obj["error"] = "Forbidden"; error_obj["message"] = "无权删除该目录"; return HttpResponse(403, "Forbidden", boost::json::serialize(error_obj)); } bool success = delete_directory(dir_path); boost::json::object result; result["success"] = success; result["timestamp"] = timestamp; result["path"] = dir_path; if (success) { result["message"] = "目录删除成功"; return HttpResponse(200, "OK", boost::json::serialize(result)); } else { result["message"] = "目录删除失败"; return HttpResponse(500, "Internal Server Error", boost::json::serialize(result)); } } catch (const std::exception& e) { boost::json::object error_obj; error_obj["error"] = "Internal Server Error"; error_obj["message"] = e.what(); return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj)); } } HttpResponse HttpServer::handle_get_video_info(const HttpRequest& request) { // 从路径中提取时间戳 /api/videos/{timestamp} std::string path = request.path; size_t pos = path.find_last_of('/'); if (pos == std::string::npos) { boost::json::object error_obj; error_obj["error"] = "Bad Request"; error_obj["message"] = "无效的请求路径"; return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj)); } std::string timestamp = path.substr(pos + 1); std::string dir_path = output_directory_ + "/" + timestamp; auto videos = list_videos_in_directory(dir_path); boost::json::array videos_array; for (const auto& video : videos) { boost::json::object video_obj; video_obj["filename"] = video.filename; video_obj["full_path"] = video.full_path; video_obj["timestamp_dir"] = video.timestamp_dir; video_obj["file_size"] = video.file_size; video_obj["created_time"] = video.created_time; video_obj["stream_index"] = video.stream_index; videos_array.push_back(video_obj); } boost::json::object result; result["timestamp"] = timestamp; result["count"] = videos.size(); result["videos"] = videos_array; return HttpResponse(200, "OK", boost::json::serialize(result)); } HttpResponse HttpServer::handle_video_file(const HttpRequest& request) { try { // 从路径中提取文件路径 /videos/{timestamp}/{filename} std::string path = request.path; size_t pos = path.find("/videos/"); if (pos == std::string::npos) { boost::json::object error_obj; error_obj["error"] = "Bad Request"; error_obj["message"] = "无效的请求路径"; return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj)); } // 获取相对路径(URL解码) std::string encoded_path = path.substr(pos + 8); // 跳过 "/videos/" std::string relative_path = url_decode(encoded_path); // 构建完整文件路径 std::string file_path = output_directory_ + "/" + relative_path; std::cout << "请求视频文件: " << file_path << std::endl; // 安全检查:确保文件路径在输出目录内 std::filesystem::path abs_path = std::filesystem::absolute(file_path); std::filesystem::path abs_output = std::filesystem::absolute(output_directory_); if (abs_path.string().find(abs_output.string()) != 0) { boost::json::object error_obj; error_obj["error"] = "Forbidden"; error_obj["message"] = "无权访问该文件"; return HttpResponse(403, "Forbidden", boost::json::serialize(error_obj)); } // 检查文件是否存在 if (!std::filesystem::exists(file_path)) { boost::json::object error_obj; error_obj["error"] = "Not Found"; error_obj["message"] = "文件不存在: " + file_path; return HttpResponse(404, "Not Found", boost::json::serialize(error_obj)); } // 获取文件大小 size_t file_size = std::filesystem::file_size(file_path); // 读取文件内容 std::ifstream file(file_path, std::ios::binary); if (!file.is_open()) { boost::json::object error_obj; error_obj["error"] = "Internal Server Error"; error_obj["message"] = "无法打开文件"; return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj)); } // 读取全部文件内容 std::string content(file_size, '\0'); file.read(&content[0], file_size); file.close(); std::cout << "发送视频文件: " << file_path << " (" << file_size << " bytes)" << std::endl; // 构建响应 HttpResponse response; response.status_code = 200; response.status_message = "OK"; // 根据文件扩展名设置Content-Type std::string ext = std::filesystem::path(file_path).extension().string(); if (ext == ".mp4") { response.content_type = "video/mp4"; } else if (ext == ".avi") { response.content_type = "video/x-msvideo"; } else if (ext == ".mkv") { response.content_type = "video/x-matroska"; } else if (ext == ".flv") { response.content_type = "video/x-flv"; } else { response.content_type = "application/octet-stream"; } response.body = content; return response; } catch (const std::exception& e) { boost::json::object error_obj; error_obj["error"] = "Internal Server Error"; error_obj["message"] = e.what(); return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj)); } } std::vector HttpServer::list_timestamp_directories() { std::vector timestamps; try { if (!std::filesystem::exists(output_directory_)) { return timestamps; } for (const auto& entry : std::filesystem::directory_iterator(output_directory_)) { if (entry.is_directory()) { timestamps.push_back(entry.path().filename().string()); } } // 按时间戳排序(最新的在前) std::sort(timestamps.begin(), timestamps.end(), std::greater()); } catch (const std::exception& e) { std::cerr << "列出时间戳目录错误: " << e.what() << std::endl; } return timestamps; } std::vector HttpServer::list_videos_in_directory(const std::string& dir_path) { std::vector videos; try { if (!std::filesystem::exists(dir_path)) { return videos; } for (const auto& entry : std::filesystem::directory_iterator(dir_path)) { if (entry.is_regular_file()) { std::string filename = entry.path().filename().string(); std::string ext = entry.path().extension().string(); // 只列出视频文件 if (ext == ".mp4" || ext == ".avi" || ext == ".mkv" || ext == ".flv") { VideoFileInfo info; info.filename = filename; info.full_path = entry.path().string(); info.timestamp_dir = std::filesystem::path(dir_path).filename().string(); info.file_size = get_file_size(info.full_path); info.created_time = get_file_created_time(info.full_path); // 从文件名提取流索引 size_t stream_pos = filename.find("stream"); if (stream_pos != std::string::npos) { size_t num_start = stream_pos + 6; size_t num_end = filename.find_first_not_of("0123456789", num_start); if (num_end != std::string::npos) { info.stream_index = filename.substr(num_start, num_end - num_start); } } videos.push_back(info); } } } // 按文件名排序 std::sort(videos.begin(), videos.end(), [](const VideoFileInfo& a, const VideoFileInfo& b) { return a.filename < b.filename; }); } catch (const std::exception& e) { std::cerr << "列出视频文件错误: " << e.what() << std::endl; } return videos; } std::vector HttpServer::list_all_videos() { std::vector all_videos; auto timestamps = list_timestamp_directories(); for (const auto& timestamp : timestamps) { std::string dir_path = output_directory_ + "/" + timestamp; auto videos = list_videos_in_directory(dir_path); all_videos.insert(all_videos.end(), videos.begin(), videos.end()); } return all_videos; } bool HttpServer::delete_file(const std::string& file_path) { try { if (std::filesystem::exists(file_path)) { std::filesystem::remove(file_path); std::cout << "已删除文件: " << file_path << std::endl; return true; } return false; } catch (const std::exception& e) { std::cerr << "删除文件错误: " << e.what() << std::endl; return false; } } bool HttpServer::delete_directory(const std::string& dir_path) { try { if (std::filesystem::exists(dir_path)) { std::filesystem::remove_all(dir_path); std::cout << "已删除目录: " << dir_path << std::endl; return true; } return false; } catch (const std::exception& e) { std::cerr << "删除目录错误: " << e.what() << std::endl; return false; } } std::string HttpServer::get_file_created_time(const std::string& file_path) { try { auto ftime = std::filesystem::last_write_time(file_path); auto sctp = std::chrono::time_point_cast( ftime - std::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now() ); auto time_t = std::chrono::system_clock::to_time_t(sctp); auto tm = *std::localtime(&time_t); std::ostringstream oss; oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S"); return oss.str(); } catch (const std::exception& e) { return "Unknown"; } } int64_t HttpServer::get_file_size(const std::string& file_path) { try { return std::filesystem::file_size(file_path); } catch (const std::exception& e) { return 0; } } std::map HttpServer::parse_query_string(const std::string& query) { std::map params; if (query.empty()) { return params; } std::istringstream stream(query); std::string pair; while (std::getline(stream, pair, '&')) { size_t eq_pos = pair.find('='); if (eq_pos != std::string::npos) { std::string key = pair.substr(0, eq_pos); std::string value = pair.substr(eq_pos + 1); params[key] = value; } } return params; } std::string HttpServer::url_decode(const std::string& str) { std::string result; result.reserve(str.size()); for (size_t i = 0; i < str.size(); ++i) { if (str[i] == '%') { if (i + 2 < str.size()) { int value; std::istringstream is(str.substr(i + 1, 2)); if (is >> std::hex >> value) { result += static_cast(value); i += 2; } else { result += str[i]; } } else { result += str[i]; } } else if (str[i] == '+') { result += ' '; } else { result += str[i]; } } return result; } HttpResponse HttpServer::handle_static_file(const HttpRequest& request) { try { std::string file_path; std::string content_type = "text/html"; // 路由映射 if (request.path == "/" || request.path == "/index.html") { // 主页 - 显示系统信息 return generate_index_page(); } else if (request.path == "/manager" || request.path == "/manager.html") { // 视频管理界面 file_path = "video_manager.html"; content_type = "text/html"; } else if (request.path == "/api" || request.path == "/api.html") { // API文档 return generate_api_doc(); } else { // 404 boost::json::object error_obj; error_obj["error"] = "Not Found"; error_obj["message"] = "请求的资源不存在"; return HttpResponse(404, "Not Found", boost::json::serialize(error_obj)); } // 读取静态文件 if (!std::filesystem::exists(file_path)) { boost::json::object error_obj; error_obj["error"] = "Not Found"; error_obj["message"] = "文件不存在: " + file_path; return HttpResponse(404, "Not Found", boost::json::serialize(error_obj)); } std::ifstream file(file_path); if (!file.is_open()) { boost::json::object error_obj; error_obj["error"] = "Internal Server Error"; error_obj["message"] = "无法打开文件"; return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj)); } std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); file.close(); HttpResponse response; response.status_code = 200; response.status_message = "OK"; response.content_type = content_type; response.body = content; return response; } catch (const std::exception& e) { boost::json::object error_obj; error_obj["error"] = "Internal Server Error"; error_obj["message"] = e.what(); return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj)); } } HttpResponse HttpServer::handle_list_reports(const HttpRequest& request) { try { auto timestamps = list_timestamp_directories(); // 构建JSON响应 boost::json::array reports_array; for (const auto& timestamp : timestamps) { std::string report_path = output_directory_ + "/" + timestamp + "/rtsp_report.json"; // 检查报告文件是否存在 if (std::filesystem::exists(report_path)) { // 读取报告文件 std::ifstream file(report_path); if (file.is_open()) { std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); file.close(); try { // 解析JSON auto report = boost::json::parse(content); boost::json::object report_info; report_info["timestamp"] = timestamp; report_info["has_report"] = true; // 提取摘要信息 if (report.is_object()) { auto& obj = report.as_object(); if (obj.contains("summary")) { report_info["summary"] = obj.at("summary"); } if (obj.contains("report_generated_at")) { report_info["generated_at"] = obj.at("report_generated_at"); } } reports_array.push_back(report_info); } catch (...) { // JSON解析失败,跳过 } } } } boost::json::object result; result["count"] = reports_array.size(); result["reports"] = reports_array; return HttpResponse(200, "OK", boost::json::serialize(result)); } catch (const std::exception& e) { boost::json::object error_obj; error_obj["error"] = "Internal Server Error"; error_obj["message"] = e.what(); return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj)); } } HttpResponse HttpServer::handle_get_report(const HttpRequest& request) { try { // 从路径中提取时间戳 /api/report/{timestamp} std::string path = request.path; size_t pos = path.find_last_of('/'); if (pos == std::string::npos) { boost::json::object error_obj; error_obj["error"] = "Bad Request"; error_obj["message"] = "无效的请求路径"; return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj)); } std::string timestamp = path.substr(pos + 1); std::string report_path = output_directory_ + "/" + timestamp + "/rtsp_report.json"; // 检查文件是否存在 if (!std::filesystem::exists(report_path)) { boost::json::object error_obj; error_obj["error"] = "Not Found"; error_obj["message"] = "报告文件不存在"; error_obj["timestamp"] = timestamp; return HttpResponse(404, "Not Found", boost::json::serialize(error_obj)); } // 读取报告文件 std::ifstream file(report_path); if (!file.is_open()) { boost::json::object error_obj; error_obj["error"] = "Internal Server Error"; error_obj["message"] = "无法打开报告文件"; return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj)); } std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); file.close(); // 验证JSON格式 try { boost::json::parse(content); } catch (const std::exception& e) { boost::json::object error_obj; error_obj["error"] = "Internal Server Error"; error_obj["message"] = "报告文件格式错误"; return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj)); } return HttpResponse(200, "OK", content); } catch (const std::exception& e) { boost::json::object error_obj; error_obj["error"] = "Internal Server Error"; error_obj["message"] = e.what(); return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj)); } } HttpResponse HttpServer::generate_index_page() { std::string html = R"( RTSP视频流管理系统

🌅 RTSP视频流管理系统

🚀 快速开始

欢迎使用RTSP视频流管理系统!

📹 视频管理 📊 API文档
)"; HttpResponse response; response.status_code = 200; response.status_message = "OK"; response.content_type = "text/html"; response.body = html; return response; } HttpResponse HttpServer::generate_api_doc() { std::string html = R"( API文档

📊 API文档

GET/api/videos

列出所有视频文件

DELETE/api/video

删除指定视频文件

)"; HttpResponse response; response.status_code = 200; response.status_message = "OK"; response.content_type = "text/html"; response.body = html; return response; } } // namespace jtjai_media