Configurações de plugins

Nesta página

Os plugins precisam de configuração — chaves de API, flags de funcionalidades, preferências de exibição. O EmDash fornece dois mecanismos: um esquema de configuração para opções editáveis pelo admin e um armazenamento KV para acesso programático.

Esquema de configuração

Declare um esquema de configuração em admin.settingsSchema para gerar automaticamente uma UI de admin:

import { definePlugin } from "emdash";

export default definePlugin({
	id: "seo",
	version: "1.0.0",

	admin: {
		settingsSchema: {
			siteTitle: {
				type: "string",
				label: "Site Title",
				description: "Used in title tags and meta",
				default: "",
			},
			maxTitleLength: {
				type: "number",
				label: "Max Title Length",
				description: "Characters before truncation",
				default: 60,
				min: 30,
				max: 100,
			},
			generateSitemap: {
				type: "boolean",
				label: "Generate Sitemap",
				description: "Automatically generate sitemap.xml",
				default: true,
			},
			defaultRobots: {
				type: "select",
				label: "Default Robots",
				options: [
					{ value: "index,follow", label: "Index & Follow" },
					{ value: "noindex,follow", label: "No Index, Follow" },
					{ value: "noindex,nofollow", label: "No Index, No Follow" },
				],
				default: "index,follow",
			},
			apiKey: {
				type: "secret",
				label: "API Key",
				description: "Encrypted at rest",
			},
		},
	},
});

O EmDash gera um formulário de configuração na secção admin do plugin. Os utilizadores editam as configurações sem tocar no código.

Tipos de campo

String

Campo de texto para strings de uma ou várias linhas.

siteTitle: {
  type: "string",
  label: "Site Title",
  description: "Optional help text",
  default: "My Site",
  multiline: false  // Defina true para textarea
}

Number

Campo numérico com restrições min/max opcionais.

maxItems: {
  type: "number",
  label: "Maximum Items",
  default: 100,
  min: 1,
  max: 1000
}

Boolean

Interruptor para valores verdadeiro/falso.

enabled: {
  type: "boolean",
  label: "Enabled",
  description: "Turn this feature on or off",
  default: true
}

Select

Lista pendente para opções predefinidas.

theme: {
  type: "select",
  label: "Theme",
  options: [
    { value: "light", label: "Light" },
    { value: "dark", label: "Dark" },
    { value: "auto", label: "System" }
  ],
  default: "auto"
}

Secret

Campo encriptado para valores sensíveis como chaves de API. Nunca enviado ao cliente após guardar.

apiKey: {
  type: "secret",
  label: "API Key",
  description: "Stored encrypted"
}

Aceder às configurações

Leia configurações em hooks e rotas via ctx.kv:

"content:beforeSave": async (event, ctx) => {
  // Ler uma configuração
  const maxLength = await ctx.kv.get<number>("settings:maxTitleLength");
  const apiKey = await ctx.kv.get<string>("settings:apiKey");

  // Usar valores padrão se não definidos
  const limit = maxLength ?? 60;

  ctx.log.info("Using max length", { limit });
  return event.content;
}

As configurações são guardadas com o prefixo settings: por convenção. Isto distingue valores configuráveis pelo utilizador do estado interno do plugin.

API do armazenamento KV

O armazenamento KV (ctx.kv) é um armazenamento chave-valor de uso geral para dados do 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 }>>;
}

Ler valores

// Obter um valor único
const enabled = await ctx.kv.get<boolean>("settings:enabled");

// Obter com tipo
const config = await ctx.kv.get<{ url: string; timeout: number }>("state:config");

Escrever valores

// Definir um valor
await ctx.kv.set("settings:lastSync", new Date().toISOString());

// Definir valores complexos
await ctx.kv.set("state:cache", {
	data: items,
	expiry: Date.now() + 3600000,
});

Listar valores

// Listar todas as configurações
const settings = await ctx.kv.list("settings:");
// Devolve: [{ key: "settings:enabled", value: true }, ...]

// Listar todas as chaves do plugin
const all = await ctx.kv.list();

Eliminar valores

const deleted = await ctx.kv.delete("state:tempData");
// Devolve true se a chave existia

Convenções de nomenclatura de chaves

Use prefixos para organizar os dados KV:

PrefixoFinalidadeExemplo
settings:Preferências configuráveissettings:apiKey
state:Estado interno do pluginstate:lastSync
cache:Dados em cachecache:results
// Bom: prefixos claros
await ctx.kv.set("settings:webhookUrl", url);
await ctx.kv.set("state:lastRun", timestamp);
await ctx.kv.set("cache:feed", feedData);

// Evitar: sem prefixo, finalidade pouco clara
await ctx.kv.set("url", url);

Configurações vs Storage vs KV

Escolha o mecanismo de armazenamento adequado:

Caso de usoMecanismo
Preferências editáveis pelo adminadmin.settingsSchema + ctx.kv com settings:
Estado interno do pluginctx.kv com state:
Coleções de documentosctx.storage

Configurações são para valores configuráveis pelo utilizador — coisas que um admin pode alterar. Têm uma UI gerada automaticamente.

KV é para estado interno como timestamps, cursores de sincronização ou cálculos em cache. Sem UI, apenas código.

Storage é para coleções de documentos com consultas indexadas — submissões de formulários, registos de auditoria, etc.

Carregar configurações em rotas

As rotas de API podem expor configurações a componentes de UI de admin:

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 };
    }
  }
}

Valores padrão

As configurações do settingsSchema não são persistidas automaticamente. São valores padrão na UI de admin. O código deve tratar valores em falta:

"content:afterSave": async (event, ctx) => {
  // Sempre fornecer um valor alternativo
  const enabled = await ctx.kv.get<boolean>("settings:enabled") ?? true;
  const maxItems = await ctx.kv.get<number>("settings:maxItems") ?? 100;

  if (!enabled) return;
  // ...
}

Alternativamente, persista os valores padrão em plugin:install:

hooks: {
  "plugin:install": async (_event, ctx) => {
    // Persistir valores padrão do esquema
    await ctx.kv.set("settings:enabled", true);
    await ctx.kv.set("settings:maxItems", 100);
  }
}

Implementação do armazenamento

Os valores KV são guardados na tabela _options com chaves com namespace do plugin:

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

O prefixo plugin:seo: é adicionado automaticamente. O código usa settings:siteTitle e o EmDash guarda como plugin:seo:settings:siteTitle.

Isto garante que os plugins não podem sobrescrever acidentalmente os dados uns dos outros.