UNPKG

@gftdcojp/gftd-orm

Version:

Enterprise-grade real-time data platform with ksqlDB, inspired by Supabase architecture

325 lines 11.1 kB
/** * Next.js環境のサーバー専用クライアント * Server ComponentsやAPI Routes、middleware等で使用 */ // Mock DatabaseClient implementation // @todo [P2] Replace with actual DatabaseClient implementation class DatabaseClient { constructor(config) { console.log('[MOCK] DatabaseClient created:', config); } async initialize() { console.log('[MOCK] DatabaseClient initialized'); } from(table) { return { select: () => ({ eq: () => ({ execute: () => Promise.resolve({ data: [], error: null }) }) }), insert: (data) => Promise.resolve({ data: [], error: null }), update: (data) => Promise.resolve({ data: [], error: null }), delete: () => Promise.resolve({ data: [], error: null }), execute: () => Promise.resolve({ data: [], error: null }) }; } async sql(query, params) { return { data: [], queryId: 'mock-query' }; } async health() { return { status: 'ok' }; } } import { RealtimeClient } from '../realtime-client'; import { isServerSide } from '../utils/env'; import { log } from '../utils/logger'; import { getCoreConfig, getDatabaseConfig, getRealtimeConfig } from '../config'; /** * サーバー専用のGFTD-ORMクライアント */ export class ServerClient { constructor(config) { this._database = null; this._realtime = null; this.initialized = false; this._isConnected = false; this.validateServerEnvironment(); this.config = config; } /** * サーバー環境の検証 */ validateServerEnvironment() { if (!isServerSide()) { throw new Error('ServerClient can only be used in server environment. ' + 'Use BrowserClient for client-side rendering.'); } } /** * データベースクライアントを取得 */ get database() { if (!this._database) { throw new Error('Database not initialized. Call initialize() first.'); } return this._database; } /** * リアルタイムクライアントを取得 */ get realtime() { if (!this._realtime) { throw new Error('Realtime not initialized or not configured.'); } return this._realtime; } /** * クライアントを初期化 */ async initialize() { if (this.initialized) { return; } try { log.info('Initializing server client...'); // Database初期化(サーバー版) this._database = new DatabaseClient({ ksql: { url: this.config.database.ksql.url, apiKey: this.config.database.ksql.apiKey, // TODO: apiSecretの実装が完了するまでheadersで渡す headers: { 'User-Agent': 'gftd-orm-server-client', 'X-Client-Type': 'server', ...(this.config.database.ksql.apiSecret && { 'X-API-Secret': this.config.database.ksql.apiSecret }), ...this.config.database.ksql.headers, }, }, schemaRegistry: { url: this.config.database.schemaRegistry.url, apiKey: this.config.database.schemaRegistry.apiKey, // TODO: authUser, authPasswordの実装が完了するまでコメントアウト // headers: { // 'X-Auth-User': this.config.database.schemaRegistry.authUser, // 'X-Auth-Password': this.config.database.schemaRegistry.authPassword, // }, }, }); await this._database.initialize(); // Realtime初期化(サーバー版) if (this.config.realtime) { this._realtime = new RealtimeClient({ url: this.config.realtime.url, apiKey: this.config.realtime.apiKey, // TODO: apiSecretの実装が完了するまでコメントアウト // headers: { // 'X-API-Secret': this.config.realtime.apiSecret, // }, autoReconnect: this.config.realtime.autoReconnect ?? true, reconnectInterval: this.config.realtime.reconnectInterval ?? 5000, maxReconnectAttempts: this.config.realtime.maxReconnectAttempts ?? 10, }); } this.initialized = true; this._isConnected = true; log.info('Server client initialized successfully'); } catch (error) { log.error(`Failed to initialize server client: ${error}`); this._isConnected = false; throw error; } } /** * Supabaseライクなテーブルアクセス */ from(table) { return this.database.from(table); } /** * SQL クエリを実行 */ async query(sql, params) { try { const startTime = Date.now(); const result = await this.database.sql(sql, params); const executionTime = Date.now() - startTime; return { data: result.data, error: null, metadata: { rowCount: result.data.length, executionTime, queryId: result.queryId, }, }; } catch (error) { log.error(`Query execution failed: ${error}`); return { data: [], error: error, }; } } /** * ストリーミングクエリを実行(サーバー版は高機能) */ async stream(sql, onData, onError) { try { // TODO: DatabaseClientのstream機能が実装されるまでの暫定実装 let isActive = true; let isPaused = false; const mockTerminate = () => { isActive = false; log.info('Stream terminated'); }; // 実際の実装では、this.database.stream(sql, onData, onError) を使用 // const { terminate } = await this.database.stream(sql, onData, onError); return { terminate: mockTerminate, pause: () => { isPaused = true; // サーバーサイドではより高度なポーズ制御が可能 log.info('Stream paused'); }, resume: () => { isPaused = false; // サーバーサイドではより高度な再開制御が可能 log.info('Stream resumed'); }, isActive: () => isActive && !isPaused, }; } catch (error) { log.error(`Stream execution failed: ${error}`); throw error; } } /** * リアルタイムチャンネル */ channel(name) { if (!this._realtime) { throw new Error('Realtime not configured. Please provide realtime config when creating the client.'); } return this._realtime.channel(name); } /** * ヘルスチェック(サーバー版は詳細な情報を提供) */ async health() { const connections = { ksqldb: this._database ? 'connected' : 'disconnected', schemaRegistry: this._database ? 'connected' : 'disconnected', realtime: this._realtime ? 'connected' : 'disconnected', }; const features = [ 'database', 'schema-registry', ...(this._realtime ? ['realtime'] : []), 'audit', 'rate-limit', 'advanced-security', ]; try { // データベースの健康状態をチェック if (this._database) { await this._database.health(); } return { status: 'ok', version: '25.07.6', features, connections, environment: 'server', }; } catch (error) { log.error(`Health check failed: ${error}`); return { status: 'error', version: '25.07.6', features, connections: { ...connections, ksqldb: 'error', }, environment: 'server', }; } } /** * 接続状態を確認 */ isConnected() { return this._isConnected && this.initialized; } /** * すべての接続を閉じる */ async disconnect() { try { if (this._realtime) { this._realtime.disconnect(); } this._isConnected = false; this.initialized = false; log.info('Server client disconnected'); } catch (error) { log.error(`Error during disconnect: ${error}`); throw error; } } /** * 環境変数から設定を自動読み込み */ static fromEnvironment() { const coreConfig = getCoreConfig(); const databaseConfig = getDatabaseConfig(); const realtimeConfig = getRealtimeConfig(); const config = { url: coreConfig.url, key: coreConfig.serviceRoleKey, database: { ksql: { url: databaseConfig.ksql.url, apiKey: databaseConfig.ksql.apiKey, apiSecret: databaseConfig.ksql.apiSecret, }, schemaRegistry: { url: databaseConfig.schemaRegistry.url, apiKey: databaseConfig.schemaRegistry.apiKey, authUser: databaseConfig.schemaRegistry.authUser, authPassword: databaseConfig.schemaRegistry.authPassword, }, }, realtime: { url: realtimeConfig.url, apiKey: realtimeConfig.apiKey, }, }; return new ServerClient(config); } } /** * サーバー専用クライアント作成関数 */ export function createServerClient(config) { return new ServerClient(config); } /** * Next.js Server Components用のクライアント作成関数 */ export function createNextServerClient(config) { return createServerClient(config); } /** * 環境変数から自動的にサーバークライアントを作成 */ export function createServerClientFromEnv() { return ServerClient.fromEnvironment(); } //# sourceMappingURL=server.js.map