Compare commits

...
12 Commits
Author SHA1 Message Date
radoslaw.smolinski cca2b20c3d 1.5.6 2026-09-28 19:44:34 +02:00
radoslaw.smolinski 105c454d73 Added OG support 2026-09-28 19:44:28 +02:00
radoslaw.smolinski cf6feefebc 1.5.5 2026-09-28 16:47:11 +02:00
radoslaw.smolinski 1857c8f771 Sitemap language normalize trough all websites 2026-09-28 16:47:06 +02:00
radoslaw.smolinski 542ad4ab9c 1.5.4 2026-09-28 16:35:00 +02:00
radoslaw.smolinski 9bf8599116 Fix for plugin.ts 2026-09-28 16:34:54 +02:00
radoslaw.smolinski 170c9a53f1 1.5.3 2026-09-28 16:25:36 +02:00
radoslaw.smolinski e0d5095099 Fix for sitemap.css n2 2026-09-28 16:22:49 +02:00
radoslaw.smolinski 08a8a10478 1.5.2 2026-09-28 16:13:46 +02:00
radoslaw.smolinski 7bbaf14d14 Fixed sitemap.css 2026-09-28 16:13:33 +02:00
radoslaw.smolinski b7d0b01122 1.5.1 2026-09-28 16:01:09 +02:00
radoslaw.smolinski 4627266577 2FA Fix 2026-09-28 16:01:03 +02:00
12 changed files with 162 additions and 36 deletions
+19 -8
View File
@@ -18,7 +18,7 @@
--badge-bg: #f3f4f6; --badge-bg: #f3f4f6;
--badge-border: #e5e7eb; --badge-border: #e5e7eb;
--badge-text: #4b5563; --badge-text: #4b5563;
--accent: #f97316; --accent: #027bd0;
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
@@ -34,12 +34,13 @@
--badge-bg: #1a1c26; --badge-bg: #1a1c26;
--badge-border: #282b3d; --badge-border: #282b3d;
--badge-text: #9ca3af; --badge-text: #9ca3af;
--accent: #fb923c; --accent: #027bd0;
} }
} }
urlset { urlset {
display: block; display: flex;
flex-direction: column;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, Helvetica, Arial, sans-serif; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, Helvetica, Arial, sans-serif;
background-color: var(--bg); background-color: var(--bg);
color: var(--text-main); color: var(--text-main);
@@ -55,6 +56,7 @@ urlset {
urlset::before { urlset::before {
content: "XML Sitemap"; content: "XML Sitemap";
display: block; display: block;
order: -2;
font-size: 1.35rem; font-size: 1.35rem;
font-weight: 600; font-weight: 600;
letter-spacing: -0.02em; letter-spacing: -0.02em;
@@ -66,6 +68,7 @@ urlset::before {
urlset::after { urlset::after {
content: "Intecion.com, Technology — engineered for modern digital experiences."; content: "Intecion.com, Technology — engineered for modern digital experiences.";
display: block; display: block;
order: -1;
font-size: 0.8rem; font-size: 0.8rem;
color: var(--text-muted); color: var(--text-muted);
padding-bottom: 1.25rem; padding-bottom: 1.25rem;
@@ -76,6 +79,8 @@ urlset::after {
/* URL card */ /* URL card */
url { url {
display: block; display: block;
width: 100%;
order: 0;
background-color: var(--card); background-color: var(--card);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 8px; border-radius: 8px;
@@ -114,17 +119,17 @@ priority {
} }
lastmod::before { lastmod::before {
content: "Zaktualizowano: "; content: "Updated: ";
color: var(--text-muted); color: var(--text-muted);
} }
changefreq::before { changefreq::before {
content: "Częstotliwość: "; content: "Frequency: ";
color: var(--text-muted); color: var(--text-muted);
} }
priority::before { priority::before {
content: "Priorytet: "; content: "Priority: ";
color: var(--text-muted); color: var(--text-muted);
} }
@@ -132,7 +137,7 @@ priority::before {
link { link {
display: inline-block; display: inline-block;
font-size: 0.7rem; font-size: 0.7rem;
font-weight: 500; font-weight: 600;
background-color: var(--badge-bg); background-color: var(--badge-bg);
border: 1px solid var(--badge-border); border: 1px solid var(--badge-border);
color: var(--badge-text); color: var(--badge-text);
@@ -140,4 +145,10 @@ link {
border-radius: 4px; border-radius: 4px;
margin-right: 0.3rem; margin-right: 0.3rem;
margin-top: 0.35rem; margin-top: 0.35rem;
} text-transform: uppercase;
}
link::before {
content: attr(hreflang);
}
+16 -1
View File
@@ -17,12 +17,27 @@ export type PageMetadata = {
url: string; url: string;
}[]; }[];
locale?: string; locale?: string;
/** Site/brand name for the OG card. */
siteName?: string;
title: string; title: string;
/** 'website' | 'article' etc. Defaults to 'website'. */
type?: string;
/** Canonical URL of this page. */
url?: string;
}; };
/** robots directives — set to noindex/follow for legal/thin/search pages. */ /**
* robots directives. Indexed pages get generous snippet/preview limits by
* default (Google shows richer results); noindex pages get index:false.
*/
robots?: { robots?: {
follow: boolean; follow: boolean;
index: boolean; index: boolean;
/** 'none' | 'standard' | 'large' — image preview size in results. */
'max-image-preview'?: 'large' | 'none' | 'standard';
/** Max text snippet length; -1 = no limit. Next maps to max-snippet. */
'max-snippet'?: number;
/** Max video preview seconds; -1 = no limit. */
'max-video-preview'?: number;
}; };
title: string; title: string;
}; };
+20 -4
View File
@@ -71,16 +71,32 @@ import { buildHreflangAlternates } from './hreflang.js';
...images && { ...images && {
images images
}, },
locale type: 'website',
locale,
...siteName ? {
siteName
} : {},
...canonical ? {
url: canonical
} : {}
}, },
// noindex → tell search engines to exclude the page but still follow links // robots: noindex pages are excluded (follow keeps link authority). Indexed
// (authority flows through). For legal/thin/search-result pages. // pages get generous snippet/preview limits so Google can show rich results
// (long snippets, large image previews, full video previews).
...meta?.noindex ? { ...meta?.noindex ? {
robots: { robots: {
follow: true, follow: true,
index: false index: false
} }
} : {} } : {
robots: {
follow: true,
index: true,
'max-image-preview': 'large',
'max-snippet': -1,
'max-video-preview': -1
}
}
}; };
} }
File diff suppressed because one or more lines are too long
+9 -3
View File
@@ -67,9 +67,15 @@ import { buildSeoPlugin, injectAutoFillMeta, injectSeoTabs } from './modules/seo
// optional peer dependency at build time (it isn't in the plugin's own // optional peer dependency at build time (it isn't in the plugin's own
// node_modules). Avoids TS2307 without @ts-expect-error; the module // node_modules). Avoids TS2307 without @ts-expect-error; the module
// exists at runtime in projects that installed it. // exists at runtime in projects that installed it.
const pkg = '@clocklimited/payload-2fa'; // @ts-ignore
const { totpPlugin } = await import(pkg); const mod = await import('@clocklimited/payload-2fa');
config = await totpPlugin({ // The package exports `payloadTotp`; older/other builds may use
// `totpPlugin`. Accept either so a rename doesn't break us.
const totp = mod.payloadTotp ?? mod.totpPlugin;
if (typeof totp !== 'function') {
throw new Error('expected export payloadTotp (or totpPlugin) to be a function — ' + 'check the installed @clocklimited/payload-2fa version');
}
config = await totp({
collection: tf.collectionSlug ?? 'users', collection: tf.collectionSlug ?? 'users',
forceSetup: true, forceSetup: true,
totp: { totp: {
+1 -1
View File
File diff suppressed because one or more lines are too long
+4 -1
View File
@@ -253,8 +253,11 @@ sprawdzane przed dostępem do DANYCH, nie tylko UI panelu).
### Zależność ### Zależność
```bash ```bash
pnpm add @clocklimited/payload-2fa pnpm add @clocklimited/payload-2fa@3.0.0-beta.7
``` ```
Uwaga: pakiet nie ma jeszcze stabilnego 3.0.0 — użyj konkretnej wersji beta
(albo `^3.0.0-0`, żeby dopuścić prereleasy). Sam `^3.0.0` da błąd
ERR_PNPM_NO_MATCHING_VERSION.
To PEER dependency — ipal-kit importuje ją dynamicznie tylko gdy 2FA włączone To PEER dependency — ipal-kit importuje ją dynamicznie tylko gdy 2FA włączone
(domyślnie). Bez niej i z włączonym 2FA plugin rzuci jasny błąd. (domyślnie). Bez niej i z włączonym 2FA plugin rzuci jasny błąd.
+25
View File
@@ -843,6 +843,31 @@ const jsonLd = buildArticleJsonLd({
Dane z dokumentu/panelu. Google wymaga headline + dat dla rich result. Dane z dokumentu/panelu. Google wymaga headline + dat dla rich result.
## Robots — rich results (max-snippet, image/video preview)
Strony indeksowane dostają automatycznie dyrektywy pozwalające Google na bogate
wyniki (buildMetadata generuje):
- `max-snippet: -1` — pełnej długości snippet (bez limitu)
- `max-image-preview: large` — duży podgląd obrazu w wynikach
- `max-video-preview: -1` — pełny podgląd wideo
Strony noindex (polityki) dostają `index: false, follow: true` — bez powyższych.
Zero konfiguracji — działa automatycznie dla każdej strony przez createPageMetadata.
## Open Graph — pełne pola (type, siteName, url, description)
buildMetadata generuje kompletny OG dla social share (Facebook, LinkedIn, Slack):
- `og:title` — tytuł strony
- `og:description` — meta.description → fallback siteDescription (dziedziczy)
- `og:image` — defaultShareImage / obraz strony
- `og:type` — 'website'
- `og:site_name` — siteName z panelu
- `og:url` — canonical strony
- `og:locale` — język strony
Wszystko z panelu, automatycznie. Opis OG dziedziczy z meta.description (albo
globalnego siteDescription gdy pusty) — patrz niżej.
## Fallback meta description (siteDescription) ## Fallback meta description (siteDescription)
Strona bez `meta.description` → plugin używa globalnego `siteDescription` z Strona bez `meta.description` → plugin używa globalnego `siteDescription` z
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@intecion/ipal-kit", "name": "@intecion/ipal-kit",
"version": "1.5.0", "version": "1.5.6",
"description": "Intecion Payload Advanced Library — a Payload CMS 3 plugin: i18n, SEO, forms, consent, analytics, blog/archives.", "description": "Intecion Payload Advanced Library — a Payload CMS 3 plugin: i18n, SEO, forms, consent, analytics, blog/archives.",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
+19 -8
View File
@@ -18,7 +18,7 @@
--badge-bg: #f3f4f6; --badge-bg: #f3f4f6;
--badge-border: #e5e7eb; --badge-border: #e5e7eb;
--badge-text: #4b5563; --badge-text: #4b5563;
--accent: #f97316; --accent: #027bd0;
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
@@ -34,12 +34,13 @@
--badge-bg: #1a1c26; --badge-bg: #1a1c26;
--badge-border: #282b3d; --badge-border: #282b3d;
--badge-text: #9ca3af; --badge-text: #9ca3af;
--accent: #fb923c; --accent: #027bd0;
} }
} }
urlset { urlset {
display: block; display: flex;
flex-direction: column;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, Helvetica, Arial, sans-serif; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, Helvetica, Arial, sans-serif;
background-color: var(--bg); background-color: var(--bg);
color: var(--text-main); color: var(--text-main);
@@ -55,6 +56,7 @@ urlset {
urlset::before { urlset::before {
content: "XML Sitemap"; content: "XML Sitemap";
display: block; display: block;
order: -2;
font-size: 1.35rem; font-size: 1.35rem;
font-weight: 600; font-weight: 600;
letter-spacing: -0.02em; letter-spacing: -0.02em;
@@ -66,6 +68,7 @@ urlset::before {
urlset::after { urlset::after {
content: "Intecion.com, Technology — engineered for modern digital experiences."; content: "Intecion.com, Technology — engineered for modern digital experiences.";
display: block; display: block;
order: -1;
font-size: 0.8rem; font-size: 0.8rem;
color: var(--text-muted); color: var(--text-muted);
padding-bottom: 1.25rem; padding-bottom: 1.25rem;
@@ -76,6 +79,8 @@ urlset::after {
/* URL card */ /* URL card */
url { url {
display: block; display: block;
width: 100%;
order: 0;
background-color: var(--card); background-color: var(--card);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 8px; border-radius: 8px;
@@ -114,17 +119,17 @@ priority {
} }
lastmod::before { lastmod::before {
content: "Zaktualizowano: "; content: "Updated: ";
color: var(--text-muted); color: var(--text-muted);
} }
changefreq::before { changefreq::before {
content: "Częstotliwość: "; content: "Frequency: ";
color: var(--text-muted); color: var(--text-muted);
} }
priority::before { priority::before {
content: "Priorytet: "; content: "Priority: ";
color: var(--text-muted); color: var(--text-muted);
} }
@@ -132,7 +137,7 @@ priority::before {
link { link {
display: inline-block; display: inline-block;
font-size: 0.7rem; font-size: 0.7rem;
font-weight: 500; font-weight: 600;
background-color: var(--badge-bg); background-color: var(--badge-bg);
border: 1px solid var(--badge-border); border: 1px solid var(--badge-border);
color: var(--badge-text); color: var(--badge-text);
@@ -140,4 +145,10 @@ link {
border-radius: 4px; border-radius: 4px;
margin-right: 0.3rem; margin-right: 0.3rem;
margin-top: 0.35rem; margin-top: 0.35rem;
} text-transform: uppercase;
}
link::before {
content: attr(hreflang);
}
+33 -4
View File
@@ -20,12 +20,27 @@ export type PageMetadata = {
description?: string description?: string
images?: { url: string }[] images?: { url: string }[]
locale?: string locale?: string
/** Site/brand name for the OG card. */
siteName?: string
title: string title: string
/** 'website' | 'article' etc. Defaults to 'website'. */
type?: string
/** Canonical URL of this page. */
url?: string
} }
/** robots directives — set to noindex/follow for legal/thin/search pages. */ /**
* robots directives. Indexed pages get generous snippet/preview limits by
* default (Google shows richer results); noindex pages get index:false.
*/
robots?: { robots?: {
follow: boolean follow: boolean
index: boolean index: boolean
/** 'none' | 'standard' | 'large' — image preview size in results. */
'max-image-preview'?: 'large' | 'none' | 'standard'
/** Max text snippet length; -1 = no limit. Next maps to max-snippet. */
'max-snippet'?: number
/** Max video preview seconds; -1 = no limit. */
'max-video-preview'?: number
} }
title: string title: string
} }
@@ -140,10 +155,24 @@ export function buildMetadata({
title, title,
...(description && { description }), ...(description && { description }),
...(images && { images }), ...(images && { images }),
type: 'website',
locale, locale,
...(siteName ? { siteName } : {}),
...(canonical ? { url: canonical } : {}),
}, },
// noindex → tell search engines to exclude the page but still follow links // robots: noindex pages are excluded (follow keeps link authority). Indexed
// (authority flows through). For legal/thin/search-result pages. // pages get generous snippet/preview limits so Google can show rich results
...(meta?.noindex ? { robots: { follow: true, index: false } } : {}), // (long snippets, large image previews, full video previews).
...(meta?.noindex
? { robots: { follow: true, index: false } }
: {
robots: {
follow: true,
index: true,
'max-image-preview': 'large' as const,
'max-snippet': -1,
'max-video-preview': -1,
},
}),
} }
} }
+14 -4
View File
@@ -76,11 +76,21 @@ export const ipalKit = (options: IpalOptions): Plugin => {
// optional peer dependency at build time (it isn't in the plugin's own // optional peer dependency at build time (it isn't in the plugin's own
// node_modules). Avoids TS2307 without @ts-expect-error; the module // node_modules). Avoids TS2307 without @ts-expect-error; the module
// exists at runtime in projects that installed it. // exists at runtime in projects that installed it.
const pkg = '@clocklimited/payload-2fa' // @ts-ignore
const { totpPlugin } = (await import(pkg)) as { const mod = (await import('@clocklimited/payload-2fa')) as {
totpPlugin: (opts: Record<string, unknown>) => Plugin payloadTotp?: (opts: Record<string, unknown>) => Plugin
totpPlugin?: (opts: Record<string, unknown>) => Plugin
} }
config = await totpPlugin({ // The package exports `payloadTotp`; older/other builds may use
// `totpPlugin`. Accept either so a rename doesn't break us.
const totp = mod.payloadTotp ?? mod.totpPlugin
if (typeof totp !== 'function') {
throw new Error(
'expected export payloadTotp (or totpPlugin) to be a function — ' +
'check the installed @clocklimited/payload-2fa version',
)
}
config = await totp({
collection: tf.collectionSlug ?? 'users', collection: tf.collectionSlug ?? 'users',
forceSetup: true, // ENFORCED — every user must set up TOTP; no opt-out forceSetup: true, // ENFORCED — every user must set up TOTP; no opt-out
totp: { issuer: tf.issuer }, totp: { issuer: tf.issuer },