97 lines
3.1 KiB
JavaScript
97 lines
3.1 KiB
JavaScript
import { randomUUID } from "node:crypto";
|
|
import { getPool } from "../db/pool";
|
|
const DEFAULT_SETTINGS = {
|
|
volume: 80,
|
|
language: "id-ID",
|
|
heartbeat_interval_seconds: 60
|
|
};
|
|
function nowIso() {
|
|
return new Date().toISOString();
|
|
}
|
|
function mapConfig(row) {
|
|
return {
|
|
device_id: row.device_id,
|
|
config_version: Number(row.config_version),
|
|
settings_json: row.settings_json || {},
|
|
updated_at: row.updated_at
|
|
};
|
|
}
|
|
function mapAck(row) {
|
|
return {
|
|
id: row.id,
|
|
device_id: row.device_id,
|
|
config_version: Number(row.config_version),
|
|
status: row.status,
|
|
reason: row.reason || undefined,
|
|
payload_json: row.payload_json || {},
|
|
acked_at: row.acked_at
|
|
};
|
|
}
|
|
export async function getDeviceConfig(deviceId) {
|
|
const { rows } = await getPool().query("SELECT * FROM device_configs WHERE device_id = $1", [deviceId]);
|
|
return rows[0] ? mapConfig(rows[0]) : null;
|
|
}
|
|
export async function getOrCreateDeviceConfig(deviceId) {
|
|
const existing = await getDeviceConfig(deviceId);
|
|
if (existing) {
|
|
return existing;
|
|
}
|
|
return upsertDeviceConfig({
|
|
device_id: deviceId,
|
|
settings_json: DEFAULT_SETTINGS
|
|
});
|
|
}
|
|
export async function upsertDeviceConfig(payload) {
|
|
const existing = await getDeviceConfig(payload.device_id);
|
|
const nextVersion = payload.config_version || (existing ? existing.config_version + 1 : 1);
|
|
const { rows } = await getPool().query(`INSERT INTO device_configs (device_id, config_version, settings_json, updated_at)
|
|
VALUES ($1,$2,$3,$4)
|
|
ON CONFLICT (device_id) DO UPDATE
|
|
SET config_version = EXCLUDED.config_version,
|
|
settings_json = EXCLUDED.settings_json,
|
|
updated_at = EXCLUDED.updated_at
|
|
RETURNING *`, [payload.device_id, nextVersion, payload.settings_json, nowIso()]);
|
|
return mapConfig(rows[0]);
|
|
}
|
|
export async function createDeviceConfigAck(payload) {
|
|
const { rows } = await getPool().query(`INSERT INTO device_config_acks (
|
|
id,
|
|
device_id,
|
|
config_version,
|
|
status,
|
|
reason,
|
|
payload_json,
|
|
acked_at
|
|
) VALUES ($1,$2,$3,$4,$5,$6,$7)
|
|
RETURNING *`, [
|
|
`cfgack_${randomUUID()}`,
|
|
payload.device_id,
|
|
payload.config_version,
|
|
payload.status,
|
|
payload.reason || null,
|
|
payload.payload_json || {},
|
|
nowIso()
|
|
]);
|
|
return mapAck(rows[0]);
|
|
}
|
|
export async function listDeviceConfigAcks(deviceId, limit = 50) {
|
|
const { rows } = await getPool().query(`SELECT * FROM device_config_acks
|
|
WHERE device_id = $1
|
|
ORDER BY acked_at DESC
|
|
LIMIT $2`, [deviceId, Math.min(Math.max(limit, 1), 200)]);
|
|
return rows.map(mapAck);
|
|
}
|
|
export async function getLatestDeviceConfigAck(deviceId) {
|
|
const { rows } = await getPool().query(`SELECT * FROM device_config_acks
|
|
WHERE device_id = $1
|
|
ORDER BY acked_at DESC
|
|
LIMIT 1`, [deviceId]);
|
|
return rows[0] ? mapAck(rows[0]) : null;
|
|
}
|
|
export function toDeviceConfigPayload(config) {
|
|
return { ...config };
|
|
}
|
|
export function toDeviceConfigAckPayload(ack) {
|
|
return { ...ack };
|
|
}
|