parent
cf2ea84bc9
commit
b1c8d4f910
@ -7,6 +7,7 @@ const nextConfig: NextConfig = {
|
|||||||
NEXT_PUBLIC_API_URL: "https://api.app.witlab.us",
|
NEXT_PUBLIC_API_URL: "https://api.app.witlab.us",
|
||||||
// NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
|
// NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
|
||||||
NEXT_PUBLIC_APP_URL: "https://app.witlab.us",
|
NEXT_PUBLIC_APP_URL: "https://app.witlab.us",
|
||||||
|
NEXT_PUBLIC_AUTH_REDIRECT_URL: "https://witlab.us",
|
||||||
},
|
},
|
||||||
images: {
|
images: {
|
||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
|
|||||||
@ -1,8 +1,50 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import createMiddleware from "next-intl/middleware";
|
import createMiddleware from "next-intl/middleware";
|
||||||
|
|
||||||
import { routing } from "./i18n/routing";
|
import { routing } from "./i18n/routing";
|
||||||
|
import { ROUTES } from "./shared/constants/client-routes";
|
||||||
|
|
||||||
export default createMiddleware(routing);
|
const publicRoutes = [
|
||||||
|
ROUTES.authCallback(),
|
||||||
|
ROUTES.paymentSuccess(),
|
||||||
|
ROUTES.paymentFailed(),
|
||||||
|
];
|
||||||
|
|
||||||
|
function isPublicRoute(pathname: string): boolean {
|
||||||
|
const pathWithoutLocale = pathname.replace(/^\/[a-z]{2}\//, "/");
|
||||||
|
return publicRoutes.some(route => pathWithoutLocale.startsWith(route));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAuthMiddleware() {
|
||||||
|
return async function authMiddleware(request: NextRequest) {
|
||||||
|
const pathname = request.nextUrl.pathname;
|
||||||
|
|
||||||
|
if (isPublicRoute(pathname)) {
|
||||||
|
return NextResponse.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = request.cookies.get("accessToken")?.value;
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
return NextResponse.redirect(
|
||||||
|
process.env.NEXT_PUBLIC_AUTH_REDIRECT_URL || ""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.next();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const intlMiddleware = createMiddleware(routing);
|
||||||
|
|
||||||
|
export default async function middleware(request: NextRequest) {
|
||||||
|
const authResponse = await createAuthMiddleware()(request);
|
||||||
|
if (authResponse.status !== 200) {
|
||||||
|
return authResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
return intlMiddleware(request);
|
||||||
|
}
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
// Match all pathnames except for
|
// Match all pathnames except for
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { redirect } from "next/navigation";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { getServerAccessToken } from "../auth/token";
|
import { getServerAccessToken } from "../auth/token";
|
||||||
@ -18,6 +19,7 @@ type RequestOpts = Omit<RequestInit, "method" | "body"> & {
|
|||||||
query?: Record<string, unknown>; // query-string
|
query?: Record<string, unknown>; // query-string
|
||||||
schema?: z.ZodTypeAny; // runtime validation
|
schema?: z.ZodTypeAny; // runtime validation
|
||||||
revalidate?: number;
|
revalidate?: number;
|
||||||
|
skipAuthRedirect?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HttpClient {
|
class HttpClient {
|
||||||
@ -44,7 +46,14 @@ class HttpClient {
|
|||||||
body?: unknown,
|
body?: unknown,
|
||||||
errorMessage?: string
|
errorMessage?: string
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const { tags = [], schema, query, revalidate = 300, ...rest } = opts;
|
const {
|
||||||
|
tags = [],
|
||||||
|
schema,
|
||||||
|
query,
|
||||||
|
revalidate = 300,
|
||||||
|
skipAuthRedirect = false,
|
||||||
|
...rest
|
||||||
|
} = opts;
|
||||||
|
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
const accessToken = await getServerAccessToken();
|
const accessToken = await getServerAccessToken();
|
||||||
@ -61,7 +70,12 @@ class HttpClient {
|
|||||||
|
|
||||||
const payload = await res.json().catch(() => null);
|
const payload = await res.json().catch(() => null);
|
||||||
|
|
||||||
if (!res.ok) throw new ApiError(res.status, payload, errorMessage);
|
if (!res.ok) {
|
||||||
|
if (res.status === 401 && !skipAuthRedirect) {
|
||||||
|
redirect(process.env.NEXT_PUBLIC_AUTH_REDIRECT_URL || "");
|
||||||
|
}
|
||||||
|
throw new ApiError(res.status, payload, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
const data = payload as T;
|
const data = payload as T;
|
||||||
return schema ? schema.parse(data) : data;
|
return schema ? schema.parse(data) : data;
|
||||||
|
|||||||
@ -10,6 +10,9 @@ const createRoute = (segments: string[]): string => {
|
|||||||
export const ROUTES = {
|
export const ROUTES = {
|
||||||
home: () => createRoute([]),
|
home: () => createRoute([]),
|
||||||
|
|
||||||
|
// Auth
|
||||||
|
authCallback: () => createRoute(["auth", "callback"]),
|
||||||
|
|
||||||
// Breath
|
// Breath
|
||||||
breath: (id: string) => createRoute(["breath", id]),
|
breath: (id: string) => createRoute(["breath", id]),
|
||||||
breathResult: (id: string, resultId: string) =>
|
breathResult: (id: string, resultId: string) =>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user