Adresele paginilor și redirecționări
Adresa obiectului: slug și slugs
Fiecare obiect are un slug (adresă lizibilă) în fiecare limbă. Îl păstrează CRM-ul:
| Câmp | Ce înseamnă |
|---|---|
slug | slug-ul în limba răspunsului (lang); dacă pentru limbă nu există slug — cel românesc |
slugs | slug-urile în toate limbile: { ro, ru, en } — pentru linkurile spre alte limbi și hreflang |
- Slug-ul se stabilește o dată și nu se schimbă la redenumirea obiectului, așa că linkurile și rezultatele căutării nu se strică.
- Slug-ul este unic printre obiectele companiei în fiecare limbă. Dacă denumirea coincide cu una existentă, CRM-ul adaugă un sufix (
-2). - Dacă managerul totuși schimbă slug-ul manual, adresa anterioară intră în tabelul de redirecționări (mai jos) — linkul vechi nu va da 404.
Exemplu de rută pe site: /{lang}/apartament/{slug}. Schema adreselor o alegeți dvs.; CRM-ul oferă doar slug-ul.
// linkuri spre alte limbi
<link rel="alternate" hrefLang="ro" href={`https://site.example.com/ro/apartament/${u.slugs.ro}`} />
<link rel="alternate" hrefLang="ru" href={`https://site.example.com/ru/apartament/${u.slugs.ru}`} />Tabelul de redirecționări: legacyUrls()
GET /legacy-urls întoarce lista adreselor vechi. Fiecare element este un SiteLegacyUrl:
| Câmp | Ce înseamnă |
|---|---|
url | adresa veche: pagina produsului WooCommerce sau slug-ul anterior al obiectului |
path | calea adresei vechi (/ro/product/<slug>/) — cheia pentru comparația cu cererea |
lang | limba adresei vechi |
unit_id | obiectul |
slug | slug-ul curent al obiectului în limba lang; null — obiectul a fost șters |
gone | true — obiectul a fost șters |
Ce să răspundeți la cererea unei adrese vechi:
| Caz | Răspuns |
|---|---|
obiectul există în units() | 301 spre /{lang}/apartament/{slug} |
obiectul e arhivat (lipsește din units(), dar gone: false) | 301 spre catalog |
gone: true | 410 Gone — pagina nu mai există, motoarele de căutare o vor scoate din rezultate |
Comparați calea fără / final și fără a ține cont de majuscule; caracterele chirilice din linkurile vechi pot veni atât codificate procentual, cât și decodificate.
Next.js: redirects() la build
Potrivit dacă adresele vechi nu se mai schimbă.
// next.config.ts
import { createSiteClient } from '@type/site-client';
export default {
async redirects() {
const crm = createSiteClient({ baseUrl: process.env.TYPE_API_URL!, apiKey: process.env.TYPE_API_KEY! });
const { units } = await crm.units({ lang: 'ro' });
const live = new Set(units.map((u) => u.id));
return (await crm.legacyUrls())
.filter((l) => !l.gone)
.map((l) => ({
source: l.path,
destination: live.has(l.unit_id) && l.slug ? `/${l.lang}/apartament/${l.slug}` : `/${l.lang}/apartamente`,
permanent: true,
}));
},
};Adresele cu gone: true răspundeți-le cu 410 din middleware sau dintr-o rută catch-all: redirects() știe doar să redirecționeze.
Next.js: fără rebuild
Țineți tabelul în middleware.ts sau într-o rută catch-all (app/product/[...slug]/page.tsx cu permanentRedirect()): puneți legacyUrls() în cache prin unstable_cache cu eticheta rental:units și goliți-o prin webhook — redirecționările noi apar fără rebuild-ul site-ului.
Fotografii
photos[].url este un link permanent de forma /api/modules/rental/site/photos/…. Nu are nevoie de cheie, poate fi pus direct în <img> (prin crm.photoUrl(url) — adresa absolută). Linkul răspunde cu 302 spre o previzualizare WebP cu lățimea de până la 1920 px fără EXIF, Cache-Control: public, max-age=3000. Dacă fotografia a fost ștearsă sau obiectul e arhivat — 404; dacă previzualizarea încă se construiește — 503 cu Retry-After.