livrare website cu erp si crm

This commit is contained in:
DP Software services 2026-07-28 14:47:28 +03:00
parent 28773e3a72
commit 5c7bf7c295
257 changed files with 31929 additions and 0 deletions

279
website/CONFIGURATION.md Normal file
View file

@ -0,0 +1,279 @@
# 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://<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
```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://<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)
```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.