mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 14:45:24 +00:00
feat: redesign user host defaults system
This commit is contained in:
1 parent
3028ab1176
commit
a4a4d4f27b
144 files changed
+24609
-2555
No files matched your search
@@ -372,6 +372,21 @@ export function createPluginHosts({ manifest, bag, audit }: Deps): PluginHosts {
|
||||
}
|
||||
};
|
||||
|
||||
/** Host settings a plugin writes are the host's own, not its defaults. */
|
||||
const markSettingsOwn = async (
|
||||
hostId: number,
|
||||
writes: Awaited<ReturnType<typeof checkSettings>>,
|
||||
) => {
|
||||
if (writes.length === 0) return;
|
||||
const { changeHostOverrides } =
|
||||
await import("../hosts/defaults/overrides.js");
|
||||
await changeHostOverrides([hostId], {
|
||||
own: writes.flatMap(({ manifest: owner, values }) =>
|
||||
Object.keys(values).map((key): [string, string] => [owner.id, key]),
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
list: async () => {
|
||||
await requireRead();
|
||||
@@ -467,12 +482,25 @@ export function createPluginHosts({ manifest, bag, audit }: Deps): PluginHosts {
|
||||
const writes = await checkSettings(settings);
|
||||
const { createCurrentHostRepository } =
|
||||
await import("../database/repositories/factory.js");
|
||||
const { applyHostDefaultsToWrite, applyDefaultsAfterHostWrite } =
|
||||
await import("../hosts/defaults/index.js");
|
||||
const columns: Record<string, unknown> = { ...row, userId };
|
||||
columns.defaultOverrides = JSON.stringify(
|
||||
await applyHostDefaultsToWrite({
|
||||
ownerId: userId,
|
||||
hostId: null,
|
||||
columns,
|
||||
body: host as unknown as Record<string, unknown>,
|
||||
}),
|
||||
);
|
||||
const created =
|
||||
await createCurrentHostRepository().createEncryptedForUser(userId, {
|
||||
...row,
|
||||
await createCurrentHostRepository().createEncryptedForUser(
|
||||
userId,
|
||||
});
|
||||
columns,
|
||||
);
|
||||
await applyDefaultsAfterHostWrite(created.id);
|
||||
await writeSettings(created.id, writes);
|
||||
await markSettingsOwn(created.id, writes);
|
||||
await audit("hosts_create", `host ${created.id}`, { success: true });
|
||||
return (
|
||||
await toRecords([created as unknown as Record<string, unknown>])
|
||||
@@ -497,13 +525,47 @@ export function createPluginHosts({ manifest, bag, audit }: Deps): PluginHosts {
|
||||
const writes = await checkSettings(settings);
|
||||
const { createCurrentHostRepository } =
|
||||
await import("../database/repositories/factory.js");
|
||||
const { applyHostDefaultsToWrite, applyDefaultsAfterHostWrite } =
|
||||
await import("../hosts/defaults/index.js");
|
||||
const { createCurrentHostDefaultsRepository } =
|
||||
await import("../database/repositories/factory.js");
|
||||
const stored = (
|
||||
await createCurrentHostDefaultsRepository().listHosts({
|
||||
hostIds: [hostId],
|
||||
})
|
||||
)[0];
|
||||
const columns: Record<string, unknown> = { ...row };
|
||||
if (stored && stored.userId === userId) {
|
||||
columns.defaultOverrides = JSON.stringify(
|
||||
await applyHostDefaultsToWrite({
|
||||
ownerId: userId,
|
||||
hostId,
|
||||
columns,
|
||||
body: patch as unknown as Record<string, unknown>,
|
||||
stored,
|
||||
}),
|
||||
);
|
||||
}
|
||||
const updated =
|
||||
await createCurrentHostRepository().updateEncryptedForUser(
|
||||
userId,
|
||||
hostId,
|
||||
row,
|
||||
columns,
|
||||
);
|
||||
if (updated) await writeSettings(hostId, writes);
|
||||
if (updated) {
|
||||
await writeSettings(hostId, writes);
|
||||
await markSettingsOwn(hostId, writes);
|
||||
await applyDefaultsAfterHostWrite(hostId, {
|
||||
moved:
|
||||
!!stored &&
|
||||
((columns.folder !== undefined &&
|
||||
(columns.folder ?? null) !== (stored.folder ?? null)) ||
|
||||
(columns.parentHostId !== undefined &&
|
||||
(columns.parentHostId ?? null) !==
|
||||
(stored.parentHostId ?? null))),
|
||||
ownerId: userId,
|
||||
});
|
||||
}
|
||||
await audit("hosts_update", `host ${hostId}`, {
|
||||
success: updated !== null,
|
||||
});
|
||||
|
||||
@@ -816,7 +816,40 @@ export function createPluginContext(
|
||||
|
||||
getHost: (hostId, key) =>
|
||||
pluginSettings.getSetting(manifest, "host", hostId, key) as never,
|
||||
setHost: async (hostId, key, value) => {
|
||||
getHostFor: async (hostId, userId, key) => {
|
||||
const { personalHostValue } =
|
||||
await import("../hosts/defaults/personal.js");
|
||||
const personal = await personalHostValue(
|
||||
manifest,
|
||||
Number(hostId),
|
||||
userId,
|
||||
key,
|
||||
);
|
||||
return (
|
||||
personal.applies
|
||||
? personal.value
|
||||
: await pluginSettings.getSetting(manifest, "host", hostId, key)
|
||||
) as never;
|
||||
},
|
||||
getHostDefault: async (userId, key) => {
|
||||
const { userHostDefault } =
|
||||
await import("../hosts/defaults/personal.js");
|
||||
return (await userHostDefault(manifest, userId, key)) as never;
|
||||
},
|
||||
setHost: async (hostId, key, value, options) => {
|
||||
const { changeHostOverrides } =
|
||||
await import("../hosts/defaults/overrides.js");
|
||||
if (options?.inherit) {
|
||||
if (!pluginSettings.findField(manifest, "host", key)) {
|
||||
throw new Error(
|
||||
`"${key}" is not a host setting this plugin declares`,
|
||||
);
|
||||
}
|
||||
await changeHostOverrides([Number(hostId)], {
|
||||
inherit: [[pluginId, key]],
|
||||
});
|
||||
return;
|
||||
}
|
||||
const error = await pluginSettings.setSetting(
|
||||
manifest,
|
||||
"host",
|
||||
@@ -825,6 +858,7 @@ export function createPluginContext(
|
||||
value,
|
||||
);
|
||||
if (error) throw new Error(error);
|
||||
await changeHostOverrides([Number(hostId)], { own: [[pluginId, key]] });
|
||||
},
|
||||
|
||||
listHostValues: async (key) => {
|
||||
|
||||
@@ -75,7 +75,11 @@ export function onSettingsChange(
|
||||
};
|
||||
}
|
||||
|
||||
function notify(pluginId: string, key: string, value: unknown): void {
|
||||
export function notifySettingChange(
|
||||
pluginId: string,
|
||||
key: string,
|
||||
value: unknown,
|
||||
): void {
|
||||
for (const listener of listeners.get(pluginId)?.get(key) ?? []) {
|
||||
try {
|
||||
listener(value);
|
||||
@@ -92,7 +96,10 @@ function notify(pluginId: string, key: string, value: unknown): void {
|
||||
|
||||
export type SettingsValidator = (
|
||||
values: Record<string, unknown>,
|
||||
context: { hostId?: number },
|
||||
context: {
|
||||
hostId?: number;
|
||||
defaults?: { level: "admin" | "user" | "folder" };
|
||||
},
|
||||
) =>
|
||||
| Record<string, string>
|
||||
| void
|
||||
@@ -131,6 +138,7 @@ export async function validateSettingsSave(
|
||||
scope: PluginSettingsScope,
|
||||
scopeId: string | null,
|
||||
values: Record<string, unknown>,
|
||||
defaults?: { level: "admin" | "user" | "folder" },
|
||||
): Promise<Record<string, string>> {
|
||||
const errors: Record<string, string> = {};
|
||||
for (const entry of validators.get(pluginId) ?? []) {
|
||||
@@ -138,6 +146,7 @@ export async function validateSettingsSave(
|
||||
try {
|
||||
const result = await entry.run(values, {
|
||||
hostId: scope === "host" && scopeId ? Number(scopeId) : undefined,
|
||||
...(defaults ? { defaults } : {}),
|
||||
});
|
||||
Object.assign(errors, result ?? {});
|
||||
} catch (error) {
|
||||
@@ -299,7 +308,7 @@ export async function setSetting(
|
||||
shouldEncrypt,
|
||||
);
|
||||
|
||||
notify(manifest.id, key, coerced);
|
||||
notifySettingChange(manifest.id, key, coerced);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user