Malton London Press
En líneaLa web y el sistema de publicación de una editorial académica de acceso abierto. Lo desarrollé yo, administro el servidor y despliego cada cambio.
- Mi papel
- Único desarrollador
- Año
- 2026
- Hecho con
- Next.js · Payload CMS · PostgreSQL · Turborepo · PM2 · Nginx · Hetzner · GitHub Actions
Desplázate en horizontal para ver el diagrama completo.
El problema
Una editorial académica necesita algo más que una web. Cada libro y cada artículo de congreso tiene que poder leerse y descargarse gratis, los editores tienen que publicar sin tocar código y Google Scholar tiene que encontrar e indexar cada artículo. Si no lo hace, el trabajo de los autores queda invisible.
Cómo está construido
Un monorepo con pnpm y Turborepo, con dos apps y un paquete compartido: un sitio en Next.js para los lectores y Payload CMS sobre PostgreSQL, donde trabajan los editores y de donde el sitio saca su contenido. Los lectores consultan libros y actas de congresos y descargan los PDF, y los autores envían propuestas de libros a través de un formulario. Todo corre en un único servidor de Hetzner detrás de Nginx, y PM2 mantiene las dos apps en modo cluster. Cada push ejecuta lint, comprobación de tipos y tests en GitHub Actions. El deploy se lanza a mano: el workflow vuelve a pasar las comprobaciones sobre ese commit exacto, se conecta al servidor por SSH, ejecuta el mismo script de deploy que ejecutaría una persona (install, migrate, build, reload) y después hace polling de un endpoint de salud desde fuera hasta que el sitio responde.
Decisiones importantes
Los deploys solo se lanzan a mano
Hacer push no despliega nada, así que un commit malo en main no puede llegar a producción por sí solo. El workflow llama al mismo script que ejecutaría una persona, por lo que el camino manual y el automático no pueden divergir.
Sin rollback automático
Hacer checkout de código antiguo no deshace una migración de base de datos que ya se ha ejecutado, así que volver atrás sigue siendo un paso deliberado. Antes de cada deploy se guarda en el servidor el commit anterior, de modo que el rollback es un solo comando.
Metadatos que Google Scholar puede leer
Cada página de artículo y de libro lleva las etiquetas de citación de Highwire Press, con cada autor seguido de su propia institución y su ORCID, para que Google Scholar y los gestores de referencias como Zotero indexen bien esas páginas.
Código del repositorio
/** One citation_author per person, each followed by its own institution and ORCID. */
function authorTags(people: Person[] | null | undefined): CitationTag[] {
const tags: CitationTag[] = []
for (const person of people ?? []) {
const name = person.name?.trim()
if (!name) continue
tags.push(['citation_author', name])
const institution = person.affiliation?.trim()
if (institution) tags.push(['citation_author_institution', institution])1
const orcid = orcidUrl(person.orcid)
if (orcid) tags.push(['citation_author_orcid', orcid])
}
return tags
}
// …
/** Tags for a conference paper page (/proceedings/[volume]/[paper]). */
export function paperCitationTags(
paper: Pick<Paper, 'title' | 'slug' | 'authors' | 'doi' | 'pdf' | 'pagesRange'>,
volume: Pick<Book, 'title' | 'slug' | 'publicationDate' | 'isbn' | 'conference'>,
siteUrl: string = publicSiteUrl(),
): CitationTag[] {
const tags: CitationTag[] = []
add(tags, 'citation_title', paper.title)
tags.push(...authorTags(paper.authors))
add(tags, 'citation_publication_date', citationDate(volume.publicationDate))2
// …
const site = siteUrl.replace(/\/+$/, '')
// Same host and directory as this page (Scholar ignores a PDF anywhere else).
if (hasFile(paper.pdf)) add(tags, 'citation_pdf_url', `${site}${paperPdfPath(volume.slug, paper.slug)}`)3
add(tags, 'citation_abstract_html_url', `${site}/proceedings/${volume.slug}/${paper.slug}`)
add(tags, 'citation_language', 'en')
return tags
}
Línea 9. Scholar asocia una institución y un ORCID a la etiqueta de autor que tienen justo antes, así que el orden de estas etiquetas tiene significado.
Línea 27. Las fechas salen como YYYY/MM/DD en UTC, el formato que pide Scholar.
Línea 31. Scholar solo acepta un PDF que esté en el mismo host y directorio que la página del artículo, así que el enlace se construye a partir de la ruta de la propia página.
En qué punto está
En línea desde enero de 2026 en maltonlondonpress.com. Me encargo de que siga funcionando y despliego cada cambio.