Commit Graph

20 Commits

Author SHA1 Message Date
Jakub Zych
d4ba3e2ddf feat: check-git-install --check-links refuses local dependencies
- --check-links DIR... reports every link:, file:, workspace: or portal:
  dependency in each DIR/package.json and exits 1 (L4 CR-01)
- the tag mode runs the same check on the installed layer before building
- --self-test adds planted package.json cases next to the lockfile case
- README Releasing: swap the link for the seo tag pin, run --check-links,
  then the tag mode after pushing
2026-11-04 22:54:18 +01:00
Jakub Zych
d64fe47adf fix: path-form listing routes use the SEO module's resolved pagination
- journalPaginationSource answers runtimeConfig.public.seo.pagination (the
  SEO module's option with its defaults), else the raw seo.pagination
- the module computes the paged routes inside its extendPages callback, so
  an app with only form: 'path' gets /blog/strona/:page and
  /blog/page/:page (L4 WR-03)
2026-11-04 22:53:13 +01:00
Jakub Zych
839db5156c fix: post pages keep og:type article and their article times
- journalArticleMeta(post) answers og:type article and the publish and
  update times; PostPage always sets them, the CMS-owned keys keep the
  no-answer fallback (L4 WR-02)
- expect-acme.txt: one og:type article and the article times with a CMS
  answer (heading-on) and without one (uncategorised-note)
2026-11-04 22:51:31 +01:00
Jakub Zych
e8f307b248 fix: check-git-install pins only the layer's commit and builds the playground config
- the lockfile check counts only vue-journal-layer's commit, so the layer's
  own git dependency on vue-seo-layer no longer fails it
- the scratch app takes the playground's nuxt.config.ts and app.vue (the
  search route, one post per category page, the layout trail), which
  testing/expect-acme.txt is written against
- --self-test checks the lockfile reading
2026-11-04 19:52:22 +01:00
Jakub Zych
1080a3f0c4 feat: WebP and srcset for featured images
- JournalFeaturedImage renders <picture> from featured_images[].variants:
  a WebP source and the original format as srcset fallback, the
  original's width and height, alt; lazy in cards, eager with
  fetchpriority high on the post page; unchanged without variants
- app/utils/srcset.ts (imageSrcsets) with node tests
- acme fixtures and SSR expectations for both cases
2026-11-04 17:10:30 +01:00
Jakub Zych
adcea4d538 feat: category neighbours on the post page
The post page links previous_in_category and next_in_category (the
Journal's opt-in scored related mode) as server-rendered links with the
title in the anchor text when the Journal answers one, else the
site-wide previous_post and next_post (postNeighbours). Types, a fixture
post and expectation lines for both cases.
2026-11-04 14:15:22 +01:00
Jakub Zych
ed50826bf7 docs: README for the SEO layer
The layer on vue-seo-layer: the CMS head and fallback meta, ItemList
nodes, the body link pipeline, breadcrumbs and pagination, the heading
convention in both Journal H1 modes, the postTitleTag and breadcrumbs
options, the post-title tokens, the purge route's new home, the smoke
grammar and expectation files, and upgrade notes from 0.7.
2026-11-04 14:07:54 +01:00
Jakub Zych
1dd3176060 fix: link post authors by slug, not by login 2026-10-31 13:13:38 +01:00
Jakub Zych
102da43f76 feat: opt-in Journal search page and JournalSearchBox
- journal.routes.search (off by default) registers journal-search, the
  results page for ?q= in the API's relevance order (no sort is sent),
  server-rendered with a Polish-aware result count.
- JournalSearchBox: an icon button and a WAI-ARIA combobox with debounced
  ?fields=suggest suggestions, stale answers dropped, query words marked
  ignoring diacritics; a plain GET form without JS.
- The module keeps the search path out of the route cache and the sitemap
  and marks it noindex, follow.
- Client: suggestPosts and the fields option; useJournalLinks.searchLink.
2026-10-30 18:14:05 +01:00
Jakub Zych
e0cc87118c fix: keep the site cache purge token out of the page payload (NUXT_CACHE_PURGE_TOKEN)
nuxt-site-config copies every NUXT_SITE_* variable into the public site
config, so NUXT_SITE_CACHE_PURGE_TOKEN reached every page's payload as
cachePurgeToken (and NUXT_SITE_CACHE_ALLOW_PROXIED as cacheAllowProxied).

- The purge settings move to runtimeConfig cachePurge.token
  (NUXT_CACHE_PURGE_TOKEN) and cachePurge.allowProxied
  (NUXT_CACHE_PURGE_ALLOW_PROXIED), outside that prefix.
- The old siteCache.* names keep working for one release: a non-empty
  new token wins, either allowProxied flag turns it on, and one
  deprecation warning per process names the variables (never a value).
- The shared Nitro plugin server/plugins/site-cache-purge.ts strips
  cachePurgeToken and cacheAllowProxied from nuxt-site-config's stack on
  site-config:init, so a site on the old names leaks nothing either; it
  does nothing without nuxt-site-config.
- The shared lib, route and plugin and tests/sitePurge.test.ts are
  byte-identical in vue-journal-layer, vue-menu-layer and vue-faq-layer,
  with three sha256 pins.
- README: new names, the deprecation, the stripping and the advice to
  rotate a token ever set under the old name.
2026-10-20 20:49:34 +02:00
Jakub Zych
f5d7aacf11 feat(19.3-06): purge route accepts proxied requests on siteCache.allowProxied (WR-03)
- runtimeConfig.siteCache.allowProxied (NUXT_SITE_CACHE_ALLOW_PROXIED, default
  false) lets the shared purge route accept a request that came through a
  TLS-terminating proxy; the bearer token is then the only gate
- shared lib, route and spec stay byte-identical across the three layers,
  with updated sha256 pins and two new sitePurge specs

- version 0.5.0
2026-10-15 22:42:09 +02:00
Jakub Zych
3e0b37756e feat(19.3-04): shared site cache purge route POST /api/__site__/cache/purge
- server/lib/siteCachePurge.ts and the route, byte for byte from
  vue-faq-layer (sha256 pinned in tests/sitePurge.test.ts)
- runtimeConfig.siteCache.purgeToken (NUXT_SITE_CACHE_PURGE_TOKEN) and the
  '/api/__site__/**': { cache: false } route rule
- /api/__journal__/cache/purge, app/utils/siteCache.ts and
  journal.cachePurgeToken unchanged, documented as aliases for one release
- version 0.4.0
2026-10-15 18:35:47 +02:00
03a90e952d fix: forward the visitor address on route-cached renders
Nitro's cached handler renders from a header-less copy of the request, so
under swr route rules useJournalApi, /rss.xml and the sitemap source
forwarded only the Nuxt peer and every cache miss shared one Journal API
rate-limit bucket (deferred in Phase 16). A Nitro plugin records
x-forwarded-for plus the peer in event.context.journalForwardedFor on the
request hook; the three callers forward it. Same key as the host app's
workaround, which keeps working. The acme SSR smoke's x-forwarded-for check
on the now route-cached playground is the test.
2026-10-10 18:40:25 +02:00
4f55511ece test: site cache purge authorization, purge, body parsing and revalidation
- exact names: purge auth disabled, proxied before the token, missing or
  wrong bearer; purge removes every route cache entry and only those
- parsePurgeEvent kinds, control characters, 200-character cap, non-objects
- privateRevalidate with and without max-age, the 304 form, static values
2026-10-10 18:36:13 +02:00
0a01bfeae4 feat: browsers revalidate route-cached pages; document the site cache purge
- privateRevalidate sets max-age=0 on cache-control values with s-maxage
- server/plugins/journal-revalidate.ts wraps res.setHeader per request, so
  Nitro's 200 and h3's 304 answers both carry it
- purge-check: max-age=0 on 200 and 304, purged _payload.json, a second
  purge after re-caching, X-Real-IP refused, 404 on a server without a token
- README: cachePurgeToken row and a Site cache purge section
2026-10-10 18:27:12 +02:00
759e684e86 chore: release v0.2.0 2026-10-10 12:19:24 +02:00
a6ee443885 feat: keep Blog Editor markup in the sanitizer and expand Journal embed placeholders into privacy-enhanced iframes
- htmlGate.ts mirrors the four editor-output rules of the Go policy:
  figure data-embed (^youtube$) and data-id (^[A-Za-z0-9_-]{11}$),
  img alt with any text, ol start (integer)
- embeds.ts: EMBED_PROVIDERS (youtube on youtube-nocookie.com),
  EMBED_PLACEHOLDER and expandEmbeds
- JournalPostBody and JournalCategoryHeader render
  expandEmbeds(sanitizeJournalHtml(html)); payload transforms only sanitize
- .journal-embed styles (16 / 9 block, borderless iframe)
- tests: byte copy of the editor corpus, mirror and pipeline cases;
  acme hello-acme carries a placeholder and the SSR smoke checks the iframe
- README Embeds section and decision note Policy amendment
2026-10-10 12:19:00 +02:00
5beb1c5634 fix: sanitize journal HTML instead of blanking it on a regex match
The three-regex gate could be bypassed (an attribute without a space,
<svg/onload>, entity-encoded or tab-split javascript: URLs, MathML
xlink:href, base, meta refresh, forms) and it blanked a whole post or
category when ordinary text such as "Data: 12 maja 2024" matched.

- sanitizeJournalHtml runs sanitize-html with the Journal plugin's
  bluemonday policy: UGC elements plus footer, class, schema.org
  microdata, four validated inline styles, footnote roles, dir=auto,
  two chat transcript data attributes; http, https, mailto and
  relative URLs only
- gateJournalHtml and isSafeJournalHtml keep their signatures; html is
  always the sanitized value and is never blanked
- post body, category content and both page payloads are sanitized;
  the blocked notice, its style and string, and the blocked props and
  content_blocked field are removed
- toPostView moves to postView.ts so list pages and cards do not load
  the sanitizer
- tests cover every bypass from the review, the Data:/metadata: text
  and an imported author footer; the SSR smoke expects the unsafe
  sample's safe paragraphs
2026-10-10 00:01:01 +02:00
a4dca42d77 test(16-05): Journal layer util tests
- node --test over routes, client (fake fetcher, include_empty=1),
  pagination, htmlGate (Go FormatHTML parity cases), sitemap, seo and
  pl/en i18n parity
- package script test and a Testing line in the README
2026-10-09 20:03:46 +02:00
783426c5d8 feat: Journal author and tag pages, SEO, sitemap source and RSS route
- author pages resolve slug or login and canonicalise to the slug
- tag pages, per-page SEO meta and canonicals that keep ?page=N
- sitemap source pages the API and builds locs from journal.routes
- /rss.xml proxies the API feed with x-forwarded-for
- neutral README; version 0.1.0
2026-10-09 18:06:20 +02:00