import Vapor import Fluent import Clairvoyant import ClairvoyantBinaryCodable import ClairvoyantVapor var server: SQLiteDatabase! private var provider: VaporMetricProvider! = nil private var status: Metric! private let scheduler = MultiThreadedEventLoopGroup(numberOfThreads: 2) private var configurationError: Error? = nil func configure(_ app: Application) async throws { let resourceFolder = URL(fileURLWithPath: app.directory.resourcesDirectory) let publicDirectory = app.directory.publicDirectory let configPath = URL(fileURLWithPath: app.directory.resourcesDirectory) .appendingPathComponent("config.json") let configuration = try Configuration(loadFromUrl: configPath) let logFolder = configuration.logURL(possiblyRelativeTo: resourceFolder) let monitor = MetricObserver( logFileFolder: logFolder, logMetricId: "schafkopf.log") MetricObserver.standard = monitor status = Metric( "schafkopf.status", name: "Status", description: "The main status of the server") try await status.update(.initializing) app.http.server.configuration.port = configuration.serverPort switch app.environment { case .production: log("[DEVELOPMENT] Using in-memory database") app.databases.use(.sqlite(.memory), as: .sqlite) default: app.logger.logLevel = .notice let dataDirectory = configuration.customDataDirectory(or: publicDirectory) let dbFile = dataDirectory.appendingPathComponent("db.sqlite").path log("[PRODUCTION] Using database at \(dbFile)") app.databases.use(.sqlite(.file(dbFile)), as: .sqlite) } app.migrations.add(UserTableMigration()) app.migrations.add(PasswordResetMigration()) do { try await app.autoMigrate() } catch { await monitor.log("Failed to migrate database: \(error)") try await status.update(.initializationFailure) return } // serve files from /Public folder app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory)) let db = app.databases.database(.sqlite, logger: .init(label: "Init"), on: app.databases.eventLoopGroup.next())! server = try await SQLiteDatabase(database: db, mail: configuration.mail) // register routes routes(app) // Expose metrics provider = .init(observer: monitor, accessManager: configuration.monitoringTokens) provider.asyncScheduler = scheduler provider.registerRoutes(app) try await status.update(.nominal) } func shutdown() { scheduler.schedule { await server.disconnectAllSockets() try await scheduler.shutdownGracefully() } } func log(_ message: String) { guard let observer = MetricObserver.standard else { print(message) return } scheduler.schedule { await observer.log(message) } }