El esquema en código. El contenido por HTTP.

Define colecciones en TypeScript, edítalas en el admin, léelas por REST, GraphQL o el loader de Astro. Alojado en la UE, o en tus propios servidores.

El admin de Osprey editando una página publicada en el sitio: la página en vivo en el centro con catorce campos editables marcados, la lista de campos a la izquierda, el selector de idioma y el botón Publicar en la barra. El admin de Osprey editando una página publicada en el sitio: la página en vivo en el centro con catorce campos editables marcados, la lista de campos a la izquierda, el selector de idioma y el botón Publicar en la barra.
El admin, editando una página en vivo. Una captura, no un mockup. El admin existe en inglés e italiano, así que estas capturas son el conjunto en inglés.

Cuatro cosas que puedes comprobar.

  • El esquema Catorce tipos de campo Texto, textarea, texto enriquecido, número, booleano, fecha y hora, slug, select, relación, media, grupo, array, JSON, bloques. Todos exportados por el paquete core.
  • La lectura pública Sin clave, 404 por defecto Una colección se lee sin clave solo si su regla de acceso admite a un llamante anónimo. Todo lo demás responde 404, nunca 403.
  • Los idiomas Diez idiomas de contenido Campos localizados, una matriz de cobertura por página e idioma, y los huecos en un CSV.
  • Las claves Hash, mostradas una vez Las claves API se guardan como hash HMAC con pepper. La ves al crearla; el admin conserva los últimos cuatro caracteres.
Cada función, en detalle →

De un archivo TypeScript a una página en la web.

Desplázate. El mismo contenido, en cada una de sus cuatro paradas.

  1. 01 · Esquema

    Defines la colección en TypeScript.

    Slug, campos, reglas de acceso. Vive en tu repositorio y pasa por revisión como cualquier otro código.

    content/blog-post.tsTS
    import { defineCollection, fields } from '@osprey/core'
    
    export const blogPost = defineCollection({
      slug: 'blog-post',
      access: { read: () => true },
      fields: {
        slug:        fields.slug({ from: 'title' }),
        locale:      fields.text({ required: true }),
        title:       fields.text({ required: true }),
        excerpt:     fields.textarea(),
        body:        fields.richText({ required: true }),
        publishedAt: fields.datetime(),
      },
    })
  2. 02 · Admin

    El admin lee el esquema y dibuja el formulario.

    Campos obligatorios, selector de idioma, cuerpo en texto enriquecido. Nada se configura dos veces.

  3. 03 · API

    Las entradas publicadas salen en JSON.

    Un GET, sin clave, con SEO, canonical y hreflang ya fusionados en cada entrada.

    entriesHTTP
    GET /api/v1/collections/blog-post/entries?locale=en
    Host: cms.altovar.net
    
    200 OK
    { "data": [ {
        "id": "fdoc_16d9i3ef",
        "data":       { "title": "EU-first content infrastructure", … },
        "seo":        { "metaTitle": "EU-first content infrastructure", … },
        "canonical":  "https://…/en/eu-first-content-infrastructure",
        "alternates": { "en": …, "x-default": … }
      } ],
      "pagination": { "page": 1, "limit": 20, "total": 3 } }
  4. 04 · Sitio

    Tu frontend las renderiza.

    El loader de Astro descarga las entradas al construir. El blog de abajo se construye exactamente así, desde este CMS.

    Abrir el blog en vivo →
    src/content.config.tsASTRO
    import { defineCollection } from 'astro:content'
    import { ospreyLoader } from '@osprey/astro'
    
    const blog = defineCollection({
      loader: ospreyLoader({
        collection: 'blog-post',
        baseUrl:    'https://cms.altovar.net',
        tenantId:   'altovar',
      }),
    })

El admin, en tu tema y en tu idioma.

Claro u oscuro, inglés o italiano. Cada captura de este sitio sigue el tema que eliges en la cabecera: es el mismo admin, fotografiado dos veces.

Tu contenido. Tu servidor. Hoy.

Plan gratuito, sin tarjeta. Alojado en la UE, o en tus propias máquinas.