mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-30 07:05:24 +00:00
fix: finish 2.9.0 plugin moves and get every check green
This commit is contained in:
1 parent
8c1e02af89
commit
6489b62ece
335 files changed
+29878
-17749
No files matched your search
@@ -20,7 +20,10 @@ import {
|
||||
isRedactedSecret,
|
||||
validateSettingValue,
|
||||
} from "@termix/plugin-sdk/settings";
|
||||
import { createCurrentPluginSettingsRepository } from "../database/repositories/factory.js";
|
||||
import {
|
||||
createCurrentHostRepository,
|
||||
createCurrentPluginSettingsRepository,
|
||||
} from "../database/repositories/factory.js";
|
||||
import type { PluginSettingsScope } from "../database/repositories/plugin-settings-repository.js";
|
||||
import {
|
||||
decryptSystemSecret,
|
||||
@@ -87,9 +90,67 @@ function notify(pluginId: string, key: string, value: unknown): void {
|
||||
}
|
||||
}
|
||||
|
||||
export type SettingsValidator = (
|
||||
values: Record<string, unknown>,
|
||||
context: { hostId?: number },
|
||||
) =>
|
||||
| Record<string, string>
|
||||
| void
|
||||
| undefined
|
||||
| Promise<Record<string, string> | void | undefined>;
|
||||
|
||||
const validators = new Map<
|
||||
string,
|
||||
Set<{ scope: PluginSettingsScope; run: SettingsValidator }>
|
||||
>();
|
||||
|
||||
/** A plugin's own check on a settings save, before anything is written. */
|
||||
export function onSettingsValidate(
|
||||
pluginId: string,
|
||||
scope: PluginSettingsScope,
|
||||
run: SettingsValidator,
|
||||
): () => void {
|
||||
let set = validators.get(pluginId);
|
||||
if (!set) {
|
||||
set = new Set();
|
||||
validators.set(pluginId, set);
|
||||
}
|
||||
const entry = { scope, run };
|
||||
set.add(entry);
|
||||
return () => {
|
||||
set!.delete(entry);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the plugin's validators over a save. Returns field key to message;
|
||||
* a validator that throws rejects the whole save under "_".
|
||||
*/
|
||||
export async function validateSettingsSave(
|
||||
pluginId: string,
|
||||
scope: PluginSettingsScope,
|
||||
scopeId: string | null,
|
||||
values: Record<string, unknown>,
|
||||
): Promise<Record<string, string>> {
|
||||
const errors: Record<string, string> = {};
|
||||
for (const entry of validators.get(pluginId) ?? []) {
|
||||
if (entry.scope !== scope) continue;
|
||||
try {
|
||||
const result = await entry.run(values, {
|
||||
hostId: scope === "host" && scopeId ? Number(scopeId) : undefined,
|
||||
});
|
||||
Object.assign(errors, result ?? {});
|
||||
} catch (error) {
|
||||
errors._ = error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
/** Drops every listener a plugin registered. Called when it deactivates. */
|
||||
export function clearSettingsListeners(pluginId: string): void {
|
||||
listeners.delete(pluginId);
|
||||
validators.delete(pluginId);
|
||||
}
|
||||
|
||||
/** The fields a manifest declares for one scope. */
|
||||
@@ -114,7 +175,7 @@ export function declaredFields(
|
||||
key: host.enableKey,
|
||||
type: "boolean",
|
||||
labelKey: host.enableLabelKey,
|
||||
default: false,
|
||||
default: host.enableDefault ?? false,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
@@ -241,6 +302,40 @@ export async function setSetting(
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Every stored value of one host field, with each host's owner. */
|
||||
export async function listHostValues(
|
||||
manifest: PluginManifest,
|
||||
key: string,
|
||||
): Promise<Array<{ hostId: number; userId: string; value: unknown }>> {
|
||||
const field = findField(manifest, "host", key);
|
||||
if (!field) {
|
||||
throw new Error(
|
||||
`"${key}" is not a host settings field this plugin declares`,
|
||||
);
|
||||
}
|
||||
if (field.type === "secret") {
|
||||
throw new Error(`"${key}" is a secret and cannot be listed`);
|
||||
}
|
||||
|
||||
const rows = await createCurrentPluginSettingsRepository().listByKey(
|
||||
manifest.id,
|
||||
"host",
|
||||
key,
|
||||
);
|
||||
const hosts = createCurrentHostRepository();
|
||||
const result: Array<{ hostId: number; userId: string; value: unknown }> = [];
|
||||
for (const row of rows) {
|
||||
const hostId = Number(row.scopeId);
|
||||
if (!Number.isInteger(hostId) || row.value === null) continue;
|
||||
const host = await hosts.findById(hostId);
|
||||
if (!host?.userId) continue;
|
||||
const value = decode(row.value);
|
||||
if (value === undefined) continue;
|
||||
result.push({ hostId, userId: host.userId as string, value });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export interface GetAllOptions {
|
||||
/** Replaces every secret with { set } so the value never leaves the server. */
|
||||
redactSecrets?: boolean;
|
||||
|
||||
Reference in new issue
Block a user