← back to Mfr Review Viewer Corruption

lib/fm-record-snapshot.mjs

59 lines

// fm-record-snapshot.mjs — READ-ONLY full-field snapshot of ONE WALLPAPER master.
//
// Given a FileMaker recordId, returns its COMPLETE fieldData from the WALLPAPER db
// via the filemaker-mcp getRecord (import-only; does NOT modify that project). Used
// two ways:
//   • /api/fm-record-snapshot — the viewer shows the full record before proposing a delete.
//   • /api/stage — the server writes each delete-candidate's full field snapshot to
//     data/restore/<recordId>-<ISO>.json so an APPROVED delete is reversible (recreatable).
//
// PURELY READ-ONLY: FileMaker record GET only. Never writes to FileMaker. Loads the
// filemaker-mcp .env for FM Cloud creds. Same layout the candidates resolver uses.

import { readFileSync, existsSync } from 'node:fs';
import path from 'node:path';
import os from 'node:os';

const FM_PROJECT = path.join(os.homedir(), 'Projects', 'filemaker-mcp');
const FM_ENV = path.join(FM_PROJECT, '.env');
const FM_CLIENT = path.join(FM_PROJECT, 'src', 'fm-client.js');
const FM_DB = 'WALLPAPER';
const FM_LAYOUT = '*List Wallpapers - Full View';

// ---- load the filemaker-mcp .env (read-only) so FM Cloud creds are present ----
function loadFmEnv() {
  if (!existsSync(FM_ENV)) return;
  for (const line of readFileSync(FM_ENV, 'utf8').split('\n')) {
    const m = line.match(/^([A-Z_]+)=(.*)$/);
    if (m && !process.env[m[1]]) process.env[m[1]] = m[2];
  }
}
loadFmEnv();

// lazy-import getRecord (ES module) once, cached
let _getRecord = null;
async function getGetRecord() {
  if (_getRecord) return _getRecord;
  const mod = await import('file://' + FM_CLIENT);
  _getRecord = mod.getRecord;
  return _getRecord;
}

// Return { recordId, db, layout, captured_at, fieldData, modId } for one master.
// Throws on FM/creds error (caller catches + degrades).
export async function snapshotRecord(recordId) {
  const rid = String(recordId || '').trim();
  if (!rid) throw new Error('recordId required');
  const getRecord = await getGetRecord();
  const rec = await getRecord(FM_DB, FM_LAYOUT, rid);
  if (!rec) throw new Error(`WALLPAPER master ${rid} not found`);
  return {
    recordId: rec.recordId || rid,
    db: FM_DB,
    layout: FM_LAYOUT,
    modId: rec.modId || null,
    captured_at: new Date().toISOString(),
    fieldData: rec.fieldData || {},
  };
}