feat: move desktop, status, import, audit and branding features into plugins

This commit is contained in:
LukeGus committed 2026-10-18 21:17:54 -05:00
1 parent 9d93fb0893
commit 1cdbfc674c
166 files changed
+21298 -12041

No files matched your search

@@ -0,0 +1,87 @@
/**
* Moves the audit forwarding collector URL and token out of the core settings
* table into the audit-tools plugin's admin settings. The token is stored
* encrypted with the installation key, like any plugin secret setting, and the
* core copies are removed once the plugin has its own.
*
* Idempotent: a value the plugin already has is kept, and a core value is only
* removed after the plugin holds a copy. Does nothing until the plugin row
* exists (plugin_settings has a foreign key to plugins).
*/
import { databaseLogger } from "../utils/logger.js";
import {
createCurrentPluginRepository,
createCurrentPluginSettingsRepository,
createCurrentSettingsRepository,
} from "../database/repositories/factory.js";
import { encryptSystemSecret } from "../utils/system-secret-crypto.js";
const PLUGIN_ID = "audit-tools";
const URL_SETTING = "audit_log_forward_url";
const TOKEN_SETTING = "audit_log_forward_token";
export interface AuditForwardingMigrationResult {
url: boolean;
token: boolean;
}
export async function runAuditForwardingMigration(): Promise<AuditForwardingMigrationResult> {
const result: AuditForwardingMigrationResult = { url: false, token: false };
try {
if (!(await createCurrentPluginRepository().findById(PLUGIN_ID))) {
return result;
}
} catch {
return result;
}
const core = createCurrentSettingsRepository();
const plugin = createCurrentPluginSettingsRepository();
const legacyUrl = (await core.get(URL_SETTING))?.trim();
const legacyToken = (await core.get(TOKEN_SETTING))?.trim();
const has = async (key: string) => {
const row = await plugin.get(PLUGIN_ID, "admin", null, key);
return !!row && row.value !== null && row.value !== '""';
};
if (legacyUrl) {
if (!(await has("url"))) {
await plugin.set(
PLUGIN_ID,
"admin",
null,
"url",
JSON.stringify(legacyUrl),
);
result.url = true;
}
if (legacyToken && !(await has("token"))) {
await plugin.set(
PLUGIN_ID,
"admin",
null,
"token",
JSON.stringify(await encryptSystemSecret(legacyToken)),
true,
);
result.token = true;
}
}
// Safe to drop now: the plugin holds the values, or never had a collector.
if (legacyUrl ? await has("url") : true) {
if (legacyUrl) await core.delete(URL_SETTING);
if (legacyToken && (await has("token"))) await core.delete(TOKEN_SETTING);
else if (!legacyUrl && legacyToken) await core.delete(TOKEN_SETTING);
}
if (result.url || result.token) {
databaseLogger.info("Moved audit forwarding settings into audit-tools", {
operation: "audit_forwarding_migration",
});
}
return result;
}
@@ -0,0 +1,64 @@
/**
* Moves the global status check interval into the host-status plugin, before
* it activates, so its first checks use the interval an admin chose instead of
* the plugin's 30 second default.
*
* Idempotent: a value the plugin already has is kept.
*/
import { databaseLogger } from "../utils/logger.js";
import {
createCurrentPluginRepository,
createCurrentPluginSettingsRepository,
createCurrentSettingsRepository,
} from "../database/repositories/factory.js";
const PLUGIN_ID = "host-status";
const LEGACY_KEY = "global_status_check_interval";
function seconds(value: unknown): number | null {
const parsed = Number(value);
return Number.isInteger(parsed) && parsed >= 5 && parsed <= 3600
? parsed
: null;
}
export async function runHostStatusSettingsMigration(): Promise<number | null> {
try {
const plugin = await createCurrentPluginRepository().findById(PLUGIN_ID);
if (!plugin) return null;
const settings = createCurrentPluginSettingsRepository();
const existing = await settings.get(
PLUGIN_ID,
"admin",
null,
"intervalSeconds",
);
if (existing && existing.value !== null) return null;
const legacy = seconds(
await createCurrentSettingsRepository().get(LEGACY_KEY),
);
if (legacy === null) return null;
await settings.set(
PLUGIN_ID,
"admin",
null,
"intervalSeconds",
JSON.stringify(legacy),
);
databaseLogger.info("Moved the status check interval into the plugin", {
operation: "host_status_settings_migration",
intervalSeconds: legacy,
});
return legacy;
} catch (error) {
databaseLogger.warn("Host status settings migration failed", {
operation: "host_status_settings_migration",
error: error instanceof Error ? error.message : String(error),
});
return null;
}
}
+6
View File
@@ -10,6 +10,8 @@ import { runOpksshConfigMigration } from "./opkssh-config-migration.js";
import { runTermixIdentityCaMigration } from "./termix-identity-ca-migration.js";
import { runSessionRecordingDataMigration } from "./session-recording-data-migration.js";
import { runNotificationChannelMigration } from "./notification-channel-migration.js";
import { runHostStatusSettingsMigration } from "./host-status-settings-migration.js";
import { runAuditForwardingMigration } from "./audit-forwarding-migration.js";
export async function runPluginDataMoves(): Promise<void> {
await runLdapProviderMigration();
@@ -19,4 +21,8 @@ export async function runPluginDataMoves(): Promise<void> {
await runSessionRecordingDataMigration();
// Automations name channels by id, so they are in place before it starts.
await runNotificationChannelMigration();
// The plugin forwards from its own settings, so they are in place first.
await runAuditForwardingMigration();
// Sets the check interval, which the plugin reads when it activates.
await runHostStatusSettingsMigration();
}