0

I am developing a system that requires me to query an SQL database, it is located on the local network but NOT on the same system (not SQLExpress.)

I have managed to query a user for input on the webpage and send that information to Main.JS, however I am not sure on the most effective way to query the database.

I'm very new to both SQL and electron.js, so lots of detail would be preferred, thank you!

Following other Stack Overflow questions (notably), I have:

preload.js:

const invoke = (channel, args, cb = () => { return }) => {
    ipcRenderer.invoke(channel, args).then((res) => {
        cb(res);
    });
};

const handle = (channel, cb) => {
    ipcRenderer.on(channel, function (Event, message) {
        cb(Event, message);
    });
};

contextBridge.exposeInMainWorld("GlobalApi", {
    invoke: invoke,
    handle: handle
});

let get = function (path) {
    let data = dbFuncions.readSomeDatafromDB("path");
    return data; // Returning the function itself is a no-no shown below
    // return dbFunctions.readSomeDatafromDB("path"); Don't do this
}
contextBridge.exposeInMainWorld("myCoolApi", {
    get: get
});

renderer.js:

const { ipcRenderer } = require('electron');
const loginForm = document.getElementById('login-form');
const usernameInput = document.getElementById('username');
const passwordInput = document.getElementById('password');

loginForm.addEventListener('submit', (event) => {
    event.preventDefault();

    const username = usernameInput.value;
    const password = passwordInput.value;

    // This runs when the user submits their username and password.
    ipcRenderer.send('login', { username, password });
});


// This runs when the username and password has been checked (in main.js) and a success (true) or failure (false) has been recieved.
ipcRenderer.on('login-status', (event, isSuccess) => {
    const failureMessage = document.createElement('p');
    const successMessage = document.createElement('p');
    if (isSuccess) {
        successMessage.textContent = 'Login successful';
        successMessage.style.color = 'green';
        loginForm.appendChild(successMessage);
        setTimeout(() => {
            loginForm.removeChild(successMessage);
        }, 2000);
    } else {
        failureMessage.textContent = 'Incorrect details.';
        failureMessage.style.color = 'red';
        loginForm.appendChild(failureMessage);
        setTimeout(() => {
            loginForm.removeChild(failureMessage);
        }, 2000);
    }
});

main.js:

const { app, BrowserWindow, ipcMain } = require('electron');
var Connection = require('tedious').Connection;
var Request = require('tedious').Request
var TYPES = require('tedious').TYPES;
var dbFunctions = require('sql') //I know I'm missing a function here. The 'sql module' mentioned in the Stack Overflow 
const path = require('path');
let mainWindow;

var config = {
    server: 'your_server.database.windows.net',  //update me
    authentication: {
        type: 'default',
        options: {
            userName: 'your_username', //update me
            password: 'your_password'  //update me
        }
    },
    options: {
        // If modifying to work with Microsoft Azure (which I'm not), you need encryption:
        encrypt: true,
        database: 'your_database'  //update me
    }
};  
var connection = new Connection(config);
connection.on('connect', function (err) {
    // If no error, then good to proceed.  
    console.log("Connected");
    executeStatementLogin();
});

function executeStatementLogin() {
    // This needs updating!
    var request = new Request("SELECT username, password FROM table_name WHERE username = " + , function (err) {
        if (err) {
            console.log(err);
        }
    });
    // This needs updating!
    request.addParameter('Name', TYPES.NVarChar, 'SQL Server Express 2019');
    request.addParameter('Number', TYPES.NVarChar, 'SQLEXPRESS2019');
    request.addParameter('Cost', TYPES.Int, 11);
    request.addParameter('Price', TYPES.Int, 11);
    request.on('row', function (columns) {
        columns.forEach(function (column) {
            if (column.value === null) {
                console.log('NULL');
            } else {
                console.log("Product id of inserted item is " + column.value);
            }
        });
    });

    // Close the connection after the final event emitted by the request, after the callback passes
    request.on("requestCompleted", function (rowCount, more) {
        connection.close();
    });
}
function createWindow() {
    mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false,
            enableWebSQL: true,
            preload: path.join(__dirname, 'preload.js')
        },
    });

    mainWindow.loadFile(path.join(__dirname, 'index.html'));

    mainWindow.on('closed', () => {
        mainWindow = null;
    });
}

app.on('ready', () => {
    createWindow();
});

app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

// This is old code that I made. This runs when the user clicks submit and has username and password. This could be modified to do the SQL query, too
ipcMain.on('login', (event, loginData) => {
    const { username, password } = loginData;

    // Here, you can perform the necessary login verification logic
    // For demonstration purposes, we'll just log the username and password
    console.log(`Received login request. Username: ${username}, Password: ${password}`);

    let data = dbFunctions.get(path);

    window.webContents.send(
        path,
        data

    );
    if (username == 'exampletest' && password == 'testexample') { //Hardcoded solution to test if ifSuccess works and the result gets sent back (which it does)
        isSuccess = true
    } else {
        isSuccess = false
    }
    

    setTimeout(() => {
        event.reply('login-status', isSuccess);
    }, 2000);
});

ipcMain.on('login', (event, loginData) => is old code that I made. It has both the username and the password the user inputted already. This could potentially be modified to perform the SQL, by creating the SQL query there and executing this.

Alternatively, modifying the solution I found here which is all of preload.js and config, connection, types, dbFunctions, request and executeStatementLogin in main.js.

When I run the program, it loads, however on submit a runtime error is given. Both solutions run at the same time. My solution works fine (but does not implement SQL), and the alternative solution throws an error that dbFunctions does not have a 'get' function. This is because I do not know what the SQL module used to actually GET dbFunctions is, as it is seemingly never specified. Once I have dbFunctions, I can start to modify the code that needs to be updated (//update me), though I am not sure how I am going to modify the username and password in the config file based on user input.

Any help at all would be appreciated. Thank you! (assume I know nothing)

addyHyena
  • 56
  • 6

1 Answers1

1

There was already a similar question here: Electron Secure Mysql Credentials

Also, you should never ever embed credentials into the electron application, as it is absolutely insecure. You app should not be calling SQL backend directly.

  • The linked question is similar, but again, does not answer my question. The final result will not have credentials embedded, it's testing code that I implemented to make sure the success and failure states worked properly. If I am not calling SQL backend directly, what should I do? – addyHyena Jun 12 '23 at 11:12
  • 1
    you should write an api that does the sql calls, and you can secure this api using oauth – Davy Jun 12 '23 at 11:16
  • Thank you Davy, I'll look into it. – addyHyena Jun 12 '23 at 11:43