UNPKG

@crowdin/app-project-module

Version:

Module that generates for you all common endpoints for serving standalone Crowdin App

838 lines (837 loc) 40.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.D1Storage = void 0; const types_1 = require("../modules/integration/util/types"); const crypto_1 = require("crypto"); class D1Storage { constructor(config) { this.migrated = false; this.tables = { crowdin_credentials: `( id varchar not null primary key, app_secret varchar null, domain varchar null, user_id varchar null, agent_id varchar null, organization_id varchar null, base_url varchar null, access_token varchar not null, refresh_token varchar not null, expire varchar not null, type varchar not null )`, integration_credentials: `( id varchar not null primary key, credentials varchar not null, crowdin_id varchar not null, managers varchar null )`, sync_settings: `( id integer not null primary key autoincrement, files varchar null, integration_id varchar not null, crowdin_id varchar not null, type varchar not null, provider varchar not null )`, app_metadata: `( id varchar not null primary key, data varchar null, crowdin_id varchar null )`, files_snapshot: `( id integer not null primary key autoincrement, integration_id varchar not null, crowdin_id varchar not null, files varchar null, provider varchar not null )`, webhooks: `( id integer not null primary key autoincrement, file_id varchar not null, integration_id varchar not null, crowdin_id varchar not null, provider varchar not null )`, user_errors: `( id integer not null primary key autoincrement, action varchar not null, message varchar not null, data varchar null, created_at varchar not null, crowdin_id varchar not null, integration_id varchar null )`, integration_settings: `( id integer not null primary key autoincrement, integration_id varchar not null, crowdin_id varchar not null, config varchar null )`, job: `( id varchar not null primary key, integration_id varchar not null, crowdin_id varchar not null, type varchar not null, title varchar null, progress integer DEFAULT 0, status varchar DEFAULT '${types_1.JobStatus.CREATED}', payload varchar null, info varchar null, data varchar null, attempt varchar DEFAULT 0, errors varchar null, processed_entities varchar null, initiated_by varchar null, created_at varchar not null, updated_at varchar null, finished_at varchar null )`, translation_file_cache: `( id integer not null primary key autoincrement, integration_id varchar not null, crowdin_id varchar not null, file_id integer not null, language_id varchar not null, etag varchar )`, unsynced_files: `( id integer not null primary key autoincrement, integration_id varchar not null, crowdin_id varchar not null, files varchar null )`, synced_data: `( id integer not null primary key autoincrement, files varchar null, integration_id varchar not null, crowdin_id varchar not null, type varchar not null, updated_at varchar null )`, }; this.db = config.database; } ensureMigrated() { return __awaiter(this, void 0, void 0, function* () { if (!this.migrated) { yield this.migrate(); this.migrated = true; } }); } addColumn(tableName, column, defaultValue) { return __awaiter(this, void 0, void 0, function* () { const tableInfo = yield this.db.prepare(`PRAGMA table_info(${tableName})`).all(); const columns = tableInfo.results || []; const exists = columns.some((columnInfo) => columnInfo.name === column); if (!exists) { yield this.db.prepare(`ALTER TABLE ${tableName} ADD COLUMN ${column} varchar ${defaultValue}`).run(); } }); } updateTables() { return __awaiter(this, void 0, void 0, function* () { yield this.addColumn('job', 'processed_entities', 'null'); yield this.addColumn('job', 'initiated_by', 'null'); }); } migrate(skipOnboarding) { return __awaiter(this, void 0, void 0, function* () { if (!skipOnboarding) { try { // Create all tables const statements = []; const tableEntries = Object.keys(this.tables).map((key) => [ key, this.tables[key], ]); for (const [tableName, tableSchema] of tableEntries) { statements.push(this.db.prepare(`CREATE TABLE IF NOT EXISTS ${tableName} ${tableSchema}`)); } // Execute all table creation in batch yield this.db.batch(statements); // Update tables with new columns if needed yield this.updateTables(); } catch (e) { console.error(e); } } }); } saveCrowdinCredentials(credentials) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; yield this.db .prepare('INSERT INTO crowdin_credentials(id, app_secret, domain, user_id, agent_id, organization_id, base_url, access_token, refresh_token, expire, type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)') .bind(...this.sanitizeBindValues(credentials.id, credentials.appSecret, credentials.domain, (_a = credentials.userId) === null || _a === void 0 ? void 0 : _a.toString(), (_b = credentials.agentId) === null || _b === void 0 ? void 0 : _b.toString(), (_c = credentials.organizationId) === null || _c === void 0 ? void 0 : _c.toString(), credentials.baseUrl, credentials.accessToken, credentials.refreshToken, credentials.expire, credentials.type)) .run(); }); } updateCrowdinCredentials(credentials) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; yield this.db .prepare('UPDATE crowdin_credentials SET app_secret = ?, domain = ?, user_id = ?, agent_id = ?, organization_id = ?, base_url = ?, access_token = ?, refresh_token = ?, expire = ? WHERE id = ?') .bind(...this.sanitizeBindValues(credentials.appSecret, credentials.domain, (_a = credentials.userId) === null || _a === void 0 ? void 0 : _a.toString(), (_b = credentials.agentId) === null || _b === void 0 ? void 0 : _b.toString(), (_c = credentials.organizationId) === null || _c === void 0 ? void 0 : _c.toString(), credentials.baseUrl, credentials.accessToken, credentials.refreshToken, credentials.expire, credentials.id)) .run(); }); } getCrowdinCredentials(id) { return __awaiter(this, void 0, void 0, function* () { const row = yield this.db .prepare('SELECT id, app_secret as appSecret, domain, user_id as userId, agent_id as agentId, organization_id as organizationId, base_url as baseUrl, access_token as accessToken, refresh_token as refreshToken, expire, type FROM crowdin_credentials WHERE id = ?') .bind(id) .first(); if (row) { return Object.assign(Object.assign({}, row), { userId: row.userId ? parseInt(row.userId, 10) : row.userId, agentId: row.agentId ? parseInt(row.agentId, 10) : row.agentId, organizationId: row.organizationId ? parseInt(row.organizationId, 10) : row.organizationId }); } }); } getAllCrowdinCredentials() { return __awaiter(this, void 0, void 0, function* () { const result = yield this.db .prepare('SELECT id, app_secret as appSecret, domain, user_id as userId, agent_id as agentId, organization_id as organizationId, base_url as baseUrl, access_token as accessToken, refresh_token as refreshToken, expire, type FROM crowdin_credentials') .all(); return (result.results || []).map((row) => (Object.assign(Object.assign({}, row), { userId: row.userId ? parseInt(row.userId, 10) : row.userId, agentId: row.agentId ? parseInt(row.agentId, 10) : row.agentId, organizationId: row.organizationId ? parseInt(row.organizationId, 10) : row.organizationId }))); }); } deleteCrowdinCredentials(id) { return __awaiter(this, void 0, void 0, function* () { const statements = [ this.db.prepare('DELETE FROM crowdin_credentials WHERE id = ?').bind(id), this.db.prepare('DELETE FROM integration_credentials WHERE crowdin_id = ?').bind(id), this.db.prepare('DELETE FROM sync_settings WHERE crowdin_id = ?').bind(id), this.db.prepare('DELETE FROM app_metadata WHERE crowdin_id = ?').bind(id), this.db.prepare('DELETE FROM files_snapshot WHERE crowdin_id = ?').bind(id), this.db.prepare('DELETE FROM webhooks WHERE crowdin_id = ?').bind(id), this.db.prepare('DELETE FROM user_errors WHERE crowdin_id = ?').bind(id), this.db.prepare('DELETE FROM integration_settings WHERE crowdin_id = ?').bind(id), this.db.prepare('DELETE FROM job WHERE crowdin_id = ?').bind(id), this.db.prepare('DELETE FROM translation_file_cache WHERE crowdin_id = ?').bind(id), this.db.prepare('DELETE FROM unsynced_files WHERE crowdin_id = ?').bind(id), this.db.prepare('DELETE FROM synced_data WHERE crowdin_id = ?').bind(id), ]; yield this.db.batch(statements); }); } saveIntegrationCredentials(id, credentials, crowdinId) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('INSERT INTO integration_credentials(id, credentials, crowdin_id) VALUES (?, ?, ?)') .bind(id, credentials, crowdinId) .run(); }); } updateIntegrationCredentials(id, credentials) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('UPDATE integration_credentials SET credentials = ? WHERE id = ?') .bind(credentials, id) .run(); }); } updateIntegrationManagers(id, managers) { return __awaiter(this, void 0, void 0, function* () { yield this.db.prepare('UPDATE integration_credentials SET managers = ? WHERE id = ?').bind(managers, id).run(); }); } getIntegrationCredentials(id) { return __awaiter(this, void 0, void 0, function* () { const row = yield this.db .prepare('SELECT id, credentials, crowdin_id as crowdinId, managers FROM integration_credentials WHERE id = ?') .bind(id) .first(); if (row) { return row; } }); } getAllIntegrationCredentials(crowdinId) { return __awaiter(this, void 0, void 0, function* () { const result = yield this.db .prepare('SELECT id, credentials, crowdin_id as crowdinId, managers FROM integration_credentials WHERE crowdin_id = ?') .bind(crowdinId) .all(); return result.results || []; }); } deleteIntegrationCredentials(id) { return __awaiter(this, void 0, void 0, function* () { const statements = [ this.db.prepare('DELETE FROM integration_credentials WHERE id = ?').bind(id), this.db.prepare('DELETE FROM sync_settings WHERE integration_id = ?').bind(id), this.db.prepare('DELETE FROM files_snapshot WHERE integration_id = ?').bind(id), this.db.prepare('DELETE FROM webhooks WHERE integration_id = ?').bind(id), this.db.prepare('DELETE FROM user_errors WHERE integration_id = ?').bind(id), this.db.prepare('DELETE FROM integration_settings WHERE integration_id = ?').bind(id), this.db.prepare('DELETE FROM job WHERE integration_id = ?').bind(id), this.db.prepare('DELETE FROM translation_file_cache WHERE integration_id = ?').bind(id), this.db.prepare('DELETE FROM unsynced_files WHERE integration_id = ?').bind(id), this.db.prepare('DELETE FROM synced_data WHERE integration_id = ?').bind(id), ]; yield this.db.batch(statements); }); } deleteAllIntegrationCredentials(crowdinId) { return __awaiter(this, void 0, void 0, function* () { const statements = [ this.db.prepare('DELETE FROM integration_credentials WHERE crowdin_id = ?').bind(crowdinId), this.db.prepare('DELETE FROM sync_settings WHERE crowdin_id = ?').bind(crowdinId), this.db.prepare('DELETE FROM files_snapshot WHERE crowdin_id = ?').bind(crowdinId), this.db.prepare('DELETE FROM webhooks WHERE crowdin_id = ?').bind(crowdinId), this.db.prepare('DELETE FROM user_errors WHERE crowdin_id = ?').bind(crowdinId), this.db.prepare('DELETE FROM job WHERE crowdin_id = ?').bind(crowdinId), this.db.prepare('DELETE FROM unsynced_files WHERE crowdin_id = ?').bind(crowdinId), this.db.prepare('DELETE FROM synced_data WHERE crowdin_id = ?').bind(crowdinId), ]; yield this.db.batch(statements); }); } saveMetadata(id, metadata, crowdinId) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('INSERT INTO app_metadata(id, data, crowdin_id) VALUES (?, ?, ?)') .bind(id, JSON.stringify(metadata), crowdinId) .run(); }); } updateMetadata(id, metadata, crowdinId) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('UPDATE app_metadata SET data = ?, crowdin_id = ? WHERE id = ?') .bind(JSON.stringify(metadata), crowdinId, id) .run(); }); } getMetadata(id) { return __awaiter(this, void 0, void 0, function* () { const result = yield this.db.prepare('SELECT data FROM app_metadata WHERE id = ?').bind(id).first(); return (result === null || result === void 0 ? void 0 : result.data) ? JSON.parse(result.data) : undefined; }); } getAllMetadata() { return __awaiter(this, void 0, void 0, function* () { const result = yield this.db.prepare('SELECT * FROM app_metadata').all(); return result.results || []; }); } deleteMetadata(id) { return __awaiter(this, void 0, void 0, function* () { yield this.db.prepare('DELETE FROM app_metadata WHERE id = ?').bind(id).run(); }); } getSyncSettingsByProvider(integrationId, provider) { return __awaiter(this, void 0, void 0, function* () { const row = yield this.db .prepare('SELECT id, files, integration_id as integrationId, crowdin_id as crowdinId, type, provider FROM sync_settings WHERE integration_id = ? AND provider = ?') .bind(integrationId, provider) .first(); if (row) { return row; } }); } getSyncSettingsBySchedule(type, schedule) { return __awaiter(this, void 0, void 0, function* () { const result = yield this.db .prepare(` SELECT s.id, s.files, s.integration_id as integrationId, s.crowdin_id as crowdinId, s.type, s.provider FROM sync_settings s INNER JOIN integration_settings i ON s.integration_id = i.integration_id WHERE s.type = ? AND CASE WHEN i.config IS NULL THEN 0 ELSE json_extract(i.config, '$.schedule') = ? END `) .bind(type, schedule) .all(); return result.results || []; }); } saveSyncSettings(files, integrationId, crowdinId, type, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('INSERT INTO sync_settings(files, integration_id, crowdin_id, type, provider) VALUES (?, ?, ?, ?, ?)') .bind(files, integrationId, crowdinId, type, provider) .run(); }); } updateSyncSettings(files, integrationId, crowdinId, type, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('UPDATE sync_settings SET files = ? WHERE integration_id = ? AND crowdin_id = ? AND type = ? AND provider = ?') .bind(files, integrationId, crowdinId, type, provider) .run(); }); } getSyncSettings(integrationId, crowdinId, type, provider) { return __awaiter(this, void 0, void 0, function* () { const row = yield this.db .prepare('SELECT id, files, integration_id as integrationId, crowdin_id as crowdinId, type, provider FROM sync_settings WHERE integration_id = ? AND crowdin_id = ? AND type = ? AND provider = ?') .bind(integrationId, crowdinId, type, provider) .first(); if (row) { return row; } }); } saveFilesSnapshot(files, integrationId, crowdinId, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('INSERT INTO files_snapshot(files, integration_id, crowdin_id, provider) VALUES (?, ?, ?, ?)') .bind(files, integrationId, crowdinId, provider) .run(); }); } updateFilesSnapshot(files, integrationId, crowdinId, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('UPDATE files_snapshot SET files = ? WHERE integration_id = ? AND crowdin_id = ? AND provider = ? ') .bind(files, integrationId, crowdinId, provider) .run(); }); } getFilesSnapshot(integrationId, crowdinId, provider) { return __awaiter(this, void 0, void 0, function* () { const row = yield this.db .prepare('SELECT id, files, integration_id as integrationId, crowdin_id as crowdinId, provider FROM files_snapshot WHERE integration_id = ? AND crowdin_id = ? AND provider = ?') .bind(integrationId, crowdinId, provider) .first(); if (row) { return row; } }); } getAllWebhooks(integrationId, crowdinId, provider) { return __awaiter(this, void 0, void 0, function* () { const result = yield this.db .prepare('SELECT id, file_id as fileId, integration_id as integrationId, crowdin_id as crowdinId, provider FROM webhooks WHERE integration_id = ? AND crowdin_id = ? AND provider = ?') .bind(integrationId, crowdinId, provider) .all(); return result.results || []; }); } getWebhooks(fileId, integrationId, crowdinId, provider) { return __awaiter(this, void 0, void 0, function* () { const row = yield this.db .prepare('SELECT id, file_id as fileId, integration_id as integrationId, crowdin_id as crowdinId, provider FROM webhooks WHERE file_id = ? AND integration_id = ? AND crowdin_id = ? AND provider = ?') .bind(fileId, integrationId, crowdinId, provider) .first(); if (row) { return row; } }); } saveWebhooks(fileId, integrationId, crowdinId, provider) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('INSERT INTO webhooks(file_id, integration_id, crowdin_id, provider) VALUES (?, ?, ?, ?)') .bind(fileId, integrationId, crowdinId, provider) .run(); }); } deleteWebhooks(fileIds, integrationId, crowdinId, provider) { return __awaiter(this, void 0, void 0, function* () { if (fileIds.length === 0) { return; } const placeholders = fileIds.map(() => '?').join(','); yield this.db .prepare(`DELETE FROM webhooks WHERE file_id IN (${placeholders}) AND integration_id = ? AND crowdin_id = ? AND provider = ?`) .bind(...fileIds, integrationId, crowdinId, provider) .run(); }); } getAllUserErrors(crowdinId, integrationId) { return __awaiter(this, void 0, void 0, function* () { let whereIntegrationCondition = 'integration_id is NULL'; const params = [crowdinId]; if (integrationId) { whereIntegrationCondition = 'integration_id = ?'; params.push(integrationId); } const result = yield this.db .prepare(`SELECT id, action, message, data, created_at as createdAt, integration_id as integrationId, crowdin_id as crowdinId FROM user_errors WHERE crowdin_id = ? AND ${whereIntegrationCondition}`) .bind(...this.sanitizeBindValues(...params)) .all(); return result.results || []; }); } saveUserError(action, message, data, createdAt, crowdinId, integrationId) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('INSERT INTO user_errors(action, message, data, created_at, integration_id, crowdin_id) VALUES (?, ?, ?, ?, ?, ?)') .bind(...this.sanitizeBindValues(action, message, data, createdAt, integrationId, crowdinId)) .run(); }); } deleteUserErrors(date, crowdinId, integrationId) { return __awaiter(this, void 0, void 0, function* () { let whereIntegrationCondition = 'integration_id is NULL'; const params = [date, crowdinId]; if (integrationId) { whereIntegrationCondition = 'integration_id = ?'; params.push(integrationId); } yield this.db .prepare(`DELETE FROM user_errors WHERE created_at < ? AND crowdin_id = ? AND ${whereIntegrationCondition}`) .bind(...this.sanitizeBindValues(...params)) .run(); }); } deleteAllUsersErrorsOlderThan(date) { return __awaiter(this, void 0, void 0, function* () { yield this.db.prepare('DELETE FROM user_errors WHERE created_at < ?').bind(date).run(); }); } saveIntegrationConfig(integrationId, crowdinId, config) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('INSERT INTO integration_settings(integration_id, crowdin_id, config) VALUES (?, ?, ?)') .bind(integrationId, crowdinId, config) .run(); }); } getAllIntegrationConfigs(crowdinId) { return __awaiter(this, void 0, void 0, function* () { const result = yield this.db .prepare('SELECT config, integration_id as integrationId FROM integration_settings WHERE crowdin_id = ?') .bind(crowdinId) .all(); return result.results || []; }); } getIntegrationConfig(integrationId) { return __awaiter(this, void 0, void 0, function* () { const row = yield this.db .prepare('SELECT config FROM integration_settings WHERE integration_id = ?') .bind(integrationId) .first(); if (row) { return row; } }); } updateIntegrationConfig(integrationId, config) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('UPDATE integration_settings SET config = ? WHERE integration_id = ?') .bind(config, integrationId) .run(); }); } createJob(params) { return __awaiter(this, void 0, void 0, function* () { var _a; const id = (0, crypto_1.randomUUID)(); yield this.db .prepare('INSERT INTO job(id, integration_id, crowdin_id, type, payload, title, initiated_by, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)') .bind(id, params.integrationId, params.crowdinId, params.type, params.payload, params.title, (_a = params.initiatedBy) !== null && _a !== void 0 ? _a : null, Date.now().toString()) .run(); return id; }); } updateJob(params) { return __awaiter(this, void 0, void 0, function* () { const updateFields = ['updated_at = ?']; const updateParams = [Date.now().toString()]; if (params.progress) { updateFields.push('progress = ?'); updateParams.push(Math.round(params.progress)); if (params.progress >= 100) { updateFields.push('finished_at = ?'); updateParams.push(Date.now().toString()); } } if (params.status) { updateFields.push('status = ?'); updateParams.push(params.status); if (!updateFields.includes('finished_at = ?') && [types_1.JobStatus.FAILED, types_1.JobStatus.CANCELED].includes(params.status)) { updateFields.push('finished_at = ?'); updateParams.push(Date.now().toString()); } } if (params.data) { updateFields.push('data = ?'); updateParams.push(params.data); } if (params.info) { updateFields.push('info = ?'); updateParams.push(params.info); } if (params.attempt) { updateFields.push('attempt = ?'); updateParams.push(params.attempt); } if (params.errors) { updateFields.push('errors = ?'); updateParams.push(JSON.stringify(params.errors)); } if (params.processedEntities) { updateFields.push('processed_entities = ?'); updateParams.push(params.processedEntities); } updateParams.push(params.id); const query = `UPDATE job SET ${updateFields.join(', ')} WHERE id = ?`; yield this.db .prepare(query) .bind(...updateParams) .run(); }); } getJob(params) { return __awaiter(this, void 0, void 0, function* () { const row = yield this.db .prepare(` SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status, title, info, data, attempt, errors, processed_entities as processedEntities, initiated_by as initiatedBy, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt FROM job WHERE id = ? `) .bind(params.id) .first(); if (row) { return row; } }); } getActiveJobs(params) { return __awaiter(this, void 0, void 0, function* () { const result = yield this.db .prepare(` SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status, title, info, data, attempt, errors, processed_entities as processedEntities, initiated_by as initiatedBy, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt FROM job WHERE integration_id = ? AND crowdin_id = ? AND finished_at is NULL `) .bind(params.integrationId, params.crowdinId) .all(); return result.results || []; }); } deleteFinishedJobs() { return __awaiter(this, void 0, void 0, function* () { yield this.db.prepare('DELETE FROM job WHERE finished_at IS NOT NULL').run(); }); } getAllInProgressJobs() { return __awaiter(this, void 0, void 0, function* () { const result = yield this.db .prepare(` SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status, title, info, data, attempt, errors, processed_entities as processedEntities, initiated_by as initiatedBy, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt FROM job WHERE status IN (?,?) AND finished_at is NULL `) .bind(types_1.JobStatus.IN_PROGRESS, types_1.JobStatus.CREATED) .all(); return result.results || []; }); } getAllJobs(params) { return __awaiter(this, void 0, void 0, function* () { const result = yield this.db .prepare(` SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status, title, info, data, attempt, initiated_by as initiatedBy, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt FROM job WHERE integration_id = ? AND crowdin_id = ? ORDER BY created_at DESC LIMIT ? OFFSET ? `) .bind(params.integrationId, params.crowdinId, params.limit, params.offset) .all(); return result.results || []; }); } saveTranslationCache(params) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('INSERT INTO translation_file_cache(integration_id, crowdin_id, file_id, language_id, etag) VALUES (?, ?, ?, ?, ?)') .bind(params.integrationId, params.crowdinId, params.fileId, params.languageId, params.etag) .run(); }); } getFileTranslationCache(params) { return __awaiter(this, void 0, void 0, function* () { const result = yield this.db .prepare(` SELECT integration_id as integrationId, crowdin_id as crowdinId, file_id as fileId, language_id as languageId, etag FROM translation_file_cache WHERE integration_id = ? AND crowdin_id = ? AND file_id = ? `) .bind(params.integrationId, params.crowdinId, params.fileId) .all(); return result.results || []; }); } getFileTranslationCacheByLanguage(params) { return __awaiter(this, void 0, void 0, function* () { const row = yield this.db .prepare(` SELECT integration_id as integrationId, crowdin_id as crowdinId, file_id as fileId, language_id as languageId, etag FROM translation_file_cache WHERE integration_id = ? AND crowdin_id = ? AND file_id = ? AND language_id = ? `) .bind(params.integrationId, params.crowdinId, params.fileId, params.languageId) .first(); if (row) { return row; } }); } updateTranslationCache(params) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('UPDATE translation_file_cache SET etag = ? WHERE integration_id = ? AND crowdin_id = ? AND file_id = ? AND language_id = ?') .bind(params.etag, params.integrationId, params.crowdinId, params.fileId, params.languageId) .run(); }); } saveUnsyncedFiles(params) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('INSERT INTO unsynced_files(integration_id, crowdin_id, files) VALUES (?, ?, ?)') .bind(params.integrationId, params.crowdinId, params.files) .run(); }); } updateUnsyncedFiles(params) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('UPDATE unsynced_files SET files = ? WHERE integration_id = ? AND crowdin_id = ?') .bind(params.files, params.integrationId, params.crowdinId) .run(); }); } getUnsyncedFiles(params) { return __awaiter(this, void 0, void 0, function* () { const row = yield this.db .prepare('SELECT files FROM unsynced_files WHERE integration_id = ? AND crowdin_id = ?') .bind(params.integrationId, params.crowdinId) .first(); if (row) { return row; } }); } registerCustomTable(tableName, schema) { return __awaiter(this, void 0, void 0, function* () { const schemaKeys = Object.keys(schema); const columns = schemaKeys.map((name) => `${name} ${schema[name]}`).join(', '); yield this.db.prepare(`CREATE TABLE IF NOT EXISTS ${tableName} (${columns})`).run(); }); } insertRecord(tableName, data) { return __awaiter(this, void 0, void 0, function* () { const keys = Object.keys(data); const columns = keys.join(', '); const placeholders = keys.map(() => '?').join(', '); const values = keys.map((key) => data[key]); yield this.db .prepare(`INSERT INTO ${tableName} (${columns}) VALUES (${placeholders})`) .bind(...this.sanitizeBindValues(...values)) .run(); }); } selectRecords(tableName_1) { return __awaiter(this, arguments, void 0, function* (tableName, options = {}, params = []) { var _a; const columns = ((_a = options.columns) === null || _a === void 0 ? void 0 : _a.length) ? options.columns.join(', ') : '*'; const distinctKeyword = options.distinct ? 'DISTINCT ' : ''; const whereClause = options.whereClause ? ` ${options.whereClause}` : ''; const orderByClause = options.orderBy ? ` ORDER BY ${options.orderBy}` : ''; const limitClause = options.limit ? ` LIMIT ${options.limit}` : ''; const offsetClause = options.offset ? ` OFFSET ${options.offset}` : ''; const query = `SELECT ${distinctKeyword}${columns} FROM ${tableName}${whereClause}${orderByClause}${limitClause}${offsetClause};`; const stmt = this.db.prepare(query); const result = params.length > 0 ? yield stmt.bind(...this.sanitizeBindValues(...params)).all() : yield stmt.all(); return result.results || []; }); } updateRecord(tableName_1, data_1, whereClause_1) { return __awaiter(this, arguments, void 0, function* (tableName, data, whereClause, params = []) { const setClause = Object.keys(data) .map((key) => `${key} = ?`) .join(', '); const values = Object.values(data); const query = `UPDATE ${tableName} SET ${setClause} ${whereClause};`; yield this.db .prepare(query) .bind(...this.sanitizeBindValues(...values, ...params)) .run(); }); } deleteRecord(tableName_1, whereClause_1) { return __awaiter(this, arguments, void 0, function* (tableName, whereClause, params = []) { const query = `DELETE FROM ${tableName} ${whereClause};`; const stmt = this.db.prepare(query); if (params.length > 0) { yield stmt.bind(...this.sanitizeBindValues(...params)).run(); } else { yield stmt.run(); } }); } saveSyncedData(files, integrationId, crowdinId, type) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('INSERT INTO synced_data(files, integration_id, crowdin_id, type, updated_at) VALUES (?, ?, ?, ?, ?)') .bind(files, integrationId, crowdinId, type, Date.now().toString()) .run(); }); } updateSyncedData(files, integrationId, crowdinId, type) { return __awaiter(this, void 0, void 0, function* () { yield this.db .prepare('UPDATE synced_data SET files = ?, updated_at = ? WHERE integration_id = ? AND crowdin_id = ? AND type = ?') .bind(files, Date.now().toString(), integrationId, crowdinId, type) .run(); }); } getSyncedData(integrationId, crowdinId, type) { return __awaiter(this, void 0, void 0, function* () { const row = yield this.db .prepare('SELECT id, files, integration_id as integrationId, crowdin_id as crowdinId, type, updated_at as updatedAt FROM synced_data WHERE integration_id = ? AND crowdin_id = ? AND type = ?') .bind(integrationId, crowdinId, type) .first(); if (row) { return row; } }); } /** * Converts undefined values to null for D1 compatibility * D1 does not support undefined values, so we convert them to null */ sanitizeBindValues(...values) { return values.map((v) => (v === undefined ? null : v)); } } exports.D1Storage = D1Storage;