Next.js Discord

Discord Forum

Dev vs Production Build Issues

Unanswered
Jboncz posted this in #help-forum
Open in Discord
I am having some issues when I build and start my project that im not seeing in development. The endpoint is querying LDAP using some helper functions ive included in the snippet, typically they live in the lib directory but to isolate the issue I brought them into the .js file.

Ive done some troubleshooting and I can see everything getting executed. The client is connected, it just doesnt seem to return any additional data. Im unsure if there is some caching issue or something im just unaware of. thanks in advance!

21 Replies

import isimLdap_config from '@/configs/isimLdap_config';
import ldapjs from 'ldapjs';

async function generateLdapFilter(obj) {
    let filter = '';
    const keys = Object.keys(obj);

    for (const key of keys) {
        const value = obj[key];
        if (key !== 'operType') {
            if (typeof value === 'string') {
                filter += `(${key}=${value})`;
            }
            else if (Array.isArray(value)) {
                let subFilter = '';
                value.map((arrValue) => {
                    subFilter += `(${key}=${arrValue})`;
                });
                subFilter = `(|${subFilter})`;
                filter += subFilter;
            }
            else if (typeof value === 'object') {
                const subFilter = await generateLdapFilter(obj[key]);
                filter += subFilter;
            }
        }
    }
    //Handling missing opertype
    if (!obj.operType) {
        obj.operType = 'and';
    }

    if (obj.operType === 'and') {
        filter = `(&${filter})`;
    }
    else if (obj.operType === 'or') {
        filter = `(|${filter})`;
    }
    return filter;
}

async function bindClient(client) {
    return new Promise((resolve, reject) => {
        client.bind(isimLdap_config.uid, isimLdap_config.pwd, (err) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
}

async function unbindClient(client) {
    return new Promise((resolve, reject) => {
        client.unbind((err) => {
            if (err) {
                reject(err);
            }
            else {
                resolve();
            }
        });
    });
}

async function createClientAndBind() {
    const client = ldapjs.createClient({ url: isimLdap_config.url });
    await bindClient(client);
    return client;
}

function normalizeLdapObject(ldapObj) {
    if (!ldapObj) {
        return [];
    }

    return ldapObj.map(entry => {
        const response = { dn: entry.objectName };

        for (const attribute of entry.attributes) {
            const key = attribute.type.toLowerCase();
            response[key] = attribute.values.length === 1 ? attribute.values[0] : attribute.values;
        }

        return response;
    });
}

async function searchLdap(client, searchBase, filter, attributes) {
    return new Promise((resolve, reject) => {
        client.search(searchBase, { scope: 'subtree', filter, attributes }, (err, res) => {
            let entries = [];
            if (err) {
                reject(err);
            }
            res.on('searchEntry', (entry) => {
                entries.push(entry.pojo);
            });
            res.on('end', () => {
                resolve(entries);
            });
            res.on('error', (err) => {
                reject(err);
            });
        });
    });
}

export async function POST(request) {
    let requestJson, ldapFilter;
    try {
        requestJson = await request.json();
        console.log(requestJson)
        if (!requestJson.query) {
            return Response.json({ message: 'No query object' }, { status: 500 })
        }
        ldapFilter = await generateLdapFilter(requestJson.query)
        const client = await createClientAndBind()
        let searchResults = await searchLdap(client, isimLdap_config.ou_identity, ldapFilter, ['uid', 'title', 'bccostcenterdesc', 'givenname', 'sn', 'mail', 'employeetype', 'erglobalid'])
        searchResults = await normalizeLdapObject(searchResults);

        return Response.json({ ldapFilter: ldapFilter, results: searchResults }, { status: 200 })
    }
    catch (error) {
        return Response.json({ message: error.message }, { status: 501 })
    }
}
American Crow
```ts code ```
@American Crow ` ts code `
eh?
American Crow
^activates code highlighting in discord
for typescript in that case
Just slap it at the top of what I pasted in?
American Crow
yea just add a ts behind the first three `
Nice good to know! Thank you!
American Crow
you welcome otherwise people won't help. I have no idea about he actual issue though :x sorry
Yeah the get endpoint isnt being used. 🙂 Its just there to be there tbh.
Its the post endpoint that has the issue.
American Crow
Oh okay i just saw the GET and thought this might be it
No worries! I appreciate the responses regardless!
American Crow
And you do call that POST Endpont from a Client Component?
Yes, but I even tried it over postman just to make sure it wasnt an issue within the front end.
American Crow
Yea i c that was my last idea lets see if someone smarter can help
I appreciate it, you never know what someone has overlooked. I was having the issue last night, ive learned really early on to just sleep on it and look at it again the next day id say 60% of the time I realize what I was doing wrong, this isnt one of those times!
When I hit the endpoint in postman, or the fronend, I can see the ldap filter get generated, but it doesnt find results, which led me down the path to check if the client wasnt connecting, so I hard coded the connection parameters (which has been put back to an external file to obfuscate the credentials) and would get no results, when I made the query in dev it works without issue.

Theres no router caching for POST end points to my knowledge but it feels like there is something going wrong wit hthe cacheing
export async function POST(request) {
    let requestJson = await request.json();
    let time = new Date();
    async function bindToLDAP() {
        return new Promise((resolve, reject) => {
            const client = ldapjs.createClient({ url: 'ldap://myserver:389' });

            client.bind('cn=root', 'xyz123', (err) => {
                if (err) {
                    reject(err);
                } 
                else {
                    console.log('LDAP bind successful');
                    resolve(client);
                }
            });
        });
    }
    async function destroyClient(client) {
        return new Promise((resolve, reject) => {
            client.unbind((err) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve(client);
                }
            });
        });
    }
    async function searchLdap(client, searchBase, filter, attributes) {
        return new Promise((resolve, reject) => {
            client.search(searchBase, { scope: 'subtree', filter, attributes }, (err, res) => {
                let entries = [];
                if (err) {
                    reject(err);
                }
                res.on('searchEntry', (entry) => {
                    entries.push(entry.pojo);
                });
                res.on('end', () => {
                    resolve(entries);
                });
                res.on('error', (err) => {
                    reject(err);
                });
            });
        });
    }


    let client = await bindToLDAP();
    console.log(client.connected)
    let results = await searchLdap(client, 'ou=people,erglobalid=00000000000000000000,ou=org','(uid=userid)',['uid'])

    client = await destroyClient(client);
    console.log(client.connected)
    return Response.json({ message: time, results: results }, { status: 200 })
}


I went back to the basics and hard coded everything just in case. Getting output stating that the client is connected, then disconnected when I call destroyClient. In Dev I get results whereas in build version I don't. Date time is updated at every POST request to the url, so I can confidently say its not a cacheing issue.... At a loss :/
I am willing to pay to have someone help me get over this hurdle I'm facing. Ive tried reverting to as early as next v14.0 to see if there is an issue in the lastest release to no avail. I am trying to avoid adding this to external rest service in favor of using route handlers in nextjs.

If anyone has any ideas or wants to talk about payment for helping with the issue, feel free to reply 🙂