UNPKG

@gftdcojp/ksqldb-orm

Version:

ksqldb-orm - Server-Side TypeScript ORM for ksqlDB with enterprise security extensions

435 lines 15.1 kB
"use strict"; /** * クライアントサイド用HTTPクライアント(ブラウザ環境) */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.KsqlDbClientServer = exports.KsqlDbClientBrowser = exports.HttpClient = void 0; exports.createKsqlDbClientBrowser = createKsqlDbClientBrowser; exports.createKsqlDbClientServer = createKsqlDbClientServer; const env_1 = require("./utils/env"); const ksqldb_client_1 = require("./ksqldb-client"); const logger_1 = require("./utils/logger"); /** * URLのHTTPS検証を行う * @param url 検証対象のURL * @param enforceHttps HTTPSを強制するかどうか */ function validateHttpsUrl(url, enforceHttps = true) { try { const urlObj = new URL(url); // テスト環境ではHTTPを許可 const allowHttp = typeof process !== 'undefined' && (process.env?.GFTD_ALLOW_HTTP === 'true' || process.env?.NODE_ENV === 'test'); if (urlObj.protocol === 'http:' && !allowHttp) { const message = `Insecure HTTP connection detected: ${url}. HTTPS is required for security.`; if (enforceHttps) { throw new Error(`SECURITY ERROR: ${message}`); } else { logger_1.log.warn(`SECURITY WARNING: ${message}`); } } } catch (error) { if (error instanceof TypeError) { throw new Error(`Invalid URL format: ${url}`); } throw error; } } /** * ブラウザ環境用HTTPクライアント */ class HttpClient { constructor(config) { if (!(0, env_1.isBrowser)()) { throw new Error('HttpClient can only be used in browser environment'); } // デフォルトでHTTPSを強制 const enforceHttps = config.enforceHttps !== false; // URLのHTTPS検証 validateHttpsUrl(config.url, enforceHttps); this.config = { ...config, enforceHttps }; this.defaultHeaders = { 'Content-Type': 'application/json', ...config.headers, }; // 認証情報がある場合は Authorization ヘッダーを追加 if (config.apiKey && config.apiSecret) { const credentials = btoa(`${config.apiKey}:${config.apiSecret}`); this.defaultHeaders['Authorization'] = `Basic ${credentials}`; } // セキュリティヘッダーを追加 this.defaultHeaders['X-Content-Type-Options'] = 'nosniff'; this.defaultHeaders['X-Frame-Options'] = 'DENY'; this.defaultHeaders['X-XSS-Protection'] = '1; mode=block'; } /** * GET リクエストを送信 */ async get(path, params) { const url = new URL(path, this.config.url); if (params) { Object.keys(params).forEach(key => { url.searchParams.append(key, params[key]); }); } const response = await this.fetch(url.toString(), { method: 'GET', headers: this.defaultHeaders, }); return this.handleResponse(response); } /** * POST リクエストを送信 */ async post(path, data) { const url = new URL(path, this.config.url); const response = await this.fetch(url.toString(), { method: 'POST', headers: this.defaultHeaders, body: data ? JSON.stringify(data) : undefined, }); return this.handleResponse(response); } /** * PUT リクエストを送信 */ async put(path, data) { const url = new URL(path, this.config.url); const response = await this.fetch(url.toString(), { method: 'PUT', headers: this.defaultHeaders, body: data ? JSON.stringify(data) : undefined, }); return this.handleResponse(response); } /** * DELETE リクエストを送信 */ async delete(path) { const url = new URL(path, this.config.url); const response = await this.fetch(url.toString(), { method: 'DELETE', headers: this.defaultHeaders, }); return this.handleResponse(response); } /** * ksqlDB クエリを実行 */ async executeQuery(sql) { const response = await this.post('/ksql', { ksql: sql, streamsProperties: {}, }); return response; } /** * Pull Query を実行(ブラウザ用) * @param sql - 実行するSQL文 * @param options - Pull Queryのオプション */ async executePullQuery(sql, options = {}) { const { format = 'object' } = options; const response = await this.post('/query-stream', { sql, properties: {}, }); // ブラウザ環境では、レスポンスの形式が異なる可能性があるため、 // 基本的な変換処理を実装 if (format === 'object' && response && Array.isArray(response.data) && response.columnNames) { const transformedData = (0, ksqldb_client_1.transformArrayRowsToObjects)(response.data, response.columnNames); return { ...response, data: transformedData }; } return response; } async fetch(url, options) { // URLのHTTPS検証 validateHttpsUrl(url, this.config.enforceHttps); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.config.timeout || 30000); // セキュリティ設定を追加 const secureOptions = { ...options, signal: controller.signal, mode: 'cors', credentials: 'same-origin', referrerPolicy: 'strict-origin-when-cross-origin', }; try { const response = await fetch(url, secureOptions); clearTimeout(timeoutId); return response; } catch (error) { clearTimeout(timeoutId); if (error instanceof Error && error.name === 'AbortError') { throw new Error('Request timeout'); } throw error; } } async handleResponse(response) { if (!response.ok) { const errorText = await response.text(); throw new Error(`HTTP ${response.status}: ${errorText}`); } const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { return response.json(); } return response.text(); } } exports.HttpClient = HttpClient; /** * ブラウザ環境用ksqlDBクライアント */ class KsqlDbClientBrowser { constructor(config) { this.config = config; this.httpClient = new HttpClient({ url: config.url, apiKey: config.apiKey, apiSecret: config.apiSecret, headers: config.headers, timeout: 30000, }); } /** * クエリを実行 */ async executeQuery(sql) { try { const response = await this.httpClient.executeQuery(sql); return response; } catch (error) { throw new Error(`ksqlDB query failed: ${error.message}`); } } /** * Pull Query を実行 * @param sql - 実行するSQL文 * @param options - Pull Queryのオプション(デフォルトでオブジェクト形式を返す) */ async executePullQuery(sql, options = {}) { console.log(`[DEBUG] KsqlDbClientBrowser.executePullQuery - SQL: ${sql}`); console.log(`[DEBUG] KsqlDbClientBrowser.executePullQuery - Options:`, options); console.log(`[DEBUG] KsqlDbClientBrowser.executePullQuery - Config:`, this.config); try { const response = await this.httpClient.executePullQuery(sql, options); console.log(`[DEBUG] KsqlDbClientBrowser.executePullQuery - Response:`, response); return response; } catch (error) { console.error(`[ERROR] KsqlDbClientBrowser.executePullQuery failed:`, error); // ストリーム vs テーブルの問題を特定しやすくする if (error.message && (error.message.includes('stream') || error.message.includes('table'))) { console.error(`[ERROR] KsqlDbClientBrowser.executePullQuery - Possible stream/table issue. SQL: ${sql}`); console.error(`[ERROR] KsqlDbClientBrowser.executePullQuery - Remember: Pull queries work only on TABLES, not STREAMS`); } throw new Error(`ksqlDB pull query failed: ${error.message || 'Unknown error'}`); } } /** * DDL文を実行 */ async executeDDL(ddl) { try { const response = await this.httpClient.executeQuery(ddl); if (response && response[0] && response[0].errorMessage) { throw new Error(`DDL execution failed: ${response[0].errorMessage.message}`); } return response; } catch (error) { throw new Error(`ksqlDB DDL failed: ${error.message}`); } } /** * ストリーム/テーブル一覧を取得 */ async listStreams() { return this.executeQuery('LIST STREAMS;'); } async listTables() { return this.executeQuery('LIST TABLES;'); } /** * トピック一覧を取得 */ async listTopics() { return this.executeQuery('LIST TOPICS;'); } /** * スキーマ情報を取得 */ async describeStream(streamName) { return this.executeQuery(`DESCRIBE ${streamName};`); } async describeTable(tableName) { return this.executeQuery(`DESCRIBE ${tableName};`); } /** * 接続状態を確認 */ async isConnected() { try { await this.executeQuery('SHOW QUERIES;'); return true; } catch { return false; } } /** * クライアント設定を取得 */ getConfig() { return this.config; } } exports.KsqlDbClientBrowser = KsqlDbClientBrowser; /** * ブラウザ環境用ksqlDBクライアントを作成 */ function createKsqlDbClientBrowser(config) { return new KsqlDbClientBrowser(config); } /** * サーバー環境用ksqlDBクライアント */ class KsqlDbClientServer { constructor(config) { this.config = config; } /** * クエリを実行 */ async executeQuery(sql) { // サーバー環境では、ksqldb-clientの関数を直接使用 const { executeQuery } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client'))); return executeQuery(sql); } /** * Pull Query を実行 * @param sql - 実行するSQL文 * @param options - Pull Queryのオプション(デフォルトでオブジェクト形式を返す) */ async executePullQuery(sql, options = {}) { const { executePullQuery } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client'))); return executePullQuery(sql, options); } /** * Push Query を実行 */ async executePushQuery(sql, onData, onError, onComplete) { const { executePushQuery } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client'))); return executePushQuery(sql, onData, onError, onComplete); } /** * DDL文を実行 */ async executeDDL(ddl) { const { executeDDL } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client'))); return executeDDL(ddl); } /** * ストリーム/テーブル一覧を取得 */ async listStreams() { const { listStreams } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client'))); return listStreams(); } async listTables() { const { listTables } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client'))); return listTables(); } /** * トピック一覧を取得 */ async listTopics() { const { listTopics } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client'))); return listTopics(); } /** * スキーマ情報を取得 */ async describeStream(streamName) { const { describeStream } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client'))); return describeStream(streamName); } async describeTable(tableName) { const { describeTable } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client'))); return describeTable(tableName); } /** * 接続状態を確認 */ async isConnected() { const { isConnected } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client'))); return isConnected(); } /** * クライアント設定を取得 */ getConfig() { return this.config; } /** * クライアントを初期化 */ async initialize() { const { initializeKsqlDbClient } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client'))); initializeKsqlDbClient(this.config); } } exports.KsqlDbClientServer = KsqlDbClientServer; /** * サーバー環境用ksqlDBクライアントを作成 */ function createKsqlDbClientServer(config) { return new KsqlDbClientServer(config); } //# sourceMappingURL=http-client.js.map