feat(azure): add PostgreSQL Flexible Server + Azure Files persistent storage
- infra.bicep: Storage Account + File Share (mirofish-uploads, 100GB SMB), envStorage to register share in Container Apps Env, PostgreSQL Flexible Server (v16, 32GB) with mirofish database and Azure-services firewall rule; new secure outputs: storageConnectionString, databaseUrl, storageAccountKey - container-app.bicep: storageConnectionString/databaseUrl/storageAccountName/ fileShareName params; volume mount at /mnt/uploads (Azure Files); OASIS_SIMULATION_DATA_DIR + UPLOAD_FOLDER + STORAGE_TYPE env vars set conditionally based on whether storage is configured - 1-infra.sh: POSTGRES_ADMIN_PASSWORD required, register Storage/PostgreSQL providers, pass new Bicep params, print generated DATABASE_URL and STORAGE_CONNECTION_STRING for pasting into config.sh - 2-build-deploy.sh: DATABASE_URL + STORAGE_CONNECTION_STRING required and forwarded to container-app.bicep - config.sh.example: add POSTGRES_ADMIN_PASSWORD, POSTGRES_ADMIN_USER, POSTGRES_SKU, STORAGE_CONNECTION_STRING, STORAGE_ACCOUNT_NAME, FILE_SHARE_NAME, DATABASE_URL - pyproject.toml + uv.lock: add psycopg2-binary>=2.9.9 (PostgreSQL driver) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e7641e9831
commit
842cf09a10
|
|
@ -34,6 +34,7 @@ source "$CONFIG_FILE"
|
|||
REQUIRED_VARS=(
|
||||
AZURE_SUBSCRIPTION_ID AZURE_LOCATION
|
||||
RESOURCE_GROUP PROJECT_NAME
|
||||
POSTGRES_ADMIN_PASSWORD
|
||||
)
|
||||
for var in "${REQUIRED_VARS[@]}"; do
|
||||
if [[ -z "${!var:-}" ]]; then
|
||||
|
|
@ -51,6 +52,8 @@ echo " Subscripció : $AZURE_SUBSCRIPTION_ID"
|
|||
echo " Localització: $AZURE_LOCATION"
|
||||
echo " Grup recurs : $RESOURCE_GROUP"
|
||||
echo " ACR : $ACR_NAME"
|
||||
echo " PostgreSQL : ${PROJECT_NAME}-pg"
|
||||
echo " Storage : ${PROJECT_NAME}store"
|
||||
echo "════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
|
|
@ -60,9 +63,11 @@ az account set --subscription "$AZURE_SUBSCRIPTION_ID"
|
|||
|
||||
# ── Registrar proveïdors necessaris ──────────────────────────────────────────
|
||||
echo "→ Registrant proveïdors Azure (pot trigar uns minuts la primera vegada)..."
|
||||
az provider register --namespace Microsoft.App --wait
|
||||
az provider register --namespace Microsoft.OperationalInsights --wait
|
||||
az provider register --namespace Microsoft.ContainerRegistry --wait
|
||||
az provider register --namespace Microsoft.App --wait
|
||||
az provider register --namespace Microsoft.OperationalInsights --wait
|
||||
az provider register --namespace Microsoft.ContainerRegistry --wait
|
||||
az provider register --namespace Microsoft.Storage --wait
|
||||
az provider register --namespace Microsoft.DBforPostgreSQL --wait
|
||||
|
||||
# ── Crear Resource Group ──────────────────────────────────────────────────────
|
||||
echo "→ Creant Resource Group '$RESOURCE_GROUP'..."
|
||||
|
|
@ -73,26 +78,41 @@ az group create \
|
|||
echo " ✓ Resource Group llest"
|
||||
|
||||
# ── Desplegar infraestructura via Bicep ──────────────────────────────────────
|
||||
echo "→ Desplegant infraestructura (ACR + Log Analytics + Container Apps Env)..."
|
||||
echo "→ Desplegant infraestructura (ACR + Container Apps Env + Storage + PostgreSQL)..."
|
||||
INFRA_OUTPUT=$(az deployment group create \
|
||||
--resource-group "$RESOURCE_GROUP" \
|
||||
--template-file "${SCRIPT_DIR}/infra.bicep" \
|
||||
--parameters \
|
||||
projectName="$PROJECT_NAME" \
|
||||
location="$AZURE_LOCATION" \
|
||||
postgresAdminPassword="$POSTGRES_ADMIN_PASSWORD" \
|
||||
postgresAdminUser="${POSTGRES_ADMIN_USER:-mirofish}" \
|
||||
postgresSku="${POSTGRES_SKU:-B_Standard_B1ms}" \
|
||||
--output json)
|
||||
|
||||
# Extreure outputs del desplegament
|
||||
ACR_LOGIN_SERVER=$(echo "$INFRA_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['acrLoginServer']['value'])")
|
||||
ACR_NAME_OUT=$(echo "$INFRA_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['acrName']['value'])")
|
||||
ENV_ID=$(echo "$INFRA_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['containerAppsEnvId']['value'])")
|
||||
ACR_LOGIN_SERVER=$(echo "$INFRA_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['acrLoginServer']['value'])")
|
||||
ACR_NAME_OUT=$(echo "$INFRA_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['acrName']['value'])")
|
||||
ENV_ID=$(echo "$INFRA_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['containerAppsEnvId']['value'])")
|
||||
STORAGE_ACCOUNT_NAME=$(echo "$INFRA_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['storageAccountName']['value'])")
|
||||
FILE_SHARE_NAME=$(echo "$INFRA_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['fileShareName']['value'])")
|
||||
POSTGRES_HOST=$(echo "$INFRA_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['postgresHost']['value'])")
|
||||
STORAGE_CONNECTION_STRING=$(echo "$INFRA_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['storageConnectionString']['value'])")
|
||||
DATABASE_URL=$(echo "$INFRA_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['databaseUrl']['value'])")
|
||||
|
||||
echo ""
|
||||
echo "════════════════════════════════════════════════════════"
|
||||
echo " Infraestructura creada correctament!"
|
||||
echo "════════════════════════════════════════════════════════"
|
||||
echo " ACR Login Server : $ACR_LOGIN_SERVER"
|
||||
echo " Container Apps Env ID: $ENV_ID"
|
||||
echo " ACR Login Server : $ACR_LOGIN_SERVER"
|
||||
echo " Container Apps Env ID : $ENV_ID"
|
||||
echo " Storage Account : $STORAGE_ACCOUNT_NAME"
|
||||
echo " File Share : $FILE_SHARE_NAME"
|
||||
echo " PostgreSQL host : $POSTGRES_HOST"
|
||||
echo ""
|
||||
echo " Afegeix a config.sh (valors generats per Azure):"
|
||||
echo " STORAGE_CONNECTION_STRING='$STORAGE_CONNECTION_STRING'"
|
||||
echo " DATABASE_URL='$DATABASE_URL'"
|
||||
echo ""
|
||||
echo " Proper pas: bash azure/2-build-deploy.sh"
|
||||
echo "════════════════════════════════════════════════════════"
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ source "$CONFIG_FILE"
|
|||
REQUIRED_VARS=(
|
||||
AZURE_SUBSCRIPTION_ID RESOURCE_GROUP PROJECT_NAME
|
||||
DEMO_PASSWORD SECRET_KEY LLM_API_KEY LLM_BASE_URL LLM_MODEL_NAME
|
||||
DATABASE_URL STORAGE_CONNECTION_STRING
|
||||
)
|
||||
# Validate graph backend config
|
||||
GRAPH_BACKEND="${GRAPH_BACKEND:-zep}"
|
||||
|
|
@ -157,6 +158,10 @@ DEPLOY_OUTPUT=$(az deployment group create \
|
|||
reportAgentMaxToolCalls="${REPORT_AGENT_MAX_TOOL_CALLS:-5}" \
|
||||
reportAgentMaxReflectionRounds="${REPORT_AGENT_MAX_REFLECTION_ROUNDS:-2}" \
|
||||
reportAgentTemperature="${REPORT_AGENT_TEMPERATURE:-0.5}" \
|
||||
storageConnectionString="${STORAGE_CONNECTION_STRING:-}" \
|
||||
storageAccountName="${STORAGE_ACCOUNT_NAME:-}" \
|
||||
fileShareName="${FILE_SHARE_NAME:-mirofish-uploads}" \
|
||||
databaseUrl="${DATABASE_URL:-}" \
|
||||
--output json)
|
||||
|
||||
FQDN=$(echo "$DEPLOY_OUTPUT" | python3 -c "import sys,json; print(json.load(sys.stdin)['properties']['outputs']['containerAppFqdn']['value'])")
|
||||
|
|
|
|||
|
|
@ -70,6 +70,32 @@ LLM_SMALL_API_KEY=""
|
|||
LLM_SMALL_BASE_URL=""
|
||||
LLM_SMALL_MODEL_NAME=""
|
||||
|
||||
# ── PostgreSQL Flexible Server ────────────────────────────────────────────────
|
||||
# Contrasenya de l'administrador de la BD PostgreSQL
|
||||
# Genera-la amb: python -c "import secrets; print(secrets.token_urlsafe(24))"
|
||||
POSTGRES_ADMIN_PASSWORD="<contrasenya-segura-postgres>"
|
||||
|
||||
# Usuari administrador de PostgreSQL (per defecte: mirofish)
|
||||
POSTGRES_ADMIN_USER="mirofish"
|
||||
|
||||
# SKU de PostgreSQL: B_Standard_B1ms (dev/test) | GP_Standard_D2s_v3 (producció)
|
||||
POSTGRES_SKU="B_Standard_B1ms"
|
||||
|
||||
# ── Storage (generats per 1-infra.sh — afegir després d'executar-lo) ─────────
|
||||
# Connection string d'Azure Files (output de 1-infra.sh)
|
||||
# Format: DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;EndpointSuffix=core.windows.net
|
||||
STORAGE_CONNECTION_STRING="<output-de-1-infra.sh>"
|
||||
|
||||
# Nom del Storage Account (output de 1-infra.sh)
|
||||
STORAGE_ACCOUNT_NAME="<output-de-1-infra.sh>"
|
||||
|
||||
# Nom del File Share (per defecte: mirofish-uploads)
|
||||
FILE_SHARE_NAME="mirofish-uploads"
|
||||
|
||||
# DATABASE_URL PostgreSQL (output de 1-infra.sh)
|
||||
# Format: postgresql+psycopg2://mirofish:<password>@<host>/mirofish?sslmode=require
|
||||
DATABASE_URL="<output-de-1-infra.sh>"
|
||||
|
||||
# ── Simulació OASIS (valors per defecte recomanats) ───────────────────────────
|
||||
OASIS_DEFAULT_MAX_ROUNDS="10"
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,20 @@ param neo4jPassword string = ''
|
|||
@secure()
|
||||
param secretKey string
|
||||
|
||||
@description('Connection string del Storage Account per a Azure Files (output d\'infra.bicep)')
|
||||
@secure()
|
||||
param storageConnectionString string = ''
|
||||
|
||||
@description('DATABASE_URL PostgreSQL (output d\'infra.bicep: postgresql+psycopg2://...)')
|
||||
@secure()
|
||||
param databaseUrl string = ''
|
||||
|
||||
@description('Nom del Storage Account (output d\'infra.bicep)')
|
||||
param storageAccountName string = ''
|
||||
|
||||
@description('Nom del File Share d\'Azure Files (output d\'infra.bicep)')
|
||||
param fileShareName string = 'mirofish-uploads'
|
||||
|
||||
// ─── Paràmetres LLM principal ─────────────────────────────────────────────────
|
||||
|
||||
@description('URL base de l\'API LLM principal')
|
||||
|
|
@ -144,11 +158,13 @@ var mandatorySecrets = [
|
|||
{ name: 'secret-key', value: secretKey }
|
||||
]
|
||||
var optionalSecrets = concat(
|
||||
empty(llmBoostApiKey) ? [] : [{ name: 'llm-boost-api-key', value: llmBoostApiKey }],
|
||||
empty(llmEmbedApiKey) ? [] : [{ name: 'llm-embed-api-key', value: llmEmbedApiKey }],
|
||||
empty(llmSmallApiKey) ? [] : [{ name: 'llm-small-api-key', value: llmSmallApiKey }],
|
||||
empty(zepApiKey) ? [] : [{ name: 'zep-api-key', value: zepApiKey }],
|
||||
empty(neo4jPassword) ? [] : [{ name: 'neo4j-password', value: neo4jPassword }]
|
||||
empty(llmBoostApiKey) ? [] : [{ name: 'llm-boost-api-key', value: llmBoostApiKey }],
|
||||
empty(llmEmbedApiKey) ? [] : [{ name: 'llm-embed-api-key', value: llmEmbedApiKey }],
|
||||
empty(llmSmallApiKey) ? [] : [{ name: 'llm-small-api-key', value: llmSmallApiKey }],
|
||||
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 }]
|
||||
)
|
||||
var allSecrets = concat(mandatorySecrets, optionalSecrets)
|
||||
|
||||
|
|
@ -174,13 +190,19 @@ var mandatoryEnv = [
|
|||
{ name: 'REPORT_AGENT_MAX_REFLECTION_ROUNDS', value: reportAgentMaxReflectionRounds }
|
||||
{ name: 'REPORT_AGENT_TEMPERATURE', value: reportAgentTemperature }
|
||||
{ name: 'FLASK_DEBUG', value: 'False' }
|
||||
// Storage: si s'usa Azure Files, les dades OASIS es guarden al volum muntat
|
||||
{ name: 'OASIS_SIMULATION_DATA_DIR', value: empty(storageAccountName) ? '/app/backend/uploads/simulations' : '/mnt/uploads/simulations' }
|
||||
{ name: 'UPLOAD_FOLDER', value: empty(storageAccountName) ? '/app/backend/uploads' : '/mnt/uploads' }
|
||||
{ name: 'STORAGE_TYPE', value: empty(storageConnectionString) ? 'local' : 'azure' }
|
||||
]
|
||||
var optionalEnv = concat(
|
||||
empty(llmBoostApiKey) ? [] : [{ name: 'LLM_BOOST_API_KEY', secretRef: 'llm-boost-api-key' }],
|
||||
empty(llmEmbedApiKey) ? [] : [{ name: 'LLM_EMBED_API_KEY', secretRef: 'llm-embed-api-key' }],
|
||||
empty(llmSmallApiKey) ? [] : [{ name: 'LLM_SMALL_API_KEY', secretRef: 'llm-small-api-key' }],
|
||||
empty(zepApiKey) ? [] : [{ name: 'ZEP_API_KEY', secretRef: 'zep-api-key' }],
|
||||
empty(neo4jPassword) ? [] : [{ name: 'NEO4J_PASSWORD', secretRef: 'neo4j-password' }]
|
||||
empty(llmBoostApiKey) ? [] : [{ name: 'LLM_BOOST_API_KEY', secretRef: 'llm-boost-api-key' }],
|
||||
empty(llmEmbedApiKey) ? [] : [{ name: 'LLM_EMBED_API_KEY', secretRef: 'llm-embed-api-key' }],
|
||||
empty(llmSmallApiKey) ? [] : [{ name: 'LLM_SMALL_API_KEY', secretRef: 'llm-small-api-key' }],
|
||||
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' }]
|
||||
)
|
||||
var allEnv = concat(mandatoryEnv, optionalEnv)
|
||||
|
||||
|
|
@ -230,6 +252,23 @@ resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
|
|||
cpu: json('0.5')
|
||||
memory: '1Gi'
|
||||
}
|
||||
|
||||
// Muntar Azure Files quan s'ha configurat storage
|
||||
volumeMounts: empty(storageAccountName) ? [] : [
|
||||
{
|
||||
volumeName: 'uploads'
|
||||
mountPath: '/mnt/uploads'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
// Volum Azure Files (registrat a l'entorn via infra.bicep)
|
||||
volumes: empty(storageAccountName) ? [] : [
|
||||
{
|
||||
name: 'uploads'
|
||||
storageType: 'AzureFile'
|
||||
storageName: 'uploads'
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
// MiroFish — Infraestructura base (executar una sola vegada)
|
||||
//
|
||||
// Crea:
|
||||
// - Azure Container Registry (ACR) per emmagatzemar la imatge Docker
|
||||
// - Container Apps Environment (plataforma d'execució)
|
||||
// - Azure Container Registry (ACR)
|
||||
// - Container Apps Environment
|
||||
// - Storage Account + File Share (muntat al container per a dades OASIS)
|
||||
// - Azure Database for PostgreSQL Flexible Server
|
||||
//
|
||||
// Executar amb: azure/1-infra.sh
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
|
@ -14,34 +16,123 @@ param projectName string = 'mirofish'
|
|||
@description('Localització Azure dels recursos')
|
||||
param location string = resourceGroup().location
|
||||
|
||||
@description('Contrasenya de l\'administrador de PostgreSQL')
|
||||
@secure()
|
||||
param postgresAdminPassword string
|
||||
|
||||
@description('Nom de l\'usuari administrador de PostgreSQL')
|
||||
param postgresAdminUser string = 'mirofish'
|
||||
|
||||
@description('SKU de PostgreSQL (B_Standard_B1ms per dev; GP_Standard_D2s_v3 per pro)')
|
||||
param postgresSku string = 'B_Standard_B1ms'
|
||||
|
||||
// ─── Azure Container Registry ─────────────────────────────────────────────────
|
||||
// SKU Basic: suficient per a imatges privades sense geo-replicació
|
||||
resource acr 'Microsoft.ContainerRegistry/registries@2023-01-01-preview' = {
|
||||
name: '${projectName}acr' // ACR no admet guions, tot minúscula
|
||||
name: '${projectName}acr'
|
||||
location: location
|
||||
sku: {
|
||||
name: 'Basic'
|
||||
}
|
||||
properties: {
|
||||
adminUserEnabled: true // necessari per a l'autenticació des dels scripts
|
||||
}
|
||||
sku: { name: 'Basic' }
|
||||
properties: { adminUserEnabled: true }
|
||||
}
|
||||
|
||||
// ─── Container Apps Environment ───────────────────────────────────────────────
|
||||
// Necessita el Storage Account abans per poder registrar el file share com a volum
|
||||
resource containerAppsEnv 'Microsoft.App/managedEnvironments@2023-05-01' = {
|
||||
name: '${projectName}-env'
|
||||
location: location
|
||||
properties: {}
|
||||
// TODO (ops): afegir appLogsConfiguration amb Log Analytics si es vol observabilitat
|
||||
// TODO (ops): descomentar per integrar en VNet Hub-Spoke
|
||||
// vnetConfiguration: {
|
||||
// infrastructureSubnetId: '/subscriptions/.../subnets/container-apps-subnet'
|
||||
// internal: true
|
||||
// }
|
||||
properties: {
|
||||
appLogsConfiguration: {
|
||||
destination: 'azure-monitor'
|
||||
}
|
||||
}
|
||||
dependsOn: [storageAccount]
|
||||
}
|
||||
|
||||
// Registra el File Share dins l'entorn de Container Apps
|
||||
resource envStorage 'Microsoft.App/managedEnvironments/storages@2023-05-01' = {
|
||||
name: 'uploads'
|
||||
parent: containerAppsEnv
|
||||
properties: {
|
||||
azureFile: {
|
||||
accountName: storageAccount.name
|
||||
accountKey: storageAccount.listKeys().keys[0].value
|
||||
shareName: fileShare.name
|
||||
accessMode: 'ReadWrite'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Storage Account + File Share (dades OASIS persistents) ──────────────────
|
||||
// Azure Files és necessari per a:
|
||||
// - uploads/simulations/ (SQLite DBs, JSONL, IPC files de les simulacions OASIS)
|
||||
// - uploads/projects/ (fitxers pujats per l'usuari)
|
||||
// Standard LRS: suficient per a escenaris non-HA
|
||||
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
|
||||
name: '${replace(projectName, '-', '')}store' // sense guions, màx 24 chars
|
||||
location: location
|
||||
sku: { name: 'Standard_LRS' }
|
||||
kind: 'StorageV2'
|
||||
properties: {
|
||||
minimumTlsVersion: 'TLS1_2'
|
||||
allowBlobPublicAccess: false
|
||||
supportsHttpsTrafficOnly: true
|
||||
}
|
||||
}
|
||||
|
||||
resource fileService 'Microsoft.Storage/storageAccounts/fileServices@2023-01-01' = {
|
||||
name: 'default'
|
||||
parent: storageAccount
|
||||
}
|
||||
|
||||
resource fileShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2023-01-01' = {
|
||||
name: 'mirofish-uploads'
|
||||
parent: fileService
|
||||
properties: {
|
||||
shareQuota: 100 // GB; augmenta si les simulacions creixen
|
||||
enabledProtocols: 'SMB'
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Azure Database for PostgreSQL Flexible Server ────────────────────────────
|
||||
// Flexible Server és el recomanat per a desplegaments nous (Single Server deprecated)
|
||||
// La base de dades 'mirofish' es crea automàticament
|
||||
resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2023-06-01-preview' = {
|
||||
name: '${projectName}-pg'
|
||||
location: location
|
||||
sku: {
|
||||
name: postgresSku
|
||||
tier: startsWith(postgresSku, 'B_') ? 'Burstable' : 'GeneralPurpose'
|
||||
}
|
||||
properties: {
|
||||
administratorLogin: postgresAdminUser
|
||||
administratorLoginPassword: postgresAdminPassword
|
||||
version: '16'
|
||||
storage: { storageSizeGB: 32 }
|
||||
backup: { backupRetentionDays: 7, geoRedundantBackup: 'Disabled' }
|
||||
highAvailability: { mode: 'Disabled' }
|
||||
// Accés públic desactivat; usa firewall rule per a Container Apps o VNet
|
||||
network: { publicNetworkAccess: 'Enabled' }
|
||||
authConfig: { activeDirectoryAuth: 'Disabled', passwordAuth: 'Enabled' }
|
||||
}
|
||||
}
|
||||
|
||||
resource postgresDb 'Microsoft.DBforPostgreSQL/flexibleServers/databases@2023-06-01-preview' = {
|
||||
name: 'mirofish'
|
||||
parent: postgresServer
|
||||
properties: { charset: 'UTF8', collation: 'en_US.utf8' }
|
||||
}
|
||||
|
||||
// Regla de firewall per permetre tràfic de serveis Azure (inclou Container Apps)
|
||||
resource postgresFirewallAzure 'Microsoft.DBforPostgreSQL/flexibleServers/firewallRules@2023-06-01-preview' = {
|
||||
name: 'allow-azure-services'
|
||||
parent: postgresServer
|
||||
properties: {
|
||||
startIpAddress: '0.0.0.0'
|
||||
endIpAddress: '0.0.0.0'
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Outputs (usats pels scripts de deploy) ───────────────────────────────────
|
||||
@description('URL de login de l\'ACR (ex: mirofsihacr.azurecr.io)')
|
||||
@description('URL de login de l\'ACR')
|
||||
output acrLoginServer string = acr.properties.loginServer
|
||||
|
||||
@description('Nom del recurs ACR')
|
||||
|
|
@ -49,3 +140,27 @@ output acrName string = acr.name
|
|||
|
||||
@description('ID del Container Apps Environment')
|
||||
output containerAppsEnvId string = containerAppsEnv.id
|
||||
|
||||
@description('Nom del Storage Account')
|
||||
output storageAccountName string = storageAccount.name
|
||||
|
||||
@description('Clau primària del Storage Account (per a AZURE_STORAGE_CONNECTION_STRING)')
|
||||
@sensitive()
|
||||
output storageAccountKey string = storageAccount.listKeys().keys[0].value
|
||||
|
||||
@description('Connection string del Storage Account (per a AZURE_STORAGE_CONNECTION_STRING)')
|
||||
@sensitive()
|
||||
output storageConnectionString string = 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};EndpointSuffix=core.windows.net'
|
||||
|
||||
@description('Nom del File Share d\'Azure Files')
|
||||
output fileShareName string = fileShare.name
|
||||
|
||||
@description('FQDN del servidor PostgreSQL')
|
||||
output postgresHost string = postgresServer.properties.fullyQualifiedDomainName
|
||||
|
||||
@description('Usuari administrador de PostgreSQL')
|
||||
output postgresAdminUser string = postgresAdminUser
|
||||
|
||||
@description('DATABASE_URL per a la Container App (postgresql+psycopg2://...)')
|
||||
@sensitive()
|
||||
output databaseUrl string = 'postgresql+psycopg2://${postgresAdminUser}:${postgresAdminPassword}@${postgresServer.properties.fullyQualifiedDomainName}/mirofish?sslmode=require'
|
||||
|
|
|
|||
|
|
@ -44,6 +44,9 @@ dependencies = [
|
|||
# Azure 存储
|
||||
"azure-storage-blob>=12.19.0",
|
||||
|
||||
# PostgreSQL driver (necessari quan DATABASE_URL és postgres://)
|
||||
"psycopg2-binary>=2.9.9",
|
||||
|
||||
# 安全 & 认证
|
||||
"bcrypt>=4.1.0",
|
||||
"flask-jwt-extended>=4.6.0",
|
||||
|
|
|
|||
|
|
@ -1505,6 +1505,7 @@ dependencies = [
|
|||
{ name = "gunicorn" },
|
||||
{ name = "markdown" },
|
||||
{ name = "openai" },
|
||||
{ name = "psycopg2-binary" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pyjwt" },
|
||||
{ name = "pymupdf" },
|
||||
|
|
@ -1549,6 +1550,7 @@ requires-dist = [
|
|||
{ name = "neo4j", marker = "extra == 'graphiti'", specifier = ">=5.26.0" },
|
||||
{ name = "openai", specifier = ">=1.0.0" },
|
||||
{ name = "pipreqs", marker = "extra == 'dev'", specifier = ">=0.5.0" },
|
||||
{ name = "psycopg2-binary", specifier = ">=2.9.9" },
|
||||
{ name = "pydantic", specifier = ">=2.0.0" },
|
||||
{ name = "pyjwt", specifier = ">=2.8.0" },
|
||||
{ name = "pymupdf", specifier = ">=1.24.0" },
|
||||
|
|
@ -2187,6 +2189,58 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/05/33/2d74d588408caedd065c2497bdb5ef83ce6082db01289a1e1147f6639802/psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8", size = 249898, upload-time = "2024-01-19T20:47:59.238Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psycopg2-binary"
|
||||
version = "2.9.12"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2a/60/a3624f79acea344c16fbef3a94d28b89a8042ddfb8f3e4ca83f538671409/psycopg2_binary-2.9.12.tar.gz", hash = "sha256:5ac9444edc768c02a6b6a591f070b8aae28ff3a99be57560ac996001580f294c", size = 379686, upload-time = "2026-04-21T09:40:34.304Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/19/d4ce60954f3bb9d8e3bc5e5c4d1f2487de2d3851bf2391d54954c9df12a6/psycopg2_binary-2.9.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5c8ce6c61bd1b1f6b9c24ee32211599f6166af2c55abb19456090a21fd16554b", size = 3712338, upload-time = "2026-04-20T23:34:03.961Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/71/c85409ee0d78890f0660eff262e815e7dd2bb741a17611d82e9e8cd9dc5e/psycopg2_binary-2.9.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b4a9eaa6e7f4ff91bec10aa3fb296878e75187bced5cc4bafe17dc40915e1326", size = 3822407, upload-time = "2026-04-20T23:34:05.977Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/ed/60486c2c7f0d4d1ede2bfb1ed27e2498477ce646bc7f6b2759906303117e/psycopg2_binary-2.9.12-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c6528cefc8e50fcc6f4a107e27a672058b36cc5736d665476aeb413ba88dbb06", size = 4578425, upload-time = "2026-04-20T23:34:08.246Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/b9/656cb03fad9f4f49f2145c334b1126ee75189929ca4e6187d485a2d59951/psycopg2_binary-2.9.12-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e4e184b1fb6072bf05388aa41c697e1b2d01b3473f107e7ec44f186a32cfd0b8", size = 4273709, upload-time = "2026-04-20T23:34:10.974Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/66/08cf0da0e25cc6fb142c89be45fc8418792858f0c4cbff5e24530ff02cd6/psycopg2_binary-2.9.12-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4766ab678563054d3f1d064a4db19cc4b5f9e3a8d9018592a8285cf200c248f3", size = 5893779, upload-time = "2026-04-20T23:34:13.905Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/d7/eecd9ce8e146d3721115d82d3836efdbb712187e4590325df549989d18f4/psycopg2_binary-2.9.12-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5a0253224780c978746cb9be55a946bcdaf40fe3519c0f622924cdabdafe2c39", size = 4109308, upload-time = "2026-04-20T23:34:16.761Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/2e/b1dc289b362cc8d45697b57eefbd673186f49a4ea0906928988e3affcc98/psycopg2_binary-2.9.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0dc9228d47c46bda253d2ecd6bb93b56a9f2d7ad33b684a1fa3622bf74ffe30c", size = 3654405, upload-time = "2026-04-20T23:34:19.303Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/e4/4c4aea6473214dbdbd0fbba11aa4691e76dc01722c55724c5951719865ff/psycopg2_binary-2.9.12-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f921f3cd87035ef7df233383011d7a53ea1d346224752c1385f1edfd790ceb6a", size = 3299187, upload-time = "2026-04-20T23:34:21.206Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/5d/b03b99986446a4f57b170ed9a2579fb7ff9783ca0fa5226b19db99737fee/psycopg2_binary-2.9.12-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3d999bd982a723113c1a45b55a7a6a90d64d0ed2278020ed625c490ff7bef96c", size = 3047716, upload-time = "2026-04-20T23:34:23.077Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/86/382ee4afbd1d97500c9d2862b20c2fdeddf4b7335e984df3fb4309f64108/psycopg2_binary-2.9.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29d4d134bd0ab46ffb04e94aa3c5fa3ef582e9026609165e2f758ff76fc3a3be", size = 3349237, upload-time = "2026-04-20T23:34:25.211Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/16/9a57c75ba1eda7165c017342f526810d5f5a12647dde749c99ae9a7141d7/psycopg2_binary-2.9.12-cp311-cp311-win_amd64.whl", hash = "sha256:cb4a1dacdd48077150dc762a9e5ddbf32c256d66cb46f80839391aa458774936", size = 2757036, upload-time = "2026-04-20T23:34:27.77Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/9f/ef4ef3c8e15083df90ca35265cfd1a081a2f0cc07bb229c6314c6af817f4/psycopg2_binary-2.9.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5cdc05117180c5fa9c40eea8ea559ce64d73824c39d928b7da9fb5f6a9392433", size = 3712459, upload-time = "2026-04-20T23:34:30.549Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/01/3dd14e46ba48c1e1a6ec58ee599fa1b5efa00c246d5046cd903d0eeb1af1/psycopg2_binary-2.9.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d3227a3bc228c10d21011a99245edca923e4e8bf461857e869a507d9a41fe9f6", size = 3822936, upload-time = "2026-04-20T23:34:32.77Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/f7/0640e4901119d8a9f7a1784b927f494e2198e213ceb593753d1f2c8b1b30/psycopg2_binary-2.9.12-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:995ce929eede89db6254b50827e2b7fd61e50d11f0b116b29fffe4a2e53c4580", size = 4578676, upload-time = "2026-04-20T23:34:35.18Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/55/44df3965b5f297c50cc0b1b594a31c67d6127a9d133045b8a66611b14dfb/psycopg2_binary-2.9.12-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9fe06d93e72f1c048e731a2e3e7854a5bfaa58fc736068df90b352cefe66f03f", size = 4274917, upload-time = "2026-04-20T23:34:37.982Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/4b/74535248b1eac0c9336862e8617c765ac94dac76f9e25d7c4a79588c8907/psycopg2_binary-2.9.12-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40e7b28b63aaf737cb3a1edc3a9bbc9a9f4ad3dcb7152e8c1130e4050eddcb7d", size = 5894843, upload-time = "2026-04-20T23:34:40.856Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/ba/f1bf8d2ae71868ad800b661099086ee52bc0f8d9f05be1acd8ebb06757cc/psycopg2_binary-2.9.12-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:89d19a9f7899e8eb0656a2b3a08e0da04c720a06db6e0033eab5928aabe60fa9", size = 4110556, upload-time = "2026-04-20T23:34:44.016Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/46/c15706c338403b7c420bcc0c2905aad116cc064545686d8bf85f1999ea00/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:612b965daee295ae2da8f8218ce1d274645dc76ef3f1abf6a0a94fd57eff876d", size = 3655714, upload-time = "2026-04-20T23:34:46.233Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/7c/a2d5dc09b64a4564db242a0fe418fde7d33f6f8259dd2c5b9d7def00fb5a/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b9a339b79d37c1b45f3235265f07cdeb0cb5ad7acd2ac7720a5920989c17c24e", size = 3301154, upload-time = "2026-04-20T23:34:49.528Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/e8/cc8c9a4ce71461f9ec548d38cadc41dc184b34c73e6455450775a9334ccd/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:3471336e1acfd9c7fe507b8bad5af9317b6a89294f9eb37bd9a030bb7bebcdc6", size = 3048882, upload-time = "2026-04-20T23:34:51.86Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/6a/31e2296bc0787c5ab75d3d118e40b239db8151b5192b90b77c72bc9256e9/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7af18183109e23502c8b2ae7f6926c0882766f35b5175a4cd737ad825e4d7a1b", size = 3351298, upload-time = "2026-04-20T23:34:54.124Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/a8/75f4e3e11203b590150abed2cf7794b9c9c9f7eceddae955191138b44dde/psycopg2_binary-2.9.12-cp312-cp312-win_amd64.whl", hash = "sha256:398fcd4db988c7d7d3713e2b8e18939776fd3fb447052daae4f24fa39daede4c", size = 2757230, upload-time = "2026-04-20T23:34:56.242Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/bb/4608c96f970f6e0c56572e87027ef4404f709382a3503e9934526d7ba051/psycopg2_binary-2.9.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7c729a73c7b1b84de3582f73cdd27d905121dc2c531f3d9a3c32a3011033b965", size = 3712419, upload-time = "2026-04-20T23:34:58.754Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/af/48f76af9d50d61cf390f8cd657b503168b089e2e9298e48465d029fcc713/psycopg2_binary-2.9.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4413d0caef93c5cf50b96863df4c2efe8c269bf2267df353225595e7e15e8df7", size = 3822990, upload-time = "2026-04-20T23:35:00.821Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/df/aba0f99397cd811d32e06fc0cc781f1f3ce98bc0e729cb423925085d781a/psycopg2_binary-2.9.12-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4dfcf8e45ebb0c663be34a3442f65e17311f3367089cd4e5e3a3e8e62c978777", size = 4578696, upload-time = "2026-04-20T23:35:03.409Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/9c/eaa74021ac4e4d5c2f83d82fc6615a63f4fe6c94dc4e94c3990427053f67/psycopg2_binary-2.9.12-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c41321a14dd74aceb6a9a643b9253a334521babfa763fa873e33d89cfa122fb5", size = 4274982, upload-time = "2026-04-20T23:35:05.583Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/ed/c25deff98bd26187ba48b3b250a3ffc3037c46c5b89362534a15d200e0db/psycopg2_binary-2.9.12-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83946ba43979ebfdc99a3cd0ee775c89f221df026984ba19d46133d8d75d3cd9", size = 5894867, upload-time = "2026-04-20T23:35:07.902Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/81/8d0e21ca77373c6c9589e5c4528f6e8f0c08c62cafc76fb0bddb7a2cee22/psycopg2_binary-2.9.12-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:411e85815652d13560fbe731878daa5d92378c4995a22302071890ec3397d019", size = 4110578, upload-time = "2026-04-20T23:35:10.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/fc/f481e2435bd8f742d0123309174aae4165160ad3ef17c1b99c3622c241d2/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c8ad4c08e00f7679559eaed7aff1edfffc60c086b976f93972f686384a95e2c", size = 3655816, upload-time = "2026-04-20T23:35:12.56Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/79/b9f46466bdbe9f239c96cde8be33c1aace4842f06013b47b730dc9759187/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:00814e40fa23c2b37ef0a1e3c749d89982c73a9cb5046137f0752a22d432e82f", size = 3301307, upload-time = "2026-04-20T23:35:15.029Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/19/7dc003b32fe35024df89b658104f7c8538a8b2dcbde7a4e746ce929742e7/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:98062447aebc20ed20add1f547a364fd0ef8933640d5372ff1873f8deb9b61be", size = 3048968, upload-time = "2026-04-20T23:35:16.757Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/58/2dbd7db5c604d45f4950d988506aae672a14126ec22998ced5021cbb76bb/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:66a7685d7e548f10fb4ce32fb01a7b7f4aa702134de92a292c7bd9e0d3dbd290", size = 3351369, upload-time = "2026-04-20T23:35:18.933Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/ee/dee8dcaad07f735824de3d6563bc67119fa6c28257b17977a8d624f02fab/psycopg2_binary-2.9.12-cp313-cp313-win_amd64.whl", hash = "sha256:b6937f5fe4e180aeee87de907a2fa982ded6f7f15d7218f78a083e4e1d68f2a0", size = 2757347, upload-time = "2026-04-20T23:35:21.283Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/1b/708c0dca874acfad6d65314271859899a79007686f3a1f74e82a2ed4b645/psycopg2_binary-2.9.12-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6f3b3de8a74ef8db215f22edffb19e32dc6fa41340456de7ec99efdc8a7b3ec2", size = 3712428, upload-time = "2026-04-20T23:35:23.453Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/39/ddbea9d4b4de6aca9431b6ed253f530f8a02d3b8f9bcfd0dbfe2b3de6fe4/psycopg2_binary-2.9.12-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1006fb62f0f0bc5ce256a832356c6262e91be43f5e4eb15b5eaf38079464caf2", size = 3823184, upload-time = "2026-04-20T23:35:25.92Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/a0/bc2fef74b106fa345567122a0659e6d94512ed7dc0131ec44c9e5aba3725/psycopg2_binary-2.9.12-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:840066105706cd2eb29b9a1c2329620056582a4bf3e8169dec5c447042d0869f", size = 4579157, upload-time = "2026-04-20T23:35:28.542Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/d7/d4e3b2005d3de607ca4fbb0e8742e248056e52184a6b94ebda3c1c2c329b/psycopg2_binary-2.9.12-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:863f5d12241ebe1c76a72a04c2113b6dc905f90b9cef0e9be0efd994affd9354", size = 4274970, upload-time = "2026-04-20T23:35:30.418Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/42/c9853f8db3967fe08bcde11f53d53b85d351750cae726ce001cb68afa9c1/psycopg2_binary-2.9.12-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a99eaab34a9010f1a086b126de467466620a750634d114d20455f3a824aae033", size = 5895175, upload-time = "2026-04-20T23:35:33.584Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/fd/b82b5601a97630308bef079f545ffec481bbbc795c2ba5ec416a01d03f60/psycopg2_binary-2.9.12-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ffdd7dc5463ccd61845ac37b7012d0f35a1548df9febe14f8dd549be4a0bc81e", size = 4110658, upload-time = "2026-04-20T23:35:35.638Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/8c/32ca69b0389ef25dd22937bf9e8fbe2ce27aea20b05ded48c4ce4cb42475/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:54a0dfecab1b48731f934e06139dfe11e24219fb6d0ceb32177cf0375f14c7b5", size = 3656251, upload-time = "2026-04-20T23:35:37.854Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/29/96992a2b59e3b9d730fcf9612d0a387305025dc867a9fc490a9e496e074e/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:96937c9c5d891f772430f418a7a8b4691a90c3e6b93cf72b5bd7cad8cbca32a5", size = 3301810, upload-time = "2026-04-20T23:35:39.927Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/ad/44b06659949b243ae10112cd3b20a197f9bf3e81d5651379b9eb889bfaad/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:77b348775efd4cdab410ec6609d81ccecd1139c90265fa583a7255c8064bc03d", size = 3048977, upload-time = "2026-04-20T23:35:41.806Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/f2/10a1bcebadb6aa55e280e1f58975c36a7b560ea525184c7aa4064c466633/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:527e6342b3e44c2f0544f6b8e927d60de7f163f5723b8f1dfa7d2a84298738cd", size = 3351466, upload-time = "2026-04-20T23:35:43.993Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/be/b732c8418ffa5bcfda002890f5dc4c869fc17db66ff11f53b17cfe44afc0/psycopg2_binary-2.9.12-cp314-cp314-win_amd64.whl", hash = "sha256:f12ae41fcafadb39b2785e64a40f9db05d6de2ac114077457e0e7c597f3af980", size = 2848762, upload-time = "2026-04-20T23:35:46.421Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ptyprocess"
|
||||
version = "0.7.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue