All files / src middleware.ts

90.9% Statements 60/66
95.83% Branches 23/24
100% Functions 1/1
90.9% Lines 60/66

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97      1x 1x 1x     1x 1x             1x   1x 19x 19x   19x 19x 2x 2x   2x 1x 1x 1x 1x   1x 1x 2x   19x 1x 1x   18x 19x   3x 1x 1x   2x   3x 1x 1x 1x 1x 1x 1x 3x     19x 2x 2x 2x 2x 2x 2x     19x                 19x 2x 2x 2x 2x 2x 2x 2x   12x     19x 1x 1x   12x 12x  
import type { MiddlewareHandler } from 'astro';
 
// Constants
import { API_ROUTES, PRIVATE_ROUTES, ROUTES } from './constants/routes';
import { COOKIE_KEYS } from './constants/auth';
import { HTTP_HEADERS } from './constants/http';
 
// Utils
import { proxyRequest } from './utils/proxy';
import {
  checkPermission,
  findMatchedRoute,
  isTokenExpired,
} from './utils/auth';
 
// Services
import { getRefreshToken } from '@/services/auth';
 
export const onRequest: MiddlewareHandler = async (context, next) => {
  const user = await context.session?.get('user');
  const { pathname, search } = context.url;
 
  const tokenShared = context.cookies.get(COOKIE_KEYS.TOKEN_SHARED)?.value;
  if (!!tokenShared && !!user?.token && isTokenExpired(user?.token)) {
    const { data } = await getRefreshToken({ token: tokenShared });
    const token = data?.id_token || '';
 
    if (token) {
      const newUser = {
        ...user,
        token,
      };
 
      await context.session?.set('user', newUser);
    }
  }
 
  if (user && pathname === ROUTES.SIGN_IN) {
    return Response.redirect(new URL(ROUTES.HOME, context.url), 302);
  }
 
  const matchedPrivateRoute = findMatchedRoute(pathname, PRIVATE_ROUTES);
  if (!!matchedPrivateRoute) {
    // If user is not logged in → redirect to sign-in
    if (!user) {
      return Response.redirect(new URL(ROUTES.SIGN_IN, context.url), 302);
    }
 
    const isNoPermission = !checkPermission(user, matchedPrivateRoute);
    // If user is logged in but does not have the required role → return 404
    if (isNoPermission) {
      const res = await next(new URL(ROUTES.NOT_FOUND, context.url));
      return new Response(await res.text(), {
        status: 404,
        headers: res.headers,
      });
    }
  }
 
  // Proxy /rpc requests
  if (pathname.startsWith(API_ROUTES.RPC)) {
    const res = await proxyRequest(context, pathname + search);
    return new Response(await res.text(), {
      status: res.status,
      headers: res.headers,
    });
  }
 
  // Proxy /docs requests (e.g. export) stream body to keep binary (XLSX) intact.
  if (pathname.startsWith(API_ROUTES.DOCS)) {
    const res = await proxyRequest(context, pathname + search);
    return new Response(res.body, {
      status: res.status,
      headers: res.headers,
    });
  }
 
  // Proxy /ws requests (WebSocket)
  if (pathname.startsWith(API_ROUTES.WS)) {
    const res = await proxyRequest(context, pathname + search);
    return new Response(null, {
      status: res.status,
      headers: res.headers,
      ...((res as any).webSocket && { webSocket: (res as any).webSocket }),
    });
  }
 
  const response = await next();
 
  // Force back/forward navigation to refetch instead of showing a stale cached page.
  if (response.headers.get(HTTP_HEADERS.CONTENT_TYPE)?.includes('text/html')) {
    response.headers.set(HTTP_HEADERS.CACHE_CONTROL, 'no-store');
  }
 
  return response;
};