Commit Graph
27 Commits
Author SHA1 Message Date
davide 49c42ecc96 Replace Strapi CMS with PocketBase
Strapi + Postgres are gone in favor of PocketBase: a single Go binary
with embedded SQLite, built-in admin UI and per-collection API rules.
No content existed yet, so this is a clean swap with no data migration.

Collections and rules are defined as code in pocketbase/pb_migrations/
and applied automatically on first boot. Draft & Publish has no native
PocketBase equivalent, so it's reproduced with a nullable `publishedAt`
field enforced by listRule/viewRule, matching the old Strapi semantics.

Routing flips: PocketBase's admin UI and REST/file API are hardwired to
`/_/` and `/api/*` at the domain root (its own dashboard assets and API
calls reference those paths directly, so a stripped path prefix like
`/admin/*` would break them). `/api` is therefore reserved for
PocketBase now, and the frontend's Nitro endpoints move to `/content/*`
(frontend/server/routes/content/, not server/api/). A `/admin` vanity
route in Nitro (not Caddy) redirects to `/_/`, so it works the same in
dev, where Caddy isn't part of the stack, and in production.

frontend/server/utils/strapi.ts becomes pocketbase.ts; queries.ts is
rewritten for PocketBase's filter/sort/fields/expand query syntax.
StrapiImage becomes MediaImage (no width/height — PocketBase file
fields don't store dimensions, and the cover images already reserve
their aspect ratio via CSS, so this is not a regression).

docs/*.md, CLAUDE.md and README.md are updated in the same commit.
2026-09-11 11:25:14 +02:00
davide 91540224a9 Lighten Strapi CMS: drop unused cloud plugin, disable telemetry/promo UI
Remove @strapi/plugin-cloud (deploy is self-hosted via Docker/Ansible,
never Strapi Cloud), disable the admin NPS/Enterprise-promotion flags
(single-admin panel, no upsell needed), disable Strapi's anonymous
telemetry, and prune dev dependencies from the production Docker image
layer.
2026-09-09 15:48:19 +02:00
davide 47a8e7788f Add docs/ with architecture, content model, frontend reference
Require CLAUDE.md changes to keep docs/ in sync with new features
or architecture.
2026-09-09 15:28:38 +02:00
davide 0d56769388 Home: only latest article reachable via 'read the latest article' link v1.0.0 2026-08-28 08:27:57 +02:00
davide 91b61caa37 Simplify home page and nav to a single latest-article link
Home now shows only the latest article, pointing to the full list on
/blog instead of duplicating recent articles in a grid. Nav trims to
Home, Tutti gli articoli, and the corralito page; the footer drops the
now-redundant Categorie column.
2026-08-27 13:35:29 +02:00
davide b7bb664c95 Add landing page content and a courses page, restyle both
Turn the home page into a single-column feature: a lead image, the
opening statement as the page's sole h1, a second image, and the body
copy, with the existing latest-articles section still below. Add a new
static page for the crypto courses pitch ("come difendersi dal
corralito"), styled like an article with a cover image and a contact
CTA (email + Telegram) instead of the referenced but nonexistent
contact form. Link the new page from the header nav and footer.
2026-08-26 10:46:46 +02:00
davide 809b60d85e Remove i18n, keep only Italian and rely on browser translation
The interface was translated in en/it/es/fr via @nuxtjs/i18n. Drop the
module, the locale files, and the language switcher: the UI is now
static Italian text, with translation for foreign visitors left to the
Google Translate browser extension instead of the app.
2026-08-26 09:39:05 +02:00
davide 448dd881e4 Ignore ops/ (holds VPS credentials, never commit) 2026-08-25 20:07:09 +02:00
davide 8021ea7254 Route every Strapi plugin path to the CMS, not just /admin
Strapi mounts each plugin's admin API at its own top-level path
(/content-manager, /upload, /i18n, ...), not nested under /admin as
previously assumed. That broke the dashboard widgets: recent/count
documents calls landed on the frontend instead of Strapi. List every
prefix actually used by the installed plugins (core-bundled ones plus
users-permissions and cloud from cms/package.json) explicitly in the
Caddyfile.
2026-08-25 20:06:40 +02:00
davide e927396505 Serve the CMS admin panel under /admin on the public domain
Replace the separate CMS subdomain with path-based routing in Caddy:
/admin and /uploads go to Strapi, everything else to the frontend.
Strapi 5 nests its whole admin panel (UI and API) under /admin, so this
one prefix is enough and never collides with the frontend's own /api
routes. Drops CMS_DOMAIN and the unused STRAPI_URL env var on the cms
service; PUBLIC_STRAPI_URL now points at the same origin as the site.

Authentication is unchanged: Strapi's own admin login still gates the
panel, this only changes how it's reached.
2026-08-25 19:49:37 +02:00
davide cb29c68d75 Fix ACME DNS resolution failures in the caddy container
The host's systemd-resolved stub (127.0.0.53) is unreachable from inside
the container network namespace, so Caddy could not resolve Let's Encrypt
endpoints. Point it at public resolvers instead.
2026-08-25 19:23:56 +02:00
davide 071bc1eaa5 Make the site responsive for phones, tablets, and desktop
Mobile-first layout with breakpoints at 48rem (tablet) and 64rem (desktop):
stacked nav on phones, progressive column counts for the article grid and
footer, scrollable markdown tables, larger tap targets on nav links.
2026-08-25 19:04:40 +02:00
davide abeabb3a44 Add the official social marks to the footer links
The marks come from the simple-icons package (CC0) and are inlined at build
time — the earlier hand-drawn glyphs were wrong, the X mark in particular is
not a plain cross. Importing the three SVGs individually rather than the
package index keeps the other icons out of the bundle: 10 kB, not 5 MB.

Rendered monochrome in the surrounding text colour, with the text label kept
next to each one.
2026-08-25 16:11:14 +02:00
davide 0229960a15 Add the social profiles to the footer 2026-08-25 15:35:29 +02:00
davide 31dbf56235 Show the author and publication time on articles
Strapi already records which admin user created an entry, so the byline needs
no extra field to fill in: the Article content type sets populateCreatorFields
and the API layer exposes only that user's firstname and lastname. The name
also feeds the BlogPosting structured data.

Also fixes the gap below the article header, which until now came from the
cover image's margin and therefore vanished on articles without a cover,
leaving the body text against the byline.
2026-08-25 15:30:57 +02:00
davide c08f53ca69 Add a language switcher with English, Italian, Spanish and French
The interface is translated through @nuxtjs/i18n: strings live in
i18n/locales/*.json, routes are prefixed except for the default English, and
useLocaleHead emits the lang attribute, the hreflang alternates and og:locale.

The switcher is a native select rather than a custom dropdown: keyboard
support, the platform picker on mobile and correct labelling come for free.

Article content stays single-language — this translates the site chrome only.
2026-08-25 14:23:09 +02:00
davide f5639111f9 Add the contact address to the footer 2026-08-25 13:44:14 +02:00
davide 9256def085 Use the full seal for the favicon at every size 2026-08-25 13:31:27 +02:00
davide 4e8d1a892e Use the CorralitoComing seal as the site logo and favicon
The seal already carries the name, so it replaces the text wordmark in the
header and opens the footer. Small icon sizes use the central emblem only: the
circular lettering is unreadable below 48px.
2026-08-25 13:25:13 +02:00
davide 55b70f0805 Restyle the frontend as an editorial magazine layout
Centred wordmark over a rule of category links, a lead article above a grid of
cards, centred article headers over a narrow reading column, and a footer split
into columns. Neutral warm-grey palette with a serif display face for headings
and small uppercase labels for metadata.
2026-08-25 12:22:05 +02:00
davide 005c90f33a Rename the site to CorralitoComing and switch the UI to English
The name and tagline live in one place, shared/utils/site.ts, and feed the
header, the footer, the home page and the title template.
2026-08-25 12:08:01 +02:00
davide d97fd3f92f Explain how to generate the .env secrets 2026-08-25 11:45:41 +02:00
davide c9ee871c44 Document the stack, the dev workflow and the production deployment
README gets step-by-step instructions for both: secrets generation and the
local stack on one side, DNS records, firewall, environment variables, TLS and
backups on the other.

CLAUDE.md records the decisions that are not visible in the code: why the
browser never reaches Strapi, why the content model stays this small, and where
the public permissions are granted.
2026-08-25 11:42:33 +02:00
davide 960b007ba7 Add Docker Compose stack with PostgreSQL and Caddy
Production traffic enters through Caddy, which terminates TLS for the public
domain and the CMS subdomain and sets HSTS and the other security headers.
PostgreSQL, Strapi and Nuxt publish no ports of their own.

docker-compose.dev.yml publishes the ports on localhost and drops Caddy for
local testing. It is a separate file rather than an override.yml so it can only
be applied when passed explicitly, never by accident in production.
2026-08-25 11:42:24 +02:00
davide a808f1010f Add Nuxt frontend with blog index, article and category pages
The browser never talks to Strapi: pages call Nitro endpoints under
server/api/, which are the only place Strapi queries are built. That keeps the
Strapi URL internal to the Docker network and avoids CORS entirely.

Article bodies are rendered from Markdown to HTML in the endpoint, so the
content is server-rendered and crawlable, marked stays out of the client
bundle, and the meta description is derived from the opening of the body.

Pages carry canonical URLs, Open Graph tags and BlogPosting structured data,
and handle empty, missing and failed states.
2026-08-25 11:42:17 +02:00
davide ec5976135f Add Strapi CMS with Article and Category content types
Article is deliberately minimal: title, slug, Markdown body, cover image and
category. No Author content type — the administrator is the only writer.

The bootstrap hook grants the Public role find/findOne on the two content types
and disables self-registration, so the public API is read-only and the site has
no front-end accounts.
2026-08-25 11:42:00 +02:00
davide 8e0a33b4fd Add project documentation, license and gitignore 2026-08-25 10:17:53 +02:00