feat: add EURUSD.csv data loading on Docker startup
This commit is contained in:
parent
011bea2350
commit
6522f501b6
3 changed files with 132 additions and 1 deletions
11
Dockerfile
11
Dockerfile
|
|
@ -31,8 +31,17 @@ COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
|
||||||
# Copy drizzle migrations
|
# Copy drizzle migrations
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/drizzle ./drizzle
|
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
|
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
|
ENV NODE_ENV=production PORT=3000 HOSTNAME=0.0.0.0
|
||||||
|
|
||||||
USER nextjs
|
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
|
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"]
|
||||||
|
|
|
||||||
111
scripts/load-initial-data.js
Normal file
111
scripts/load-initial-data.js
Normal file
|
|
@ -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();
|
||||||
11
scripts/startup.sh
Normal file
11
scripts/startup.sh
Normal file
|
|
@ -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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue