@@ -24,6 +24,8 @@ import {
2424 getDbStatus
2525} from './db'
2626import { GlobalSettingsInitService } from './global-settings'
27+ import { GlobalSettings } from './global-settings/helpers' ;
28+ import { GlobalSettingsService } from './services/globalSettingsService' ; // Import the service
2729import type SqliteDriver from 'better-sqlite3' ; // For type checking in onClose
2830import type { FastifyInstance } from 'fastify'
2931
@@ -145,19 +147,85 @@ export const createServer = async () => {
145147 } ) ;
146148 server . log . info ( '@fastify/cookie registered.' ) ;
147149
148- // Register Swagger for API documentation
150+ await registerFastifyPlugins ( server ) // Existing plugin registrations
151+
152+ // Register favicon after Swagger to exclude it from documentation
153+ const fastifyFavicon = await import ( 'fastify-favicon' ) ;
154+ await server . register ( fastifyFavicon . default , {
155+ path : '../shared/public/img' ,
156+ name : 'favicon.ico' ,
157+ maxAge : 604800
158+ } )
159+
160+ // Register the global authentication hook
161+ // This hook will run on every request to populate request.user and request.session
162+ server . addHook ( 'onRequest' , authHook ) ;
163+ server . log . info ( 'Global auth hook registered.' ) ;
164+
165+ // Create and configure the plugin manager
166+ const isDevelopment = process . env . NODE_ENV !== 'production' ;
167+ const pluginManager = new PluginManager ( {
168+ paths : [
169+ process . env . PLUGINS_PATH || ( isDevelopment
170+ ? path . join ( process . cwd ( ) , 'src' , 'plugins' )
171+ : path . join ( __dirname , 'plugins' ) ) ,
172+ ] ,
173+ plugins : { }
174+ } )
175+
176+ pluginManager . setApp ( server ) ; // Set app early for plugins that might need it
177+
178+ // Discover available plugins first
179+ await pluginManager . discoverPlugins ( ) ;
180+
181+ // Register plugin table definitions (populates inputPluginTableDefinitions in db/index.ts)
182+ // This must happen before initializeDatabase, which generates the actual schema
183+ registerPluginTables ( pluginManager . getAllPlugins ( ) ) ;
184+
185+ // Initialize database-dependent services
186+ await initializeDatabaseDependentServices ( server , pluginManager ) ;
187+
188+ // Conditionally register Swagger for API documentation
189+ // This is placed after DB & global settings initialization to ensure settings are available
190+ let swaggerEnabled : boolean ;
191+ if ( ( server as any ) . db === null ) {
192+ server . log . info ( 'Database not available. Enabling Swagger documentation by default during setup phase.' ) ;
193+ swaggerEnabled = true ;
194+ } else {
195+ try {
196+ server . log . info ( 'Database is available. Checking "global.enable_swagger_docs" setting.' ) ;
197+ swaggerEnabled = await GlobalSettings . getBoolean ( 'global.enable_swagger_docs' , true ) ;
198+ // The log message below was removed as it's covered by more specific logs later.
199+ } catch ( error ) {
200+ server . log . error ( 'Error fetching "global.enable_swagger_docs" setting. Defaulting to true.' , error ) ;
201+ swaggerEnabled = true ;
202+ }
203+ }
204+
205+ // Always register Swagger and SwaggerUI. Access will be controlled by the preHandler.
206+ // The swaggerEnabled variable is now only used for an initial log message.
207+ if ( swaggerEnabled ) {
208+ server . log . info ( 'Initial check: Swagger documentation will be attempted to register. Access controlled by preHandler.' ) ;
209+ } else {
210+ server . log . info ( 'Initial check: Swagger documentation was disabled by setting at startup, but routes will still be registered. Access controlled by preHandler.' ) ;
211+ }
212+
213+ const host = process . env . HOST || 'localhost' ;
214+ const port = process . env . PORT || '3000' ;
215+ const serverUrl = `http://${ host } :${ port } ` ;
216+
149217 await server . register ( fastifySwagger , {
150218 openapi : {
151219 openapi : '3.0.0' ,
152220 info : {
153221 title : 'DeployStack Backend API' ,
154222 description : 'API documentation for DeployStack Backend' ,
155- version : '0.20.5'
223+ version : '0.20.5' // We need to make this dynamic from package.json
156224 } ,
157225 servers : [
158226 {
159- url : 'http://localhost:3000' ,
160- description : ' Development server'
227+ url : serverUrl ,
228+ description : process . env . NODE_ENV === 'development' ? ' Development server' : 'Server '
161229 }
162230 ] ,
163231 components : {
@@ -181,11 +249,39 @@ export const createServer = async () => {
181249 } ,
182250 uiHooks : {
183251 onRequest : function ( _request , _reply , next ) { next ( ) } ,
184- preHandler : function ( _request , _reply , next ) { next ( ) }
252+ preHandler : async function ( request , reply , next ) {
253+ // On-the-fly check for swagger documentation
254+ let showSwagger = true ; // Default to true if setting is missing or error occurs
255+ if ( ( request . server as any ) . db !== null ) {
256+ try {
257+ await GlobalSettings . refreshCaches ( ) ; // Attempt to refresh any underlying caches
258+ const setting = await GlobalSettingsService . get ( 'global.enable_swagger_docs' ) ;
259+ if ( setting && typeof setting . value === 'string' ) {
260+ const valueLower = setting . value . toLowerCase ( ) ;
261+ showSwagger = ! ( valueLower === 'false' || valueLower === '0' || valueLower === 'no' || valueLower === 'off' || valueLower === 'disabled' ) ;
262+ } else {
263+ // If setting is not found or value is not a string, default to true (as per defaultValue in global.ts)
264+ showSwagger = true ;
265+ }
266+ request . server . log . info ( `Swagger UI access check (using Service): "global.enable_swagger_docs" is ${ showSwagger } . Raw value: ${ setting ? setting . value : 'Not found' } ` ) ;
267+ } catch ( err ) {
268+ request . server . log . error ( 'Error fetching "global.enable_swagger_docs" with Service in preHandler. Defaulting to show Swagger.' , err ) ;
269+ showSwagger = true ;
270+ }
271+ } else {
272+ request . server . log . info ( 'Swagger UI access check: Database not available, showing Swagger UI by default.' ) ;
273+ showSwagger = true ;
274+ }
275+
276+ if ( ! showSwagger ) {
277+ reply . code ( 404 ) . send ( { error : 'Not Found' , message : 'API documentation is disabled.' } ) ;
278+ } else {
279+ next ( ) ;
280+ }
281+ }
185282 } ,
186283 staticCSP : true ,
187284 transformStaticCSP : ( header ) => header ,
188- // eslint-disable-next-line @typescript-eslint/no-unused-vars
189285 transformSpecification : ( swaggerObject , _request , _reply ) => {
190286 // Remove favicon route from the API specification
191287 if ( swaggerObject . paths && swaggerObject . paths [ '/favicon.ico' ] ) {
@@ -195,45 +291,9 @@ export const createServer = async () => {
195291 } ,
196292 transformSpecificationClone : true
197293 } ) ;
198- server . log . info ( 'Swagger documentation registered at /documentation' ) ;
199-
200- await registerFastifyPlugins ( server ) // Existing plugin registrations
201-
202- // Register favicon after Swagger to exclude it from documentation
203- const fastifyFavicon = await import ( 'fastify-favicon' ) ;
204- await server . register ( fastifyFavicon . default , {
205- path : '../shared/public/img' ,
206- name : 'favicon.ico' ,
207- maxAge : 604800
208- } )
209-
210- // Register the global authentication hook
211- // This hook will run on every request to populate request.user and request.session
212- server . addHook ( 'onRequest' , authHook ) ;
213- server . log . info ( 'Global auth hook registered.' ) ;
214-
215- // Create and configure the plugin manager
216- const isDevelopment = process . env . NODE_ENV !== 'production' ;
217- const pluginManager = new PluginManager ( {
218- paths : [
219- process . env . PLUGINS_PATH || ( isDevelopment
220- ? path . join ( process . cwd ( ) , 'src' , 'plugins' )
221- : path . join ( __dirname , 'plugins' ) ) ,
222- ] ,
223- plugins : { }
224- } )
225-
226- pluginManager . setApp ( server ) ; // Set app early for plugins that might need it
227-
228- // Discover available plugins first
229- await pluginManager . discoverPlugins ( ) ;
230-
231- // Register plugin table definitions (populates inputPluginTableDefinitions in db/index.ts)
232- // This must happen before initializeDatabase, which generates the actual schema
233- registerPluginTables ( pluginManager . getAllPlugins ( ) ) ;
234-
235- // Initialize database-dependent services
236- await initializeDatabaseDependentServices ( server , pluginManager ) ;
294+ // Log registration; preHandler will control access dynamically
295+ server . log . info ( 'Swagger documentation routes registered at /documentation. Access is dynamically controlled by the "global.enable_swagger_docs" setting via a preHandler.' ) ;
296+ // The `else` block related to initial swaggerEnabled check is no longer needed here as routes are always registered.
237297
238298 // Initialize plugins (routes, hooks, etc.)
239299 // This should happen after DB and other core services are ready (or known to be unavailable)
0 commit comments