Ajustado el token de la tarjeta y solucionados problemas de conexion de
docker
This commit is contained in:
101
deployment/database/migrations/1.1.2_AlterCardIdToVarchar.sql
Normal file
101
deployment/database/migrations/1.1.2_AlterCardIdToVarchar.sql
Normal file
@@ -0,0 +1,101 @@
|
||||
-- Alter the activation_codes table to accept a 9 digit string for card_id instead of a UUID
|
||||
ALTER TABLE activation_codes ALTER COLUMN card_id TYPE VARCHAR(9);
|
||||
|
||||
-- Alter the activation_logs table to accept a 9 digit string for card_id instead of a UUID
|
||||
ALTER TABLE activation_logs ALTER COLUMN card_id TYPE VARCHAR(9);
|
||||
|
||||
-- Drop the old procedure which accepted UUID
|
||||
DROP FUNCTION IF EXISTS activate_payment_card(UUID, TEXT, INET, JSONB);
|
||||
|
||||
-- Recreate it to accept VARCHAR(9)
|
||||
CREATE OR REPLACE FUNCTION activate_payment_card(
|
||||
p_card_id VARCHAR(9), -- 9 digit string de la tarjeta
|
||||
p_input_code TEXT, -- El código que mete el usuario, puede ser incorrecto en este punto
|
||||
p_ip_address INET, -- Datos de origen de la activación
|
||||
p_device_info JSONB -- Datos del dispositivo de origen
|
||||
)
|
||||
RETURNS TABLE (
|
||||
success BOOLEAN,
|
||||
message TEXT
|
||||
) AS $$
|
||||
DECLARE
|
||||
v_code_hash TEXT;
|
||||
v_is_used BOOLEAN;
|
||||
v_is_blocked BOOLEAN;
|
||||
v_expires_at TIMESTAMPTZ;
|
||||
v_failed_attempts INT;
|
||||
v_max_attempts CONSTANT INT := 3;
|
||||
BEGIN
|
||||
-- 1. Obtener datos del código y bloquear la fila para actualización (FOR UPDATE)
|
||||
-- sería raro que 2 clientes intentasen modificar la tarjeta a la vez.
|
||||
SELECT code_hash, is_used, is_blocked, expires_at, failed_attempts
|
||||
INTO v_code_hash, v_is_used, v_is_blocked, v_expires_at, v_failed_attempts
|
||||
FROM activation_codes
|
||||
WHERE card_id = p_card_id
|
||||
FOR UPDATE;
|
||||
|
||||
-- 2. Validaciones:
|
||||
-- 2.1 Si no existe ninguna tarjeta con ese card_id
|
||||
IF NOT FOUND THEN
|
||||
RETURN QUERY SELECT FALSE, 'CARD_NOT_FOUND';
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- 2.2 Si el código introducido ya ha sido usado
|
||||
IF v_is_used THEN
|
||||
RETURN QUERY SELECT FALSE, 'ALREADY_ACTIVATED';
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- 2.3 Si el código ya ha sido bloqueado o se ha intentado demasiadas veces
|
||||
IF v_is_blocked OR v_failed_attempts >= v_max_attempts THEN
|
||||
RETURN QUERY SELECT FALSE, 'CODE_BLOCKED';
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- 2.4 Si el código es demasiado viejo
|
||||
IF v_expires_at < NOW() THEN
|
||||
RETURN QUERY SELECT FALSE, 'CODE_EXPIRED';
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- 3. Verificación del Hash del codigo que ha introducido el usuario
|
||||
-- En la bdd guardo el hash, al procedimiento se introduce el codigo per-se
|
||||
-- Si el código NO coincide:
|
||||
IF v_code_hash != crypt(p_input_code, v_code_hash) THEN
|
||||
UPDATE activation_codes
|
||||
-- 3.1 Control de intentos
|
||||
SET failed_attempts = failed_attempts + 1,
|
||||
is_blocked = (failed_attempts + 1 >= v_max_attempts)
|
||||
WHERE card_id = p_card_id;
|
||||
-- 3.2 Se loguea el fallo
|
||||
INSERT INTO activation_logs (card_id, action_type, ip_address, device_info)
|
||||
VALUES (p_card_id, 'FAILED_ATTEMPT', p_ip_address, p_device_info);
|
||||
|
||||
RETURN QUERY SELECT FALSE, 'INVALID_CODE';
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- 4. Si el código ES correcto:
|
||||
-- Marcar código como usado
|
||||
UPDATE activation_codes
|
||||
SET is_used = TRUE
|
||||
WHERE card_id = p_card_id;
|
||||
|
||||
-- Activar la tarjeta
|
||||
UPDATE payment_cards
|
||||
SET status = 'ACTIVE',
|
||||
activated_at = NOW()
|
||||
WHERE card_id = p_card_id;
|
||||
|
||||
-- Registrar éxito en auditoría
|
||||
INSERT INTO activation_logs (card_id, action_type, ip_address, device_info)
|
||||
VALUES (p_card_id, 'ACTIVATION_SUCCESS', p_ip_address, p_device_info);
|
||||
|
||||
RETURN QUERY SELECT TRUE, 'SUCCESS';
|
||||
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
-- En caso de error inesperado, Postgres hace rollback automático
|
||||
RETURN QUERY SELECT FALSE, 'INTERNAL_ERROR: ' || SQLERRM;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
@@ -7,14 +7,11 @@ COPY ./src ./src
|
||||
|
||||
COPY tsconfig.json ./
|
||||
|
||||
COPY ./start.sh ./
|
||||
|
||||
COPY ./deployment/database/migrations ./deployment/database/migrations
|
||||
|
||||
COPY ./deployment/develop/start.sh ./
|
||||
|
||||
RUN npm config set @sf-alvar:registry https://git.savefamilygps.net/api/packages/SaveFamily/npm/
|
||||
RUN npm install
|
||||
RUN chmod +x start.sh
|
||||
|
||||
EXPOSE ${PORT}
|
||||
CMD [ "npm" ,"run", "build-start" ]
|
||||
|
||||
@@ -28,7 +28,7 @@ services:
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
"wget -q --spider http://localhost:${PORT:-3000}/health || exit 1",
|
||||
"wget -q --spider http://127.0.0.1:${PORT:-3000}/health || exit 1",
|
||||
]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
# Stage base para coordinar las fases de build y ejecucion
|
||||
FROM node:22-alpine
|
||||
# Hace falta para la herramienta de migraciones, cuando se publique se
|
||||
# sustituira por el paquete de npm
|
||||
#RUN apk --no-cache add git
|
||||
WORKDIR /app
|
||||
|
||||
WORKDIR /home/node/app
|
||||
|
||||
COPY ./package.json ./package-lock.json ./
|
||||
COPY ./src ./src
|
||||
|
||||
@@ -29,7 +29,7 @@ services:
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
'node -e "fetch(''http://localhost:'' + (process.env.PORT || 3000) + ''/health'').then(r => { if (!r.ok) process.exit(1) }).catch(() => process.exit(1))"',
|
||||
"wget -q --spider http://127.0.0.1:${PORT:-3000}/health || exit 1",
|
||||
]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: Local development server
|
||||
name: Local
|
||||
color: "#2E8A54"
|
||||
variables:
|
||||
- name: baseUrl
|
||||
value: http://localhost:3000
|
||||
value: http://127.0.0.1:3000
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@sf-alvar/db-migrate": "^1.0.4",
|
||||
"@sf-alvar/db-migrate": "^1.0.6",
|
||||
"axios": "^1.13.6",
|
||||
"cors": "^2.8.6",
|
||||
"dotenv": "^17.3.1",
|
||||
@@ -2616,9 +2616,9 @@
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@sf-alvar/db-migrate": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://git.savefamilygps.net/api/packages/SaveFamily/npm/%40sf-alvar%2Fdb-migrate/-/1.0.4/db-migrate-1.0.4.tgz",
|
||||
"integrity": "sha512-Ac18zzpvSEyjG+ESYZmuqmgCMuLbr4uuBoC8Xjm2rv62fTh1fxe7jqG79agvydz7QMQ1ZL5fC83htpm7zUTV1w==",
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://git.savefamilygps.net/api/packages/SaveFamily/npm/%40sf-alvar%2Fdb-migrate/-/1.0.6/db-migrate-1.0.6.tgz",
|
||||
"integrity": "sha512-TvvbF30Y1pgi6RMejXnc68AmzIXH/OBzCyUNWzwQYY21islPmXjI8gtG6C2qq7We2s4h35hvPe208vRqQnQU5g==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"pg": "^8.18.0",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"build": "tsc",
|
||||
"build:esbuild": "esbuild --bundle src/main.ts --outdir=dist --platform=node --format=esm --packages=external",
|
||||
"start": "node dist/main.js",
|
||||
"migrate": "db-migrate -e .env -m ./deployment/database/migrations/ -t 99.0.0",
|
||||
"migrate": "node ./node_modules/@sf-alvar/db-migrate/lib/index.js -e .env -m ./deployment/database/migrations/ -t 99.0.0",
|
||||
"build-start": "npm run build:esbuild && npm run start"
|
||||
},
|
||||
"author": "",
|
||||
@@ -28,7 +28,7 @@
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sf-alvar/db-migrate": "^1.0.4",
|
||||
"@sf-alvar/db-migrate": "^1.0.6",
|
||||
"axios": "^1.13.6",
|
||||
"cors": "^2.8.6",
|
||||
"dotenv": "^17.3.1",
|
||||
|
||||
@@ -17,6 +17,7 @@ export class NfcController {
|
||||
|
||||
public generateActivationTag() {
|
||||
return async (req: Request, res: Response) => {
|
||||
console.log(("REQ!", req))
|
||||
const body = req.body
|
||||
const validate = baseValidator.validate(body)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { UUID, uuidv7 } from "uuidv7";
|
||||
import { BodyValidator, type Validator } from "./BodyValidator.js";
|
||||
|
||||
const cardIdExists: Validator<{ card_id?: string }> = {
|
||||
@@ -7,10 +6,10 @@ const cardIdExists: Validator<{ card_id?: string }> = {
|
||||
errorMsg: "El campo card_id esta undefined"
|
||||
}
|
||||
|
||||
const cardIdIsUUIDv7: Validator<{ card_id: string }> = {
|
||||
const cardIdIsToken: Validator<{ card_id: string }> = {
|
||||
field: "card_id",
|
||||
validationFunc: (body) => UUID.parse(body.card_id) != undefined,
|
||||
errorMsg: "El campo card_id no es un uuidv7"
|
||||
validationFunc: (body) => typeof body.card_id === 'string' && body.card_id.length === 9,
|
||||
errorMsg: "El campo card_id no tiene una logitdud de 9 digitos"
|
||||
}
|
||||
|
||||
export const baseValidator = new BodyValidator([cardIdExists, cardIdIsUUIDv7])
|
||||
export const baseValidator = new BodyValidator([cardIdExists, cardIdIsToken])
|
||||
|
||||
@@ -4,7 +4,7 @@ import { httpclient } from "config/httpclient.config.js";
|
||||
import { pgClient } from "config/pgclient.config.js";
|
||||
import { NfcRepository } from "./Nfc.repository.js";
|
||||
import type { ServerContext } from "domain/ServerContext.js";
|
||||
import { uuidv7 } from "uuidv7";
|
||||
|
||||
|
||||
await describe("NfcRepository Integration Tests", async () => {
|
||||
const serverContext: ServerContext = {
|
||||
@@ -13,7 +13,7 @@ await describe("NfcRepository Integration Tests", async () => {
|
||||
};
|
||||
|
||||
const repo = new NfcRepository(serverContext);
|
||||
const testCardId = uuidv7()
|
||||
const testCardId = "123456789";
|
||||
const testCode = "12345678";
|
||||
|
||||
let lastCodeGenerated: string | undefined = undefined;
|
||||
@@ -66,7 +66,7 @@ await describe("NfcRepository Integration Tests", async () => {
|
||||
});
|
||||
|
||||
await test("findActivationCodes should return empty array if card has no codes", async () => {
|
||||
const nonExistentCard = uuidv7()
|
||||
const nonExistentCard = "987654321";
|
||||
const result = await repo.findActivationCodes(nonExistentCard);
|
||||
|
||||
if (result.error) {
|
||||
|
||||
Reference in New Issue
Block a user