Ir al contenido

Configuración de plugins

Los plugins suelen necesitar configuración: claves API, banderas de funciones o preferencias de visualización. EmDash ofrece dos mecanismos para cubrirlo: un esquema de ajustes para opciones editables por administradores y un almacén KV para acceso programático.

Declara un esquema de ajustes en admin.settingsSchema para generar automáticamente una interfaz de administración:

import { definePlugin } from "emdash";
export default definePlugin({
id: "seo",
version: "1.0.0",
admin: {
settingsSchema: {
siteTitle: {
type: "string",
label: "Titulo del sitio",
description: "Se usa en las etiquetas de titulo y los metadatos",
default: "",
},
maxTitleLength: {
type: "number",
label: "Longitud maxima del titulo",
description: "Numero de caracteres antes de truncar",
default: 60,
min: 30,
max: 100,
},
generateSitemap: {
type: "boolean",
label: "Generar sitemap",
description: "Genera sitemap.xml automaticamente",
default: true,
},
defaultRobots: {
type: "select",
label: "Robots por defecto",
options: [
{ value: "index,follow", label: "Indexar y seguir" },
{ value: "noindex,follow", label: "No indexar, seguir" },
{ value: "noindex,nofollow", label: "No indexar, no seguir" },
],
default: "index,follow",
},
apiKey: {
type: "secret",
label: "Clave API",
description: "Se almacena cifrada",
},
},
},
});

EmDash genera un formulario de ajustes dentro de la sección de administración del plugin. Los usuarios pueden cambiar la configuración sin tocar el código.

Campo de texto para cadenas de una sola línea o de varias líneas.

siteTitle: {
type: "string",
label: "Titulo del sitio",
description: "Texto de ayuda opcional",
default: "Mi sitio",
multiline: false // Usa true para un textarea
}

Campo numérico con restricciones opcionales de mínimo y máximo.

maxItems: {
type: "number",
label: "Numero maximo de elementos",
default: 100,
min: 1,
max: 1000
}

Interruptor de alternancia para valores verdadero/falso.

enabled: {
type: "boolean",
label: "Activado",
description: "Activa o desactiva esta funcion",
default: true
}

Menú desplegable para opciones predefinidas.

theme: {
type: "select",
label: "Tema",
options: [
{ value: "light", label: "Claro" },
{ value: "dark", label: "Oscuro" },
{ value: "auto", label: "Sistema" }
],
default: "auto"
}

Campo encriptado para valores sensibles como claves API. Nunca se envía al cliente después de guardar.

apiKey: {
type: "secret",
label: "Clave API",
description: "Se almacena cifrada"
}

Lee la configuración en hooks y rutas a través de ctx.kv:

"content:beforeSave": async (event, ctx) => {
// Leer un ajuste
const maxLength = await ctx.kv.get<number>("settings:maxTitleLength");
const apiKey = await ctx.kv.get<string>("settings:apiKey");
// Usa valores por defecto si no están configurados
const limit = maxLength ?? 60;
ctx.log.info("Usando longitud máxima", { limit });
return event.content;
}

Por convención, la configuración se guarda con el prefijo settings:. Así se distinguen los valores configurables por el usuario del estado interno del plugin.

El almacén KV (ctx.kv) es un sistema genérico de clave-valor para los datos del plugin:

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 }>>;
}
// Obtener un valor
const enabled = await ctx.kv.get<boolean>("settings:enabled");
// Obtener con tipo
const config = await ctx.kv.get<{ url: string; timeout: number }>("state:config");
// Guardar un valor
await ctx.kv.set("settings:lastSync", new Date().toISOString());
// Establecer valores complejos
await ctx.kv.set("state:cache", {
data: items,
expiry: Date.now() + 3600000,
});
// Listar todos los ajustes
const settings = await ctx.kv.list("settings:");
// Devuelve: [{ key: "settings:enabled", value: true }, ...]
// Listar todas las claves del plugin
const all = await ctx.kv.list();
const deleted = await ctx.kv.delete("state:tempData");
// Devuelve true si la clave existía

Usa prefijos para organizar los datos KV:

PrefijoPropósitoEjemplo
settings:Preferencias configurables por el usuariosettings:apiKey
state:Estado interno del pluginstate:lastSync
cache:Datos en cachécache:results
// Bien: prefijos claros
await ctx.kv.set("settings:webhookUrl", url);
await ctx.kv.set("state:lastRun", timestamp);
await ctx.kv.set("cache:feed", feedData);
// Evitar: sin prefijo, propósito poco claro
await ctx.kv.set("url", url);

Elige el mecanismo de almacenamiento correcto:

Caso de UsoMecanismo
Preferencias editables por el administradoradmin.settingsSchema + ctx.kv con settings:
Estado interno del pluginctx.kv con state:
Colecciones de documentosctx.storage

Configuración es para valores configurables por el usuario—cosas que un administrador podría cambiar. Obtienen una interfaz generada automáticamente.

KV es para estado interno como marcas de tiempo, cursores de sincronización o cálculos en caché. Sin interfaz, solo código.

Almacenamiento es para colecciones de documentos con consultas indexadas—envíos de formularios, registros de auditoría, etc.

Las rutas API pueden exponer la configuración a componentes de la interfaz de administración:

routes: {
settings: {
handler: async (ctx) => {
const settings = await ctx.kv.list("settings:");
const result: Record<string, unknown> = {};
for (const entry of settings) {
const key = entry.key.replace("settings:", "");
result[key] = entry.value;
}
return result;
}
},
"settings/save": {
handler: async (ctx) => {
const input = ctx.input as Record<string, unknown>;
for (const [key, value] of Object.entries(input)) {
if (value !== undefined) {
await ctx.kv.set(`settings:${key}`, value);
}
}
return { success: true };
}
}
}

La configuración definida en settingsSchema no se persiste automáticamente. Actúa como valor predeterminado en la interfaz de administración, así que tu código debe manejar los valores faltantes:

"content:afterSave": async (event, ctx) => {
// Proporciona siempre un valor de respaldo
const enabled = await ctx.kv.get<boolean>("settings:enabled") ?? true;
const maxItems = await ctx.kv.get<number>("settings:maxItems") ?? 100;
if (!enabled) return;
// ...
}

Alternativamente, persiste los valores por defecto en plugin:install:

hooks: {
"plugin:install": async (_event, ctx) => {
// Persistir los valores por defecto del esquema
await ctx.kv.set("settings:enabled", true);
await ctx.kv.set("settings:maxItems", 100);
}
}

Los valores KV se almacenan en la tabla _options con claves con espacio de nombres del plugin:

INSERT INTO _options (name, value) VALUES
('plugin:seo:settings:siteTitle', '"Mi sitio"'),
('plugin:seo:settings:maxTitleLength', '60');

El prefijo plugin:seo: se agrega automáticamente. Tu código usa settings:siteTitle y EmDash lo almacena como plugin:seo:settings:siteTitle.

Esto asegura que los plugins no puedan sobrescribir accidentalmente los datos de los demás.