didi-website-erp-crm/website/CONFIGURATION.md

8.3 KiB

Configurare Website DiDi

Variabile mediu complete, integrari externe, credentiale.

Fisier .env.production

Localizat la ~/website/.env.production. NU se commiteaza.

Variabile complete

# === ERPNext ===
# URL public (afisat in browser) - pentru link-uri, redirect-uri  
NEXT_PUBLIC_ERPNEXT_URL=http://localhost:8080

# URL intern (din container) - pentru server-side API calls
ERPNEXT_API_URL=http://didi-erpnext:8080

# API token: ERPNext > User > API Access
ERPNEXT_API_KEY=85e12d367334775
ERPNEXT_API_SECRET=910a2dfda16d2af

# === Keycloak / Auth.js ===
AUTH_KEYCLOAK_ID=didi-website-server
AUTH_KEYCLOAK_SECRET=fmpupO7GkBWEy42wKkgEWIV8NcOuiK2d
AUTH_KEYCLOAK_ISSUER=https://<KEYCLOAK_HOST>/auth/realms/didi-clients

# Auth.js secret (>= 32 chars random) - openssl rand -base64 32
AUTH_SECRET=didi-nextauth-secret-change-in-production-2026

# Trust X-Forwarded-* headers (true cand e in spatele unui reverse proxy)
AUTH_TRUST_HOST=true

# URL pentru callback - acelasi cu cum acceseaza userul
AUTH_URL=http://<HOST>:3000

# === DiDi Platform API ===
# In mod DEMO ambele arata spre mock-server.js (http://localhost:4000[/api]).
# Agent V3 (analize)
DIDI_API_URL=http://didi-agent-v3:24803/api

# Framework (credite, profil user, register, cataloage, alocare pachete)
DIDI_FRAMEWORK_URL=http://didi-framework:3005

# === DiDi M2M (alocare pachete dupa plata; realm didi-admins) ===
# Flux in 3 pasi dupa confirmarea platii Stripe (vezi src/lib/didi-backend.ts):
# token client_credentials -> GET /api/admin/users?search=<email> (campul `id`)
# -> PUT /api/admin/users/{id}/subscription cu creditsRemained ABSOLUT.
# Clientul M2M trebuie sa aiba rol realm `admin` (altfel 403).
DIDI_ADMIN_TOKEN_URL=https://<KEYCLOAK_HOST>/auth/realms/didi-admins/protocol/openid-connect/token
DIDI_ADMIN_CLIENT_ID=didi-website-m2m
DIDI_ADMIN_CLIENT_SECRET=<client_secret M2M>
# id-urile planurilor backend pentru abonamente (implicit 3=paid, 6=enterprise)
#DIDI_PLAN_ID_PAID=3
#DIDI_PLAN_ID_ENTERPRISE=6

# === Stripe ===
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxx
STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx

# Stripe price IDs (subscription recurring)
STRIPE_PRICE_PAID_MONTHLY=price_xxx
STRIPE_PRICE_PAID_YEARLY=price_xxx
STRIPE_PRICE_ENTERPRISE_MONTHLY=price_xxx
STRIPE_PRICE_ENTERPRISE_YEARLY=price_xxx

# === Site ===
NEXT_PUBLIC_SITE_URL=http://<HOST>:3000
NEXT_PUBLIC_DEFAULT_LOCALE=ro

IMPORTANT: NEXT_PUBLIC_* vs server-side

Next.js variabilele cu prefix NEXT_PUBLIC_* sunt baked into JavaScript bundle la build. Pentru a le schimba in productie e nevoie de REBUILD container.

Variabilele fara prefix sunt server-side - se pot schimba prin restart (env_file reload), fara rebuild.

Schimbare Actiune
NEXT_PUBLIC_* Rebuild + restart
Restul Restart

ERPNext integration

API token

# Generare token in ERPNext
docker exec didi-erpnext bash -c '
cd /home/frappe/frappe-bench && 
bench --site didi-erp execute frappe.core.doctype.user.user.generate_keys --args "[\"Administrator\"]"
'
# Output: {"api_key": "xxx", "api_secret": "yyy"}

Pune in .env.production la ERPNEXT_API_KEY + ERPNEXT_API_SECRET.

Permisiuni

Recomandat: NU folosi Administrator. Creeaza un user dedicat:

docker exec didi-erpnext bash -c '
cd /home/frappe/frappe-bench && 
bench --site didi-erp add-user website_api@didi.local \
  --first-name Website \
  --last-name API \
  --send-welcome-email 0
'

Apoi din UI: User > website_api > Roles > Adauga "Website Integration", "Sales User", "Customer Reader". Apoi genereaza API keys pentru acest user.

Keycloak integration

Realm didi-clients

Setting Value
Realm name didi-clients
Login with email DA
Email as username DA
Registration DA (clienti se inregistreaza singuri)
Reset password DA
Brute force protection DA
Access token lifespan 10 min
SSO session max 24 ore

Client didi-website-server

Setting Value
Client ID didi-website-server
Client Type Confidential
Standard flow Enabled
Direct access grants Enabled
Service accounts Enabled (pentru server-to-server)
Valid redirect URIs http://<HOST>:3000/*, https://didi365.eu/*
Web origins http://<HOST>:3000, https://didi365.eu, +
Client Secret (in AUTH_KEYCLOAK_SECRET)

Mapping pentru email/roles

In client > Client scopes > Dedicated, adauga mappers:

  • email (Mapper Type: User Property, Property: email, Token claim name: email)
  • realm-roles (built-in, vor fi propagate)

Stripe integration

Test mode

Foloseste cheile pk_test_* si sk_test_* pentru dezvoltare. Carduri test:

  • 4242 4242 4242 4242 - success
  • 4000 0000 0000 9995 - declined (insufficient funds)
  • Orice CVC + data viitoare

Webhook

Endpoint: https://didi.tld/api/webhooks/stripe

Events de subscris:

  • checkout.session.completed (one-time + first subscription cycle)
  • customer.subscription.created
  • customer.subscription.updated
  • customer.subscription.deleted
  • invoice.paid / invoice.payment_succeeded (recurring billing)
  • invoice.payment_failed

Signing secret: Dashboard Stripe > Webhooks > [endpoint] > "Signing secret" > STRIPE_WEBHOOK_SECRET.

Idempotency

Toate evenimentele sunt scrise in tabPayment Log cu stripe_session_id UNIQUE. Daca acelasi event vine de 2 ori, al doilea esueaza silentios la INSERT (constraint violation) - flow garantat o-singura-data.

DiDi platform integration

Agent V3 (analize)

DIDI_API_URL=http://didi-agent-v3:24803/api

Endpoints folosite:

  • POST /v3/techniques/analyze (cu Bearer JWT)
  • POST /v3/ai-tampered/analyze
  • POST /v3/claims/analyze
  • POST /v3/domain/analyze
  • POST /v3/media/upload (multipart, pentru imagini/audio/video)
  • GET /v3/pipeline/{session_id}/queue-status
  • GET /v3/pipeline/{session_id}/result
  • GET /v3/pipeline/history?user_id=X (istoric analize)

Auth: Bearer JWT obtinut din Keycloak (din session-token).

Framework (credite)

DIDI_FRAMEWORK_URL=http://didi-framework:3005

Endpoints folosite:

  • GET /api/auth/credits - summary credite curente user
  • GET /api/auth/me - profil complet user

Auth: Bearer JWT.

i18n

Limbi suportate: ro (default) + en.

Fisiere: src/i18n/ro.json, src/i18n/en.json.

Switcher in navbar: src/components/layout/Navbar.tsx.

Pentru a adauga limba noua:

  1. Copiaza ro.json -> es.json (sau ce vrei)
  2. Tradu valorile
  3. Adauga in src/i18n/index.ts la lista SUPPORTED_LOCALES
  4. Adauga in Navbar la selector

Sesiuni Auth.js

Sesiunile sunt JWT (stateless) - nu se stocheaza in DB. Cookies setate:

Cookie Continut Securitate
authjs.session-token JWT semnat cu AUTH_SECRET HttpOnly, SameSite=Lax
authjs.csrf-token CSRF protection HttpOnly, SameSite=Lax
authjs.callback-url URL post-login HttpOnly, SameSite=Lax
authjs.pkce.code_verifier PKCE pentru OIDC flow HttpOnly, scurt-trait

Pentru productie cu HTTPS, schimba useSecureCookies: true in src/lib/auth.ts.

SEO

src/app/sitemap.ts + src/app/robots.ts genereaza automat:

  • /sitemap.xml - toate paginile publice + rute dinamice
  • /robots.txt - permite Google, blocheaza /dashboard/* + /api/*

Open Graph + Twitter cards sunt setate in src/app/layout.tsx.

GDPR

Cookie consent: src/components/ui/CookieConsent.tsx. 3 categorii:

  • Necesare (intotdeauna ON, NextAuth/CSRF)
  • Functionale (preferinte limba)
  • Analitice (Google Analytics - daca adaugi)

Drepturi self-service in /dashboard/profile:

  • Acces (Art. 15) - vezi datele tale in profile
  • Rectificare (Art. 16) - editezi datele
  • Portabilitate (Art. 20) - /api/gdpr/export
  • Stergere (Art. 17) - /api/gdpr/delete (cu retentie fiscala 10 ani RO)

Securitate

Headers HTTP setate de Next.js default:

  • Strict-Transport-Security (la HTTPS)
  • X-Frame-Options: DENY
  • X-Content-Type-Options: nosniff

Pentru CSP (Content Security Policy), edit next.config.ts:

headers: async () => [{
  source: '/(.*)',
  headers: [
    { key: 'Content-Security-Policy', value: "default-src 'self'; ..." }
  ]
}]

Logging

In dev: console.log -> stdout container (docker logs).

In productie: recomandare adaugare pino cu transport spre Datadog/Loki/etc.

Erorile critice (Auth, Stripe webhook, fulfillment) sunt logate cu console.error si pot fi colectate via Docker logs sau via tool extern.