easycats / server.js
no1b4me's picture
Update server.js
00b61bd verified
require('dotenv').config();
const { getRouter } = require('stremio-addon-sdk');
const addonInterface = require('./addon');
const express = require('express');
const path = require('path');
const http = require('http');
const winston = require('winston');
// Configure Winston logger
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.printf(({ level, message, timestamp }) => {
return `${timestamp} ${level}: ${message}`;
})
),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
const app = express();
// Serve static files
app.use(express.static(path.join(__dirname, 'public')));
// Basic error handling middleware
app.use((err, req, res, next) => {
logger.error('Unhandled error:', err);
res.status(500).json({ error: 'Internal server error' });
});
// Health check endpoint
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
// Serve the installation page
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
let addonRouter = null;
// Handle addon requests
app.use('/:configuration?', (req, res, next) => {
try {
if (!addonRouter) {
const configurationStr = req.params.configuration;
if (configurationStr) {
let configuration;
try {
const decodedStr = Buffer.from(configurationStr, 'base64').toString('utf-8');
configuration = JSON.parse(decodedStr);
logger.debug('Decoded configuration:', {
...configuration,
username: configuration.username ? '[REDACTED]' : undefined,
password: configuration.password ? '[REDACTED]' : undefined
});
} catch (e) {
logger.error('Failed to decode configuration:', e);
return res.status(400).json({ error: 'Invalid configuration format' });
}
const { username, password } = configuration;
if (!username || !password) {
logger.warn('Missing required configuration parameters');
return res.status(400).json({ error: 'Missing username or password' });
}
try {
const addonWithConfig = addonInterface.setConfiguration({ username, password });
addonRouter = getRouter(addonWithConfig);
logger.info('Addon router created with configuration');
} catch (e) {
logger.error('Failed to create addon router:', e);
return res.status(500).json({ error: 'Failed to initialize addon' });
}
} else {
// If no configuration, use default addon interface
addonRouter = getRouter(addonInterface);
logger.info('Addon router created with default configuration');
}
}
addonRouter(req, res, next);
} catch (error) {
logger.error('Error in addon middleware:', error);
next(error);
}
});
// Handle 404
app.use((req, res) => {
res.status(404).json({ error: 'Not found' });
});
function startServer(port) {
return new Promise((resolve, reject) => {
const server = http.createServer(app);
// Handle server errors
server.on('error', (error) => {
if (error.code === 'EADDRINUSE') {
logger.warn(`Port ${port} is in use, trying ${port + 1}`);
resolve(startServer(port + 1));
} else {
logger.error('Server error:', error);
reject(error);
}
});
// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
logger.error('Uncaught exception:', error);
process.exit(1);
});
// Handle unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
logger.error('Unhandled Rejection at:', promise, 'reason:', reason);
});
// Start the server
server.listen(port, () => {
logger.info(`Addon running on http://127.0.0.1:${port}`);
resolve(server);
});
});
}
// Start the server with automatic port selection
startServer(3000).catch(err => {
logger.error('Failed to start server:', err);
process.exit(1);
});
// Export for testing
module.exports = { app, startServer };