Conectar un proyecto de Platform
Crea el proyecto CMS, autoriza dominios y enlaza el website con un project slug estable.
10 min de lectura
Crear el proyecto CMS
En Veetcuna Platform abre CMS → Crear proyecto y completa:
Define nombre y URL
Usa un nombre reconocible y la URL canónica HTTPS del website. La URL principal queda autorizada automáticamente para el editor.
Conserva la referencia del proyecto
El SDK usa la forma
@tenant/project-slug. Copia la referencia exacta; no la sustituyas por el nombre visible ni por la URL.Abre el dashboard del proyecto
Verifica que el proyecto pertenezca al tenant correcto antes de crear credenciales o conectar un website.
Configurar dominios y locales
En Configuración → General define los idiomas soportados, el idioma predeterminado y los dominios del editor.
- La URL principal siempre está autorizada.
- Añade los orígenes completos de desarrollo o preview, por ejemplo
https://website.veetcuna.dev. - Usa solo el origen (
scheme + host + puerto), sin rutas ni comodines. - Los hosts loopback se aceptan en desarrollo; para compartir el editor usa HTTPS.
Los locales configurados en Platform deben coincidir con defineCmsLocales en el website. Una diferencia puede crear documentos que el sitio no sabe resolver.
Crear la clave de runtime
En Configuración → API keys crea el perfil Website runtime para el ambiente correspondiente. Ese perfil concede únicamente READ_CONTENT.
Platform muestra el secreto una sola vez. Guárdalo como VEETCUNA_CMS_API_KEY en el secret store del website. No uses la clave de sincronización/publicación para delivery público y nunca la envíes al navegador.
Declarar el sitio
Crea un módulo server-only, por ejemplo src/cms/site.ts. Este archivo reúne la identidad del proyecto, el registry generado, los idiomas y el puente que usa el editor para renderizar un borrador.
Importar el SDK y el registry
import 'server-only';
import {
createCmsSite,
type CmsDocument,
type CmsEditorPreviewContext,
} from '@veetcuna/cms-next';
import { defineCmsLocales } from '@veetcuna/cms-next/localization';
import { cmsGenerated } from './cms.generated';server-onlyprovoca un error de build si este módulo termina importado por un Client Component. Así evita que configuración o credenciales de runtime lleguen al navegador.createCmsSitecrea el adapter principal que después usarás comocms.page(),cms.shell()ocms.collection().CmsDocumentyCmsEditorPreviewContextsolo aportan tipos al callback del editor; no agregan código al bundle.cmsGeneratedes el índice creado porwithVeetcunaCms. Contiene las definiciones descubiertas y el identificador del release del registry; no se edita manualmente.
Definir idiomas
const locales = defineCmsLocales({
supported: ['es', 'en'],
defaultLocale: 'es',
cookie: 'website-locale',
});supported determina las claves localizadas aceptadas por el sitio. defaultLocale se usa cuando una solicitud no contiene un idioma válido y debe pertenecer a supported. cookie indica dónde conservar la preferencia del visitante; puedes omitirla para usar el nombre predeterminado del SDK.
Los mismos idiomas deben existir en Configuración → General de Platform. Si el website declara en pero Platform no, o viceversa, ambos lados pueden resolver documentos distintos.
Preparar el puente del editor
type WebsiteCmsSite = ReturnType<typeof createCmsSite<'es' | 'en'>>;
let cmsSite: WebsiteCmsSite;
export async function renderCmsEditorPreview(
context: CmsEditorPreviewContext,
document: CmsDocument
) {
'use server';
return cmsSite.renderEditorPreview(context, document);
}El editor necesita una función de servidor que pueda pedirle al website el render de un borrador. Esta función no publica ni persiste contenido: delega en el SDK la validación de la sesión y la construcción del preview.
WebsiteCmsSiteconserva el tipo de la instancia, incluidos los localeses | en.let cmsSitese declara antes del callback porque el callback delegará en esa misma instancia cuando Platform lo invoque más tarde.'use server'mantiene la ejecución en el servidor. No retires esta directiva ni conviertas el archivo en un Client Component.contextlleva el contexto del preview ydocumentel documento que debe renderizarse. Entrégalos sin modificarlos arenderEditorPreview.
Crear la instancia del sitio
cmsSite = createCmsSite({
projectSlug: process.env.VEETCUNA_CMS_PROJECT_SLUG!,
generated: cmsGenerated,
site: {
name: 'Mi website',
url: 'https://www.example.com',
defaultSocialImage: '/og.png',
},
locales,
access: 'public',
editor: { renderDocumentPreview: renderCmsEditorPreview },
});
export const cms = cmsSite;Referencia exacta @tenant/project-slug copiada desde Platform. La variable
debe existir en todos los ambientes que se conecten al CMS.
Registry code-first generado durante desarrollo y build. Permite que Platform conozca qué componentes puede editar el release desplegado.
Metadata base del website. url debe ser su origen canónico y
defaultSocialImage actúa como fallback para Open Graph.
Configuración creada con defineCmsLocales; centraliza validación, idioma
predeterminado y cookie.
Usa public para delivery público. Usa private cuando la ausencia de
VEETCUNA_CMS_API_KEY deba forzar el fallback local.
Registra el callback server-side. El editor se habilita cuando existe
VEETCUNA_CMS_API_URL, salvo que configures enabled explícitamente.
La exportación cms es la única instancia que debe consumir el resto del website. Importa desde este módulo al declarar páginas, shells, colecciones y navegación, en lugar de llamar createCmsSite varias veces.
Ejemplo completo
import 'server-only';
import {
createCmsSite,
type CmsDocument,
type CmsEditorPreviewContext,
} from '@veetcuna/cms-next';
import { defineCmsLocales } from '@veetcuna/cms-next/localization';
import { cmsGenerated } from './cms.generated';
// Debe coincidir con los idiomas configurados para el proyecto en Platform.
const locales = defineCmsLocales({
supported: ['es', 'en'],
defaultLocale: 'es',
cookie: 'website-locale',
});
type WebsiteCmsSite = ReturnType<typeof createCmsSite<'es' | 'en'>>;
let cmsSite: WebsiteCmsSite;
// Platform invoca esta Server Action para renderizar borradores autorizados.
export async function renderCmsEditorPreview(
context: CmsEditorPreviewContext,
document: CmsDocument
) {
'use server';
return cmsSite.renderEditorPreview(context, document);
}
cmsSite = createCmsSite({
// Copia en el entorno la referencia exacta @tenant/project-slug.
projectSlug: process.env.VEETCUNA_CMS_PROJECT_SLUG!,
// Índice machine-owned producido por withVeetcunaCms.
generated: cmsGenerated,
// Identidad y metadata base del website.
site: {
name: 'Mi website',
url: 'https://www.example.com',
defaultSocialImage: '/og.png',
},
locales,
// Cambia a private si el delivery siempre exige VEETCUNA_CMS_API_KEY.
access: 'public',
// Mantiene el render del borrador dentro del servidor del website.
editor: { renderDocumentPreview: renderCmsEditorPreview },
});
// Usa esta instancia al declarar páginas, shells y colecciones.
export const cms = cmsSite;Primera conexión
Ejecuta el website con VEETCUNA_CMS_API_URL y el project slug correctos. Abre una página declarada con cms.page() y selecciona Conectar editor o Editar.
Al autenticarte, Platform valida acceso al módulo CMS, tenant, proyecto, origen y permisos. Si el documento aún no existe, la sesión con capacidad document:bootstrap registra el registry y crea su primera revisión a partir del documento local. El render público nunca escribe en el CMS.
Revalidación inmediata
El SDK conserva contenido publicado hasta 24 horas como red de seguridad. Para reflejar una publicación antes, crea un Route Handler firmado en el website:
import { revalidateTag } from 'next/cache';
import {
getCmsRevalidationTags,
verifyCmsRevalidationRequest,
} from '@veetcuna/cms-next';
export const runtime = 'nodejs';
export async function POST(request: Request) {
const secret = process.env.VEETCUNA_CMS_REVALIDATION_SECRET;
if (!secret)
return Response.json({ error: 'Not configured' }, { status: 503 });
const event = await verifyCmsRevalidationRequest(request, { secret });
if (
!event ||
event.payload.projectSlug !== process.env.VEETCUNA_CMS_PROJECT_SLUG
) {
return Response.json({ error: 'Invalid request' }, { status: 401 });
}
for (const tag of getCmsRevalidationTags(event.payload)) {
revalidateTag(tag, 'max');
}
return Response.json({ revalidated: true });
}Genera un secreto aleatorio de al menos 32 caracteres, guárdalo como VEETCUNA_CMS_REVALIDATION_SECRET y registra en Platform la URL HTTPS del handler con el mismo secreto para los eventos cms.published y cms.rolled_back. El secreto no es una API key y tampoco debe llegar al cliente.