@gftdcojp/gftd-orm
Version:
Enterprise-grade real-time data platform with ksqlDB, inspired by Supabase architecture
325 lines • 11.1 kB
JavaScript
/**
* 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