livrare website cu erp si crm
This commit is contained in:
parent
28773e3a72
commit
5c7bf7c295
257 changed files with 31929 additions and 0 deletions
279
website/CONFIGURATION.md
Normal file
279
website/CONFIGURATION.md
Normal 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue