From 44b469ece3d6283690b64c2de956fce591fe4b17 Mon Sep 17 00:00:00 2001 From: dijunkun Date: Mon, 8 Dec 2025 16:43:19 +0800 Subject: [PATCH] [refactor] optimize Docker image size and build context --- .dockerignore | 36 +++++++++ docker/dockerfile | 31 ++++++-- docker/generate_certs.sh | 4 +- docker/start.sh | 10 +-- src/main.cpp | 12 ++- src/signal_server.cpp | 167 +++++++++++++++++++++++++++++---------- 6 files changed, 203 insertions(+), 57 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5ac40fe --- /dev/null +++ b/.dockerignore @@ -0,0 +1,36 @@ +# Git +.git +.gitignore +.github + +# Build artifacts +build/ +.xmake/ +*.o +*.a +*.so +*.exe + +# IDE +.vs/ +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Documentation +*.md +!README.md +!README_EN.md + +# Workflows (not needed in container) +workflows/ + +# Docker files +# docker/ + +# Temporary files +*.log +*.tmp +*.cache diff --git a/docker/dockerfile b/docker/dockerfile index 2f4ddab..ab236d2 100644 --- a/docker/dockerfile +++ b/docker/dockerfile @@ -1,22 +1,42 @@ FROM ubuntu:22.04 AS builder +# Set non-interactive mode +ENV DEBIAN_FRONTEND=noninteractive + +# Install build dependencies RUN apt-get update && \ - apt-get install -y software-properties-common git curl unzip build-essential && \ - add-apt-repository -y ppa:xmake-io/xmake && \ + apt-get install -y --no-install-recommends \ + ca-certificates \ + gnupg \ + software-properties-common \ + git \ + curl \ + unzip \ + build-essential + +# Add xmake repository and install xmake +RUN add-apt-repository -y ppa:xmake-io/xmake && \ apt-get update && \ - apt-get install -y xmake && \ + apt-get install -y --no-install-recommends xmake && \ xmake --version --root +# Clean up to reduce image size +RUN apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + WORKDIR /src COPY . . +# Build and copy only the binary, clean up build artifacts RUN xmake b -vy --root crossdesk_server && \ mkdir -p /output && \ - cp build/linux/x86_64/release/crossdesk_server /output/ + cp build/linux/x86_64/release/crossdesk_server /output/ && \ + rm -rf build .xmake FROM crossdesk/crossdesk-server-base:latest +# Generate coturn certificates and set permissions in one layer RUN mkdir -p /opt/turnserver && \ cd /opt/turnserver && \ openssl genrsa -out turn_server_pkey.pem 2048 && \ @@ -27,11 +47,10 @@ RUN mkdir -p /opt/turnserver && \ -out turn_server_cert.pem && \ chmod 600 /opt/turnserver/turn_server_pkey.pem - +# Copy files and set permissions in one layer COPY docker/start.sh /start.sh COPY docker/generate_certs.sh /docker/generate_certs.sh COPY --from=builder /output/crossdesk_server /crossdesk-server/crossdesk_server - RUN chmod +x /start.sh /docker/generate_certs.sh /crossdesk-server/crossdesk_server ENTRYPOINT ["/start.sh"] diff --git a/docker/generate_certs.sh b/docker/generate_certs.sh index ae2a6ad..774d288 100644 --- a/docker/generate_certs.sh +++ b/docker/generate_certs.sh @@ -70,8 +70,10 @@ echo "Signing server certificate with root certificate..." openssl x509 -req -in "$SERVER_CSR" -CA "$ROOT_CERT" -CAkey "$ROOT_KEY" -CAcreateserial \ -out "$SERVER_CERT" -days 3650 -sha256 -extfile "$SAN_CONF" -extensions req_ext -# 6. 生成完整链证书 +# 6. 生成完整链证书并更新 bundle.crt(包含服务器证书和根证书) cat "$SERVER_CERT" "$ROOT_CERT" > "$FULLCHAIN_CERT" +# 将完整证书链写入 bundle.crt,这样服务器可以使用完整的证书链 +cp "$FULLCHAIN_CERT" "$SERVER_CERT" # 7. 清理中间文件 rm -f "$ROOT_CERT.srl" "$SAN_CONF" "$ROOT_KEY" "$SERVER_CSR" "$FULLCHAIN_CERT" diff --git a/docker/start.sh b/docker/start.sh index 9266013..81076d3 100644 --- a/docker/start.sh +++ b/docker/start.sh @@ -80,8 +80,8 @@ exec turnserver -c "$CONF_FILE" & # start crossdesk-server as main foreground process echo "Starting crossdesk-server..." -# 程序现在使用固定目录: -# - 数据库和配置文件:/var/lib/crossdesk -# - 日志文件:/var/log/crossdesk -# 只需传递端口参数 -./crossdesk-server/crossdesk_server ${CROSSDESK_SERVER_PORT} \ No newline at end of file +echo "Certificate directory: $CERT_DIR" +echo "Certificate files:" +ls -la "$CERT_DIR" || echo "Warning: Cannot list certificate directory" + +exec ./crossdesk-server/crossdesk_server ${CROSSDESK_SERVER_PORT} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 5c1346f..ef8e41e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,8 +24,16 @@ int main(int argc, char* argv[]) { InitLogger(log_dir); - SignalServer s(std::stoi(port), certs_dir, db_path); - s.Run(); + try { + SignalServer s(std::stoi(port), certs_dir, db_path); + s.Run(); + } catch (std::exception& e) { + LOG_ERROR("Fatal error: {}", e.what()); + return 1; + } catch (...) { + LOG_ERROR("Unknown fatal error occurred"); + return 1; + } return 0; } diff --git a/src/signal_server.cpp b/src/signal_server.cpp index 9fd1423..f4d4956 100644 --- a/src/signal_server.cpp +++ b/src/signal_server.cpp @@ -1,5 +1,6 @@ #include "signal_server.h" +#include #include #include "common.h" @@ -113,6 +114,17 @@ context_ptr SignalServer::OnTlsInit(websocketpp::connection_hdl hdl) { std::string cert_file = certs_dir_ + "/crossdesk.cn_bundle.crt"; std::string key_file = certs_dir_ + "/crossdesk.cn.key"; + + // Check if certificate files exist + if (!std::filesystem::exists(cert_file)) { + LOG_ERROR("Certificate file not found: {}", cert_file); + throw std::runtime_error("Certificate file not found: " + cert_file); + } + if (!std::filesystem::exists(key_file)) { + LOG_ERROR("Private key file not found: {}", key_file); + throw std::runtime_error("Private key file not found: " + key_file); + } + ctx->use_certificate_chain_file(cert_file); ctx->use_private_key_file(key_file, asio::ssl::context::pem); @@ -121,8 +133,11 @@ context_ptr SignalServer::OnTlsInit(websocketpp::connection_hdl hdl) { "ECDHE-RSA-AES256-GCM-SHA384:" "ECDHE-ECDSA-AES128-GCM-SHA256:" "ECDHE-RSA-AES128-GCM-SHA256"); + + LOG_INFO("TLS context initialized successfully"); } catch (std::exception& e) { - std::cout << "Exception: " << e.what() << std::endl; + LOG_ERROR("Failed to initialize TLS context: {}", e.what()); + throw; // Re-throw to prevent invalid context from being used } return ctx; } @@ -142,12 +157,58 @@ void SignalServer::Run() { return; } - server_.set_reuse_addr(true); - LOG_INFO("Signal server runs on port [{}]", port_); + // Verify certificate files exist + std::string cert_file = certs_dir_ + "/crossdesk.cn_bundle.crt"; + std::string key_file = certs_dir_ + "/crossdesk.cn.key"; + if (!std::filesystem::exists(cert_file)) { + LOG_ERROR("Certificate file not found: {}", cert_file); + return; + } + if (!std::filesystem::exists(key_file)) { + LOG_ERROR("Private key file not found: {}", key_file); + return; + } - server_.listen(port_); - server_.start_accept(); - server_.run(); + server_.set_reuse_addr(true); + LOG_INFO("Signal server starting on port [{}]", port_); + LOG_INFO("Certificate directory: [{}]", certs_dir_); + LOG_INFO("Database path: [{}]", db_path_); + + try { + // Listen on all interfaces (0.0.0.0) + namespace asio = websocketpp::lib::asio; + asio::error_code ec; + server_.listen(asio::ip::tcp::v4(), port_, ec); + if (ec) { + LOG_ERROR("Failed to listen on port {}: {}", port_, ec.message()); + return; + } + LOG_INFO("Successfully bound to port [{}]", port_); + + server_.start_accept(ec); + if (ec) { + LOG_ERROR("Failed to start accepting connections: {}", ec.message()); + return; + } + LOG_INFO("Signal server listening on port [{}], waiting for connections...", + port_); + + server_.run(); + LOG_INFO("Server run() returned"); + } catch (std::exception& e) { + LOG_ERROR("Server error: {}, attempting to restart...", e.what()); + // Try to restart the server + try { + server_.stop(); + server_.listen(port_); + server_.start_accept(); + server_.run(); + } catch (std::exception& e2) { + LOG_ERROR("Failed to restart server: {}", e2.what()); + } + } catch (...) { + LOG_ERROR("Unknown error occurred in server"); + } } void SignalServer::SendMsg(websocketpp::connection_hdl hdl, json message) { @@ -164,43 +225,63 @@ void SignalServer::OnMessage(websocketpp::connection_hdl hdl, return; } - std::string payload = msg->get_payload(); - auto j = json::parse(payload); - std::string type = j["type"].get(); - - switch (HASH_STRING_PIECE(type.c_str())) { - case "ping"_H: { - if (transmission_manager_) { - transmission_manager_->UpdateWsHandleLastActiveTime(hdl); - json message = {{"type", "pong"}}; - server_.send(hdl, message.dump(), websocketpp::frame::opcode::text); - } - break; + try { + std::string payload = msg->get_payload(); + json j; + try { + j = json::parse(payload); + } catch (json::parse_error& e) { + LOG_ERROR("Failed to parse JSON message: {}", e.what()); + return; } - case "login"_H: - signal_negotiation_->login_user(hdl, j); - break; - case "user_leave_transmission"_H: - signal_negotiation_->leave_transmission(hdl, j); - break; - case "query_user_id_list"_H: - signal_negotiation_->query_user_id_list(hdl, j); - break; - case "join_transmission"_H: - signal_negotiation_->join_transmission(hdl, j); - break; - case "offer"_H: - signal_negotiation_->offer(hdl, j); - break; - case "answer"_H: - signal_negotiation_->answer(hdl, j); - break; - case "new_candidate"_H: - signal_negotiation_->new_candidate(hdl, j); - break; - case "new_candidate_mid"_H: - signal_negotiation_->new_candidate_mid(hdl, j); - default: - break; + + if (!j.contains("type")) { + LOG_ERROR("Message missing 'type' field"); + return; + } + + std::string type = j["type"].get(); + + switch (HASH_STRING_PIECE(type.c_str())) { + case "ping"_H: { + if (transmission_manager_) { + transmission_manager_->UpdateWsHandleLastActiveTime(hdl); + json message = {{"type", "pong"}}; + server_.send(hdl, message.dump(), websocketpp::frame::opcode::text); + } + break; + } + case "login"_H: + signal_negotiation_->login_user(hdl, j); + break; + case "user_leave_transmission"_H: + signal_negotiation_->leave_transmission(hdl, j); + break; + case "query_user_id_list"_H: + signal_negotiation_->query_user_id_list(hdl, j); + break; + case "join_transmission"_H: + signal_negotiation_->join_transmission(hdl, j); + break; + case "offer"_H: + signal_negotiation_->offer(hdl, j); + break; + case "answer"_H: + signal_negotiation_->answer(hdl, j); + break; + case "new_candidate"_H: + signal_negotiation_->new_candidate(hdl, j); + break; + case "new_candidate_mid"_H: + signal_negotiation_->new_candidate_mid(hdl, j); + break; + default: + LOG_WARN("Unknown message type: {}", type); + break; + } + } catch (std::exception& e) { + LOG_ERROR("Error processing message: {}", e.what()); + } catch (...) { + LOG_ERROR("Unknown error processing message"); } }