# Configurare Website DiDi Variabile mediu complete, integrari externe, credentiale. ## Fisier `.env.production` Localizat la `~/website/.env.production`. NU se commiteaza. ### Variabile complete ```env # === 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:///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://: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= (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:///auth/realms/didi-admins/protocol/openid-connect/token DIDI_ADMIN_CLIENT_ID=didi-website-m2m DIDI_ADMIN_CLIENT_SECRET= # 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://: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 ```bash # 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: ```bash 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://:3000/*`, `https://didi365.eu/*` Web origins | `http://: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) ```env 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) ```env 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`: ```typescript 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.