Aller au contenu principal

Page Management System Documentation

Overview

This system provides a backend-only, API-first approach to managing website pages with comprehensive SEO capabilities. The API is the single source of truth for page definitions, allowing pages to be added or updated from the CRM without redeploying the frontend.

Architecture

Key Features

  1. Frontend-Agnostic Design: API can be consumed by any frontend framework (React, Vue, Next.js, etc.)
  2. SEO-First: Comprehensive SEO metadata including OpenGraph, Twitter Cards, and structured data
  3. Redirect Management: Built-in 301/302 redirect handling for SEO-friendly URL changes
  4. Multi-Language Support: Full internationalization with translation system
  5. Content Scheduling: Schedule page publication and expiration
  6. Hierarchical Structure: Parent-child page relationships for navigation
  7. Analytics: Built-in page view tracking and redirect statistics

Data Models

Page Model

  • Core Fields: slug, path, title, content, active status
  • SEO: Complete meta tags, OpenGraph, Twitter Cards, canonical URLs, structured data
  • Indexation: Control search engine indexing and sitemap inclusion
  • Relationships: Parent/child pages, related pages
  • Scheduling: Publication and expiration dates
  • Analytics: View count and last viewed timestamp

Redirect Model

  • Configuration: Source path, target path, status code (301/302)
  • Matching: Exact, prefix, or regex pattern matching
  • Validation: Automatic loop detection and chain warnings
  • Analytics: Hit count and last hit timestamp
  • Expiration: Optional expiration for temporary redirects

API Endpoints

Public Endpoints (Frontend)

Pages

POST   /page/published                  - Get all published pages
GET /page/path/:path - Get page by path (primary routing endpoint)
GET /page/seo/:path - Get SEO metadata only (lightweight)
GET /page/slug/:slug - Get page by slug
GET /page/sitemap - Get sitemap data
GET /page/hierarchy/:id - Get page hierarchy (breadcrumbs)
POST /page/search - Full-text search
GET /page/:id - Get page by ID

Redirects

GET    /redirect/resolve/:path          - Resolve redirect (404 handling)
GET /redirect/check/:path - Check if redirect exists
GET /redirect/active - Get all active redirects
GET /redirect/stats - Get redirect statistics
GET /redirect/top - Get most used redirects
GET /redirect/:id - Get redirect by ID

Protected Endpoints (Admin/CRM)

Pages

POST   /page                            - Get all pages (with filters)
POST /page/new - Create page
PUT /page/edit/:id - Update page
DELETE /page/delete - Delete pages
PUT /page/toggle - Toggle active status
PUT /page/seo/bulk - Bulk update SEO

Redirects

POST   /redirect                        - Get all redirects (with filters)
POST /redirect/validate-chain - Validate redirect chain
POST /redirect/new - Create redirect
POST /redirect/bulk - Bulk create redirects
PUT /redirect/edit/:id - Update redirect
DELETE /redirect/delete - Delete redirects
PUT /redirect/toggle - Toggle active status
DELETE /redirect/clean-expired - Clean expired redirects

Frontend Integration

Basic Page Resolution

// Frontend router (Next.js example)
export async function getServerSideProps(context) {
const path = context.resolvedUrl;
const moduleId = process.env.MODULE_ID;

try {
// 1. Try to fetch page
const pageResponse = await fetch(
`${API_URL}/page/path${path}?module_id=${moduleId}`
);

if (pageResponse.ok) {
const page = await pageResponse.json();
return { props: { page } };
}

// 2. If 404, check for redirect
const redirectResponse = await fetch(
`${API_URL}/redirect/resolve${path}?module_id=${moduleId}`
);

if (redirectResponse.ok) {
const redirect = await redirectResponse.json();
return {
redirect: {
destination: redirect.targetPath,
permanent: redirect.statusCode === 301,
},
};
}

// 3. No page or redirect found
return { notFound: true };
} catch (error) {
console.error('Error resolving page:', error);
return { notFound: true };
}
}

SEO Meta Tags

// Component for rendering SEO meta tags
import Head from 'next/head';

export function SeoHead({ page }) {
const { seo, path, title } = page;

return (
<Head>
{/* Basic Meta Tags */}
<title>{seo.metaTitle || title}</title>
<meta name="description" content={seo.metaDescription} />
{seo.metaKeywords && (
<meta name="keywords" content={seo.metaKeywords.join(', ')} />
)}
<meta name="robots" content={seo.robots || 'index,follow'} />

{/* Canonical URL */}
{seo.canonicalUrl && <link rel="canonical" href={seo.canonicalUrl} />}

{/* Alternate Language URLs */}
{seo.alternateUrls?.map((alt) => (
<link
key={alt.lang}
rel="alternate"
hreflang={alt.lang}
href={alt.url}
/>
))}

{/* OpenGraph Tags */}
<meta property="og:title" content={seo.ogTitle || title} />
<meta property="og:description" content={seo.ogDescription} />
<meta property="og:type" content={seo.ogType || 'website'} />
{seo.ogImage && <meta property="og:image" content={seo.ogImage} />}

{/* Twitter Cards */}
<meta name="twitter:card" content={seo.twitterCard || 'summary'} />
{seo.twitterSite && (
<meta name="twitter:site" content={seo.twitterSite} />
)}
{seo.twitterCreator && (
<meta name="twitter:creator" content={seo.twitterCreator} />
)}
<meta name="twitter:title" content={seo.twitterTitle || title} />
<meta name="twitter:description" content={seo.twitterDescription} />
{seo.twitterImage && (
<meta name="twitter:image" content={seo.twitterImage} />
)}

{/* Structured Data (JSON-LD) */}
{seo.structuredData && (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(seo.structuredData),
}}
/>
)}
</Head>
);
}

Sitemap Generation

// Generate sitemap.xml from API
export async function generateSitemap() {
const moduleId = process.env.MODULE_ID;
const baseUrl = process.env.WEBSITE_URL;

const response = await fetch(
`${API_URL}/page/sitemap?module_id=${moduleId}`
);
const { items } = await response.json();

const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${items
.map(
(page) => `
<url>
<loc>${baseUrl}${page.path}</loc>
<lastmod>${new Date(page.updatedAt).toISOString()}</lastmod>
<changefreq>${page.sitemap.changefreq}</changefreq>
<priority>${page.sitemap.priority}</priority>
</url>`
)
.join('')}
</urlset>`;

return sitemap;
}

Best Practices

URL Structure

  1. Use Clean URLs: /about-us, /products/shoes, /blog/article-title
  2. Lowercase Only: All paths are automatically lowercased
  3. Hyphens for Spaces: Use kebab-case for slugs
  4. Language Prefixes: /en/about-us, /fr/a-propos
  5. Hierarchical Structure: Use parentPage relationships for breadcrumbs

SEO Optimization

  1. Meta Title: 50-60 characters (displayed in search results)
  2. Meta Description: 150-160 characters (search result snippet)
  3. Canonical URLs: Always set canonical URL to avoid duplicate content
  4. Alternate Languages: Specify all language versions for international SEO
  5. Structured Data: Add Schema.org markup for rich snippets
  6. Sitemap Priority:
    • Homepage: 1.0
    • Main sections: 0.8
    • Standard pages: 0.5
    • Archive pages: 0.3

Redirect Management

  1. Use 301 for Permanent Changes: Transfers SEO value to new URL
  2. Use 302 for Temporary Changes: Preserves SEO value of original URL
  3. Avoid Redirect Chains: Redirect directly to final destination
  4. Monitor Redirect Stats: Identify frequently used redirects
  5. Clean Expired Redirects: Regularly run cleanup maintenance

Performance

  1. Use Path Lookups: Primary endpoint /page/path/:path is indexed
  2. Lightweight SEO Endpoint: Use /page/seo/:path for meta tags only
  3. Cache Published Pages: Frontend can cache published pages
  4. Pagination: Use pagination for large page lists
  5. Select Fields: Only fetch fields you need

Content Management Workflow

Adding a New Page

  1. Create in CRM: Use /page/new endpoint
  2. Set Active to False: Start as draft
  3. Configure SEO: Add meta tags, OpenGraph, etc.
  4. Preview: Frontend can preview drafts with auth
  5. Publish: Set active to true
  6. Monitor: Check analytics and adjust SEO

Changing a URL

  1. Create Redirect: Before changing URL, create 301 redirect
  2. Update Page Path: Change page path to new URL
  3. Update Alternate URLs: Update language versions if applicable
  4. Test Redirect: Verify old URL redirects correctly
  5. Monitor Hits: Track redirect usage

Multi-Language Pages

  1. Create Base Page: Create page in default language
  2. Clone for Languages: Create translated versions
  3. Set Translation Refs: Link pages via translation field
  4. Set Alternate URLs: Configure hreflang tags
  5. Consistent Paths: Use language prefixes (/en/, /fr/)

Maintenance Tasks

Scheduled Publishing

// Background job to activate scheduled pages
export async function activateScheduledPages() {
const now = new Date();

await Page.updateMany(
{
active: false,
scheduledAt: { $lte: now },
},
{
$set: { active: true },
}
);
}

// Run every hour
cron.schedule('0 * * * *', activateScheduledPages);

Expire Old Pages

// Background job to deactivate expired pages
export async function deactivateExpiredPages() {
const now = new Date();

await Page.updateMany(
{
active: true,
expiresAt: { $lte: now },
},
{
$set: { active: false },
}
);
}

// Run every hour
cron.schedule('0 * * * *', deactivateExpiredPages);

Clean Expired Redirects

// Use built-in endpoint
await fetch(`${API_URL}/redirect/clean-expired?module_id=${moduleId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
});

// Run daily
cron.schedule('0 0 * * *', cleanExpiredRedirects);

Security Considerations

  1. Authentication: All write operations require JWT authentication
  2. Authorization: Use role-based access (commonSlave, etc.)
  3. Validation: Input validation on all endpoints
  4. Rate Limiting: Implement rate limiting on public endpoints
  5. XSS Protection: Sanitize HTML content before rendering
  6. CSRF Protection: Use CSRF tokens for state-changing operations

Monitoring & Analytics

Page Analytics

  • View count per page
  • Last viewed timestamp
  • Popular pages report
  • SEO performance tracking

Redirect Analytics

  • Hit count per redirect
  • Most used redirects
  • Redirect chains detection
  • Expired redirects report

Example Use Cases

E-commerce Product Pages

const productPage = {
module_id: '...',
slug: 'running-shoes-nike',
path: '/products/running-shoes-nike',
title: 'Nike Running Shoes - Professional Sports Gear',
pageType: 'standard',
active: true,
indexed: true,
seo: {
metaTitle: 'Nike Running Shoes | Buy Online',
metaDescription: 'Professional Nike running shoes with advanced cushioning...',
structuredData: {
'@context': 'https://schema.org',
'@type': 'Product',
name: 'Nike Running Shoes',
offers: {
'@type': 'Offer',
price: '129.99',
priceCurrency: 'USD',
},
},
},
sitemap: {
include: true,
priority: 0.8,
changefreq: 'weekly',
},
};

Blog Article

const blogArticle = {
module_id: '...',
slug: 'seo-best-practices-2025',
path: '/blog/seo-best-practices-2025',
title: 'SEO Best Practices for 2025',
pageType: 'article',
active: true,
indexed: true,
seo: {
metaTitle: 'SEO Best Practices for 2025 | Complete Guide',
metaDescription: 'Learn the latest SEO techniques and best practices...',
ogType: 'article',
structuredData: {
'@context': 'https://schema.org',
'@type': 'Article',
headline: 'SEO Best Practices for 2025',
author: {
'@type': 'Person',
name: 'John Doe',
},
datePublished: '2025-01-15',
},
},
sitemap: {
include: true,
priority: 0.7,
changefreq: 'monthly',
},
};

URL Migration with Redirects

// Old URL structure: /page.php?id=123
// New URL structure: /about-us

const redirect = {
module_id: '...',
sourcePath: '/page.php',
targetPath: '/about-us',
statusCode: 301,
matchType: 'exact',
active: true,
reason: 'Migrated from old PHP site to new structure',
};

Troubleshooting

Page Not Found

  1. Check active status
  2. Check scheduledAt (may not be published yet)
  3. Check expiresAt (may have expired)
  4. Verify path format (must start with /)
  5. Check indexed if using indexation filter

Redirect Not Working

  1. Verify active status
  2. Check expiresAt
  3. Test matchType (exact vs prefix vs regex)
  4. Check caseSensitive setting
  5. Verify no redirect loops exist

SEO Issues

  1. Verify meta tags are populated
  2. Check canonical URL configuration
  3. Ensure alternate URLs are set for all languages
  4. Validate structured data JSON-LD
  5. Check robots.txt and sitemap.xml

Support

For issues or questions:

  • Review API documentation
  • Check error logs
  • Verify authentication tokens
  • Test endpoints with Postman/Insomnia
  • Monitor database indexes