opral / inlang-paraglide-js

Tree-shakable i18n library build on the inlang ecosystem.
https://inlang.com/m/gerre34r/library-inlang-paraglideJs
22 stars 0 forks source link

Paraglide-Next: Redirects cause middleware to throw #138

Closed LorisSigrist closed 1 month ago

roderik commented 1 month ago
    "@inlang/paraglide-next": "0.3.1",

This is the middleware

import { graphql } from "@/lib/gql";
import { middleware as i18nMiddleware, redirect } from "@/lib/i18n";
import gqlRequest from "graphql-request";
import type { NextRequest } from "next/server";
import { pathToRegexp } from "path-to-regexp";
import type { Address } from "viem";
import { auth } from "./lib/auth";

const precomputePathRegex = (patterns: (RegExp | string)[]) => {
  return patterns.map((pattern) => (pattern instanceof RegExp ? pattern : pathToRegexp(pattern)));
};

const buildRouteMatcher = (routes: (RegExp | string)[]) => {
  const matchers = precomputePathRegex(routes);
  return (request: NextRequest) => matchers.some((matcher) => matcher.test(request.nextUrl.pathname));
};

const isAuthRoute = buildRouteMatcher(["/auth", "/auth/(.*)"]);
const isWeb3AuthRoute = buildRouteMatcher(["/auth/web3", "/auth/web3/(.*)"]);
const isPolygonIdAuthRoute = buildRouteMatcher(["/auth/polygonid", "/auth/polygonid/(.*)"]);
const isPolygonIdConnectAuthRoute = buildRouteMatcher(["/auth/polygonid/connect"]);
const isPolygonIdRegisterAuthRoute = buildRouteMatcher(["/auth/polygonid/register"]);
const isPolygonIdHoldAuthRoute = buildRouteMatcher(["/auth/polygonid/hold"]);
const isPolygonIdRequestAuthRoute = buildRouteMatcher(["/auth/polygonid/request"]);

const CheckForVerifiedCredential = graphql(/* GraphQL */ `
  query CheckForVerifiedCredential($address: String!) {
    blockchain_address_by_pk(address: $address) {
      credentials_aggregate {
        aggregate {
          count
        }
      }
    }
  }
`);

const hasVerifiedCredential = async (address: Address) => {
  const response = await gqlRequest(
    process.env.HASURA_GRAPHQL || "",
    CheckForVerifiedCredential,
    {
      address,
    },
    {
      "x-hasura-admin-secret": process.env.HASURA_SECRET || "",
    },
  );
  return (response.blockchain_address_by_pk?.credentials_aggregate.aggregate?.count ?? 0) > 0;
};

const CheckExistingRegistration = graphql(/* GraphQL */ `
  query CheckExistingRegistration($did: String!, $address: String!){
    customer_registration_aggregate(where: {did: {_eq: $did}, address: {_eq: $address}}) {
      aggregate {
        count
      }
    }
  }
`);

const hasExistingRegistration = async (address: Address, did: string) => {
  const response = await gqlRequest(
    process.env.HASURA_GRAPHQL || "",
    CheckExistingRegistration,
    {
      address,
      did,
    },
    {
      "x-hasura-admin-secret": process.env.HASURA_SECRET || "",
    },
  );
  return (response?.customer_registration_aggregate?.aggregate?.count ?? 0) > 0;
};

// TODO
// const CheckIssuedCredential = graphql(/* GraphQL */ `
//   query CheckExistingRegistration($did: String!, $address: String!){
//     customer_registration_aggregate(where: {did: {_eq: $did}, address: {_eq: $address}}) {
//       aggregate {
//         count
//       }
//     }
//   }
// `);

// TODO
const hasIssuedCredential = async (did: string) => {
  // const response = await gqlRequest(
  //   process.env.HASURA_GRAPHQL || "",
  //   CheckExistingRegistration,
  //   {
  //     address,
  //     did,
  //   },
  //   {
  //     "x-hasura-admin-secret": process.env.HASURA_SECRET || "",
  //   },
  // );
  // return (response?.customer_registration_aggregate?.aggregate?.count ?? 0) > 0;

  return false;
};

export async function middleware(request: NextRequest) {
  const session = await auth();

  if (isAuthRoute(request)) {
    if (isWeb3AuthRoute(request)) {
      // we are on the web3 route, we only need to be here if we are not connected yet
      // we can get this from the session, if the address is set.
      if (session?.user.address) {
        if (await hasVerifiedCredential(session.user.address)) {
          // since we already did the KYC, the user can go to the customer section
          return redirect("/customer");
        }
        return redirect("/auth/polygonid");
      }
    } else {
      // this check is actually extra, but for clarity sake i want to be explicit
      if (isPolygonIdAuthRoute(request)) {
        // first, if address is not set, we should not be here at all
        if (!session?.user.address) {
          return redirect("/auth/web3");
        }

        // we are only need to be in this section if we have not kyced the user yet
        if (await hasVerifiedCredential(session.user.address)) {
          // since we already did the KYC, the user can go to the customer section
          return redirect("/customer");
        }

        if (isPolygonIdConnectAuthRoute(request)) {
          // if we have a did in the session, we are already connected in this section
          if (session?.user.did) {
            if (await hasIssuedCredential(session.user.did)) {
              return redirect("/auth/polygonid/request");
            }
            if (await hasExistingRegistration(session.user.address, session.user.did)) {
              // since we already did the KYC, the user can go to the customer section
              return redirect("/auth/polygonid/hold");
            }
            // no issued credential, and no registration, go to registration
            return redirect("/auth/polygonid/request");
          }
        } else {
          // the connect page handles redirection on its own, so we would end up on the register or request pages
          if (isPolygonIdRequestAuthRoute(request)) {
            // TODO
          } else {
            // we need a did to register or hold
            if (!session?.user.did) {
              return redirect("/auth/polygonid/connect");
            }
            if (isPolygonIdRegisterAuthRoute(request)) {
              if (await hasExistingRegistration(session.user.address, session.user.did)) {
                // if we are already registered, we can go to the hold page
                return redirect("/auth/polygonid/hold");
              }
            } else {
              if (isPolygonIdHoldAuthRoute(request)) {
                if (!(await hasExistingRegistration(session.user.address, session.user.did))) {
                  // we cannot be on the hold page if we are not registered
                  return redirect("/auth/polygonid/register");
                }
              }
            }
          }
        }

        // final option if we did not match anything else
        return redirect("/auth/polygonid/connect");
      }
    }

    // final option if we are in the root of auth
    return redirect("/auth/web3");
  }

  return i18nMiddleware(request);
}

export const config = {
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     */
    "/((?!api|_next/static|_next/image|favicon.ico).*)",
  ],
};