Impostazioni

In questa pagina

I plugin sandbox memorizzano le loro impostazioni nell’archivio KV per plugin e renderizzano l’interfaccia di modifica come pagina Block Kit. Il modulo admin.settingsSchema generato automaticamente che i plugin nativi possono utilizzare non è disponibile nel sandbox — invece, si descrive il modulo in JSON e lo si serve da una route.

È un po’ più di lavoro rispetto a settingsSchema, ma tutto avviene attraverso lo stesso meccanismo che il plugin utilizza già per hook e route — non c’è nulla di extra da imparare.

L’archivio KV

Ogni plugin ottiene un archivio chiave-valore privato accessibile come ctx.kv in qualsiasi hook o route. È la posizione canonica per le impostazioni e qualsiasi altro piccolo stato persistente:

interface KVAccess {
	get<T>(key: string): Promise<T | null>;
	set(key: string, value: unknown): Promise<void>;
	delete(key: string): Promise<boolean>;
	list(prefix?: string): Promise<Array<{ key: string; value: unknown }>>;
}

KV è per plugin — le chiavi che scrivi sono archiviate sotto il tuo ID plugin e non sono visibili ad altri plugin.

Lettura e scrittura

// Lettura
const enabled = await ctx.kv.get<boolean>("settings:enabled");
const config = await ctx.kv.get<{ url: string; timeout: number }>("state:config");

// Scrittura
await ctx.kv.set("settings:lastSync", new Date().toISOString());
await ctx.kv.set("state:cache", { data: items, expiry: Date.now() + 3600000 });

// Eliminazione
const deleted = await ctx.kv.delete("state:tempData");

// Elencare per prefisso
const allSettings = await ctx.kv.list("settings:");
// → [{ key: "settings:enabled", value: true }, ...]

Convenzioni di denominazione delle chiavi

Utilizzare prefissi per mantenere separati diversi tipi di valori. La convenzione nei plugin EmDash:

PrefissoScopoEsempio
settings:Preferenze configurabili dall’utentesettings:apiKey
state:Stato interno del pluginstate:lastSync
cache:Dati in cachecache:results
// Prefissi chiari
await ctx.kv.set("settings:webhookUrl", url);
await ctx.kv.set("state:lastRun", timestamp);
await ctx.kv.set("cache:feed", feedData);

// Evitare chiavi senza prefisso
await ctx.kv.set("url", url);

Interfaccia delle impostazioni in Block Kit

I plugin sandbox descrivono la loro pagina delle impostazioni come Block Kit. L’amministratore invia un’interazione page_load a una route sul tuo plugin (convenzionalmente routes.admin), e il plugin restituisce una descrizione JSON del modulo. Quando l’utente fa clic su Salva, l’amministratore invia un’interazione block_action o form_submit indietro; il plugin scrive in KV e restituisce blocchi aggiornati.

import { definePlugin } from "emdash";
import type { PluginContext } from "emdash";

interface BlockInteraction {
	type: "page_load" | "block_action" | "form_submit";
	page?: string;
	action_id?: string;
	values?: Record<string, unknown>;
}

export default definePlugin({
	routes: {
		admin: {
			handler: async (routeCtx, ctx: PluginContext) => {
				const interaction = routeCtx.input as BlockInteraction;

				if (interaction.type === "page_load" && interaction.page === "/settings") {
					return renderSettings(ctx);
				}

				if (interaction.type === "form_submit" && interaction.action_id === "save") {
					await saveSettings(ctx, interaction.values ?? {});
					return {
						...(await renderSettings(ctx)),
						toast: { message: "Settings saved", type: "success" },
					};
				}

				return { blocks: [] };
			},
		},
	},
});

async function renderSettings(ctx: PluginContext) {
	const apiKey = (await ctx.kv.get<string>("settings:apiKey")) ?? "";
	const enabled = (await ctx.kv.get<boolean>("settings:enabled")) ?? true;
	const maxItems = (await ctx.kv.get<number>("settings:maxItems")) ?? 100;

	return {
		blocks: [
			{ type: "header", text: "Plugin settings" },
			{
				type: "form",
				block_id: "settings",
				fields: [
					{
						type: "secret_input",
						action_id: "apiKey",
						label: "API key",
						initial_value: apiKey,
					},
					{
						type: "toggle",
						action_id: "enabled",
						label: "Enabled",
						initial_value: enabled,
					},
					{
						type: "number_input",
						action_id: "maxItems",
						label: "Max items",
						min: 1,
						max: 1000,
						initial_value: maxItems,
					},
				],
				submit: { label: "Save", action_id: "save" },
			},
		],
	};
}

async function saveSettings(ctx: PluginContext, values: Record<string, unknown>) {
	for (const [key, value] of Object.entries(values)) {
		if (value !== undefined) {
			await ctx.kv.set(`settings:${key}`, value);
		}
	}
}

Per collegare la pagina delle impostazioni nella barra laterale dell’amministratore, dichiararla nel descrittore:

adminPages: [{ path: "/settings", label: "Settings", icon: "settings" }],

EmDash instrada automaticamente le interazioni page_load per quel percorso alla tua route admin.

Vedere Block Kit per l’insieme completo di tipi di blocco, campi del modulo, campi condizionali e gli helper del costruttore @emdash-cms/blocks.

Valori segreti

Il campo secret_input di Block Kit si renderizza come input mascherato. Trattare qualsiasi valore che l’utente inserisce lì con cura:

{
	type: "secret_input",
	action_id: "apiKey",
	label: "API key",
	// Non inizializzare initial_value con il segreto reale — passare una stringa vuota o un sentinella,
	// e sovrascrivere solo quando l'utente inserisce un valore non vuoto.
	initial_value: "",
}

Durante il salvataggio, saltare le stringhe vuote per evitare di cancellare il segreto esistente ad ogni salvataggio:

async function saveSettings(ctx: PluginContext, values: Record<string, unknown>) {
	if (typeof values.apiKey === "string" && values.apiKey.length > 0) {
		await ctx.kv.set("settings:apiKey", values.apiKey);
	}
	// ... altri campi
}

Valori predefiniti

Le letture KV restituiscono null per le chiavi che non sono state scritte. Passare i valori predefiniti al sito di lettura:

const enabled = (await ctx.kv.get<boolean>("settings:enabled")) ?? true;
const maxItems = (await ctx.kv.get<number>("settings:maxItems")) ?? 100;

Oppure persistere i valori predefiniti durante l’installazione:

hooks: {
	"plugin:install": async (_event, ctx) => {
		await ctx.kv.set("settings:enabled", true);
		await ctx.kv.set("settings:maxItems", 100);
	},
},

Il compromesso è che plugin:install viene eseguito una volta per installazione. Se si fornisce una nuova impostazione in una versione successiva, solo le nuove installazioni vedono il valore predefinito — le installazioni esistenti necessitano di una migrazione in plugin:activate (idempotente: scrivere solo se mancante) o di continuare a utilizzare il fallback al momento della lettura.

Impostazioni vs. archiviazione vs. KV

Caso d’usoMeccanismo
Preferenze modificabili dall’amministratorectx.kv con prefisso settings: + pagina Block Kit
Stato interno del pluginctx.kv con prefisso state:
Raccolte di documenti (query)ctx.storage

KV è per valori piccoli indicizzati da una stringa — impostazioni, cursori di sincronizzazione, calcoli in cache. Nessuna query, nessun indice.

Storage è per raccolte di documenti con query indicizzate — invii di moduli, log di audit, qualsiasi cosa in cui si desidera filtrare, impaginare o contare.

Layout dell’archiviazione

I valori KV risiedono nella tabella _options con chiavi con namespace del plugin. Il tuo codice utilizza settings:apiKey; EmDash lo memorizza come plugin:<your-plugin-id>:settings:apiKey. Il prefisso viene aggiunto automaticamente e impedisce a un plugin di leggere o sovrascrivere i dati KV di un altro plugin.

Plugin nativi: settingsSchema

Se si sta scrivendo un plugin nativo (perché è necessario pagine di amministrazione React o componenti PT), è possibile dichiarare uno schema di impostazioni direttamente all’interno di definePlugin() e lasciare che EmDash generi automaticamente il modulo. Vedere Plugin nativi per quel percorso.