UNPKG

@n2flowjs/nbase

Version:

Neural Vector Database for efficient similarity search

228 lines 9.23 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const cors_1 = __importDefault(require("cors")); const express_1 = __importDefault(require("express")); const express_rate_limit_1 = __importDefault(require("express-rate-limit")); const helmet_1 = __importDefault(require("helmet")); const net_1 = __importDefault(require("net")); const config_1 = __importDefault(require("../config")); const profiling_1 = require("../utils/profiling"); // Import Database class const database_1 = require("../database/database"); // Import middleware const common_1 = require("./middleware/common"); // Import routes const index_1 = require("./routes/index"); const search_1 = require("./routes/search"); const vectors_1 = require("./routes/vectors"); const filters_1 = require("./utils/filters"); /** * Checks if a port is in use * @param port Port to check * @returns Promise resolving to true if port is in use, false otherwise */ async function isPortInUse(port) { return new Promise((resolve) => { const server = net_1.default .createServer() .once('error', (err) => { if (err.code === 'EADDRINUSE') { resolve(true); } else { resolve(false); } }) .once('listening', () => { server.close(); resolve(false); }) .listen(port); }); } /** * Creates and configures an Express server instance with the provided options. * * @param options - Configuration options for the server. If not provided, defaults will be used. * @returns An object representing the server instance, including the Express app, * a graceful shutdown handler, the database instance, and the API context. * * ### Options * - `port` (number): The port on which the server will listen. Defaults to `configDefaults.server.port`. * - `host` (string): The host address for the server. Defaults to `configDefaults.server.host`. * - `database` (object): Database configuration options, including clustering, indexing, partitioning, * persistence, and monitoring. Defaults to `configDefaults`. * - `rateLimit` (object): Rate limiting configuration. Defaults to `configDefaults.server.rateLimit`. * - `middleware` (array): Custom middleware functions to apply to the server. Defaults to an empty array. * - `debug` (boolean): Enables debug logging if set to `true`. Defaults to `false`. * - `errorHandler` (function): Custom error handling middleware. Defaults to a generic error handler. * * ### Features * - Configures security headers using `helmet`. * - Enables CORS with `cors`. * - Supports large JSON payloads with a size limit of 50MB. * - Adds logging middleware for request logging. * - Supports custom middleware injection. * - Implements rate limiting for API routes. * - Provides debug logging for incoming requests when `debug` is enabled. * - Initializes a database instance with the provided configuration. * - Registers API routes for vectors, search, and index. * - Includes error handling and a "Not Found" handler for unregistered routes. * - Provides a graceful shutdown mechanism to close database connections properly. * * ### Returns * The returned `IServerInstance` object includes: * - `app`: The configured Express application. * - `gracefulShutdown`: A function to handle graceful shutdown of the server. * - `database`: The initialized database instance. * - `context`: The API context containing shared resources. */ function createServer(options = {}) { const app = (0, express_1.default)(); // Merge default config with provided options const serverOptions = { port: options.port || config_1.default.server.port, host: options.host || config_1.default.server.host, database: { ...{ clustering: config_1.default.clustering, indexing: config_1.default.indexing, partitioning: config_1.default.partitioning, persistence: config_1.default.persistence, monitoring: config_1.default.monitoring, }, ...options.database, }, rateLimit: { ...config_1.default.server.rateLimit, ...options.rateLimit }, middleware: options.middleware || [], debug: options.debug || false, errorHandler: options.errorHandler || ((err, req, res, next) => { console.error('API Error:', err); res.status(500).json({ error: err.message, stack: process.env.NODE_ENV === 'development' ? err.stack : undefined, }); }), }; // Request timer for performance monitoring const timer = (0, profiling_1.createTimer)(); // Configure middleware app.use((0, helmet_1.default)()); // Security headers app.use((0, cors_1.default)()); // Cross-origin resource sharing app.use(express_1.default.json({ limit: '50mb' })); // JSON body parser with large payload support // Add logging middleware app.use(common_1.loggingMiddleware); // Apply custom middleware serverOptions.middleware.forEach((middleware) => app.use(middleware)); // Rate limiting if (serverOptions.rateLimit.enable) { const apiLimiter = (0, express_rate_limit_1.default)({ windowMs: serverOptions.rateLimit.windowMs, max: serverOptions.rateLimit.maxRequestsPerMinute, standardHeaders: true, legacyHeaders: false, message: 'Too many requests, please try again later', }); app.use('/api/', apiLimiter); } // Debug logging if (serverOptions.debug) { // Add debug logging for request URLs app.use((req, res, next) => { console.log(`DEBUG: Incoming request: ${req.method} ${req.originalUrl}`); next(); }); } // Initialize database instance with options const database = new database_1.Database(serverOptions.database); // Create context object with shared resources const apiContext = { timer, createFilterFunction: filters_1.createFilterFunction, database, }; /** * API Routes */ // Register route handlers app.use('/api/vectors', (0, vectors_1.vectorRoutes)(apiContext)); app.use('/api/search', (0, search_1.searchRoutes)(apiContext)); app.use('/', (0, index_1.indexRoutes)(apiContext)); // Error handling middleware app.use(serverOptions.errorHandler); // Not Found handler app.use((req, res) => { res.status(404).json({ error: `Route not found: ${req.method} ${req.url}`, }); }); // Graceful shutdown handler for proper database closing const gracefulShutdown = async () => { console.log('Closing database connections...'); await database.close().catch(console.error); console.log('Database connections closed'); }; // For backward compatibility with tests - attach gracefulShutdown to app app.gracefulShutdown = gracefulShutdown; // Return both for new code that expects the new format // but also make app the default export for backward compatibility const result = { app, gracefulShutdown, database, context: apiContext, }; Object.defineProperty(result, 'default', { value: app, }); return result; } /** * Start server */ if (require.main === module) { const startServer = async () => { const { app, gracefulShutdown } = createServer({ database: { persistence: config_1.default.persistence, partitioning: config_1.default.partitioning, indexing: config_1.default.indexing, clustering: { ...config_1.default.clustering, useCompression: false, }, }, }); const PORT = process.env.PORT || config_1.default.server.port || 1307; const HOST = process.env.HOST || config_1.default.server.host || 'localhost'; // Check if port is in use and kill process if necessary const portInUse = await isPortInUse(Number(PORT)); if (portInUse) { console.log('Port is used'); return; } const server = app.listen(PORT, () => { console.log(`API Server running on http://${HOST}:${PORT}`); }); // Graceful shutdown process.on('SIGTERM', async () => { console.log('SIGTERM received, shutting down gracefully'); await gracefulShutdown(); server.close(() => { console.log('HTTP server closed'); process.exit(0); }); }); }; startServer().catch((err) => { console.error('Failed to start server:', err); process.exit(1); }); } // Export server factory function exports.default = createServer; //# sourceMappingURL=index.js.map