@@ -516,6 +516,9 @@ export function executeDbOperation<T>(
516516 return operation ( db , schema ) ;
517517}
518518
519+ // Import plugin migration functionality
520+ import { createPluginTables as createPluginTablesImpl } from './plugin-migrations' ;
521+
519522// Plugin system functions
520523interface DatabaseExtensionWithTables extends DatabaseExtension {
521524
@@ -543,240 +546,15 @@ export function registerPluginTables(plugins: Plugin[], logger?: FastifyBaseLogg
543546}
544547
545548export async function createPluginTables ( plugins : Plugin [ ] , logger : FastifyBaseLogger ) {
546- // Plugin tables are now handled by migrations
547- logger . info ( {
548- operation : 'create_plugin_tables'
549- } , 'Plugin tables are handled by migrations.' ) ;
550- return ;
551-
552- if ( ! dbInstance || ! isDbInitialized ) {
549+ if ( ! dbInstance || ! isDbInitialized || ! dbConfig ) {
553550 logger . warn ( {
554551 operation : 'create_plugin_tables'
555552 } , 'Database not initialized, skipping plugin table creation.' ) ;
556553 return ;
557554 }
558555
559- const pluginsWithTables = plugins . filter ( plugin =>
560- plugin . databaseExtension && plugin . databaseExtension . tableDefinitions
561- ) ;
562-
563- if ( pluginsWithTables . length === 0 ) {
564- logger . info ( {
565- operation : 'create_plugin_tables'
566- } , 'No plugins with table definitions found.' ) ;
567- return ;
568- }
569-
570- logger . info ( {
571- operation : 'create_plugin_tables' ,
572- pluginCount : pluginsWithTables . length
573- } , `Creating tables for ${ pluginsWithTables . length } plugins...` ) ;
574-
575- for ( const plugin of pluginsWithTables ) {
576- const ext = plugin . databaseExtension as DatabaseExtensionWithTables ;
577- if ( ! ext . tableDefinitions ) continue ;
578-
579- for ( const [ tableName , columnDefs ] of Object . entries ( ext . tableDefinitions || { } ) ) {
580- const fullTableName = `${ plugin . meta . id } _${ tableName } ` ;
581-
582- try {
583- // Generate CREATE TABLE SQL dynamically
584- const createTableSQL = generateCreateTableSQL ( fullTableName , columnDefs ) ;
585-
586- logger . debug ( {
587- operation : 'create_plugin_tables' ,
588- pluginId : plugin . meta . id ,
589- tableName : fullTableName ,
590- sql : createTableSQL
591- } , `Creating plugin table: ${ fullTableName } ` ) ;
592-
593- // Drop the table first to ensure clean recreation (for development)
594- const dropTableSQL = `DROP TABLE IF EXISTS "${ fullTableName } "` ;
595-
596- logger . debug ( {
597- operation : 'create_plugin_tables' ,
598- pluginId : plugin . meta . id ,
599- tableName : fullTableName ,
600- sql : dropTableSQL
601- } , `Dropping existing plugin table: ${ fullTableName } ` ) ;
602-
603- // Execute the DROP TABLE statement
604- if ( dbConfig ?. type === 'sqlite' ) {
605- ( dbInstance as any ) . $client . exec ( dropTableSQL ) ;
606- } else if ( dbConfig ?. type === 'turso' ) {
607- if ( ( dbInstance as any ) . $client && typeof ( dbInstance as any ) . $client . execute === 'function' ) {
608- await ( dbInstance as any ) . $client . execute ( dropTableSQL ) ;
609- } else {
610- await dbInstance . run ( dropTableSQL ) ;
611- }
612- }
613-
614- // Execute the CREATE TABLE statement
615- if ( dbConfig ?. type === 'sqlite' ) {
616- ( dbInstance as any ) . $client . exec ( createTableSQL ) ;
617- } else if ( dbConfig ?. type === 'turso' ) {
618- if ( ( dbInstance as any ) . $client && typeof ( dbInstance as any ) . $client . execute === 'function' ) {
619- await ( dbInstance as any ) . $client . execute ( createTableSQL ) ;
620- } else {
621- await dbInstance . run ( createTableSQL ) ;
622- }
623- }
624-
625- logger . info ( {
626- operation : 'create_plugin_tables' ,
627- pluginId : plugin . meta . id ,
628- tableName : fullTableName
629- } , `✅ Created plugin table: ${ fullTableName } ` ) ;
630-
631- } catch ( error ) {
632- const typedError = error as Error ;
633- // Check if table already exists (not an error)
634- if ( typedError . message . includes ( 'already exists' ) || typedError . message . includes ( 'table' ) && typedError . message . includes ( 'already' ) ) {
635- logger . debug ( {
636- operation : 'create_plugin_tables' ,
637- pluginId : plugin . meta . id ,
638- tableName : fullTableName
639- } , `Table ${ fullTableName } already exists, skipping.` ) ;
640- } else {
641- logger . error ( {
642- operation : 'create_plugin_tables' ,
643- pluginId : plugin . meta . id ,
644- tableName : fullTableName ,
645- error : typedError ,
646- message : typedError . message
647- } , `❌ Failed to create plugin table: ${ fullTableName } ` ) ;
648- throw error ;
649- }
650- }
651- }
652- }
653-
654- logger . info ( {
655- operation : 'create_plugin_tables'
656- } , '✅ Plugin table creation completed.' ) ;
657- }
658-
659- /**
660- * Generate CREATE TABLE SQL from plugin table definitions
661- */
662- function generateCreateTableSQL ( tableName : string , columnDefs : Record < string , ( columnBuilder : any ) => any > ) : string {
663- const columns : string [ ] = [ ] ;
664-
665- for ( const [ columnName , columnDefFunc ] of Object . entries ( columnDefs ) ) {
666- // Create a mock column builder to extract column definition
667- const mockBuilder = createMockColumnBuilder ( ) ;
668- const columnDef = columnDefFunc ( mockBuilder ) ;
669-
670- // Convert the column definition to SQL
671- const sqlColumn = convertColumnDefToSQL ( columnName , columnDef ) ;
672- columns . push ( sqlColumn ) ;
673- }
674-
675- return `CREATE TABLE IF NOT EXISTS "${ tableName } " (\n ${ columns . join ( ',\n ' ) } \n)` ;
676- }
677-
678- /**
679- * Create a mock column builder that captures column definition details
680- */
681- function createMockColumnBuilder ( ) {
682- const createColumn = ( type : string ) => {
683- const column = {
684- type,
685- isPrimaryKey : false ,
686- isNotNull : false ,
687- isUnique : false ,
688- defaultValue : undefined as any ,
689- references : undefined as any ,
690- } ;
691-
692- return {
693- ...column ,
694- primaryKey ( ) {
695- column . isPrimaryKey = true ;
696- return this ;
697- } ,
698-
699- notNull ( ) {
700- column . isNotNull = true ;
701- return this ;
702- } ,
703-
704- unique ( ) {
705- column . isUnique = true ;
706- return this ;
707- } ,
708-
709- default ( value : any ) {
710- column . defaultValue = value ;
711- return this ;
712- } ,
713-
714- defaultNow ( ) {
715- column . defaultValue = "strftime('%s', 'now')" ;
716- return this ;
717- } ,
718-
719- references ( ref : any ) {
720- column . references = ref ;
721- return this ;
722- }
723- } ;
724- } ;
725-
726- return ( columnName : string , options ?: any ) => {
727- // Determine column type based on name patterns and options
728- let type = 'TEXT' ;
729-
730- if ( options ?. mode === 'timestamp' || columnName . toLowerCase ( ) . includes ( 'at' ) || columnName . toLowerCase ( ) . includes ( 'date' ) ) {
731- type = 'INTEGER' ; // SQLite uses INTEGER for timestamps
732- } else if ( columnName . toLowerCase ( ) . includes ( 'id' ) || columnName . toLowerCase ( ) . includes ( 'count' ) ||
733- columnName . toLowerCase ( ) . includes ( 'age' ) || columnName . toLowerCase ( ) . includes ( 'quantity' ) ||
734- columnName . toLowerCase ( ) . includes ( 'order' ) || columnName . toLowerCase ( ) . includes ( 'number' ) ) {
735- type = 'INTEGER' ;
736- }
737-
738- const column = createColumn ( type ) ;
739-
740- // For timestamp columns with mode, automatically set default if not already set
741- if ( options ?. mode === 'timestamp' && ! column . defaultValue ) {
742- column . defaultValue = "strftime('%s', 'now')" ;
743- }
744-
745- return column ;
746- } ;
747- }
748-
749- /**
750- * Convert column definition object to SQL string
751- */
752- function convertColumnDefToSQL ( columnName : string , columnDef : any ) : string {
753- let sql = `"${ columnName } " ${ columnDef . type } ` ;
754-
755- if ( columnDef . isPrimaryKey ) {
756- sql += ' PRIMARY KEY' ;
757- }
758-
759- if ( columnDef . isNotNull ) {
760- sql += ' NOT NULL' ;
761- }
762-
763- if ( columnDef . isUnique ) {
764- sql += ' UNIQUE' ;
765- }
766-
767- if ( columnDef . defaultValue !== undefined ) {
768- if ( typeof columnDef . defaultValue === 'string' && columnDef . defaultValue . includes ( 'strftime' ) ) {
769- sql += ` DEFAULT (${ columnDef . defaultValue } )` ;
770- } else if ( typeof columnDef . defaultValue === 'string' ) {
771- sql += ` DEFAULT '${ columnDef . defaultValue } '` ;
772- } else if ( typeof columnDef . defaultValue === 'boolean' ) {
773- sql += ` DEFAULT ${ columnDef . defaultValue ? 1 : 0 } ` ;
774- } else {
775- sql += ` DEFAULT ${ columnDef . defaultValue } ` ;
776- }
777- }
778-
779- return sql ;
556+ // Use the extracted plugin migration functionality
557+ await createPluginTablesImpl ( plugins , dbInstance , dbConfig , logger ) ;
780558}
781559
782560export async function initializePluginDatabases ( db : AnyDatabase , plugins : Plugin [ ] , logger : FastifyBaseLogger ) {
0 commit comments