Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/config/db-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ interface DBConfig {
user: string
password: string | undefined
database: string
ssl?: any
ssl?: object
}

// ========================================
Expand Down
6 changes: 3 additions & 3 deletions backend/controllers/auth/user-controllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,10 @@ export const resetUserPassword = async (req: Request, res: Response): Promise<vo
}

res.status(200).json({ message: 'Contraseña actualizada correctamente' })
} catch (error: any) {
} catch (error) {
logger.error({ err: error }, 'Error resetUserPassword')
if (error.message === 'Usuario no encontrado') {
res.status(404).json({ error: error.message })
if ((error as Error).message === 'Usuario no encontrado') {
res.status(404).json({ error: (error as Error).message })
return
}
res.status(500).json({ error: 'Error al resetear contraseña' })
Expand Down
3 changes: 1 addition & 2 deletions backend/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ export default defineConfig(
globals: globals.node,
},
rules: {
// 341 existing uses: warn until they are typed, so new code is still flagged
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-explicit-any': 'error',
'prefer-const': ['error', { destructuring: 'all' }],
'@typescript-eslint/no-unused-vars': [
'error',
Expand Down
31 changes: 18 additions & 13 deletions backend/repositories/auth/user-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,12 @@ export class UserRepository {
created_at: new Date(createdAt),
is_active: 1,
}
} catch (error: any) {
} catch (error) {
logger.error({ err: error }, 'Error creating user')
if (error.code === 'ER_DUP_ENTRY') {
const err = new Error('El nombre de usuario o email ya existe') as any
if ((error as { code?: string }).code === 'ER_DUP_ENTRY') {
const err = new Error('El nombre de usuario o email ya existe') as Error & {
code?: string
}
err.code = 'ER_DUP_ENTRY'
throw err
}
Expand Down Expand Up @@ -130,8 +132,8 @@ export class UserRepository {
try {
Validation.username(username)
Validation.password(password)
} catch (err: any) {
throw new Error(err.message, { cause: err })
} catch (err) {
throw new Error((err as Error).message, { cause: err })
}

const [rows] = await db.query<UserWithRole[]>(
Expand Down Expand Up @@ -397,19 +399,22 @@ export class UserRepository {
// Devolver el usuario completo actualizado
const updatedUser = await this.getById(id)
return updatedUser
} catch (error: any) {
} catch (error) {
await dbConnection.rollback()
logger.error({ err: error }, 'Error en update')

const message = (error as Error).message
const code = (error as { code?: string }).code

if (
error.message.includes('no encontrado') ||
error.message.includes('no válido') ||
error.message.includes('No hay campos')
message.includes('no encontrado') ||
message.includes('no válido') ||
message.includes('No hay campos')
) {
throw error
}

if (error.code === 'ER_DUP_ENTRY') {
if (code === 'ER_DUP_ENTRY') {
throw new Error('El nombre de usuario o email ya existe', { cause: error })
}

Expand Down Expand Up @@ -551,7 +556,7 @@ export class UserRepository {
}

return updatedUser
} catch (error: any) {
} catch (error) {
await dbConnection.rollback()
logger.error({ err: error }, 'Error en updateProfile')
throw error
Expand Down Expand Up @@ -613,7 +618,7 @@ export class UserRepository {
logger.info({ event: 'password_changed', userId }, '[AUTH] password changed')

return true
} catch (error: any) {
} catch (error) {
await dbConnection.rollback()
logger.error({ err: error }, 'Error en updatePassword')
throw error
Expand Down Expand Up @@ -646,7 +651,7 @@ export class UserRepository {
logger.info({ event: 'password_reset_by_admin', userId }, '[AUTH] password reset by admin')

return true
} catch (error: any) {
} catch (error) {
logger.error({ err: error }, 'Error en resetPassword')
throw error
}
Expand Down
42 changes: 32 additions & 10 deletions backend/scripts/checklist-report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,38 @@
import db from '../config/db.js'
import { getTodayMadrid } from '../config/date-utils.js'
import { printable } from '../services/text/control-chars.js'
import type { RowDataPacket } from 'mysql2'

const CHECKLIST_ID = process.argv[2] ?? 'cl-night-audit'
const HOTEL_DATE = process.argv[3] ?? getTodayMadrid()

const fmt = (d: any) =>
const fmt = (d: unknown) =>
d instanceof Date ? d.toISOString().slice(0, 19).replace('T', ' ') : String(d).slice(0, 19)

interface RunRow extends RowDataPacket {
id: number
checklist_id: string
hotel_date: string | Date
started_at: string | Date
}

interface StepRow extends RowDataPacket {
step_id: string
done: number
done_at: string | Date | null
done_by: string | null
}

interface CommentRow extends RowDataPacket {
step_id: string
comentario: string
created_at: string | Date
autor: string | null
}

async function main() {
// 1. Encontrar el run
const [runs] = await (db as any).query(
const [runs] = await db.query<RunRow[]>(
`SELECT id, checklist_id, hotel_date, started_at
FROM checklist_runs
WHERE checklist_id = ? AND DATE(hotel_date) = ? AND reset_at IS NULL
Expand All @@ -43,7 +65,7 @@ async function main() {
console.log(`\n=== ${run.checklist_id} | ${HOTEL_DATE} (run_id=${run.id}) ===\n`)

// 2. Tareas completadas
const [steps] = await (db as any).query(
const [steps] = await db.query<StepRow[]>(
`SELECT
css.step_id,
css.done,
Expand All @@ -56,12 +78,12 @@ async function main() {
[run.id]
)

const done = steps.filter((s: any) => s.done === 1)
const pending = steps.filter((s: any) => s.done !== 1)
const done = steps.filter((s) => s.done === 1)
const pending = steps.filter((s) => s.done !== 1)

console.log(`Completadas (${done.length}):`)
if (done.length) {
done.forEach((s: any) =>
done.forEach((s) =>
console.log(` ✓ ${s.step_id.padEnd(12)} ${fmt(s.done_at)} por: ${s.done_by ?? '—'}`)
)
} else {
Expand All @@ -70,13 +92,13 @@ async function main() {

console.log(`\nPendientes (${pending.length}):`)
if (pending.length) {
pending.forEach((s: any) => console.log(` · ${s.step_id}`))
pending.forEach((s) => console.log(` · ${s.step_id}`))
} else {
console.log(' (ninguna)')
}

// 3. Comentarios
const [comments] = await (db as any).query(
const [comments] = await db.query<CommentRow[]>(
`SELECT
csc.step_id,
csc.body AS comentario,
Expand All @@ -91,7 +113,7 @@ async function main() {

console.log(`\nComentarios (${comments.length}):`)
if (comments.length) {
comments.forEach((c: any) =>
comments.forEach((c) =>
console.log(
` [${fmt(c.created_at)}] ${c.step_id} | ${printable(c.autor ?? '—')}: ${printable(c.comentario)}`
)
Expand All @@ -104,6 +126,6 @@ async function main() {
main()
.then(() => process.exit(0))
.catch((e) => {
console.error(e.message)
console.error((e as Error).message)
process.exit(1)
})
2 changes: 1 addition & 1 deletion backend/services/cron/cron-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export class CronService {
*/
static async runNotificationsNow(): Promise<{
success: boolean
results?: any
results?: unknown
error?: string
duration?: number
}> {
Expand Down
30 changes: 20 additions & 10 deletions backend/tests/auth/demo-restriction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function buildReq(overrides: Partial<Request> = {}): Request {
headers: {},
body: {},
ip: '127.0.0.1',
socket: { remoteAddress: '127.0.0.1' } as any,
socket: { remoteAddress: '127.0.0.1' } as unknown,
get: () => undefined,
user: undefined,
...overrides,
Expand All @@ -47,8 +47,13 @@ beforeEach(() => {

// The public demo account (users.is_demo) is a real admin: only what is
// declared here or on the route is blocked
const DEMO_USER = { id: 'd1', username: 'demo', role: 'admin', isDemo: true } as any
const OWNER = { id: 'a1', username: 'admin', role: 'admin' } as any
const DEMO_USER = {
id: 'd1',
username: 'demo',
role: 'admin',
isDemo: true,
} as unknown as Request['user']
const OWNER = { id: 'a1', username: 'admin', role: 'admin' } as unknown as Request['user']

describe('demoRestriction — pass-through cases', () => {
it('lets through requests without an authenticated user (handled upstream)', () => {
Expand All @@ -61,7 +66,7 @@ describe('demoRestriction — pass-through cases', () => {

it('lets through any non-demo role (admin, recepcionista, etc.)', () => {
const req = buildReq({
user: { id: 'u1', role: 'admin' } as any,
user: { id: 'u1', role: 'admin' } as unknown as Request['user'],
method: 'DELETE',
originalUrl: '/api/parking/bookings/123',
})
Expand All @@ -85,8 +90,9 @@ describe('demoRestriction — blocked-attempt log', () => {
},
})
denyDemo(req, buildRes(), next)
const logged = (DemoActivityRepository.logActivity as any).mock.calls[0][0]
.body_preview as string
const logged = (
DemoActivityRepository.logActivity as unknown as { mock: { calls: unknown[][] } }
).mock.calls[0][0].body_preview as string
expect(logged).not.toMatch(/secret/)
expect(JSON.parse(logged)).toEqual({
currentPassword: '[REDACTED]',
Expand All @@ -105,7 +111,7 @@ describe('demoRestriction — demo account (is_demo)', () => {
user: DEMO_USER,
method: 'POST',
originalUrl: '/api/maintenance/1/images',
headers: { 'content-type': contentType } as any,
headers: { 'content-type': contentType } as unknown as Request['headers'],
})
const res = buildRes()
demoRestriction(req, res, next)
Expand All @@ -120,7 +126,7 @@ describe('demoRestriction — demo account (is_demo)', () => {
user: DEMO_USER,
method: 'DELETE',
originalUrl: '/api/parking/bookings/ABC',
headers: { 'content-type': 'application/json' } as any,
headers: { 'content-type': 'application/json' } as unknown as Request['headers'],
})
const res = buildRes()
demoRestriction(req, res, next)
Expand All @@ -131,7 +137,9 @@ describe('demoRestriction — demo account (is_demo)', () => {
const req = buildReq({
user: OWNER,
method: 'POST',
headers: { 'content-type': 'multipart/form-data; boundary=x' } as any,
headers: {
'content-type': 'multipart/form-data; boundary=x',
} as unknown as Request['headers'],
})
const res = buildRes()
demoRestriction(req, res, next)
Expand Down Expand Up @@ -190,7 +198,9 @@ describe('blocking a request without a JSON body', () => {
user: DEMO_USER,
method: 'POST',
body: undefined,
headers: { 'content-type': 'multipart/form-data; boundary=x' } as any,
headers: {
'content-type': 'multipart/form-data; boundary=x',
} as unknown as Request['headers'],
})
const res = buildRes()
demoRestriction(req, res, next)
Expand Down
5 changes: 3 additions & 2 deletions backend/tests/auth/user-repository-login.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import bcrypt from 'bcrypt'
import db from '../../config/db.js'
import { UserRepository } from '../../repositories/auth/user-repository.js'
import type { RowDataPacket } from 'mysql2'

const TEST_USERNAME = 'authtest_user_h115'
const TEST_PASSWORD = 'CorrectHorseBattery42!'
Expand Down Expand Up @@ -35,7 +36,7 @@ beforeAll(async () => {
)

// Usuario inactivo: reusa el demo si existe y is_active=0 (caso real tras H1-12).
const [rows] = await db.query<any[]>(
const [rows] = await db.query<RowDataPacket[]>(
`SELECT id FROM users WHERE username = 'demo' AND is_active = 0 LIMIT 1`
)
inactiveUserId = rows[0]?.id ?? null
Expand All @@ -57,7 +58,7 @@ describe('UserRepository.login — integration', () => {
expect(user.username).toBe(TEST_USERNAME)
expect(user.email).toBe(TEST_EMAIL)
expect(user.role).toBe('admin')
expect((user as any).password).toBeUndefined()
expect((user as Record<string, unknown>).password).toBeUndefined()
})

it('rejects with "Credenciales inválidas" when password is wrong', async () => {
Expand Down
Loading
Loading