#include "server.h" #include #ifdef VOICECAT_HAS_NET #define ASIO_STANDALONE 1 #include #include #include #include "conn_session.h" #include "core/worker_pool.h" #include "crypto/crypto.h" #include "db.h" #include "identity.h" #include "net/transport.h" #include "session_registry.h" namespace voicecat::server { int Server::run() { // ── Identity + cert ────────────────────────────────────────────────────── ServerIdentityManager id_mgr; std::string error; if (!id_mgr.init(cfg_.data_dir, cfg_.server_name, error)) { std::fprintf(stderr, "[server] identity init failed: %s\n", error.c_str()); return 1; } // ── Database + bootstrap admin ─────────────────────────────────────────── auto db = std::make_shared(cfg_.data_dir + "/voicecat.db"); if (!db->open(error)) { std::fprintf(stderr, "[server] db open failed: %s\n", error.c_str()); return 1; } if (db->is_empty()) { std::string pw = Database::generate_password(20); auto acc = db->create_account("admin", pw, true, error); if (!acc) { std::fprintf(stderr, "[server] failed to create admin account: %s\n", error.c_str()); return 1; } std::printf("\n"); std::printf("┌─────────────────────────────────────────────────────────┐\n"); std::printf("│ First run — admin account created │\n"); std::printf("│ username : %-44s│\n", "admin"); std::printf("│ password : %-44s│\n", pw.c_str()); std::printf("│ Change with: voicecat-admin account reset admin │\n"); std::printf("└─────────────────────────────────────────────────────────┘\n"); std::printf("\n"); } // ── Session registry ───────────────────────────────────────────────────── auto registry = std::make_shared(); registry->init_default_channels(); // ── Worker pool ────────────────────────────────────────────────────────── auto workers = std::make_shared(3); // ── Asio io_context ────────────────────────────────────────────────────── asio::io_context io; // Capture all locals by reference for the factory lambda (io lifetime is > factory) voicecat::net::TcpAcceptor acceptor( io, cfg_.bind_port, [&](asio::ip::tcp::socket sock) -> std::shared_ptr { auto session = std::make_shared( db, registry, workers, id_mgr.identity().fingerprint, cfg_.allow_guests); // Use shared_ptr (not weak_ptr) so TcpServerConn keeps ConnSession alive. // cycle is broken by weak_tcp in the send/close fns below. voicecat::net::TcpChannelCallbacks cbs; cbs.on_frame = [session](std::vector f) { session->on_frame(std::move(f)); }; cbs.on_disconnected = [session] { session->on_disconnect(); }; cbs.on_error = [session](std::error_code) { session->on_disconnect(); }; // Create a TLS context for this connection (server role). auto tls = std::make_unique( voicecat::crypto::TlsContext::Role::Server, &id_mgr.cert()); auto tcp = std::make_shared( std::move(sock), std::move(cbs), std::move(tls)); // Give session its send/close capability (weak_ptr avoids cycle) std::weak_ptr weak_tcp = tcp; session->set_io( [weak_tcp](std::vector frame) { if (auto t = weak_tcp.lock()) t->send_frame(std::move(frame)); }, [weak_tcp] { if (auto t = weak_tcp.lock()) t->close(); }); uint64_t sid = registry->register_session(session); session->set_session_id(sid); session->begin(); // NOTE: do NOT call tcp->start() here — TcpAcceptor::do_accept() calls it. return tcp; }); acceptor.start(); // Arm programmatic stop (for tests and embedders). { std::lock_guard lk(stop_mutex_); stop_fn_ = [&io] { io.stop(); }; } // Notify caller of the actual bound port (matters when bind_port==0). uint16_t bound = acceptor.local_port(); if (cfg_.on_ready) cfg_.on_ready(bound); // Graceful shutdown on SIGINT/SIGTERM asio::signal_set signals(io, SIGINT, SIGTERM); signals.async_wait([&](std::error_code, int sig) { std::printf("\n[server] signal %d — shutting down\n", sig); acceptor.stop(); io.stop(); }); std::printf("[voicecat-server] %s — listening on :%u\n", cfg_.server_name.c_str(), bound); std::printf("[voicecat-server] fingerprint: %s\n", id_mgr.fingerprint_display().c_str()); io.run(); { std::lock_guard lk(stop_mutex_); stop_fn_ = nullptr; } workers->join(); return 0; } void Server::stop() { std::lock_guard lk(stop_mutex_); if (stop_fn_) stop_fn_(); } } // namespace voicecat::server #else // !VOICECAT_HAS_NET namespace voicecat::server { int Server::run() { std::fprintf(stderr, "[server] stub: VOICECAT_HAS_NET not defined (build with m1-dev)\n"); std::printf(" server_name : %s\n", cfg_.server_name.c_str()); std::printf(" data_dir : %s\n", cfg_.data_dir.c_str()); std::printf(" bind_port : %u\n", cfg_.bind_port); return 0; } void Server::stop() {} } // namespace voicecat::server #endif // VOICECAT_HAS_NET