app.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #include "app.hpp"
  2. #include "stores/user_store.hpp"
  3. #include "stores/workspace_store.hpp"
  4. #include "stores/invitation_store.hpp"
  5. #include "stores/assistant_store.hpp"
  6. #include "stores/calendar_store.hpp"
  7. #include "stores/settings_store.hpp"
  8. #include "services/auth_service.hpp"
  9. #include "services/workspace_service.hpp"
  10. #include "services/invitation_service.hpp"
  11. #include "services/assistant_service.hpp"
  12. #include "services/calendar_service.hpp"
  13. #include "routes/auth_routes.hpp"
  14. #include "routes/user_routes.hpp"
  15. #include "routes/workspace_routes.hpp"
  16. #include "routes/invitation_routes.hpp"
  17. #include "routes/assistant_routes.hpp"
  18. #include "routes/calendar_routes.hpp"
  19. #include "routes/settings_routes.hpp"
  20. #include <smartbotic/microbit/common/config_loader.hpp>
  21. #include <spdlog/spdlog.h>
  22. #include <filesystem>
  23. namespace smartbotic::microbit {
  24. App::App(const json& config) : config_(config) {
  25. bindAddress_ = ConfigLoader::get<std::string>(config_, "http.bind_address", "0.0.0.0");
  26. httpPort_ = ConfigLoader::get<uint16_t>(config_, "http.port", 8090);
  27. staticFilesPath_ = ConfigLoader::get<std::string>(config_, "http.static_files.path", "webui/dist");
  28. }
  29. App::~App() {
  30. stop();
  31. }
  32. void App::connectDatabase() {
  33. smartbotic::database::Client::Config dbConfig;
  34. dbConfig.address = ConfigLoader::get<std::string>(config_, "database.rpc_address", "localhost:9004");
  35. dbConfig.timeoutMs = ConfigLoader::get<uint32_t>(config_, "database.timeout_ms", 5000);
  36. dbConfig.maxRetries = ConfigLoader::get<uint32_t>(config_, "database.max_retries", 3);
  37. db_ = std::make_unique<smartbotic::database::Client>(dbConfig);
  38. if (!db_->connect()) {
  39. throw std::runtime_error("Failed to connect to database at " + dbConfig.address);
  40. }
  41. spdlog::info("Connected to database at {}", dbConfig.address);
  42. }
  43. void App::initializeComponents() {
  44. // Auth middleware
  45. auth::AuthConfig authConfig;
  46. authConfig.enabled = ConfigLoader::get<bool>(config_, "auth.enabled", true);
  47. authConfig.jwtSecret = ConfigLoader::get<std::string>(config_, "auth.jwt_secret", "");
  48. authConfig.accessTokenLifetimeSec = ConfigLoader::get<uint32_t>(config_, "auth.access_token_lifetime_sec", 900);
  49. authConfig.refreshTokenLifetimeSec = ConfigLoader::get<uint32_t>(config_, "auth.refresh_token_lifetime_sec", 604800);
  50. authConfig.minPasswordLength = ConfigLoader::get<uint32_t>(config_, "auth.password_policy.min_length", 8);
  51. authConfig.requireNumber = ConfigLoader::get<bool>(config_, "auth.password_policy.require_number", true);
  52. authConfig.requireSpecial = ConfigLoader::get<bool>(config_, "auth.password_policy.require_special", false);
  53. if (authConfig.jwtSecret.empty() || authConfig.jwtSecret == "change-me-in-production") {
  54. spdlog::warn("JWT secret not configured or using default. Set JWT_SECRET environment variable.");
  55. }
  56. authMiddleware_ = std::make_unique<auth::AuthMiddleware>(authConfig);
  57. // SMTP client
  58. smtp::SmtpConfig smtpConfig;
  59. smtpConfig.host = ConfigLoader::get<std::string>(config_, "smtp.host", "");
  60. smtpConfig.port = ConfigLoader::get<uint16_t>(config_, "smtp.port", 587);
  61. smtpConfig.username = ConfigLoader::get<std::string>(config_, "smtp.username", "");
  62. smtpConfig.password = ConfigLoader::get<std::string>(config_, "smtp.password", "");
  63. smtpConfig.fromAddress = ConfigLoader::get<std::string>(config_, "smtp.from_address", "noreply@smartbotics.ai");
  64. smtpConfig.fromName = ConfigLoader::get<std::string>(config_, "smtp.from_name", "Smartbotic");
  65. smtpConfig.useTls = ConfigLoader::get<bool>(config_, "smtp.use_tls", true);
  66. smtpClient_ = std::make_unique<smtp::SmtpClient>(smtpConfig);
  67. // CallerAI client
  68. callerai::CallerAIConfig calleraiConfig;
  69. calleraiConfig.apiUrl = ConfigLoader::get<std::string>(config_, "callerai.api_url", "http://localhost:8080");
  70. calleraiConfig.apiKey = ConfigLoader::get<std::string>(config_, "callerai.api_key", "");
  71. calleraiConfig.timeoutSec = ConfigLoader::get<uint32_t>(config_, "callerai.timeout_sec", 30);
  72. calleraiClient_ = std::make_unique<callerai::CallerAIClient>(calleraiConfig);
  73. // Stores
  74. userStore_ = std::make_unique<UserStore>(db_.get());
  75. workspaceStore_ = std::make_unique<WorkspaceStore>(db_.get());
  76. invitationStore_ = std::make_unique<InvitationStore>(db_.get());
  77. assistantStore_ = std::make_unique<AssistantStore>(db_.get());
  78. calendarStore_ = std::make_unique<CalendarStore>(db_.get());
  79. settingsStore_ = std::make_unique<SettingsStore>(db_.get());
  80. // Services
  81. authService_ = std::make_unique<AuthService>(userStore_.get(), authMiddleware_.get());
  82. workspaceService_ = std::make_unique<WorkspaceService>(workspaceStore_.get());
  83. invitationService_ = std::make_unique<InvitationService>(
  84. invitationStore_.get(), workspaceStore_.get(), userStore_.get(), smtpClient_.get());
  85. assistantService_ = std::make_unique<AssistantService>(assistantStore_.get(), calleraiClient_.get());
  86. calendarService_ = std::make_unique<CalendarService>(calendarStore_.get());
  87. }
  88. void App::setupCors(httplib::Server& svr) {
  89. bool corsEnabled = ConfigLoader::get<bool>(config_, "cors.enabled", true);
  90. if (!corsEnabled) return;
  91. svr.Options(R"(/api/.*)", [](const httplib::Request&, httplib::Response& res) {
  92. res.set_header("Access-Control-Allow-Origin", "*");
  93. res.set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
  94. res.set_header("Access-Control-Allow-Headers", "Authorization, Content-Type, X-Requested-With");
  95. res.set_header("Access-Control-Max-Age", "86400");
  96. res.status = 204;
  97. });
  98. svr.set_post_routing_handler([](const httplib::Request&, httplib::Response& res) {
  99. res.set_header("Access-Control-Allow-Origin", "*");
  100. });
  101. }
  102. void App::setupRoutes(httplib::Server& svr) {
  103. // Health check
  104. svr.Get("/api/v1/health", [](const httplib::Request&, httplib::Response& res) {
  105. res.set_content(R"({"status":"ok"})", "application/json");
  106. });
  107. // Auth middleware
  108. svr.set_pre_routing_handler([this](const httplib::Request& req, httplib::Response& res) {
  109. if (req.method == "OPTIONS") {
  110. return httplib::Server::HandlerResponse::Unhandled;
  111. }
  112. if (req.path.find("/api/") != 0) {
  113. return httplib::Server::HandlerResponse::Unhandled;
  114. }
  115. if (authMiddleware_->isPublicPath(req.path)) {
  116. return httplib::Server::HandlerResponse::Unhandled;
  117. }
  118. if (!authMiddleware_->authenticate(req, res)) {
  119. return httplib::Server::HandlerResponse::Handled;
  120. }
  121. return httplib::Server::HandlerResponse::Unhandled;
  122. });
  123. // Register all routes
  124. setupAuthRoutes(svr, *this);
  125. setupUserRoutes(svr, *this);
  126. setupWorkspaceRoutes(svr, *this);
  127. setupInvitationRoutes(svr, *this);
  128. setupAssistantRoutes(svr, *this);
  129. setupCalendarRoutes(svr, *this);
  130. setupSettingsRoutes(svr, *this);
  131. }
  132. void App::start() {
  133. if (running_) return;
  134. spdlog::info("Starting Smartbotic MicroBit v{}", "0.1.0");
  135. connectDatabase();
  136. initializeComponents();
  137. httpServer_ = std::make_unique<httplib::Server>();
  138. setupCors(*httpServer_);
  139. setupRoutes(*httpServer_);
  140. // Serve static files
  141. bool serveStatic = ConfigLoader::get<bool>(config_, "http.static_files.enabled", true);
  142. if (serveStatic && std::filesystem::exists(staticFilesPath_)) {
  143. httpServer_->set_mount_point("/", staticFilesPath_);
  144. spdlog::info("Serving static files from {}", staticFilesPath_);
  145. }
  146. running_ = true;
  147. httpThread_ = std::thread([this]() {
  148. spdlog::info("HTTP server listening on {}:{}", bindAddress_, httpPort_);
  149. httpServer_->listen(bindAddress_, httpPort_);
  150. });
  151. }
  152. void App::stop() {
  153. if (!running_) return;
  154. running_ = false;
  155. if (httpServer_) {
  156. httpServer_->stop();
  157. }
  158. if (httpThread_.joinable()) {
  159. httpThread_.join();
  160. }
  161. spdlog::info("Smartbotic MicroBit stopped");
  162. }
  163. } // namespace smartbotic::microbit