Ctrl K
ComunidadGuías…

scripts/post_migrate_retention_env.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
require("dotenv").config();

const { Client } = require("pg");

/**
 * One-shot carry-over of the retention a self-host instance enforced before per-dashboard retention
 * (v1.3.5 and earlier). The per-dashboard picker did nothing then; the events TTL from the retired
 * DATA_RETENTION_DAYS env var (default 1 year) was the real retention. Every dashboard is raised to
 * at least that, so the first retention purge deletes nothing v1.3.5 kept. A marker comment on
 * "DashboardSettings" makes it run once, so a retention set after the upgrade is not overridden.
 */

const DEFAULT_RETENTION_DAYS = 365;
const UNLIMITED_RETENTION_DAYS = -1;
// The presets above the default in DATA_RETENTION_PRESETS (dashboard/src/utils/settingsUtils.ts)
const LONGER_PRESETS = [730, 1095, 1825];
const MARKER_PREFIX = "retention_env_carried_over=";

// Mirrors v1.3.5's parse::<i32>(): -1 kept data forever, a positive value was a TTL in days, anything else was ignored
function resolveEffectiveDays(raw) {
  const value = (raw ?? "").trim();
  if (!/^[+-]?\d+$/.test(value)) return DEFAULT_RETENTION_DAYS;
  const days = Number(value);
  if (days === UNLIMITED_RETENTION_DAYS) return UNLIMITED_RETENTION_DAYS;
  if (days <= DEFAULT_RETENTION_DAYS) return DEFAULT_RETENTION_DAYS;
  return LONGER_PRESETS.find((preset) => preset >= days) ?? UNLIMITED_RETENTION_DAYS;
}

function carriedOverDays(storedDays, effectiveDays) {
  if (storedDays === UNLIMITED_RETENTION_DAYS || effectiveDays === UNLIMITED_RETENTION_DAYS) {
    return UNLIMITED_RETENTION_DAYS;
  }
  return Math.max(storedDays, effectiveDays);
}

async function main() {
  if (process.env.IS_CLOUD === "true") return;

  const raw = process.env.DATA_RETENTION_DAYS?.trim();
  const days = resolveEffectiveDays(raw);

  const databaseUrl = process.env.POSTGRES_URL;
  if (!databaseUrl) {
    console.error("post_migrate_retention_env: POSTGRES_URL must be set in the environment.");
    process.exit(1);
  }

  const client = new Client({ connectionString: databaseUrl });
  try {
    await client.connect();
    await client.query("BEGIN");
    const { rows } = await client.query(
      `SELECT obj_description('"DashboardSettings"'::regclass, 'pg_class') AS marker`,
    );
    if (rows[0]?.marker?.startsWith(MARKER_PREFIX)) {
      await client.query("ROLLBACK");
      console.log(`post_migrate_retention_env: already carried over (${rows[0].marker}), skipping.`);
      return;
    }
    const { rows: settings } = await client.query(
      `SELECT "id", "dataRetentionDays" FROM "DashboardSettings" FOR UPDATE`,
    );
    const raisedIds = settings
      .filter((row) => carriedOverDays(row.dataRetentionDays, days) !== row.dataRetentionDays)
      .map((row) => row.id);
    if (raisedIds.length > 0) {
      await client.query(
        `UPDATE "DashboardSettings" SET "dataRetentionDays" = $1, "updatedAt" = CURRENT_TIMESTAMP
         WHERE "id" = ANY($2)`,
        [days, raisedIds],
      );
    }
    // COMMENT takes no bind parameters; days is an integer computed above
    await client.query(`COMMENT ON TABLE "DashboardSettings" IS '${MARKER_PREFIX}${days}'`);
    await client.query("COMMIT");
    const target = days === UNLIMITED_RETENTION_DAYS ? "Keep forever" : `${days} days`;
    const source = raw ? `DATA_RETENTION_DAYS=${raw}` : "DATA_RETENTION_DAYS unset";
    const hint = raw ? " DATA_RETENTION_DAYS has no further effect; remove it from .env." : "";
    console.log(
      `post_migrate_retention_env: ${source}, raised ${raisedIds.length} dashboard(s) to ${target}.${hint}`,
    );
  } catch (error) {
    await client.query("ROLLBACK").catch(() => {});
    console.error(
      "post_migrate_retention_env: failed to carry the pre-upgrade retention over to dashboard retention:",
      // A refused connection is an AggregateError with an empty message, only its code says why
      (error instanceof Error && error.message) || String(error?.code ?? error),
    );
    process.exit(1);
  } finally {
    await client.end().catch(() => {});
  }
}

if (require.main === module) main();

module.exports = { resolveEffectiveDays, carriedOverDays };