Added protecting hooks
This commit is contained in:
+8
-1
@@ -103,11 +103,17 @@ export default buildConfig({
|
||||
|
||||
| Moduł | Opis | Dok |
|
||||
|---|---|---|
|
||||
| i18n | Lokalizacja, negocjacja locale, ścieżki URL | [i18n.md](./i18n.md) |
|
||||
| i18n | Lokalizacja, negocjacja locale, ścieżki URL, strona jednojęzyczna | [i18n.md](./i18n.md) |
|
||||
| hooks | Hooki: revalidate ISR, slug history 301, ochrona stron systemowych | [hooks.md](./hooks.md) |
|
||||
| kolekcje-katalog | Jakie kolekcje budować, kiedy, jak wpiąć (minimum nie maksimum) | [kolekcje-katalog.md](./kolekcje-katalog.md) |
|
||||
| fundamenty-projektu | Struktura katalogów, nazewnictwo, konwencje | [fundamenty-projektu.md](./fundamenty-projektu.md) |
|
||||
| deployment | Zmienne .env, ISR/SSG, force-dynamic, Coolify/Docker | [deployment.md](./deployment.md) |
|
||||
| pages | System pages (homepage/privacy/cookies) → ścieżki | [pages.md](./pages.md) |
|
||||
| access | Role admin > editor > user, kontrola dostępu | [access.md](./access.md) |
|
||||
| payload-helpers | getSiteSettings / getSiteIntegrations | [payload-helpers.md](./payload-helpers.md) |
|
||||
| seo | Metadata, hreflang, auto-fill, plugin-seo | [seo.md](./seo.md) |
|
||||
| wymagania-prawne | **Polityki, regulaminy, baner cookies, RODO (compliance)** | [wymagania-prawne.md](./wymagania-prawne.md) |
|
||||
| standardy-kodu | **Dobre praktyki senior: typy, architektura, antywzorce** | [standardy-kodu.md](./standardy-kodu.md) |
|
||||
| architektura-tresci | **Jak budować, żeby klient wszystko edytował** (filozofia CMS) | [architektura-tresci.md](./architektura-tresci.md) |
|
||||
| blocks | RenderBlocks — silnik renderowania bloków | [blocks.md](./blocks.md) |
|
||||
| consent | Banner cookies GDPR, Google Consent Mode | [consent.md](./consent.md) |
|
||||
@@ -117,6 +123,7 @@ export default buildConfig({
|
||||
| analytics | GA4 / GTM spięte z Consent Mode | [analytics.md](./analytics.md) |
|
||||
| slug | Auto-slug z tytułu, per locale | [slug.md](./slug.md) |
|
||||
| notifications | Teksty wyników akcji (formularz) per język | [notifications.md](./notifications.md) |
|
||||
| storage | Media na Cloudflare R2 (offload z .env) | [storage.md](./storage.md) |
|
||||
| security | Nagłówki bezpieczeństwa HTTP (HSTS, X-Frame...) | [security.md](./security.md) |
|
||||
| content | Blog/archiwa: kolekcje pod stroną-archiwum, listing, paginacja | [content.md](./content.md) |
|
||||
|
||||
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
# Hooki pluginu — automatyzacja tworzenia stron
|
||||
|
||||
Plugin dostarcza hooki, które zdejmują z projektów powtarzalną robotę. Wpinasz je
|
||||
w kolekcje; działają automatycznie. Wszystkie gotowe do użycia (import z pluginu).
|
||||
|
||||
Powiązane: [pages.md](./pages.md), [seo.md](./seo.md), [wymagania-prawne.md](./wymagania-prawne.md).
|
||||
|
||||
---
|
||||
|
||||
## buildRevalidateHook — ISR odświeżany po zapisie (NAJWAŻNIEJSZY)
|
||||
|
||||
Bez tego ISR ma haczyk: redaktor zapisuje stronę i CZEKA na revalidate (do
|
||||
godziny). Z tym — zapisuje i OD RAZU widzi zmianę. To warunek, żeby ISR był
|
||||
używalny dla CMS.
|
||||
|
||||
```ts
|
||||
// kolekcja Pages — z pliku projektu, który MOŻE importować next/cache
|
||||
import { revalidatePath } from 'next/cache'
|
||||
import { buildRevalidateHook } from '@intecion/ipal-kit'
|
||||
import { i18nConfig } from '@/i18n.config'
|
||||
|
||||
const { afterChange, afterDelete } = buildRevalidateHook({
|
||||
revalidatePath, // wstrzykiwany — plugin NIE importuje next/cache
|
||||
config: i18nConfig,
|
||||
})
|
||||
|
||||
export const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
hooks: { afterChange: [afterChange], afterDelete: [afterDelete] },
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**Dlaczego revalidatePath wstrzykiwany:** plugin nie importuje `next/cache` (to
|
||||
by wywaliło Payload przy generate:importmap / czystym Node). Projekt podaje.
|
||||
|
||||
Obsługuje: wszystkie języki, root (home), zmianę slug (rewaliduje stary I nowy
|
||||
path — stary URL nie serwuje starej treści), delete.
|
||||
|
||||
---
|
||||
|
||||
## setPublishedAtHook — auto-data publikacji
|
||||
|
||||
Ustawia `publishedAt` na teraz przy pierwszej publikacji (jeśli puste). Redaktor
|
||||
nie wpisuje daty ręcznie; data jest dokładna dla Article JSON-LD i sitemap.
|
||||
|
||||
```ts
|
||||
import { setPublishedAtHook } from '@intecion/ipal-kit'
|
||||
// kolekcja z draftami (blog, artykuły):
|
||||
hooks: { beforeChange: [setPublishedAtHook] }
|
||||
```
|
||||
|
||||
Ustawia tylko przy przejściu na published; nie nadpisuje istniejącej daty
|
||||
(redaktor może backdatować ręcznie).
|
||||
|
||||
---
|
||||
|
||||
## buildPreventDeleteSystemPage — ochrona stron systemowych
|
||||
|
||||
Blokuje usunięcie strony przypisanej do roli (homepage, privacyPolicy,
|
||||
cookiePolicy, termsOfService). Redaktor nie usunie przypadkiem polityki
|
||||
prywatności albo strony głównej → nie rozbije routingu i linków compliance.
|
||||
|
||||
```ts
|
||||
import { buildPreventDeleteSystemPage } from '@intecion/ipal-kit'
|
||||
hooks: { beforeDelete: [buildPreventDeleteSystemPage({ settingsSlug: 'site-settings' })] }
|
||||
```
|
||||
|
||||
Żeby usunąć — najpierw odłącz rolę w Site Settings (świadoma decyzja).
|
||||
|
||||
---
|
||||
|
||||
## buildValidateUniqueRole — jedna strona = jedna rola
|
||||
|
||||
Zapobiega przypisaniu tej samej strony do dwóch ról systemowych (np. homepage I
|
||||
privacyPolicy naraz → niejednoznaczny routing).
|
||||
|
||||
```ts
|
||||
import { buildValidateUniqueRole } from '@intecion/ipal-kit'
|
||||
// na polu roli w SiteSettings:
|
||||
{
|
||||
name: 'privacyPolicy',
|
||||
type: 'relationship',
|
||||
relationTo: 'pages',
|
||||
hooks: { beforeValidate: [buildValidateUniqueRole({
|
||||
siblingFields: ['homepage', 'cookiePolicy', 'termsOfService'],
|
||||
})] },
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## trackSlugHistoryHook — auto-redirect 301 przy zmianie slug
|
||||
|
||||
Gdy slug się zmienia, zapisuje STARY slug do pola `slugHistory`. Projekt czyta to
|
||||
i robi 301 ze starego URL na nowy → zmiana adresu nie daje 404 (realna strata SEO
|
||||
z audytu).
|
||||
|
||||
```ts
|
||||
import { trackSlugHistoryHook } from '@intecion/ipal-kit'
|
||||
|
||||
export const Pages: CollectionConfig = {
|
||||
fields: [
|
||||
// ...
|
||||
{ name: 'slugHistory', type: 'array', admin: { readOnly: true },
|
||||
fields: [{ name: 'slug', type: 'text' }] },
|
||||
],
|
||||
hooks: { beforeChange: [trackSlugHistoryHook] },
|
||||
}
|
||||
```
|
||||
|
||||
Projekt w resolveRoute / sprawdzeniu redirectów: jeśli żądany slug jest w
|
||||
slugHistory jakiejś strony → 301 na jej aktualny slug. Przykład:
|
||||
|
||||
```ts
|
||||
// w page.tsx, gdy resolveRoute nie znajdzie strony po slug:
|
||||
const byHistory = await payload.find({
|
||||
collection: 'pages',
|
||||
where: { 'slugHistory.slug': { equals: requestedSlug } },
|
||||
limit: 1,
|
||||
})
|
||||
if (byHistory.docs[0]) {
|
||||
redirect(`/${locale}/${byHistory.docs[0].slug}`) // 301 na aktualny
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## KOLEJNOŚĆ hooków (ważne)
|
||||
|
||||
W jednej kolekcji hooki tej samej fazy uruchamiają się po kolei. Typowa Media:
|
||||
```ts
|
||||
hooks: {
|
||||
beforeOperation: [normalizeFilenameHook], // czyste nazwy
|
||||
afterChange: [afterChange], // revalidate
|
||||
afterDelete: [afterDelete],
|
||||
}
|
||||
```
|
||||
Typowa Pages:
|
||||
```ts
|
||||
hooks: {
|
||||
beforeChange: [setPublishedAtHook, trackSlugHistoryHook],
|
||||
beforeDelete: [buildPreventDeleteSystemPage(...)],
|
||||
afterChange: [afterChange], // revalidate
|
||||
afterDelete: [afterDelete],
|
||||
}
|
||||
```
|
||||
|
||||
Które hooki wpiąć zależy od kolekcji — nie każda potrzebuje wszystkich (blog:
|
||||
setPublishedAt; wszystkie z URL: revalidate + slugHistory; Pages: + preventDelete).
|
||||
Reference in New Issue
Block a user