Refactor database environment variable usage across workflows and application code
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 3m53s

- Updated Gitea workflows to standardize on `DB_NAME` instead of `DB_DATABASE` for database name references.
- Modified deployment, backup, reset, and restore workflows to ensure consistent environment variable usage.
- Removed dotenv dependency and preload script, transitioning to environment variable management directly in scripts.
- Adjusted application code to utilize `DB_NAME` for database connections and logging.
- Enhanced README to reflect changes in environment variable configuration and usage.
- Cleaned up package.json scripts to remove unnecessary preload references.
This commit is contained in:
2025-12-04 18:02:38 -08:00
parent 80d2b1ffe6
commit 9d552b7456
22 changed files with 127 additions and 176 deletions

View File

@@ -28,22 +28,18 @@ The manual-db-reset.yml workflow would be simplified to call these scripts. The
* @file This script creates a data-only backup for a single user.
* It traces relationships across tables to export all relevant data as SQL INSERT statements.
*
* Usage:
* tsx src/db/backup_user.ts --email user@example.com --output user_backup.sql
* Usage (from project root, after setting environment variables):
* export DB_USER=... DB_PASSWORD=... && npx tsx src/db/backup_user.ts --email user@example.com --output user_backup.sql
*/
import { Pool, PoolClient } from 'pg';
import fs from 'fs/promises';
import dotenv from 'dotenv';
import { logger } from '../services/logger.server';
// Load environment variables from the root .env file
dotenv.config({ path: '../../.env' });
const pool = new Pool({
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_DATABASE_PROD, // IMPORTANT: This script targets the production DB
database: process.env.DB_NAME_PROD, // IMPORTANT: This script targets the production DB
password: process.env.DB_PASSWORD,
port: parseInt(process.env.DB_PORT || '5432', 10),
});

View File

@@ -8,19 +8,14 @@
import { Pool } from 'pg';
import bcrypt from 'bcrypt';
import dotenv from 'dotenv';
import { logger } from '../services/logger';
import { CATEGORIES } from '../types';
// Load environment variables from .env file
dotenv.config({ path: '../../.env' });
const pool = new Pool({
user: process.env.DB_USER || 'postgres',
host: process.env.DB_HOST || 'localhost',
// Default to 'flyer-crawler' for local development.
database: process.env.DB_NAME || 'flyer-crawler-test',
password: process.env.DB_PASSWORD || 'fake_test_db_password', // do not replace this - use appropriate .env file
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_NAME,
password: process.env.DB_PASSWORD,
port: parseInt(process.env.DB_PORT || '5432', 10),
});

View File

@@ -1,18 +1,13 @@
// src/db/seed_admin_account.ts
import { Pool } from 'pg';
import bcrypt from 'bcrypt';
import dotenv from 'dotenv';
// Load environment variables from .env file
dotenv.config();
const pool = new Pool({
user: process.env.DB_USER || 'postgres',
host: process.env.DB_HOST || 'localhost',
// Default to 'flyer-crawler' for local development.
database: process.env.DB_NAME || 'flyer-crawler-dev',
password: process.env.DB_PASSWORD || 'fake_test_db_password', // do not replace this - use appropriate .env file
port: parseInt(process.env.DB_PORT || '5432', 10),
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_NAME,
password: process.env.DB_PASSWORD,
port: parseInt(process.env.DB_PORT || '5432', 10), // Keep fallback for port
});
const ADMIN_EMAIL = 'admin@example.com';

View File

@@ -14,7 +14,7 @@ import { sendPasswordResetEmail } from '../services/emailService.server';
const router = Router();
const JWT_SECRET = process.env.JWT_SECRET || 'your_super_secret_jwt_key_change_this';
const JWT_SECRET = process.env.JWT_SECRET!;
// Conditionally disable rate limiting for the test environment
const isTestEnv = process.env.NODE_ENV === 'test';
@@ -166,7 +166,7 @@ router.post('/forgot-password', forgotPasswordLimiter, async (req, res, next) =>
await db.createPasswordResetToken(user.user_id, tokenHash, expiresAt);
const resetLink = `${process.env.FRONTEND_URL || 'http://localhost:5173'}/reset-password/${token}`;
const resetLink = `${process.env.FRONTEND_URL}/reset-password/${token}`;
try {
await sendPasswordResetEmail(email, resetLink);

View File

@@ -12,7 +12,7 @@ import { logger } from '../services/logger.server';
import { UserProfile } from '../types';
import { omit } from '../utils/objectUtils';
const JWT_SECRET = process.env.JWT_SECRET || 'your_super_secret_jwt_key_change_this';
const JWT_SECRET = process.env.JWT_SECRET!;
const MAX_FAILED_ATTEMPTS = 5;
const LOCKOUT_DURATION_MINUTES = 15;

View File

@@ -190,9 +190,9 @@ describe('Public Routes (/api)', () => {
describe('GET /recipes/by-sale-percentage', () => {
it('should return recipes based on sale percentage', async () => {
const mockRecipes: Recipe[] = [
{ recipe_id: 1, name: 'Pasta', description: null, instructions: null, avg_rating: 0, rating_count: 0, fork_count: 0, status: 'public', created_at: new Date().toISOString() }
{ recipe_id: 1, name: 'Pasta', description: null, instructions: null, avg_rating: 0, rating_count: 0, fork_count: 0, status: 'public', created_at: new Date().toISOString() },
];
vi.mocked(db.getRecipesBySalePercentage).mockResolvedValue(mockRecipes as any);
vi.mocked(db.getRecipesBySalePercentage).mockResolvedValue(mockRecipes);
const response = await supertest(app).get('/api/recipes/by-sale-percentage?minPercentage=75');
@@ -240,9 +240,9 @@ describe('Public Routes (/api)', () => {
describe('GET /recipes/by-ingredient-and-tag', () => {
it('should return recipes for a given ingredient and tag', async () => {
const mockRecipes: Recipe[] = [
{ recipe_id: 2, name: 'Chicken Tacos', description: null, instructions: null, avg_rating: 0, rating_count: 0, fork_count: 0, status: 'public', created_at: new Date().toISOString() }
{ recipe_id: 2, name: 'Chicken Tacos', description: null, instructions: null, avg_rating: 0, rating_count: 0, fork_count: 0, status: 'public', created_at: new Date().toISOString() },
];
vi.mocked(db.findRecipesByIngredientAndTag).mockResolvedValue(mockRecipes as any);
vi.mocked(db.findRecipesByIngredientAndTag).mockResolvedValue(mockRecipes);
const response = await supertest(app).get('/api/recipes/by-ingredient-and-tag?ingredient=chicken&tag=quick');

View File

@@ -12,22 +12,20 @@ const createPool = (): Pool => {
const poolId = Math.random().toString(36).substring(2, 8);
// --- START DEBUG LOGGING ---
// Log the database connection details being used by the server process.
// This helps confirm it's using the correct .env file for the environment.
//logger.info('--- [DB POOL] Creating new PostgreSQL connection pool. ---');
//logger.info(` Host: ${process.env.DB_HOST}`);
//logger.info(` Port: ${process.env.DB_PORT}`);
//logger.info(` User: ${process.env.DB_USER}`);
//logger.info(` Database: ${process.env.DB_DATABASE}`);
//logger.info(` Database: ${process.env.DB_NAME}`);
//logger.info(` Pool Instance ID: ${poolId}`);
//logger.info('----------------------------------------------------');
const poolConfig: PoolConfig = {
user: process.env.DB_USER || 'postgres',
host: process.env.DB_HOST || 'localhost',
database: process.env.DB_NAME || 'flyer-crawler-test',
password: process.env.DB_PASSWORD || 'fake_test_db_password', // do not replace this - use appropriate .env file
port: parseInt(process.env.DB_PORT || '5432', 10),
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_NAME,
password: process.env.DB_PASSWORD,
port: parseInt(process.env.DB_PORT || '5432', 10), // Keep fallback for port as it's less sensitive
};
// --- ADD DEBUG LOGGING HERE ---

View File

@@ -12,7 +12,7 @@ import * as emailService from './emailService.server';
import * as db from './db/index.db';
import { generateFlyerIcon } from '../utils/imageProcessor';
export const connection = new IORedis(process.env.REDIS_URL || 'redis://127.0.0.1:6379', {
export const connection = new IORedis(process.env.REDIS_URL!, {
maxRetriesPerRequest: null, // Important for BullMQ
password: process.env.REDIS_PASSWORD, // Add the password from environment variables
});
@@ -224,7 +224,7 @@ export const flyerWorker = new Worker<FlyerJobData>(
// Control the number of concurrent jobs. This directly limits parallel calls to the AI API.
// It's sourced from an environment variable for easy configuration without code changes.
// The Google AI free tier limit is 60 RPM, so a low concurrency is safe.
concurrency: parseInt(process.env.WORKER_CONCURRENCY || '5', 10),
concurrency: parseInt(process.env.WORKER_CONCURRENCY!, 10),
}
);
@@ -307,7 +307,7 @@ export const cleanupWorker = new Worker<CleanupJobData>(
}
// 2. Determine the base path for the flyer images.
const storagePath = process.env.STORAGE_PATH || '/var/www/flyer-crawler.projectium.com/flyer-images';
const storagePath = process.env.STORAGE_PATH!;
// 3. Delete the main flyer image.
const mainImagePath = path.join(storagePath, path.basename(flyer.image_url));

View File

@@ -29,7 +29,7 @@ describe('Authentication API Integration', () => {
console.log(` Host: ${process.env.DB_HOST}`);
console.log(` Port: ${process.env.DB_PORT}`);
console.log(` User: ${process.env.DB_USER}`);
console.log(` Database: ${process.env.DB_DATABASE}`);
console.log(` Database: ${process.env.DB_NAME}`);
console.log('-----------------------------------------------------\n');
// --- END DEBUG LOGGING ---

View File

@@ -6,11 +6,11 @@ import { execSync } from 'child_process';
const getPool = () => {
// This pool connects using the test-specific environment variables
// passed from the Gitea workflow (e.g., DB_DATABASE="flyer-crawler-test").
// passed from the Gitea workflow (e.g., DB_NAMEyer-crawler-test").
return new Pool({
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_DATABASE,
database: process.env.DB_NAME,
password: process.env.DB_PASSWORD,
port: parseInt(process.env.DB_PORT || '5432', 10),
});
@@ -29,7 +29,7 @@ export async function setup() {
console.log(` Host: ${process.env.DB_HOST}`);
console.log(` Port: ${process.env.DB_PORT}`);
console.log(` User: ${process.env.DB_USER}`);
console.log(` Database: ${process.env.DB_DATABASE}`);
console.log(` Database: ${process.env.DB_NAME}`);
console.log('-------------------------------------------\n');
// --- END DEBUG LOGGING ---
console.log('\nResetting test database schema...');

View File

@@ -6,7 +6,7 @@ import { Pool } from 'pg';
export const testPool = new Pool({
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_DATABASE,
database: process.env.DB_NAME,
password: process.env.DB_PASSWORD,
port: parseInt(process.env.DB_PORT || '5432', 10),
});