Files
PasswordManager/mssql-wrapper.js

105 lines
3.7 KiB
JavaScript
Raw Normal View History

2026-01-22 12:59:10 -05:00
// Vibe coded since I needed more resources on my VPS :/
const Database = require('better-sqlite3');
// 1. Connect to the file (creates it if missing)
const db = new Database('project.db');
db.pragma('journal_mode = WAL'); // Faster, safer writes
class FakeMssqlRequest {
constructor(db) {
this.db = db;
this.params = {}; // Stores your inputs: .input('id', 123)
}
// Mimic the .input() method
// We ignore 'type' because SQLite is loosely typed
input(name, type, value) {
// Handle case where value is passed as 2nd arg (omitting type)
if (value === undefined) value = type;
this.params[name] = value;
return this; // Return 'this' to allow chaining (.input().input())
}
// Mimic the .query() method
async query(sqlString) {
try {
let cleanSql = sqlString;
// --- FIX 1: Handle TOP -> LIMIT ---
// Regex detects: SELECT TOP (@amount) ... OR SELECT TOP 10 ...
// It captures the value (@amount or 10)
const topRegex = /SELECT\s+TOP\s*\(?(@?\w+)\)?/i;
const topMatch = cleanSql.match(topRegex);
if (topMatch) {
const limitValue = topMatch[1]; // Extracts '@amount' or '10'
// 1. Remove "TOP (@amount)" from the start
cleanSql = cleanSql.replace(topRegex, 'SELECT ');
// 2. Remove any trailing semicolon so we can append
cleanSql = cleanSql.replace(/;\s*$/, '');
// 3. Append LIMIT to the end
cleanSql += ` LIMIT ${limitValue}`;
}
// 1. Compatibility Fixes (Optional but helpful)
// Replace 'GETDATE()' with SQLite's "datetime('now')"
cleanSql = cleanSql.replace(/GETDATE\(\)/gi, "datetime('now', 'localtime')")
// MS SQL: SYSUTCDATETIME() -> SQLite: datetime('now') (Already UTC)
.replace(/SYSUTCDATETIME\(\)/gi, "datetime('now')")
// --- FIX 3: Clean up Square Brackets ---
.replace(/\[/g, '"').replace(/\]/g, '"')
// --- FIX 4: Handle String Concatenation in LIKE clauses ---
// MSSQL: LIKE '%' + @param + '%'
// SQLite: LIKE '%' || @param || '%'
// This regex looks for: LIKE '%' + @anything + '%'
.replace(/LIKE\s+'%'\s*\+\s*(@\w+)\s*\+\s*'%'/gi, "LIKE '%' || $1 || '%'");
// 2. Determine if it's a SELECT or INSERT/UPDATE
const stmt = this.db.prepare(cleanSql);
console.log(`Running:\n${cleanSql}`)
if (cleanSql.trim().toUpperCase().startsWith('SELECT')) {
// READ: Use .all()
const rows = stmt.all(this.params);
console.log(rows);
return { recordset: rows, rowsAffected: [rows.length] };
} else {
// WRITE: Use .run()
const info = stmt.run(this.params);
console.log(`else`)
return { recordset: [], rowsAffected: [info.changes] };
}
} catch (err) {
console.error("SQLite Error:", err.message);
console.error("Query was:", sqlString);
throw err;
}
}
}
// Mimic the main 'pool' object
const pool = {
connected: true,
request: () => new FakeMssqlRequest(db),
close: () => db.close()
};
module.exports = {
// Mimic the sql.connect() function
connect: async () => {
console.log("Connected to SQLite (via Wrapper)");
return pool;
},
// Export types to prevent "sql.Int is undefined" errors
Int: 'Int',
NVarChar: 'NVarChar',
Bit: 'Bit',
DateTime: 'DateTime'
};