fix: critical boolean compatibility for PostgreSQL/SQLite
Fixed boolean value handling differences between databases: - SQLite stores booleans as 0/1, PostgreSQL as true/false - Add formatBoolean() calls to critical queries that were failing Files fixed: - adminEvents.js: Fixed status filters and archive queries - adminDashboard.js: Fixed active/archived event counts - expirationChecker.js: Fixed expiration checking queries - dbCompat.js: Updated to avoid circular dependency Added migration 024 to: - Enable foreign keys for SQLite (PRAGMA foreign_keys = ON) - Document boolean compatibility requirements This fixes queries returning 0 results in SQLite when checking boolean columns like is_active, is_archived. Critical for proper event management and expiration handling. Note: 23 more boolean comparisons remain to be fixed in other files.
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Fix boolean compatibility issues between PostgreSQL and SQLite
|
||||
* This migration updates the database configuration and existing data
|
||||
*/
|
||||
|
||||
exports.up = async function(knex) {
|
||||
const isPostgres = knex.client.config.client === 'pg';
|
||||
|
||||
if (!isPostgres) {
|
||||
// Enable foreign keys for SQLite
|
||||
await knex.raw('PRAGMA foreign_keys = ON');
|
||||
|
||||
// Note: SQLite stores booleans as 0/1
|
||||
// No data migration needed as Knex handles this automatically
|
||||
// But queries must use formatBoolean() helper
|
||||
|
||||
console.log('SQLite boolean compatibility check:');
|
||||
console.log('- SQLite stores booleans as 0/1');
|
||||
console.log('- All boolean comparisons should use formatBoolean() helper');
|
||||
console.log('- Foreign keys enabled');
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
exports.down = async function(knex) {
|
||||
// No rollback needed
|
||||
return Promise.resolve();
|
||||
};
|
||||
@@ -2,6 +2,7 @@ const express = require('express');
|
||||
const { db } = require('../database/db');
|
||||
const { adminAuth } = require('../middleware/auth-enhanced-v2');
|
||||
const { sanitizeDays, addDateRangeCondition } = require('../utils/sqlSecurity');
|
||||
const { formatBoolean } = require('../utils/dbCompat');
|
||||
const router = express.Router();
|
||||
|
||||
// Get dashboard statistics
|
||||
@@ -9,8 +10,8 @@ router.get('/stats', adminAuth, async (req, res) => {
|
||||
try {
|
||||
// Get active events count
|
||||
const activeEvents = await db('events')
|
||||
.where('is_active', true)
|
||||
.where('is_archived', false)
|
||||
.where('is_active', formatBoolean(true))
|
||||
.where('is_archived', formatBoolean(false))
|
||||
.count('id as count')
|
||||
.first();
|
||||
|
||||
@@ -20,8 +21,8 @@ router.get('/stats', adminAuth, async (req, res) => {
|
||||
const now = new Date();
|
||||
|
||||
const expiringEvents = await db('events')
|
||||
.where('is_active', true)
|
||||
.where('is_archived', false)
|
||||
.where('is_active', formatBoolean(true))
|
||||
.where('is_archived', formatBoolean(false))
|
||||
.where('expires_at', '<=', sevenDaysFromNow.toISOString())
|
||||
.where('expires_at', '>', now.toISOString())
|
||||
.count('id as count')
|
||||
@@ -56,7 +57,7 @@ router.get('/stats', adminAuth, async (req, res) => {
|
||||
|
||||
// Get archived events count
|
||||
const archivedEvents = await db('events')
|
||||
.where('is_archived', true)
|
||||
.where('is_archived', formatBoolean(true))
|
||||
.count('id as count')
|
||||
.first();
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ const { archiveEvent } = require('../services/archiveService');
|
||||
const { escapeLikePattern } = require('../utils/sqlSecurity');
|
||||
const { formatDate } = require('../utils/dateFormatter');
|
||||
const { validatePasswordInContext, getBcryptRounds } = require('../utils/passwordValidation');
|
||||
const { formatBoolean } = require('../utils/dbCompat');
|
||||
|
||||
// Create new event
|
||||
router.post('/', adminAuth, [
|
||||
@@ -183,17 +184,17 @@ router.get('/', adminAuth, async (req, res) => {
|
||||
|
||||
// Apply status filter
|
||||
if (status === 'active') {
|
||||
query = query.where('is_active', true).where('is_archived', false);
|
||||
query = query.where('is_active', formatBoolean(true)).where('is_archived', formatBoolean(false));
|
||||
} else if (status === 'archived') {
|
||||
query = query.where('is_archived', true);
|
||||
query = query.where('is_archived', formatBoolean(true));
|
||||
} else if (status === 'inactive') {
|
||||
query = query.where('is_active', false).where('is_archived', false);
|
||||
query = query.where('is_active', formatBoolean(false)).where('is_archived', formatBoolean(false));
|
||||
} else if (status === 'expiring') {
|
||||
const sevenDaysFromNow = new Date();
|
||||
sevenDaysFromNow.setDate(sevenDaysFromNow.getDate() + 7);
|
||||
query = query
|
||||
.where('is_active', true)
|
||||
.where('is_archived', false)
|
||||
.where('is_active', formatBoolean(true))
|
||||
.where('is_archived', formatBoolean(false))
|
||||
.where('expires_at', '<=', sevenDaysFromNow.toISOString())
|
||||
.where('expires_at', '>', new Date().toISOString());
|
||||
}
|
||||
@@ -554,7 +555,7 @@ router.post('/bulk-archive', adminAuth, [
|
||||
// Get all events to archive
|
||||
const events = await db('events')
|
||||
.whereIn('id', eventIds)
|
||||
.where('is_archived', false);
|
||||
.where('is_archived', formatBoolean(false));
|
||||
|
||||
if (events.length === 0) {
|
||||
return res.status(400).json({ error: 'No valid events found to archive' });
|
||||
|
||||
@@ -4,6 +4,7 @@ const { archiveEvent } = require('./archiveService');
|
||||
const { queueEmail } = require('./emailProcessor');
|
||||
const logger = require('../utils/logger');
|
||||
const { formatDate } = require('../utils/dateFormatter');
|
||||
const { formatBoolean } = require('../utils/dbCompat');
|
||||
|
||||
function startExpirationChecker() {
|
||||
// Check every hour for expired events and warnings
|
||||
@@ -21,8 +22,8 @@ async function checkExpirations() {
|
||||
|
||||
// Check for events needing warning emails
|
||||
const eventsNeedingWarning = await db('events')
|
||||
.where('is_active', true)
|
||||
.where('is_archived', false)
|
||||
.where('is_active', formatBoolean(true))
|
||||
.where('is_archived', formatBoolean(false))
|
||||
.where('expires_at', '<=', warningDate)
|
||||
.where('expires_at', '>', now);
|
||||
|
||||
@@ -40,8 +41,8 @@ async function checkExpirations() {
|
||||
|
||||
// Check for expired events
|
||||
const expiredEvents = await db('events')
|
||||
.where('is_active', true)
|
||||
.where('is_archived', false)
|
||||
.where('is_active', formatBoolean(true))
|
||||
.where('is_archived', formatBoolean(false))
|
||||
.where('expires_at', '<=', now);
|
||||
|
||||
for (const event of expiredEvents) {
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
* Handles differences between PostgreSQL and SQLite
|
||||
*/
|
||||
|
||||
const { db } = require('../database/db');
|
||||
// Note: Requiring db here creates circular dependency
|
||||
// db should be passed as parameter or required where needed
|
||||
|
||||
/**
|
||||
* Get database client type
|
||||
@@ -58,10 +59,11 @@ function addDays(date, days) {
|
||||
|
||||
/**
|
||||
* Get date extraction SQL that works on both databases
|
||||
* @param {object} db - Knex database instance
|
||||
* @param {string} column - Column name
|
||||
* @returns {object} Knex raw query
|
||||
*/
|
||||
function dateExtractSQL(column) {
|
||||
function dateExtractSQL(db, column) {
|
||||
if (isPostgreSQL()) {
|
||||
return db.raw(`DATE(${column})`);
|
||||
} else {
|
||||
@@ -72,10 +74,11 @@ function dateExtractSQL(column) {
|
||||
|
||||
/**
|
||||
* Get database size query
|
||||
* @param {object} db - Knex database instance
|
||||
* @param {string} dbName - Database name
|
||||
* @returns {Promise<number>} Size in bytes
|
||||
*/
|
||||
async function getDatabaseSize(dbName) {
|
||||
async function getDatabaseSize(db, dbName) {
|
||||
if (isPostgreSQL()) {
|
||||
const result = await db.raw('SELECT pg_database_size(?) as size', [dbName]);
|
||||
return result.rows[0]?.size || 0;
|
||||
|
||||
Reference in New Issue
Block a user