| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- #pragma once
- #include <memory>
- #include <mutex>
- #include <set>
- #include "http_server.hpp"
- #include "websocket_server.hpp"
- #include "auth/jwt_utils.hpp"
- #include "auth/auth_store.hpp"
- #include "auth/auth_middleware.hpp"
- #include "runners/runner_registry.hpp"
- #include "runners/load_balancer.hpp"
- #include "storage/storage_client.hpp"
- #include "config/config_loader.hpp"
- #include "scheduler/workflow_scheduler.hpp"
- #include "scheduler/database_watcher.hpp"
- #include "api/project_controller.hpp"
- #include "auth/access.hpp"
- #include "retention/retention_service.hpp"
- #include "settings/settings_store.hpp"
- #include "settings/settings_accessor.hpp"
- namespace smartbotic::webserver::nodes {
- class NodeStore;
- }
- namespace smartbotic::webserver::grpc {
- class NodeSyncServer;
- class CredentialServer;
- class WorkflowControlServiceImpl;
- }
- namespace smartbotic::credentials {
- class CredentialStore;
- }
- namespace smartbotic::webserver::api {
- class AuthController;
- class UserController;
- class WorkflowController;
- class WorkflowGroupController;
- class ExecutionController;
- class FileController;
- class ProxyController;
- class NodeController;
- class RunnerController;
- class WebhookController;
- class DatabaseController;
- class CredentialController;
- class CertificateController;
- class SettingsController;
- }
- namespace smartbotic::webserver {
- // Server-side projection of the executions collection. The list endpoint reads
- // this instead of the collection so the database never sends nodeExecutions,
- // which carry base64 images and dwarf every other field.
- inline constexpr const char* kExecutionsSummaryView = "executions_summary";
- struct CredentialsConfig {
- std::string master_key = "dev-key-change-in-production";
- int pbkdf2_iterations = 100000;
- };
- struct WebServerServiceConfig {
- int http_port = 8080;
- int node_sync_port = 9002; // gRPC port for node sync service
- int credential_service_port = 9003; // gRPC port for credential service
- std::string static_files_path = "./webui/dist";
- std::string database_address = "localhost:9004";
- std::string database_project = "smartbotic-automation";
- int max_upload_mb = 32;
- // Bounds WebhookController's dispatch pool, which runs immediate-mode
- // form submissions off the request thread. The form endpoint is public
- // and unauthenticated by design, so these keep a burst of submissions
- // from spawning without limit.
- int form_dispatch_threads = 4;
- int form_dispatch_queue_capacity = 32;
- // Bounds WorkflowController's dispatch pool, which runs the editor's
- // "Execute" button off the request thread for the same reason the form
- // pool above exists - the runner's ExecuteWorkflow RPC is synchronous
- // regardless of wait_for_completion. This path is authenticated, so the
- // exhaustion risk is lower than the form pool's, but bounding it still
- // costs nothing.
- int workflow_dispatch_threads = 4;
- int workflow_dispatch_queue_capacity = 32;
- runners::RunnerRegistryConfig runner_config;
- runners::LoadBalancerConfig load_balancer_config;
- auth::JwtUtils::Config jwt_config;
- CredentialsConfig credentials_config;
- };
- class WebServerService {
- public:
- explicit WebServerService(const WebServerServiceConfig& config);
- ~WebServerService();
- static WebServerServiceConfig loadConfig(const std::filesystem::path& path);
- void start();
- void stop();
- // Access components
- auth::AuthStore& authStore() { return *auth_store_; }
- runners::RunnerRegistry& runnerRegistry() { return *runner_registry_; }
- WebSocketServer& wsServer() { return *ws_server_; }
- WorkflowScheduler& scheduler() { return *scheduler_; }
- private:
- void setupRoutes();
- void loadScheduledWorkflows();
- void ensureExecutionsSummaryView();
- void executeScheduledWorkflow(const std::string& workflow_id,
- const std::string& trigger_node_id,
- const std::string& trigger_type,
- const nlohmann::json& extra_trigger_data = nlohmann::json::object());
- // Runs the workflow a failing workflow nominates as its error handler, if
- // it names one. Called when an execution reports failure.
- // Count how many times in a row a workflow has failed, and switch it off
- // when that passes what its settings allow. The count lives on the workflow
- // rather than in memory: a restart must not forgive a workflow that has
- // been failing for an hour, and the number is worth being able to see.
- void noteExecutionOutcome(const std::string& workflow_id,
- const std::string& execution_id,
- bool failed,
- const std::string& error);
- // Close executions a runner cannot possibly still be running. A runner
- // holds what it is executing in memory, so anything recorded as running
- // against one that has just come up has nobody left to finish it - and
- // would sit at "running" until somebody noticed and pressed Stop.
- void reconcileOrphanedExecutions(const std::string& runner_id,
- const std::string& address);
- void runErrorWorkflow(const std::string& failed_workflow_id,
- const std::string& failed_execution_id,
- const std::string& error_message);
- WebServerServiceConfig config_;
- // Core components
- std::unique_ptr<storage::StorageClient> storage_;
- // Built right after storage_ and before anything that reads a live
- // setting - a live accessor handed out before this exists would be a
- // reference to nothing.
- std::unique_ptr<settings::SettingsStore> settings_store_;
- std::unique_ptr<settings::SettingsAccessor> settings_;
- std::unique_ptr<auth::JwtUtils> jwt_;
- std::unique_ptr<auth::AuthStore> auth_store_;
- std::unique_ptr<auth::AuthMiddleware> auth_middleware_;
- std::unique_ptr<runners::RunnerRegistry> runner_registry_;
- std::unique_ptr<runners::LoadBalancer> load_balancer_;
- std::unique_ptr<nodes::NodeStore> node_store_;
- std::unique_ptr<retention::RetentionService> retention_;
- std::unique_ptr<credentials::CredentialStore> credential_store_;
- // Servers
- std::unique_ptr<HttpServer> http_server_;
- std::unique_ptr<WebSocketServer> ws_server_;
- std::unique_ptr<grpc::NodeSyncServer> node_sync_server_;
- std::unique_ptr<grpc::CredentialServer> credential_server_;
- std::unique_ptr<grpc::WorkflowControlServiceImpl> workflow_control_service_;
- // Scheduler
- std::unique_ptr<WorkflowScheduler> scheduler_;
- std::unique_ptr<DatabaseWatcher> db_watcher_;
- std::unique_ptr<auth::AccessControl> access_;
- std::unique_ptr<api::ProjectController> project_ctrl_;
- // Controllers (must outlive httplib::Server callbacks)
- std::unique_ptr<api::AuthController> auth_ctrl_;
- std::unique_ptr<api::UserController> user_ctrl_;
- std::unique_ptr<api::WorkflowController> workflow_ctrl_;
- std::unique_ptr<api::WorkflowGroupController> workflow_group_ctrl_;
- std::unique_ptr<api::ExecutionController> execution_ctrl_;
- std::unique_ptr<api::FileController> file_ctrl_;
- std::unique_ptr<api::ProxyController> proxy_ctrl_;
- // Execution events can arrive more than once for the same execution, so a
- // failure is remembered briefly to keep one failure from starting the error
- // workflow twice.
- std::mutex handled_failures_mutex_;
- std::set<std::string> handled_failures_;
- std::unique_ptr<api::NodeController> node_ctrl_;
- std::unique_ptr<api::RunnerController> runner_ctrl_;
- std::unique_ptr<api::WebhookController> webhook_ctrl_;
- std::unique_ptr<api::DatabaseController> database_ctrl_;
- std::unique_ptr<api::CredentialController> credential_ctrl_;
- std::unique_ptr<api::CertificateController> certificate_ctrl_;
- std::unique_ptr<api::SettingsController> settings_ctrl_;
- };
- } // namespace smartbotic::webserver
|