NextJs 14 middleware not executing every time

Here is a login page of my nextjs web app, however, every time I click on login button and make a login httpPOST request to my backend server and got a 200 ok response, when I try to redirect user to the ‘/market’ route page, it does not work, clicking login button does nothing besides keeps receiving 200 ok response and console logging the response object, it just does not redirect! I had to forced the browser to refresh then it redirects to ‘/market’ page properly, I have no idea what is wrong. I had some stuff defined in middleware, they work fine, but just in case, I will attach my code for middleware below.

Here is the full github repo if you wanna see it clearer: link

"use client";
import { Alert, Button, TextField } from '@mui/material'
import Link from 'next/link';
import React from 'react';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogTitle from '@mui/material/DialogTitle';
import { useState } from 'react';
import { useRouter } from "next/navigation";

const LoginPage = () => {
  const router = useRouter()
  const [open, setOpen] = useState(false);
  const [showError, setShowError] = useState(false)
  const [errorMessage, setErrorMessage] = useState("")

  const handleClickOpen = () => {
    setOpen(true);
  };

  const handleClose = () => {
    setOpen(false);
  }

  const hideErrorMsg = () => {
    setTimeout(() => {
      setShowError(false)
    }, 1500);
  }

  const [username, setUsername] = useState("")
  const [password, setPassword] = useState("")

  function handleUsername(e: React.ChangeEvent<HTMLInputElement>) {
    // console.log(e.target.value)
    setUsername(e.target.value)
  }

  function handlePassword(e: React.ChangeEvent<HTMLInputElement>) {
    // console.log(e.target.value)
    setPassword(e.target.value)
  }
  
  async function handleOnSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault()
    const response = await fetch("/api/login", {
      method: 'POST',
      headers: {
        "content-type": "application/json"
      },
      body: JSON.stringify({
        username: username,
        password: password
      })
    })
    // router.push('/market')
    if (response.ok) {
      console.log(response)
      router.push('/market')
      window.location.reload()
    }

    if (!response.ok) {
      const data = await response.json()
      const { message } = data
      setErrorMessage(message)
      setShowError(true)
      hideErrorMsg();
    }
  }

  return (
    <div className='bg-albion_color min-h-albion'>
      <div className='pt-20'>
        <h1 className='font-bold uppercase text-4xl text-center'>Login</h1>
        <form className='pt-5' onSubmit={handleOnSubmit}>
          <div className='flex justify-center'>
            <div className='flex flex-col'>
              <div className='flex items-center mb-10'>
                <label className='w-20 mr-2'>Username</label>
                <TextField id="filled-basic" label="username" variant="filled" color='success' sx={{ "& .MuiInputBase-input": { height: "10px" } }} onChange={handleUsername} />
              </div>
              <div className='flex items-center'>
                <label className='w-20 mr-2'>Password</label>
                <TextField id="filled-basic2" type='password' label="password" variant="filled" color='success' sx={{ "& .MuiInputBase-input": { height: "10px" } }} onChange={handlePassword} />
              </div>
              {
                showError &&
                <div className='mt-8'>
                  <Alert severity="error" variant='outlined'>{errorMessage}</Alert>
                </div>
              }
              <div className='mt-8 ml-24'>
                <Button className='font-semibold' variant="contained" type='submit'>Login</Button>
              </div>
              <div className='ml-52'>
                <Button className='text-black font-sans' onClick={handleClickOpen} sx={{ textTransform: "none" }}>Forgot password?</Button>
              </div>
              <div className='ml-52'>
                <Button className='text-black font-sans' sx={{ textTransform: "none" }}>
                  <Link href={"/sign-up"}>Create an account</Link>
                </Button>
              </div>
            </div>
          </div>
        </form>
      </div>
      <Dialog
        open={open}
        onClose={handleClose}
        aria-labelledby="alert-dialog-title"
        aria-describedby="alert-dialog-description"
      >
        <DialogTitle id="alert-dialog-title">
          {"Forgot your password? Really?"}
        </DialogTitle>
        <DialogContent>
          <DialogContentText id="alert-dialog-description">
            Too bad, I can't help you.
          </DialogContentText>
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose} autoFocus sx={{ fontFamily: "inherit" }}>
            OK Agree
          </Button>
        </DialogActions>
      </Dialog>
    </div>
  )
}

export default LoginPage

middleware.ts:

import { NextResponse } from 'next/server';
import { NextRequest } from 'next/server';
import { decrypt } from './lib';  // Assuming decrypt is a function in your lib

export async function middleware(request: NextRequest) {
    const path: string = request.nextUrl.pathname;
    
    // Check if the user has a valid token
    const tokenValid = await tokenCheck(request);

    // If accessing '/market' without a valid token, redirect to '/login'
    if (path === '/market' && !tokenValid) {
        return NextResponse.redirect(new URL('/login', request.url));
    }

    // If accessing '/login' with a valid token, redirect to '/market'
    if (path === '/login' && tokenValid) {
        return NextResponse.redirect(new URL('/market', request.url));
    }

    // Proceed as normal if no redirection is needed
    return NextResponse.next();
}

// Function to check if the session token is valid
async function tokenCheck(req: NextRequest): Promise<boolean> {
    const session = req.cookies.get("session")?.value;
    if (!session) {
        return false;  // No session cookie found
    }

    try {
        const cookie = await decrypt(session);  // Attempt to decrypt and validate the session
        console.log("Valid token found:", cookie);
        return true;
    } catch (error) {
        console.log("Invalid token:", error);
        return false;  // Token is either invalid or decryption failed
    }
}

// Middleware config to match specific paths
export const config = {
    matcher: ['/market', '/login'],  // Apply this middleware to the /market and /login paths
};

route.ts for ‘/api/login’ route:

'use server';
import { NextRequest, NextResponse } from 'next/server'
import { sql } from '@vercel/postgres'
import { compare } from 'bcrypt-ts'
import { loginJwt } from '@/lib'

interface loginRequestBody {
    username: string,
    password: string
}

export async function POST(req: NextRequest) {
    const body = await req.json()
    // console.log(body)
    if (!userDetailsCheck(body)) {
        return NextResponse.json({message: "Username and Password cannot be empty"}, {status: 406})
    }
    // note that this is unsafe, sending credentials over http post request is not a good approach,
    // however, implementing https server is not an option at this point.

    if (!await dbCredentialCheck(body)) {
        return NextResponse.json({message: "Incorrect password."}, {status: 401})
    }
    const res = NextResponse.json({message: 'Login successful'}, {status: 200});
    const cookie = await loginJwt(body)
    const expires = new Date(Date.now() + 60 * 60 * 1000 * 2)
    res.cookies.set("session", cookie, {expires: expires, httpOnly: true})
    return res
}

async function dbCredentialCheck(body: loginRequestBody) {
    const {username, password} = body
    const queryResult = await sql `select password from players where username=${username}`
    // get the password of given username from postgres
    if (queryResult.rowCount < 1 || queryResult.rows.length < 1) {
        // no query results, user does not exist, return false right away
        return false
    }
    // 
    const storedHash = queryResult.rows[0].password
    // compare hashed passwords we fetched from db with the plaintext password sent from front end,
    // again, not a good idea to send credentials over http request, should use https instead
    if (!await compare(password, storedHash)) {
        return false
    }
    return true
}

function userDetailsCheck(body: loginRequestBody) {
    const {username, password} = body
    return username && password
}

I had to add window.location.reload() after router.push(‘/market’) to force browser to refresh, then it will work, I don’t know what the problem is, can someone please help me? This issue also occurs when click on logout, logout button clears the cookies indeed, but if browser is not refreshed, then user can still access certain page despite the fact that they have no tokens since cookies have been cleared, whereas if browser is forced to refresh, this problem goes away

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật