mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-31 15:45:23 +00:00
fix: retry failed or blocked plugins on restart instead of leaving them disabled
This commit is contained in:
1 parent
b6bb3ffc73
commit
27819c477c
3 files changed
+55
-13
No files matched your search
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Plugins to start at boot. One that failed or was blocked last run is still
|
||||||
|
* one the admin turned on, so a restart tries it again rather than leaving it
|
||||||
|
* looking disabled.
|
||||||
|
*/
|
||||||
|
export function startablePluginIds(
|
||||||
|
records: { id: string; state: string }[],
|
||||||
|
): Set<string> {
|
||||||
|
return new Set(
|
||||||
|
records
|
||||||
|
.filter((record) =>
|
||||||
|
["enabled", "failed", "blocked"].includes(record.state),
|
||||||
|
)
|
||||||
|
.map((record) => record.id),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ import { setSshAuthTypeOwnerSource } from "../hosts/connect/auth-provider-regist
|
|||||||
import { setHostProtocolSource } from "../hosts/protocol-auth/registry.js";
|
import { setHostProtocolSource } from "../hosts/protocol-auth/registry.js";
|
||||||
import { setSecretResolverOwnerSource } from "../hosts/connect/secret-resolver-registry.js";
|
import { setSecretResolverOwnerSource } from "../hosts/connect/secret-resolver-registry.js";
|
||||||
import { recordConflict } from "./conflicts.js";
|
import { recordConflict } from "./conflicts.js";
|
||||||
|
import { startablePluginIds } from "./boot-state.js";
|
||||||
import { setPluginImpersonationCheck } from "../utils/auth-manager.js";
|
import { setPluginImpersonationCheck } from "../utils/auth-manager.js";
|
||||||
import { setKeybindingActionSource } from "../database/routes/keybinding-validation.js";
|
import { setKeybindingActionSource } from "../database/routes/keybinding-validation.js";
|
||||||
|
|
||||||
@@ -94,9 +95,9 @@ export function getPluginRuntime(): { loader: PluginLoader } {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A plugin that tripped the error budget is off until an admin retries it, so
|
* A plugin that tripped the error budget is off until an admin retries it or
|
||||||
* it has to look off everywhere: no routes, greyed permissions, and a failed
|
* the server restarts, so it has to look off everywhere: no routes and greyed
|
||||||
* row that survives a restart.
|
* permissions.
|
||||||
*/
|
*/
|
||||||
async function handlePluginFailed(plugin: LoadedPlugin): Promise<void> {
|
async function handlePluginFailed(plugin: LoadedPlugin): Promise<void> {
|
||||||
unregisterPluginHttp(plugin.id);
|
unregisterPluginHttp(plugin.id);
|
||||||
@@ -245,11 +246,7 @@ export async function initializePlugins(
|
|||||||
const { createCurrentPluginRepository } =
|
const { createCurrentPluginRepository } =
|
||||||
await import("../database/repositories/factory.js");
|
await import("../database/repositories/factory.js");
|
||||||
const records = await createCurrentPluginRepository().listAll();
|
const records = await createCurrentPluginRepository().listAll();
|
||||||
const enabled = new Set(
|
const enabled = startablePluginIds(records);
|
||||||
records
|
|
||||||
.filter((record) => record.state === "enabled")
|
|
||||||
.map((record) => record.id),
|
|
||||||
);
|
|
||||||
|
|
||||||
const candidates = loaded
|
const candidates = loaded
|
||||||
.filter((plugin) => enabled.has(plugin.id))
|
.filter((plugin) => enabled.has(plugin.id))
|
||||||
@@ -278,14 +275,28 @@ async function persistRuntimeState(loaded: LoadedPlugin[]): Promise<void> {
|
|||||||
const { createCurrentPluginRepository } =
|
const { createCurrentPluginRepository } =
|
||||||
await import("../database/repositories/factory.js");
|
await import("../database/repositories/factory.js");
|
||||||
const repository = createCurrentPluginRepository();
|
const repository = createCurrentPluginRepository();
|
||||||
|
const records = new Map(
|
||||||
|
(await repository.listAll()).map((record) => [record.id, record]),
|
||||||
|
);
|
||||||
|
|
||||||
for (const plugin of loaded) {
|
for (const plugin of loaded) {
|
||||||
if (plugin.state !== "blocked" && plugin.state !== "failed") continue;
|
const record = records.get(plugin.id);
|
||||||
try {
|
try {
|
||||||
await repository.update(plugin.id, {
|
if (plugin.state === "blocked" || plugin.state === "failed") {
|
||||||
state: plugin.state,
|
await repository.update(plugin.id, {
|
||||||
lastError: plugin.lastError,
|
state: plugin.state,
|
||||||
});
|
lastError: plugin.lastError,
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
|
plugin.state === "active" &&
|
||||||
|
record &&
|
||||||
|
record.state !== "enabled"
|
||||||
|
) {
|
||||||
|
await repository.update(plugin.id, {
|
||||||
|
state: "enabled",
|
||||||
|
lastError: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Reporting state must never stop the boot.
|
// Reporting state must never stop the boot.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { startablePluginIds } from "../../plugins/boot-state.js";
|
||||||
|
|
||||||
|
describe("startablePluginIds", () => {
|
||||||
|
it("starts enabled plugins and retries ones that failed or were blocked", () => {
|
||||||
|
const ids = startablePluginIds([
|
||||||
|
{ id: "a", state: "enabled" },
|
||||||
|
{ id: "b", state: "failed" },
|
||||||
|
{ id: "c", state: "blocked" },
|
||||||
|
{ id: "d", state: "disabled" },
|
||||||
|
{ id: "e", state: "pending" },
|
||||||
|
]);
|
||||||
|
expect([...ids].sort()).toEqual(["a", "b", "c"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in new issue
Block a user