|
|
@@ -1,15 +1,85 @@
|
|
|
+#include "auth.hpp"
|
|
|
+#include "collection_registry.hpp"
|
|
|
+#include "config.hpp"
|
|
|
+#include "db_gateway.hpp"
|
|
|
+#include "key_store.hpp"
|
|
|
+#include "server.hpp"
|
|
|
+#include "settings_store.hpp"
|
|
|
#include <svapi/version.hpp>
|
|
|
+#include <spdlog/spdlog.h>
|
|
|
+#ifdef HAVE_SYSTEMD
|
|
|
+#include <systemd/sd-daemon.h>
|
|
|
+#else
|
|
|
+#define sd_notify(u, s) do {} while (0)
|
|
|
+#endif
|
|
|
+#include <atomic>
|
|
|
+#include <chrono>
|
|
|
+#include <csignal>
|
|
|
+#include <cstdlib>
|
|
|
#include <cstring>
|
|
|
#include <iostream>
|
|
|
+#include <thread>
|
|
|
+
|
|
|
+namespace {
|
|
|
+std::atomic<bool> g_running{true};
|
|
|
+void onSignal(int) { g_running = false; }
|
|
|
+std::string envOr(const char* k, const std::string& dflt = "") { const char* v = std::getenv(k); return v ? std::string(v) : dflt; }
|
|
|
+}
|
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
+ std::string configPath = "/etc/smartbotic-vectorapi/config.json";
|
|
|
+ std::string webuiDirOverride;
|
|
|
+ std::string shareDir = "/usr/share/smartbotic-vectorapi";
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
|
- if (std::strcmp(argv[i], "--version") == 0 || std::strcmp(argv[i], "-v") == 0) {
|
|
|
- std::cout << "smartbotic-vectorapi " << svapi::VERSION
|
|
|
- << " (" << svapi::GIT_COMMIT << ")\n";
|
|
|
+ if (std::strcmp(argv[i], "--config") == 0 && i + 1 < argc) configPath = argv[++i];
|
|
|
+ else if (std::strcmp(argv[i], "--webui-dir") == 0 && i + 1 < argc) webuiDirOverride = argv[++i];
|
|
|
+ else if (std::strcmp(argv[i], "--share-dir") == 0 && i + 1 < argc) shareDir = argv[++i];
|
|
|
+ else if (std::strcmp(argv[i], "--version") == 0 || std::strcmp(argv[i], "-v") == 0) {
|
|
|
+ std::cout << "smartbotic-vectorapi " << svapi::VERSION << " (" << svapi::GIT_COMMIT << ")\n";
|
|
|
return 0;
|
|
|
}
|
|
|
}
|
|
|
- std::cout << "smartbotic-vectorapi " << svapi::VERSION << " — not yet wired (see plan)\n";
|
|
|
+
|
|
|
+ svapi::Config cfg;
|
|
|
+ try { cfg = svapi::Config::load(configPath); }
|
|
|
+ catch (const std::exception& e) { std::cerr << "config error: " << e.what() << "\n"; return 1; }
|
|
|
+ spdlog::set_level(spdlog::level::from_str(cfg.logLevel));
|
|
|
+ if (!webuiDirOverride.empty()) cfg.webuiDir = webuiDirOverride;
|
|
|
+
|
|
|
+ std::signal(SIGINT, onSignal); std::signal(SIGTERM, onSignal); std::signal(SIGPIPE, SIG_IGN);
|
|
|
+
|
|
|
+ svapi::DbGateway db(cfg.dbAddress);
|
|
|
+ spdlog::info("connecting to smartbotic-database at {}", cfg.dbAddress);
|
|
|
+ for (int i = 0; i < 30 && !db.connect(); ++i) std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
|
+ if (!db.healthy()) { spdlog::error("database not reachable at {}", cfg.dbAddress); return 1; }
|
|
|
+
|
|
|
+ svapi::SettingsStore settings(db);
|
|
|
+ settings.bootstrap(envOr("OPENAI_API_KEY"));
|
|
|
+ settings.startWatch();
|
|
|
+
|
|
|
+ svapi::KeyStore keys(db);
|
|
|
+ keys.bootstrap(envOr("SMARTBOTIC_VECTORAPI_KEY"));
|
|
|
+ keys.startWatch();
|
|
|
+
|
|
|
+ svapi::CollectionRegistry registry(db);
|
|
|
+ const std::string defProj = settings.snapshot()->defaultProject;
|
|
|
+ db.ensureProject(defProj);
|
|
|
+ registry.ensure(defProj);
|
|
|
+
|
|
|
+ svapi::SessionStore sessions(std::chrono::minutes(settings.snapshot()->sessionTtlMinutes));
|
|
|
+
|
|
|
+ svapi::ServerDeps deps{db, settings, keys, registry, sessions, cfg.webuiDir, shareDir};
|
|
|
+ svapi::ApiServer server(std::move(deps));
|
|
|
+ server.registerRoutes();
|
|
|
+
|
|
|
+ std::thread http([&] {
|
|
|
+ spdlog::info("listening on {}:{}", cfg.httpBind, cfg.httpPort);
|
|
|
+ if (!server.listen(cfg.httpBind, cfg.httpPort)) { spdlog::error("bind failed"); g_running = false; }
|
|
|
+ });
|
|
|
+ sd_notify(0, "READY=1");
|
|
|
+ while (g_running) { sd_notify(0, "WATCHDOG=1"); std::this_thread::sleep_for(std::chrono::milliseconds(200)); }
|
|
|
+ sd_notify(0, "STOPPING=1");
|
|
|
+ server.stop();
|
|
|
+ if (http.joinable()) http.join();
|
|
|
return 0;
|
|
|
}
|