bolt-templates-sms-extensio.../server/postgresFdkStorage.js

125 lines
3.1 KiB
JavaScript

const { Pool } = require('pg');
const BaseStorage = require('@gofynd/fdk-extension-javascript/express/storage/base_storage');
const DEFAULT_TABLE_NAME = 'fdk session storage';
function normalizeText(value) {
return typeof value === 'string' ? value.trim() : '';
}
function quoteIdentifier(value) {
return `"${String(value).replace(/"/g, '""')}"`;
}
function coerceJsonValue(value) {
if (!value) return null;
if (typeof value === 'object') return value;
try {
return JSON.parse(value);
} catch {
return null;
}
}
class PostgresFdkStorage extends BaseStorage {
constructor(connectionString, prefixKey = 'sms_extension_', tableName = DEFAULT_TABLE_NAME) {
super(prefixKey);
this.pool = new Pool({ connectionString });
this.tableName = quoteIdentifier(tableName);
}
getFullKey(key) {
return `${this.prefixKey}${key}`;
}
async get(key) {
const fullKey = this.getFullKey(key);
const result = await this.pool.query(
`SELECT value, expires_at
FROM ${this.tableName}
WHERE storage_key = $1
ORDER BY updated_at DESC NULLS LAST, id DESC
LIMIT 1`,
[fullKey]
);
if (!result.rows.length) return null;
const row = result.rows[0];
const expiresAt = row.expires_at ? new Date(row.expires_at) : null;
if (expiresAt && expiresAt.getTime() <= Date.now()) {
await this.del(key);
return null;
}
return coerceJsonValue(row.value);
}
async set(key, value) {
return this.save(key, value, null);
}
async setex(key, value, ttl) {
const ttlSeconds = Number(ttl);
const expiresAt = Number.isFinite(ttlSeconds) && ttlSeconds > 0
? new Date(Date.now() + ttlSeconds * 1000)
: null;
return this.save(key, value, expiresAt);
}
async save(key, value, expiresAt) {
const fullKey = this.getFullKey(key);
const serializedValue = JSON.stringify(value || {});
const client = await this.pool.connect();
try {
await client.query('BEGIN');
const updateResult = await client.query(
`UPDATE ${this.tableName}
SET value = $2,
expires_at = $3,
updated_at = NOW()
WHERE storage_key = $1`,
[fullKey, serializedValue, expiresAt]
);
if (updateResult.rowCount === 0) {
await client.query(
`INSERT INTO ${this.tableName} (storage_key, value, expires_at)
VALUES ($1, $2, $3)`,
[fullKey, serializedValue, expiresAt]
);
}
await client.query('COMMIT');
return 'OK';
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
}
async del(key) {
const fullKey = this.getFullKey(key);
await this.pool.query(`DELETE FROM ${this.tableName} WHERE storage_key = $1`, [fullKey]);
}
}
function createFdkStorage(connectionString) {
const normalizedConnectionString = normalizeText(connectionString);
if (!normalizedConnectionString) return null;
return new PostgresFdkStorage(normalizedConnectionString);
}
module.exports = {
createFdkStorage,
PostgresFdkStorage,
};