125 lines
3.1 KiB
JavaScript
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,
|
|
};
|