fix: restore map geometries and SMTP configuration
DH V2 CI / API · typecheck, tests, build (push) Successful in 38s
Production dependency audit / API · production dependencies (push) Successful in 9s
Production dependency audit / WEB · production dependencies (push) Successful in 8s
DH V2 CI / WEB · typecheck, build (push) Successful in 1m36s
DH V2 CI / Docker / migrations / production images (push) Successful in 1m45s
DH V2 CI / Promote verified main to deploy (push) Successful in 3s
Android CI / RC / Android · lint, tests, debug APK, release compile (push) Successful in 4m5s

This commit is contained in:
2026-09-15 23:58:49 -03:00
parent c9575cc520
commit cab9cd7c01
16 changed files with 107 additions and 32 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "dhv2-api",
"version": "0.29.0-12",
"version": "0.29.0-13",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "dhv2-api",
"version": "0.29.0-12",
"version": "0.29.0-13",
"license": "UNLICENSED",
"dependencies": {
"@nestjs/common": "^11.0.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "dhv2-api",
"version": "0.29.0-12",
"version": "0.29.0-13",
"private": true,
"license": "UNLICENSED",
"scripts": {
@@ -0,0 +1,27 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class F612AdminReportPermission1790142600000 implements MigrationInterface {
name = 'F612AdminReportPermission1790142600000';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
INSERT INTO role_permissions(role_id, permission_id)
SELECT role.id, permission.id
FROM roles role
JOIN permissions permission ON permission.code='inspection_reports.generate'
WHERE role.code='admin'
ON CONFLICT(role_id, permission_id) DO NOTHING
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
DELETE FROM role_permissions role_permission
USING roles role, permissions permission
WHERE role_permission.role_id=role.id
AND role_permission.permission_id=permission.id
AND role.code='admin'
AND permission.code='inspection_reports.generate'
`);
}
}
@@ -1,7 +1,7 @@
import { createCipheriv, createDecipheriv, randomBytes, randomUUID } from 'node:crypto';
import { connect as connectNet, Socket } from 'node:net';
import { connect as connectTls, TLSSocket } from 'node:tls';
import { Injectable } from '@nestjs/common';
import { Injectable, ServiceUnavailableException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { DataSource } from 'typeorm';
import { SmtpSecurityMode } from '../database/entities';
@@ -250,9 +250,9 @@ export class SmtpDeliveryService {
private encryptionKey():Buffer{
const raw=this.config.get<string>('SMTP_SETTINGS_MASTER_KEY');
if(!raw)throw new Error('SMTP_SETTINGS_MASTER_KEY no configurada');
if(!raw)throw new ServiceUnavailableException({ code:'SMTP_SETTINGS_MASTER_KEY_NOT_CONFIGURED', message:'La clave maestra para proteger credenciales SMTP no está configurada en el servidor' });
const key=/^[0-9a-fA-F]{64}$/.test(raw)?Buffer.from(raw,'hex'):Buffer.from(raw,'base64');
if(key.length!==32)throw new Error('SMTP_SETTINGS_MASTER_KEY debe contener exactamente 32 bytes');
if(key.length!==32)throw new ServiceUnavailableException({ code:'SMTP_SETTINGS_MASTER_KEY_INVALID', message:'La clave maestra SMTP del servidor tiene un formato inválido' });
return key;
}
+2 -2
View File
@@ -1,2 +1,2 @@
export const API_VERSION = '0.29.0-12';
export const API_PHASE = 'F6.11';
export const API_VERSION = '0.29.0-13';
export const API_PHASE = 'F6.12';
+3 -3
View File
@@ -4,9 +4,9 @@ import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { API_PHASE, API_VERSION } from '../../src/version';
test('health metadata reports the current F6.11 release', () => {
assert.equal(API_PHASE, 'F6.11');
test('health metadata reports the current F6.12 release', () => {
assert.equal(API_PHASE, 'F6.12');
const pkg = JSON.parse(readFileSync(resolve(process.cwd(), 'package.json'), 'utf8')) as { version: string };
assert.equal(API_VERSION, pkg.version);
assert.equal(API_VERSION, '0.29.0-12');
assert.equal(API_VERSION, '0.29.0-13');
});
@@ -13,7 +13,7 @@ test('F6.1 presentation metadata keeps the visible WEB version aligned with pack
const visibleVersion = version.match(/APP_VERSION\s*=\s*'([^']+)'/)?.[1];
assert.equal(visibleVersion, pkg.version);
assert.match(version, /APP_PHASE\s*=\s*'F6\.11/);
assert.match(version, /APP_PHASE\s*=\s*'F6\.12/);
});
test('F6.1 presentation keeps Relevamientos retired from WEB navigation and routes', () => {
@@ -0,0 +1,32 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import test from 'node:test';
const api = (path: string) => readFileSync(resolve(process.cwd(), path), 'utf8');
const web = (path: string) => readFileSync(resolve(process.cwd(), '..', 'web-v2', path), 'utf8');
test('F6.12 uses standard GeoJSON names in WEB and converts only at the API boundary', () => {
const client = web('src/lib/api.ts');
const map = web('src/features/map/DhMap.tsx');
const editor = web('src/features/map/AssetGeometryEditor.tsx');
assert.match(client, /type: 'Point'/);
assert.match(client, /type: 'LineString'/);
assert.match(client, /type: 'Polygon'/);
assert.match(client, /geometryPayload/);
assert.match(map, /feature\.geometry\.type === 'Point'/);
assert.match(editor, /setType\(value\.geometryType\)/);
});
test('F6.12 reports missing SMTP encryption configuration explicitly', () => {
const smtp = api('src/inspection-reports/smtp-delivery.service.ts');
assert.match(smtp, /SMTP_SETTINGS_MASTER_KEY_NOT_CONFIGURED/);
assert.match(smtp, /ServiceUnavailableException/);
});
test('F6.12 grants report management to the system administrator role', () => {
const migration = api('src/database/migrations/1790142600000-f6-12-admin-report-permission.ts');
assert.match(migration, /role\.code='admin'/);
assert.match(migration, /inspection_reports\.generate/);
assert.match(migration, /ON CONFLICT\(role_id, permission_id\) DO NOTHING/);
});