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- success4000 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.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.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/analyzePOST /v3/claims/analyzePOST /v3/domain/analyzePOST /v3/media/upload(multipart, pentru imagini/audio/video)GET /v3/pipeline/{session_id}/queue-statusGET /v3/pipeline/{session_id}/resultGET /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 userGET /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:
- Copiaza
ro.json->es.json(sau ce vrei) - Tradu valorile
- Adauga in
src/i18n/index.tsla listaSUPPORTED_LOCALES - 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: DENYX-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.