← back to Animate Museum Posts
src/healthServer.js
116 lines
import http from 'http';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const PORT = process.env.PORT || 7890;
// Track bot state
let lastPostTime = null;
let nextPostTime = null;
let isRunning = false;
export function updateLastPost(time) {
lastPostTime = time || new Date().toISOString();
// Next post is 6 hours from now
nextPostTime = new Date(Date.now() + 6 * 60 * 60 * 1000).toISOString();
// Save to file for persistence
const dataDir = path.join(__dirname, '..', 'data');
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
fs.writeFileSync(path.join(dataDir, 'last-run.json'), JSON.stringify({
lastPost: lastPostTime,
nextPost: nextPostTime,
updatedAt: new Date().toISOString()
}, null, 2));
}
export function setRunning(running) {
isRunning = running;
}
function getLast5Posts() {
try {
const logsDir = path.join(__dirname, '..', 'logs');
const logFile = path.join(logsDir, 'posts.log');
if (!fs.existsSync(logFile)) return [];
const content = fs.readFileSync(logFile, 'utf-8');
const lines = content.trim().split('\n').filter(Boolean);
return lines.slice(-5).map(line => {
try { return JSON.parse(line); } catch { return null; }
}).filter(Boolean);
} catch {
return [];
}
}
function loadLastRun() {
try {
const dataDir = path.join(__dirname, '..', 'data');
const lastRunFile = path.join(dataDir, 'last-run.json');
if (fs.existsSync(lastRunFile)) {
const data = JSON.parse(fs.readFileSync(lastRunFile, 'utf-8'));
lastPostTime = data.lastPost;
nextPostTime = data.nextPost;
}
} catch {}
}
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'application/json');
res.setHeader('Access-Control-Allow-Origin', '*');
if (req.url === '/health' || req.url === '/') {
const response = {
status: 'ok',
service: 'museum-art-bot',
isRunning,
lastPost: lastPostTime,
nextPost: nextPostTime,
uptime: process.uptime(),
timestamp: new Date().toISOString()
};
res.writeHead(200);
res.end(JSON.stringify(response, null, 2));
}
else if (req.url === '/status') {
const posts = getLast5Posts();
const response = {
status: 'ok',
recentPosts: posts.map(p => ({
timestamp: p.timestamp,
tweetId: p.tweetId,
artwork: p.artwork?.title,
artist: p.artwork?.artist
})),
totalPosts: posts.length,
lastPost: lastPostTime,
nextPost: nextPostTime
};
res.writeHead(200);
res.end(JSON.stringify(response, null, 2));
}
else {
res.writeHead(404);
res.end(JSON.stringify({ error: 'Not found' }));
}
});
export function startHealthServer() {
loadLastRun();
server.listen(PORT, () => {
console.log(`Health server running on port ${PORT}`);
isRunning = true;
});
return server;
}
// If run directly
if (process.argv[1] === fileURLToPath(import.meta.url)) {
startHealthServer();
}