Files
dyolink/backend/.env.example

75 lines
2.8 KiB
Plaintext
Raw Permalink Normal View History

# Database
#
# Local dev (Nest on your machine + Postgres via docker-compose.postgres.yml):
# Use host "localhost" — hostname "postgres" only works inside Docker networks.
DATABASE_URL=postgresql://dyolink_user:password@localhost:5432/dyolink_db
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_USER=dyolink_user
POSTGRES_PASSWORD=password
POSTGRES_DB=dyolink_db
#
# If you run the API inside the same Compose stack as Postgres, use instead:
# DATABASE_URL=postgresql://dyolink_user:password@postgres:5432/dyolink_db
# POSTGRES_HOST=postgres
# JWT (required for register/login)
JWT_SECRET=CHANGE_ME_TO_A_STRONG_SECRET_32_CHARS_MIN
JWT_EXPIRES_IN=7d
JWT_REFRESH_SECRET=CHANGE_ME_TO_ANOTHER_STRONG_SECRET
JWT_REFRESH_EXPIRES_IN=30d
# Application
PORT=3000
NODE_ENV=development
API_PREFIX=/api
# CORS and invite links — must match the URL where the Next.js app runs
FRONTEND_URL=http://localhost:3001
# Set true when the app is served over HTTPS (required for Secure auth cookies)
COOKIE_SECURE=false
# OAuth (optional — uncomment when configured)
# GOOGLE_CLIENT_ID=your-google-client-id
# GOOGLE_CLIENT_SECRET=your-google-client-secret
# GOOGLE_CALLBACK_URL=http://localhost:3000/auth/google/callback
# FACEBOOK_CLIENT_ID=your-facebook-app-id
# FACEBOOK_CLIENT_SECRET=your-facebook-app-secret
# FACEBOOK_CALLBACK_URL=http://localhost:3000/auth/facebook/callback
# Email (configure for production)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your_email@gmail.com
SMTP_PASSWORD=your_app_password
# SMS (sms.ir — use Sandbox API key for development)
# SMS_IR_API_KEY=4QKMiSU4Kh7tWPLCdRMV0QpDh8WgF33YkWRS18BcG3vf4QHi
SMS_IR_API_KEY=lwbK7hxmjimNjFS4g5DWahh75EKCgJUfcUIinUQzfQXwXkSp
SMS_IR_TEMPLATE_ID=123456
feat(backend): voice extraction endpoint POST /voice/extract behind JwtAuthGuard + ClinicOrgGuard, plus GET /voice/availability so the frontend can decide whether to render the microphone — it cannot learn that from NEXT_PUBLIC_*, which are baked in at build time. Audio is held in memory for the request only: never written to disk, never a Prisma row. The transcript goes back to the client and is not persisted. What is logged is structured and patient-free — clip length, which fields resolved, unresolved count, vendor cost, outcome — with log lines as the interim sink until this repo has metrics infrastructure. On extraction failure the transcript still travels back in the error details, so the words the clinician already paid for can be salvaged into a note. v1 ships ungated beyond a configured locale profile; the Plan.features design is deferred, not dropped. From review of this commit, four of which were load-bearing: - Express's 100 kb default body limit rejected any recording past ~20 seconds, making the endpoint unusable at its own 2-minute cap. Body parsers are now registered explicitly with a 10 MB limit scoped to the voice route only. Verified empirically: 600 KB reaches /api/voice/extract, while /api/auth/login still 413s. - ThrottlerGuard keys on req.ip, so behind nginx the whole deployment would share one bucket and an abuser rotating IPs would bypass it. VoiceThrottlerGuard keys on the user id instead — with no plan gate, this is the only control on metered vendor spend. - ThrottlerException had no 429 fallback and surfaced as INTERNAL_ERROR; the guard now throws VOICE_RATE_LIMITED directly. - durationMs was optional, so omitting it bypassed VOICE_MAX_RECORDING_MS entirely. It is required. - VOICE_UNSUPPORTED_FORMAT was dead code — the DTO's @IsIn already rejects unknown containers — so it is gone rather than left unreachable. ThrottlerModule is deliberately not bound as a global APP_GUARD: a global ThrottlerGuard rate-limits every route against every named throttler, which would have capped the whole API at the voice limit. All seven remaining VOICE_* codes have errors.* keys in en, fa and nl. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 18:27:14 +03:30
# ── Voice treatment entry ──────────────────────────────────────────────────────
# Without OPENROUTER_API_KEY the microphone button does not render at all.
OPENROUTER_API_KEY=
# OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
# Locales the microphone is offered in. An unknown locale here fails at boot.
# VOICE_ENABLED_LOCALES=fa,en,nl
# Models, overridable per locale (VOICE_ASR_MODEL_FA, VOICE_LLM_MODEL_NL, ...).
# All locales share these today; the per-locale override exists so Persian can be
# repointed at a specialist ASR vendor without a code change.
# VOICE_ASR_MODEL=openai/whisper-1
# VOICE_LLM_MODEL=google/gemini-3.7-flash
# VOICE_ASR_PROVIDER_FA=openrouter
# VOICE_LLM_PROVIDER_FA=openrouter
# Recording cap in ms (0 = uncapped). 2 minutes bounds worst-case vendor spend at
# about 1.3 cents per recording.
# VOICE_MAX_RECORDING_MS=120000
# Per-user rate limit on the extract endpoint. Unreachable by a human — a recording
# plus processing takes ten seconds at minimum — so it is purely an abuse guard.
# VOICE_THROTTLE_TTL=60
# VOICE_THROTTLE_LIMIT=6