Files
sass-mohfarawati/docs/ARCHITECTURE.md
T
moh dc21c33867 ADDED - Admin SEO page, robots/sitemap hardening and media/maintenance security fixes
SEO
- New Settings > SEO admin page (seo_settings in app_config): indexing switch,
  Google/Bing verification, X handle, JSON-LD identity (Person/Organization,
  sameAs), per-locale keywords, readiness checklist and open links for
  sitemap.xml / robots.txt / manifest.
- robots.txt is now dynamic: disallows admin, api, success and coming-soon
  paths; blocks everything while indexing is off or maintenance is on.
- sitemap.xml carries hreflang alternates per URL, lists only categories with
  published projects, and is empty while hidden.
- Metadata: robots + verification meta, og:locale in de_DE/en_US/ar_AR form,
  alternateLocale, twitter site/creator, project cover as OG image with
  article type, noindex on /success and /coming-soon.
- JSON-LD: WebSite + publisher graph on all public pages, CreativeWork per
  project (view-mode independent).

Security
- Maintenance bypass now requires a correctly signed admin cookie; the
  middleware previously only checked the cookie existed. Token helpers moved
  to lib/admin-session-token.ts (shared by proxy.ts and lib/admin-auth.ts).
- Media uploads: magic-byte validation against the declared type, SVG
  sanitization (script/handlers/foreignObject/javascript: rejected), upload
  folder sanitized, kind inferred from the real file.
- Media route: fixed prefix-based path check that accepted sibling
  directories, unknown extensions return 404, nosniff header, CSP sandbox on
  SVG, gif content type added.
- External media URLs: protocol-relative (//host) URLs rejected.

Portfolio
- Project and category slugs share /portfolio/[slug]; saving now rejects a
  slug already used on the other side instead of silently shadowing it.

Tooling/docs
- Lint: ignore scripts/legacy-prisma-seed.cjs, drop unused import.
- New docs/SEO.md; FEATURES, ARCHITECTURE (Drizzle instead of Prisma), admin
  spec and CLAUDE.md updated.
- Tests for all of the above (unit + integration); suite green.
2026-09-20 21:36:16 +02:00

4.1 KiB

Architecture

Stack

  • Next.js (App Router)
  • TypeScript
  • next-intl
  • Prisma + PostgreSQL
  • Tailwind CSS
  • Radix UI (via local primitives)
  • framer-motion
  • nodemailer

Application Structure

Public application

Localized public pages live under:

app/[locale]/(site)

Locale routing and configuration are defined in:

i18n/routing.ts

The public layout checks the maintenance status and redirects visitors to:

/coming-soon

when maintenance mode is enabled.


Admin application

Admin pages are implemented under:

app/_admin

Internal admin routing is rewritten to:

app/admin-internal

Development alias routes also exist under:

app/root

Routing decisions and host/path-based rewrites are implemented in:

lib/admin-routing.ts

The _admin structure should be treated as the canonical admin source.


Core Modules

Application logic lives primarily in lib/*.

Important modules include:

lib/portfolio.ts
Handles portfolio queries and localized mapping.

lib/app-config.ts
Provides application-level configuration values.

lib/media.ts
Handles media library queries and media bindings.

lib/mail.ts
Handles SMTP delivery via nodemailer.

These modules act as the primary server-side application layer.


UI Layer

Shared UI primitives live in:

components/ui

These primitives wrap Radix UI components and define the shared design system.

Public site UI components live in:

components/site
components/layout

Admin UI components live in:

components/admin
components/dashboard

Shared primitives must always originate from components/ui.

Feature-level UI should reuse these primitives rather than reimplementing them.


Persistence Layer

Database access is implemented using Prisma.

Prisma client lives in:

lib/prisma.ts

Configuration values are stored in the database via:

AppConfig

which acts as a key-value store for runtime configuration values.

Database access should always occur through server-side modules.

Client components must never access Prisma directly.


Architectural Boundaries

The following boundaries must be respected:

  • Client components must not access Prisma.
  • Business logic must not live inside UI components.
  • Shared UI must originate from components/ui.
  • Public site UI belongs in components/site.
  • Admin UI belongs in components/admin or components/dashboard.
  • Server actions act as request entry points for form submissions.
  • Server actions may call modules in lib/*.

Database queries should not be implemented directly inside route-level UI files.


Request Flow Examples

Contact submission

  1. User submits the form on

app/[locale]/(site)/contact/page.tsx

  1. Server action validates the input using Zod.
  2. Email is sent through:

lib/mail.ts

  1. The user is redirected to:

/success

or back to the form with an error state.


Portfolio publishing

  1. Admin edits a project form.
  2. Server action validates categories, fields, sections, and assets.
  3. Media selections are resolved via:

lib/media.ts

  1. Project data, sections, assets, and media usage are persisted via Prisma.
  2. Public and admin paths are revalidated.

Canonical Sources

The following files should be treated as canonical sources for specific domains:

Routing
i18n/routing.ts

Admin routing
lib/admin-routing.ts

Database access (Drizzle)
lib/db/index.ts, lib/db/schema.ts

Application configuration
lib/app-config.ts

Portfolio logic
lib/portfolio.ts

Media handling
lib/media.ts (DB), lib/media-storage.ts (filesystem + content validation), lib/media-service.ts

SEO / metadata
lib/metadata.ts, lib/seo-settings.ts, lib/seo-report.ts, app/robots.ts, app/sitemap.ts (docs/SEO.md)

Admin session token (shared by middleware and server auth)
lib/admin-session-token.ts


Proposed Future Improvements (Not Implemented)

These ideas are not currently implemented but may be introduced later:

  • Split app-config concerns into smaller modules if additional domains are added.
  • Introduce dedicated models instead of expanding the AppConfig key-value store.
  • Introduce explicit service boundaries if the application grows significantly.ڑ