middleware.ts TypeError Cannot read properties of undefined (reading modules)

45 Views Asked by At

I'm messing around with user authentication for a small personal project.

I'm trying to protect certain routes using middleware in Next.js

middleware.ts:

import { Authentication } from '@/helpers/auth';

import { NextRequest, NextResponse } from "next/server";


// Invoked on each request to the server
export function middleware(request: NextRequest) {
    
    const auth = Authentication.getInstance();

    // If no session cookie exists
    if (!request.cookies.has(LOGINCOOKIENAME)) return NextResponse.redirect( new URL('/login', request.url));

    // Check Authentication
    
    const cookie = Number(request.cookies.get(LOGINCOOKIENAME)?.value);
    
    if (auth.isAuthenticated(cookie)) return NextResponse.redirect( new URL('/login', request.url));

    return;
}

// Include only selected routes
export const config = {
    matcher: [
        '/client',
        '/translator',
        '/manager'
    ]
};

I've narrowed down the error to the line const auth = Authentication.getInstance();.

auth.ts:

import { Database } from "./SQL";

type ID = Number | undefined;

export enum AuthLevel {
    manager,
    translator,
    client
};

export enum LoginCodes {
    success,
    failure,
    username_or_password_incorrect
}

export interface Session {
    userID: ID;
    level: AuthLevel;
}

export class Authentication {
    private sessions: Session[];
    private static instance: Authentication;

    constructor() {
        this.sessions = new Array<Session>();
    }

    public static getInstance(): Authentication {
        if (!this.instance) {
            this.instance = new Authentication();
        }

        return this.instance;
    }

    public isAuthenticated(userID: ID): boolean {
        // Check if session exists already
        for (let session of this.sessions) {
            if (session.userID === userID) {
                return true;
            }
        }
        
        return false;
    }

    // Inputs will already be validated
    public async login(username: string, password: string) {
        const database: Database = Database.getInstance();
        
        const results = await database.query('SELECT * FROM users WHERE username=? AND password=?', [username, password]);

        // No matching users
        if (results.length === 0) {
            return {
                success: false,
                message: LoginCodes.username_or_password_incorrect,
            }
        }

        return {
            success: true,
            LoginCode: LoginCodes.success,
            session: {
                userID: 0,
                AuthLevel: AuthLevel.manager
            }
        };
    }
}

Following down the rabbit hole, the line const database: Database = Database.getInstance(); in Authentication::login() is causing problems.

SQL.ts (file containing the Database class):

import sqlite3 from "sqlite3";
import fs from 'fs';

const DBPATH: string = './database.db'


export class Database {
    private DB: sqlite3.Database;
    private static instance: Database;

    constructor() {
        this.DB = this.openConnection(DBPATH);
    }

    private openConnection(pathToDatabase: string): sqlite3.Database {
        return new sqlite3.Database(pathToDatabase, sqlite3.OPEN_READWRITE, (err) => {
            if (err?.message == "SQLITE_CANTOPEN: unable to open database file") {
                // Create new file
                const newFile = fs.createWriteStream(pathToDatabase);
                newFile.end();

                return this.openConnection(pathToDatabase);
            }

            if (err) return console.error(err.message);
        });
    }

    public static getInstance(): Database {
        if(!this.instance) {
            this.instance = new Database();
        }

        return Database.instance;
    }

    public async statement(sql: string, params?: any) {
        this.DB.run(sql, params, (err) => {
            console.error(err?.message);
        });
    }

    public async query(sql: string, params?: any): Promise<Array<any>> {
        const that = this;
        return new Promise((resolve, reject) => {
            that.DB.all(sql, params, (err,rows) => {
               if(err){ return reject(err); }
               resolve(rows);
            });
        });
    }
}

The error I'm getting is:

TypeError: Cannot read properties of undefined (reading 'modules')

This error happened while generating the page. Any console logs will be displayed in the terminal window.
Call Stack
<unknown>
webpack-internal:///(middleware)/./node_modules/bindings/bindings.js (17)
modules
node_modules\bindings\bindings.js (29:23)
(middleware)/./node_modules/bindings/bindings.js
file:///C:/Users/Mazec/Documents/GitHub/translation-suite-nextjs/.next/server/src/middleware.js (95:1)
__webpack_require__
file:///C:/Users/Mazec/Documents/GitHub/translation-suite-nextjs/.next/server/edge-runtime-webpack.js (37:33)
fn
file:///C:/Users/Mazec/Documents/GitHub/translation-suite-nextjs/.next/server/edge-runtime-webpack.js (268:21)
require
node_modules\sqlite3\lib\sqlite3-binding.js (1:17)
(middleware)/./node_modules/sqlite3/lib/sqlite3-binding.js
file:///C:/Users/Mazec/Documents/GitHub/translation-suite-nextjs/.next/server/src/middleware.js (623:1)
__webpack_require__
file:///C:/Users/Mazec/Documents/GitHub/translation-suite-nextjs/.next/server/edge-runtime-webpack.js (37:33)
fn
file:///C:/Users/Mazec/Documents/GitHub/translation-suite-nextjs/.next/server/edge-runtime-webpack.js (268:21)
require
node_modules\sqlite3\lib\sqlite3.js (2:16)
(middleware)/./node_modules/sqlite3/lib/sqlite3.js
file:///C:/Users/Mazec/Documents/GitHub/translation-suite-nextjs/.next/server/src/middleware.js (634:1)

Removing the line const database: Database = Database.getInstance(); causes the middleware to redirect back to /login as expected.

I've done some Googling but cannot for the life of me figure out where I'm going wrong. Any nudge in the correct direction would be greatly appreciated. Thanks.

0

There are 0 best solutions below