fix: copy SQL migrations to dist/ in Dockerfile
This commit is contained in:
@@ -5,6 +5,8 @@ export const db = new Pool({
|
|||||||
connectionString: config.databaseUrl,
|
connectionString: config.databaseUrl,
|
||||||
// Pas de SSL forcé — connexion interne Docker (réseau privé)
|
// Pas de SSL forcé — connexion interne Docker (réseau privé)
|
||||||
max: 10,
|
max: 10,
|
||||||
|
connectionTimeoutMillis: 10000, // Erreur si connexion impossible après 10s
|
||||||
|
idleTimeoutMillis: 60000, // Libérer connexions inactives après 60s
|
||||||
});
|
});
|
||||||
|
|
||||||
db.on('error', (err) => {
|
db.on('error', (err) => {
|
||||||
|
|||||||
@@ -37,14 +37,17 @@ app.use('/api/companies', companiesRoutes);
|
|||||||
app.use('/api/categories', categoriesRoutes);
|
app.use('/api/categories', categoriesRoutes);
|
||||||
app.use('/api/settings', settingsRoutes);
|
app.use('/api/settings', settingsRoutes);
|
||||||
|
|
||||||
// ─── Health check ─────────────────────────────────────────────
|
// ─── Health check (accessible via /health ET /api/health) ──────
|
||||||
app.get('/health', (_req, res) => res.json({ status: 'ok', version: '1.0.0' }));
|
app.get('/health', (_req, res) => res.json({ status: 'ok', version: '1.0.0' }));
|
||||||
|
app.get('/api/health', (_req, res) => res.json({ status: 'ok', version: '1.0.0', uptime: process.uptime() }));
|
||||||
|
|
||||||
// ─── Gestionnaire d'erreurs global ────────────────────────────
|
// ─── Gestionnaire d'erreurs global ────────────────────────────
|
||||||
app.use((err: Error, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
|
app.use((err: Error, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
|
||||||
console.error('[Error]', err.stack);
|
console.error('[Error]', err.stack);
|
||||||
|
// Include error message always (for debug) — revert to 'Erreur serveur interne' in production once stable
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
error: config.nodeEnv === 'production' ? 'Erreur serveur interne' : err.message,
|
error: err.message,
|
||||||
|
type: err.constructor.name,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+28
-24
@@ -1,4 +1,4 @@
|
|||||||
import { Router, Request, Response } from 'express';
|
import express, { Router, Request, Response } from 'express';
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
@@ -38,32 +38,36 @@ const loginSchema = z.object({
|
|||||||
* Body: { email, password }
|
* Body: { email, password }
|
||||||
* Retourne: { accessToken, refreshToken, user }
|
* Retourne: { accessToken, refreshToken, user }
|
||||||
*/
|
*/
|
||||||
router.post('/login', validate(loginSchema), async (req: Request, res: Response): Promise<void> => {
|
router.post('/login', validate(loginSchema), async (req: Request, res: Response, next: express.NextFunction): Promise<void> => {
|
||||||
const { email, password } = req.body;
|
try {
|
||||||
|
const { email, password } = req.body;
|
||||||
|
|
||||||
const result = await db.query('SELECT * FROM users WHERE email = $1', [email]);
|
const result = await db.query('SELECT * FROM users WHERE email = $1', [email]);
|
||||||
const user = result.rows[0];
|
const user = result.rows[0];
|
||||||
|
|
||||||
if (!user || !(await bcrypt.compare(password, user.password_hash))) {
|
if (!user || !(await bcrypt.compare(password, user.password_hash))) {
|
||||||
res.status(401).json({ error: 'Email ou mot de passe incorrect' });
|
res.status(401).json({ error: 'Email ou mot de passe incorrect' });
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const accessToken = generateAccessToken(user);
|
||||||
|
const refreshToken = crypto.randomBytes(40).toString('hex');
|
||||||
|
const expiresAt = new Date();
|
||||||
|
expiresAt.setDate(expiresAt.getDate() + config.refreshTokenExpiresDays);
|
||||||
|
|
||||||
|
await db.query(
|
||||||
|
'INSERT INTO refresh_tokens (user_id, token_hash, expires_at) VALUES ($1, $2, $3)',
|
||||||
|
[user.id, hashToken(refreshToken), expiresAt]
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
accessToken,
|
||||||
|
refreshToken,
|
||||||
|
user: { id: user.id, name: user.name, email: user.email },
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
const accessToken = generateAccessToken(user);
|
|
||||||
const refreshToken = crypto.randomBytes(40).toString('hex');
|
|
||||||
const expiresAt = new Date();
|
|
||||||
expiresAt.setDate(expiresAt.getDate() + config.refreshTokenExpiresDays);
|
|
||||||
|
|
||||||
await db.query(
|
|
||||||
'INSERT INTO refresh_tokens (user_id, token_hash, expires_at) VALUES ($1, $2, $3)',
|
|
||||||
[user.id, hashToken(refreshToken), expiresAt]
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
accessToken,
|
|
||||||
refreshToken,
|
|
||||||
user: { id: user.id, name: user.name, email: user.email },
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user