From 6522f501b6400431b594a12c1c893d7bba50468e Mon Sep 17 00:00:00 2001 From: Marko Djordjevic Date: Thu, 12 Feb 2026 18:48:54 +0100 Subject: [PATCH] feat: add EURUSD.csv data loading on Docker startup --- Dockerfile | 11 +++- scripts/load-initial-data.js | 111 +++++++++++++++++++++++++++++++++++ scripts/startup.sh | 11 ++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 scripts/load-initial-data.js create mode 100644 scripts/startup.sh diff --git a/Dockerfile b/Dockerfile index efb8a5a..c31412a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,8 +31,17 @@ COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules # Copy drizzle migrations COPY --from=builder --chown=nextjs:nodejs /app/drizzle ./drizzle +# Copy data loading scripts +COPY --from=builder --chown=nextjs:nodejs /app/scripts ./scripts + +# Copy initial data CSV +COPY --from=builder --chown=nextjs:nodejs /app/EURUSD.csv ./EURUSD.csv + RUN mkdir -p /app/public /app/data && chown -R nextjs:nodejs /app/public /app/data +# Make startup script executable +RUN chmod +x /app/scripts/startup.sh + ENV NODE_ENV=production PORT=3000 HOSTNAME=0.0.0.0 USER nextjs @@ -41,4 +50,4 @@ EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1 -CMD ["node", "server.js"] +CMD ["/app/scripts/startup.sh"] diff --git a/scripts/load-initial-data.js b/scripts/load-initial-data.js new file mode 100644 index 0000000..a5ee2d9 --- /dev/null +++ b/scripts/load-initial-data.js @@ -0,0 +1,111 @@ +const fs = require('fs'); +const path = require('path'); +const Database = require('better-sqlite3'); +const Papa = require('papaparse'); + +const DB_PATH = process.env.DATABASE_PATH || path.join(__dirname, '..', 'data', 'candles.db'); +const CSV_PATH = process.env.CSV_PATH || path.join(__dirname, '..', 'EURUSD.csv'); + +async function loadInitialData() { + console.log('Checking if initial data needs to be loaded...'); + + const db = new Database(DB_PATH); + + try { + // Check if candles table has any data + const count = db.prepare('SELECT COUNT(*) as count FROM candles').get(); + + if (count.count > 0) { + console.log(`Database already has ${count.count} candles. Skipping initial data load.`); + db.close(); + return; + } + + console.log('Database is empty. Loading initial data from CSV...'); + + // Check if CSV file exists + if (!fs.existsSync(CSV_PATH)) { + console.log(`CSV file not found at ${CSV_PATH}. Skipping initial data load.`); + db.close(); + return; + } + + // Read and parse CSV + const csvContent = fs.readFileSync(CSV_PATH, 'utf8'); + + Papa.parse(csvContent, { + header: true, + dynamicTyping: true, + skipEmptyLines: true, + complete: (results) => { + try { + const rows = results.data; + + if (rows.length === 0) { + console.log('CSV file is empty.'); + db.close(); + return; + } + + console.log(`Parsed ${rows.length} rows from CSV`); + + // Prepare insert statement + const insert = db.prepare( + 'INSERT INTO candles (time, open, high, low, close) VALUES (?, ?, ?, ?, ?)' + ); + + const insertMany = db.transaction((candles) => { + for (const candle of candles) { + insert.run(candle.time, candle.open, candle.high, candle.low, candle.close); + } + }); + + // Parse and prepare candle data + const candleData = rows.map((row) => { + let timestamp; + + // Handle both date strings and Unix timestamps + if (typeof row.time === 'string') { + // Try parsing as date string + const date = new Date(row.time); + if (isNaN(date.getTime())) { + throw new Error(`Invalid date format: ${row.time}`); + } + timestamp = Math.floor(date.getTime() / 1000); + } else if (typeof row.time === 'number') { + timestamp = row.time; + } else { + throw new Error(`Invalid time value: ${row.time}`); + } + + return { + time: timestamp, + open: Number(row.open), + high: Number(row.high), + low: Number(row.low), + close: Number(row.close), + }; + }); + + // Insert all candles in a transaction + insertMany(candleData); + + console.log(`Successfully loaded ${candleData.length} candles into the database.`); + } catch (error) { + console.error('Error loading initial data:', error); + } finally { + db.close(); + } + }, + error: (error) => { + console.error('CSV parsing error:', error); + db.close(); + }, + }); + } catch (error) { + console.error('Error checking database:', error); + db.close(); + } +} + +loadInitialData(); diff --git a/scripts/startup.sh b/scripts/startup.sh new file mode 100644 index 0000000..5da6bd6 --- /dev/null +++ b/scripts/startup.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -e + +echo "Running database migrations..." +# Migrations are run automatically by the app on startup + +echo "Loading initial data if needed..." +node /app/scripts/load-initial-data.js + +echo "Starting application..." +exec node server.js