Skip to content

Commit 3b20a38

Browse files
committed
fix: enhance database connection handling in SqlDriver to ensure directory existence
1 parent 3b36a95 commit 3b20a38

File tree

5 files changed

+53
-21
lines changed

5 files changed

+53
-21
lines changed

packages/metadata/src/plugin.ts

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -105,25 +105,39 @@ export class MetadataPlugin implements Plugin {
105105
});
106106

107107
// Bridge database driver from kernel service registry to MetadataManager.
108-
// DriverPlugin registers drivers as 'driver.{name}' services during init().
109-
// This runs AFTER filesystem loading so that system metadata populated via
110-
// register() above is stored in-memory only, without being persisted to the
111-
// database (which would create noisy DB state/history on every cold boot).
108+
// Uses ObjectQL engine's datasource mapping to resolve the correct driver
109+
// for sys_metadata (respects namespace → datasource routing).
112110
try {
113-
const services = ctx.getServices();
114-
for (const [serviceName, service] of services) {
115-
if (serviceName.startsWith('driver.') && service) {
116-
ctx.logger.info('[MetadataPlugin] Bridging driver to MetadataManager for database-backed persistence', {
117-
driverService: serviceName,
111+
const ql = ctx.getService<any>('objectql');
112+
if (ql) {
113+
const tableName = this.manager['config']?.tableName ?? 'sys_metadata';
114+
const driver = ql.getDriverForObject?.(tableName);
115+
if (driver) {
116+
ctx.logger.info('[MetadataPlugin] Bridging driver to MetadataManager via ObjectQL routing', {
117+
tableName,
118+
driver: driver.name,
118119
});
119-
this.manager.setDatabaseDriver(service);
120-
break; // Use the first discovered driver — typically only one driver is registered per deployment
120+
this.manager.setDatabaseDriver(driver);
121+
} else {
122+
ctx.logger.debug('[MetadataPlugin] ObjectQL could not resolve driver for metadata table', { tableName });
121123
}
122124
}
123-
} catch (e: any) {
124-
ctx.logger.debug('[MetadataPlugin] No driver service found — database metadata persistence not available', {
125-
error: e.message,
126-
});
125+
} catch {
126+
// ObjectQL not available — fall back to first available driver service
127+
try {
128+
const services = ctx.getServices();
129+
for (const [serviceName, service] of services) {
130+
if (serviceName.startsWith('driver.') && service) {
131+
ctx.logger.info('[MetadataPlugin] Bridging driver to MetadataManager (fallback: first driver)', {
132+
driverService: serviceName,
133+
});
134+
this.manager.setDatabaseDriver(service);
135+
break;
136+
}
137+
}
138+
} catch (e: any) {
139+
ctx.logger.debug('[MetadataPlugin] No driver service found', { error: e.message });
140+
}
127141
}
128142

129143
// Bridge realtime service from kernel service registry to MetadataManager.

packages/plugins/driver-sql/src/sql-driver.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ export class SqlDriver implements IDataDriver {
148148
// ===================================
149149

150150
async connect(): Promise<void> {
151-
return Promise.resolve();
151+
// Ensure the database directory exists before any query can trigger
152+
// better-sqlite3 to open the file (e.g. loadMetaFromDb on startup).
153+
await this.ensureDatabaseExists();
152154
}
153155

154156
async checkHealth(): Promise<boolean> {
@@ -1006,8 +1008,21 @@ export class SqlDriver implements IDataDriver {
10061008
// ── Database helpers ────────────────────────────────────────────────────────
10071009

10081010
protected async ensureDatabaseExists() {
1009-
// SQLite auto-creates database files — no need to check
1010-
if (this.isSqlite) return;
1011+
// SQLite auto-creates database files but NOT parent directories.
1012+
// Ensure the directory exists so better-sqlite3 can create the file.
1013+
if (this.isSqlite) {
1014+
const conn = (this.config as any).connection;
1015+
const filename = typeof conn === 'string' ? conn : conn?.filename;
1016+
if (filename && filename !== ':memory:' && !filename.startsWith(':')) {
1017+
const { dirname } = await import('node:path');
1018+
const { mkdir } = await import('node:fs/promises');
1019+
const dir = dirname(filename);
1020+
if (dir && dir !== '.') {
1021+
await mkdir(dir, { recursive: true });
1022+
}
1023+
}
1024+
return;
1025+
}
10111026

10121027
// Only PostgreSQL and MySQL support programmatic database creation
10131028
if (!this.isPostgres && !this.isMysql) return;

packages/services/service-feed/src/objects/feed-item.object.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import { ObjectSchema, Field } from '@objectstack/spec/data';
1111
* Belongs to `service-feed` package per "protocol + service ownership" pattern.
1212
*/
1313
export const FeedItem = ObjectSchema.create({
14-
name: 'sys_feed_item',
14+
namespace: 'sys',
15+
name: 'feed_item',
1516
label: 'Feed Item',
1617
pluralLabel: 'Feed Items',
1718
icon: 'message-square',

packages/services/service-feed/src/objects/feed-reaction.object.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import { ObjectSchema, Field } from '@objectstack/spec/data';
1111
* Belongs to `service-feed` package per "protocol + service ownership" pattern.
1212
*/
1313
export const FeedReaction = ObjectSchema.create({
14-
name: 'sys_feed_reaction',
14+
namespace: 'sys',
15+
name: 'feed_reaction',
1516
label: 'Feed Reaction',
1617
pluralLabel: 'Feed Reactions',
1718
icon: 'smile',

packages/services/service-feed/src/objects/record-subscription.object.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import { ObjectSchema, Field } from '@objectstack/spec/data';
1111
* Belongs to `service-feed` package per "protocol + service ownership" pattern.
1212
*/
1313
export const RecordSubscription = ObjectSchema.create({
14-
name: 'sys_record_subscription',
14+
namespace: 'sys',
15+
name: 'record_subscription',
1516
label: 'Record Subscription',
1617
pluralLabel: 'Record Subscriptions',
1718
icon: 'bell',

0 commit comments

Comments
 (0)