From 0d04dee670691c89fe9e767df07ff1b7ecb39c27 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sat, 16 May 2026 09:32:25 +0000 Subject: [PATCH] feat(azure): update deployment files for Phase 3 auth variables Replace DEMO_PASSWORD/SECRET_KEY with JWT_SECRET_KEY, ADMIN_EMAIL, ADMIN_PASSWORD, and optional ACS (Azure Communication Services) vars across config.sh.example, container-app.bicep, and 2-build-deploy.sh. Co-Authored-By: Claude Sonnet 4.6 --- azure/2-build-deploy.sh | 17 +++++++++-- azure/config.sh.example | 20 +++++++++---- azure/container-app.bicep | 60 +++++++++++++++++++++++++++++---------- 3 files changed, 74 insertions(+), 23 deletions(-) diff --git a/azure/2-build-deploy.sh b/azure/2-build-deploy.sh index 52e554e8..898c0794 100755 --- a/azure/2-build-deploy.sh +++ b/azure/2-build-deploy.sh @@ -29,7 +29,8 @@ source "$CONFIG_FILE" # ── Validar variables obligatòries ─────────────────────────────────────────── REQUIRED_VARS=( AZURE_SUBSCRIPTION_ID RESOURCE_GROUP PROJECT_NAME - DEMO_PASSWORD SECRET_KEY LLM_API_KEY LLM_BASE_URL LLM_MODEL_NAME + JWT_SECRET_KEY ADMIN_EMAIL ADMIN_PASSWORD + LLM_API_KEY LLM_BASE_URL LLM_MODEL_NAME DATABASE_URL STORAGE_CONNECTION_STRING ) # Validate graph backend config @@ -42,6 +43,9 @@ if [[ "$GRAPH_BACKEND" == "graphiti" && -z "${NEO4J_PASSWORD:-}" ]]; then echo "ERROR: NEO4J_PASSWORD is required when GRAPH_BACKEND=graphiti" exit 1 fi +if [[ -z "${ACS_CONNECTION_STRING:-}" ]]; then + echo "AVÍS: ACS_CONNECTION_STRING no configurat — emails d'invitació es mostraran als logs" +fi for var in "${REQUIRED_VARS[@]}"; do if [[ -z "${!var:-}" ]]; then echo "ERROR: La variable $var no està configurada a config.sh" @@ -133,7 +137,15 @@ DEPLOY_OUTPUT=$(az deployment group create \ acrLoginServer="$ACR_LOGIN_SERVER" \ acrUsername="$ACR_USERNAME" \ acrPassword="$ACR_PASSWORD" \ - demoPassword="$DEMO_PASSWORD" \ + jwtSecretKey="$JWT_SECRET_KEY" \ + adminEmail="$ADMIN_EMAIL" \ + adminPassword="$ADMIN_PASSWORD" \ + acsConnectionString="${ACS_CONNECTION_STRING:-}" \ + acsSenderAddress="${ACS_SENDER_ADDRESS:-}" \ + acsInvitationTtlHours="${ACS_INVITATION_TTL_HOURS:-48}" \ + acsResetPasswordTtlHours="${ACS_RESET_PASSWORD_TTL_HOURS:-1}" \ + jwtAccessTokenExpires="${JWT_ACCESS_TOKEN_EXPIRES:-28800}" \ + jwtRefreshTokenExpires="${JWT_REFRESH_TOKEN_EXPIRES:-604800}" \ llmApiKey="$LLM_API_KEY" \ llmBoostApiKey="${LLM_BOOST_API_KEY:-}" \ llmProvider="${LLM_PROVIDER:-}" \ @@ -143,7 +155,6 @@ DEPLOY_OUTPUT=$(az deployment group create \ neo4jUser="${NEO4J_USER:-neo4j}" \ neo4jDatabase="${NEO4J_DATABASE:-neo4j}" \ graphBackend="${GRAPH_BACKEND:-zep}" \ - secretKey="$SECRET_KEY" \ llmBaseUrl="$LLM_BASE_URL" \ llmModelName="$LLM_MODEL_NAME" \ llmBoostBaseUrl="${LLM_BOOST_BASE_URL:-}" \ diff --git a/azure/config.sh.example b/azure/config.sh.example index b02a565d..1a9ceccc 100644 --- a/azure/config.sh.example +++ b/azure/config.sh.example @@ -22,12 +22,22 @@ PROJECT_NAME="mirofish" # prefix per a tots els recursos Azure # ── Secrets de l'aplicació ──────────────────────────────────────────────────── -# Contrasenya de l'usuari "demo" per fer login a l'app -DEMO_PASSWORD="" +# ── Auth JWT ────────────────────────────────────────────────────────────────── +# Genera JWT_SECRET_KEY amb: python -c "import secrets; print(secrets.token_hex(32))" +JWT_SECRET_KEY="" +JWT_ACCESS_TOKEN_EXPIRES=28800 # 8h en segons +JWT_REFRESH_TOKEN_EXPIRES=604800 # 7d en segons -# Flask SECRET_KEY per signar tokens JWT -# Genera-la amb: python -c "import secrets; print(secrets.token_hex(32))" -SECRET_KEY="" +# ── Admin inicial (per flask init-system) ───────────────────────────────────── +ADMIN_EMAIL="admin@example.com" +ADMIN_PASSWORD="" + +# ── Azure Communication Services (emails d'invitació i reset) ──────────────── +# ACS és opcional en dev: els links apareixeran als logs si ACS_CONNECTION_STRING és buit +ACS_CONNECTION_STRING="" +ACS_SENDER_ADDRESS="donotreply@example.com" +ACS_INVITATION_TTL_HOURS=48 +ACS_RESET_PASSWORD_TTL_HOURS=1 # ── LLM principal (OpenAI-compatible) ───────────────────────────────────────── LLM_API_KEY="" diff --git a/azure/container-app.bicep b/azure/container-app.bicep index ae196c02..50c57021 100644 --- a/azure/container-app.bicep +++ b/azure/container-app.bicep @@ -37,10 +37,6 @@ param acrUsername string @secure() param acrPassword string -@description('Contrasenya de l\'usuari demo') -@secure() -param demoPassword string - @description('Clau de l\'API LLM principal (OpenAI-compatible)') @secure() param llmApiKey string @@ -57,9 +53,17 @@ param zepApiKey string = '' @secure() param neo4jPassword string = '' -@description('SECRET_KEY de Flask per a JWT (python -c "import secrets; print(secrets.token_hex(32))")') +@description('JWT Secret Key per a flask-jwt-extended') @secure() -param secretKey string +param jwtSecretKey string + +@description('Contrasenya de l\'admin inicial (per flask init-system)') +@secure() +param adminPassword string + +@description('Connection string Azure Communication Services (opcional)') +@secure() +param acsConnectionString string = '' @description('Connection string del Storage Account per a Azure Files (output d\'infra.bicep)') @secure() @@ -72,6 +76,24 @@ param databaseUrl string = '' @description('Nom del Storage Account (output d\'infra.bicep)') param storageAccountName string = '' +@description('Email de l\'admin inicial') +param adminEmail string = '' + +@description('Adreça remitent ACS') +param acsSenderAddress string = '' + +@description('TTL invitació en hores') +param acsInvitationTtlHours string = '48' + +@description('TTL reset password en hores') +param acsResetPasswordTtlHours string = '1' + +@description('Expiració access token JWT en segons') +param jwtAccessTokenExpires string = '28800' + +@description('Expiració refresh token JWT en segons') +param jwtRefreshTokenExpires string = '604800' + // ─── Paràmetres LLM principal ───────────────────────────────────────────────── @description('URL base de l\'API LLM principal') @@ -149,10 +171,10 @@ param reportAgentTemperature string = '0.5' // ─── Secrets i env vars condicionals (Azure rebutja secrets amb valor buit) ─── var mandatorySecrets = [ - { name: 'acr-password', value: acrPassword } - { name: 'demo-password', value: demoPassword } - { name: 'llm-api-key', value: llmApiKey } - { name: 'secret-key', value: secretKey } + { name: 'acr-password', value: acrPassword } + { name: 'jwt-secret-key', value: jwtSecretKey } + { name: 'admin-password', value: adminPassword } + { name: 'llm-api-key', value: llmApiKey } ] var optionalSecrets = concat( empty(llmBoostApiKey) ? [] : [{ name: 'llm-boost-api-key', value: llmBoostApiKey }], @@ -161,14 +183,21 @@ var optionalSecrets = concat( empty(zepApiKey) ? [] : [{ name: 'zep-api-key', value: zepApiKey }], empty(neo4jPassword) ? [] : [{ name: 'neo4j-password', value: neo4jPassword }], empty(storageConnectionString) ? [] : [{ name: 'storage-connection-string', value: storageConnectionString }], - empty(databaseUrl) ? [] : [{ name: 'database-url', value: databaseUrl }] + empty(databaseUrl) ? [] : [{ name: 'database-url', value: databaseUrl }], + empty(acsConnectionString) ? [] : [{ name: 'acs-connection-string', value: acsConnectionString }] ) var allSecrets = concat(mandatorySecrets, optionalSecrets) var mandatoryEnv = [ - { name: 'DEMO_PASSWORD', secretRef: 'demo-password' } + { name: 'JWT_SECRET_KEY', secretRef: 'jwt-secret-key' } + { name: 'ADMIN_EMAIL', value: adminEmail } + { name: 'ADMIN_PASSWORD', secretRef: 'admin-password' } + { name: 'JWT_ACCESS_TOKEN_EXPIRES', value: jwtAccessTokenExpires } + { name: 'JWT_REFRESH_TOKEN_EXPIRES', value: jwtRefreshTokenExpires } + { name: 'ACS_SENDER_ADDRESS', value: acsSenderAddress } + { name: 'ACS_INVITATION_TTL_HOURS', value: acsInvitationTtlHours } + { name: 'ACS_RESET_PASSWORD_TTL_HOURS', value: acsResetPasswordTtlHours } { name: 'LLM_API_KEY', secretRef: 'llm-api-key' } - { name: 'SECRET_KEY', secretRef: 'secret-key' } { name: 'LLM_BASE_URL', value: llmBaseUrl } { name: 'LLM_MODEL_NAME', value: llmModelName } { name: 'LLM_PROVIDER', value: llmProvider } @@ -199,7 +228,8 @@ var optionalEnv = concat( empty(zepApiKey) ? [] : [{ name: 'ZEP_API_KEY', secretRef: 'zep-api-key' }], empty(neo4jPassword) ? [] : [{ name: 'NEO4J_PASSWORD', secretRef: 'neo4j-password' }], empty(storageConnectionString) ? [] : [{ name: 'AZURE_STORAGE_CONNECTION_STRING', secretRef: 'storage-connection-string' }], - empty(databaseUrl) ? [] : [{ name: 'DATABASE_URL', secretRef: 'database-url' }] + empty(databaseUrl) ? [] : [{ name: 'DATABASE_URL', secretRef: 'database-url' }], + empty(acsConnectionString) ? [] : [{ name: 'ACS_CONNECTION_STRING', secretRef: 'acs-connection-string' }] ) var allEnv = concat(mandatoryEnv, optionalEnv) @@ -266,7 +296,7 @@ resource containerApp 'Microsoft.App/containerApps@2023-05-01' = { name: 'uploads' storageType: 'AzureFile' storageName: 'uploads' - mountOptions: 'nobrl,cache=strict,nosharesock,actimeo=30' + mountOptions: 'nobrl,cache=strict,nosharesock' } ]