Vous avez dit :
THIS FILE : /home/project/server/src/middleware/validator.ts : // server/src/middleware/validator.ts
import { Request, Response, NextFunction, RequestHandler } from 'express';
import {
  body,
  param,
  query,
  validationResult,
  ValidationChain,
} from 'express-validator';
import supabaseAdmin from '../config/supabaseClient';

// 0) Central validation-error handler
export const handleValidationErrors: RequestHandler = (req, res, next) => {
  const errs = validationResult(req);
  if (!errs.isEmpty()) {
    res.status(400).json({ errors: errs.array() });
    return;
  }
  next();
};

// 1) Cast the ValidationChain to RequestHandler
export const checkProfileIdFormat = body('profileId')
  .isUUID()
  .withMessage('profileId must be a valid UUID') as unknown as RequestHandler;

// 2) Your async ownership check, returning void
export const assertProfileOwnership: RequestHandler = async (
  req: Request,
  res: Response,
  next: NextFunction
): Promise<void> => {

  // validationResult from the previous check()
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    res.status(400).json({ errors: errors.array() });
    return;
  }

  const authHeader = req.headers.authorization;
  if (!authHeader) {
    res.status(401).json({ message: 'Unauthorized: Missing Authorization header' });
    return;
  }

  const token = authHeader.split(' ')[1]; // Bearer <token>

  try {
    const {
      data: { user },
      error: authError,
    } = await supabaseAdmin.auth.getUser(token);

    if (authError || !user) {
      console.error('JWT verification failed:', authError);
      res.status(401).json({ message: 'Unauthorized: Invalid token' });
      return;
    }

    if (req.body.profileId !== user.id) {
      res.status(403).json({ message: 'Unauthorized: profileId does not match user' });
      return;
    }

    // attach for downstream
    (req as any).user = user;
    next();
  } catch (err) {
    console.error('Error during JWT verification:', err);
    res.status(500).json({ message: 'Internal server error' });
    return;
  }
};

// 3) Export as RequestHandler[]
export const validateProfileId = [
  checkProfileIdFormat,
  assertProfileOwnership,
];

// Validation middleware for /api/auth/callback
export const authCallbackValidation = [
  query('code')
    .notEmpty()
    .withMessage('Authorization code is required')
    .trim()
    .escape(),
];

// Validation middleware for /api/fireblocks/profiles/:id/activate
export const activateProfileValidation = [
  param('id').isUUID().withMessage('Invalid profile ID format').trim().escape(),
];

// New comprehensive validation middleware
export const validateProfileData: ValidationChain[] = [
  body('firstName')
    .isString()
    .withMessage('First name must be a string')
    .trim()
    .isLength({ min: 2, max: 30 })
    .withMessage('First name must be between 2 and 50 characters')
    .notEmpty()
    .withMessage('First name is required'),
    .bail(),

  body('lastName')
    .isString()
    .withMessage('Last name must be a string')
    .trim()
    .isLength({ min: 2, max: 30 })
    .withMessage('Last name must be between 2 and 50 characters')
    .notEmpty()
    .withMessage('Last name is required'),
    .bail(),

  body('email')
    .isEmail()
    .withMessage('Invalid email address')
    .normalizeEmail()
    .notEmpty()
    .withMessage('Email is required'),
    .bail(),

  body('phoneNumber')
    .isMobilePhone()
    .withMessage('Phone number must be valid')
    .optional(),

  body('profileId')
    .isUUID()
    .withMessage('profileId must be a valid UUID'),
];
  
   // Validation errors handling as a separate middleware
  export const handleProfileDataValidationErrors = (req: Request, res: Response, next: NextFunction) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ errors: errors.array() });
      return;
    }
    next();
  };          &&&&&&&&&&&&&& this file : /home/project/server/src/app.ts : import express, { Request, Response, NextFunction } from 'express';
import cors from 'cors';
import session from 'express-session';
import config from './config';
import authCodeAuth, { ensureAuthenticated, refreshIfNeeded } from './authCodeAuth';
import cron from 'node-cron';
import { snapshotBalances } from './services/snapshotService';

import generateMandat from './generate-mandat';
import getMandatUrl from './get-mandat-url';
import sendEnvelope from './send-envelope';
import generateDic from './generate-dic';
import generateDer from './generate-der';
import generateQuestionnaireClientPhysique from './generate-questionnaire-client-physique';
import fireblocksRoutes from './routes/fireblocks';
import fireblocksWebhook from './routes/fireblocksWebhook';
import docusignWebhook from './routes/docusignWebhook';
import { validateProfileId, authCallbackValidation, validateProfileData,  handleValidationErrors, handleProfileDataValidationErrors } from './middleware/validator';
import sanitizeRoute from './routes/sanitizeRoute';



const app = express();

// Request logger: log every incoming request
app.use((req, res, next) => {
  console.log([${new Date().toISOString()}] ${req.method} ${req.originalUrl});
  next();
});

// webhook docusign 
app.use(
  '/webhook/docusign',
  express.raw({ type: 'application/json' }),
  docusignWebhook
);


// 1) Enable CORS so the front‐end can send cookies and JSON
app.use(
  cors({
    origin: 'http://localhost:5175',
    credentials: true,
  })
);

// 2) Parse incoming JSON
app.use(express.json());

// 3) Session middleware (stores DocuSign tokens in a secure cookie)
app.use(
  session({
    secret: config.sessionSecret,
    resave: false,
    saveUninitialized: false,
    cookie: {
      secure: false,    // set to true if you serve over HTTPS
      sameSite: 'lax',  // allow cross‐site on top‐level navigations
    },
  })
);

// 4) Mount the OAuth routes under /api/auth
app.use(
  '/api/auth',
  (req, res, next) => {
    next();
  },
  authCodeAuth
);

// 5) Questionnaire endpoint
app.post(
  '/api/generate-questionnaire-client-physique',
  (req, res, next) => {
    console.log([Handler] POST /api/generate-questionnaire-client-physique);
    next();
  },
  generateQuestionnaireClientPhysique
);

// 6) DocuSign / mandate endpoints
app.post(
  '/api/generate-mandat',
  (req, res, next) => {
    console.log([Handler] POST /api/generate-mandat);
    next();
  },
  generateMandat
);

app.get(
  '/api/get-mandat-url',
  (req, res, next) => {
    console.log([Handler] GET /api/get-mandat-url);
    next();
  },
  getMandatUrl
);

// 7) protect send-envelope with OAuth + validation
app.post(
  '/api/send-envelope',
  ensureAuthenticated,
  refreshIfNeeded,
  ...validateProfileId,
  sendEnvelope
);

// 8) DIC & DER endpoints
app.post(
  '/api/generate-dic',
  (req, res, next) => {
    console.log([Handler] POST /api/generate-dic);
    next();
  },
  generateDic
);
app.post(
  '/api/generate-der',
  (req, res, next) => {
    console.log([Handler] POST /api/generate-der);
    next();
  },
  generateDer
);

// 9) Fireblocks balance and activation endpoints use JSON
app.use(
  '/api/fireblocks',
  (req, res, next) => {
    console.log([Handler] USE /api/fireblocks);
    next();
  },
  fireblocksRoutes
);

// 10) Fireblocks webhooks require raw body for signature verification
app.use(
  '/webhook/fireblocks',
  (req, res, next) => {
    console.log([Handler] USE /webhook/fireblocks);
    next();
  },
  express.raw({ type: 'application/json' }),
  fireblocksWebhook
);

// Helper function to build the callback URL
function getCallbackUrl(req: Request): string {
    const configuredRedirect = process.env.DOCUSIGN_REDIRECT_URI; // optional override
    if (configuredRedirect) {
        return configuredRedirect;
    }
    const host = req.get('host')!;
    const protocol = req.protocol;
    return ${protocol}://${host}/api/auth/callback;
}

// 11) Apply validation middleware to /api/auth/callback
app.get(
   '/api/auth/callback',
   authCallbackValidation,
   handleValidationErrors,
   async (req: Request, res: Response) => {
  console.log([Handler] GET /api/auth/callback - query:, req.query);
  const code = req.query.code;
  if (!code || typeof code !== 'string') {
    console.error('[Auth] Missing or invalid authorization code');
    res.status(400).send('Missing or invalid authorization code');
    return;
  }

  try {
    const callbackUrl = getCallbackUrl(req);
    console.log('[Auth] Exchanging code for tokens with callback URL:', callbackUrl);

    const tokenResponse = await fetch(https://${config.oauthBasePath}/oauth/token, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        grant_type:    'authorization_code',
        code,
        redirect_uri:  callbackUrl,
        client_id:     config.integratorKey,
        client_secret: config.clientSecret,
      }).toString(),
    });

    console.log('[Auth] Token endpoint status:', tokenResponse.status);
    if (!tokenResponse.ok) {
      const errText = await tokenResponse.text();
      console.error('[Auth] Token exchange failed:', errText);
      res.status(500).send('Token exchange failed');
      return;
    }

    const { access_token, refresh_token, expires_in } = (await tokenResponse.json()) as {
      access_token:  string;
      refresh_token: string;
      expires_in:    number;
    };

    console.log('[Auth] Received tokens. Storing in session.');
    const sess = req.session as any;
    sess.accessToken          = access_token;
    sess.refreshToken         = refresh_token;
    sess.accessTokenExpiresAt = Date.now() + expires_in * 1000;

    // Redirect back into your React app
    const target = ${config.clientUrl}/sign;
    console.log('[Auth] Token stored. Redirecting to React app at', target);
    res.redirect(target);
    return;

  } catch (err) {
    console.error('[Auth] Callback error:', err);
    res.status(500).send('Authentication callback error');
    return;
  }
});

// Use the sanitize route
app.use('/api/sanitize', sanitizeRoute);

// 404 handler
app.use((req: Request, res: Response, next: NextFunction) => {
  console.log([Handler] 404 for ${req.method} ${req.originalUrl});
  res.status(404).json({ error: 'Not found' });
});

// Error handler
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
  console.error(
    [Error] Unhandled error on ${req.method} ${req.originalUrl}:,
    err
  );
  res.status(500).json({ error: 'Internal server error' });
});

// POST endpoint for profile update with validation chain
app.post(
  '/api/update-profile',
  // Apply validation chains separately
  ...validateProfileId,  // First, use profile ID validation
  validateProfileData,

  // Handle validation errors
  handleProfileDataValidationErrors,// Error handling for validation

  // Actual route handler after validation passes
  (req: Request, res: Response) => {
    // Handle the profile update if validation passed
    res.status(200).json({ message: 'Profile updated successfully' });
    return;
  }
);

export default app;

// Schedule balance snapshots every minute
cron.schedule('* * * * *', () => {
  console.log('[Cron] snapshotBalances running');
  snapshotBalances().catch(err =>
    console.error('Error during scheduled balance snapshot:', err)
  );
});
          ARE GIVING ME THESE ERRORS :⨯ Unable to compile TypeScript:
src/middleware/validator.ts(127,6): error TS2554: Expected 1-2 arguments, but got 0.