test-git-fcz5/index.js

256 lines
7.4 KiB
JavaScript

/**
* Production-Ready Node.js API Server
* For testing git serverless deployment
*/
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const compression = require('compression');
const { v4: uuidv4 } = require('uuid');
// Load environment variables
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 8080;
const NODE_ENV = process.env.NODE_ENV || 'development';
// ===========================================
// Middleware
// ===========================================
app.use(helmet()); // Security headers
app.use(cors()); // CORS support
app.use(compression()); // Gzip compression
app.use(morgan('combined')); // Request logging
app.use(express.json()); // Parse JSON bodies
app.use(express.urlencoded({ extended: true }));
// ===========================================
// In-Memory Database (for testing)
// ===========================================
const database = {
users: [
{ id: '1', name: 'John Doe', email: '[email protected]', createdAt: new Date().toISOString() },
{ id: '2', name: 'Jane Smith', email: '[email protected]', createdAt: new Date().toISOString() },
],
tasks: [
{ id: '1', userId: '1', title: 'Complete project', status: 'pending', createdAt: new Date().toISOString() },
{ id: '2', userId: '1', title: 'Review PR', status: 'done', createdAt: new Date().toISOString() },
]
};
// ===========================================
// Health Check Routes
// ===========================================
app.get('/', (req, res) => {
res.json({
service: 'test-git-fcz5',
version: '1.0.0',
status: 'healthy',
timestamp: new Date().toISOString(),
environment: NODE_ENV,
});
});
app.get('/health', (req, res) => {
res.json({
status: 'ok',
uptime: process.uptime(),
timestamp: new Date().toISOString(),
});
});
app.get('/ready', (req, res) => {
res.json({ ready: true });
});
// ===========================================
// Environment Info Route (for testing)
// ===========================================
app.get('/api/env', (req, res) => {
res.json({
message: 'Environment variables (for testing config overrides)',
environment: {
NODE_ENV: process.env.NODE_ENV || 'not-set',
PORT: process.env.PORT || 'not-set',
// Boltic system variables
BOLT_APPLICATION_NAME: process.env.BOLT_APPLICATION_NAME || 'not-set',
BOLT_APPLICATION_SLUG: process.env.BOLT_APPLICATION_SLUG || 'not-set',
BOLT_APPLICATION_REGION_ID: process.env.BOLT_APPLICATION_REGION_ID || 'not-set',
// Custom env vars (set from UI or boltic.yaml)
API_KEY: process.env.API_KEY ? '***hidden***' : 'not-set',
DATABASE_URL: process.env.DATABASE_URL ? '***hidden***' : 'not-set',
CUSTOM_VAR: process.env.CUSTOM_VAR || 'not-set',
UI_TEST_VAR: process.env.UI_TEST_VAR || 'not-set',
},
testInfo: {
purpose: 'Verify environment variables from UI and boltic.yaml',
configFormat: 'Check if serverlessConfig overrides are applied',
}
});
});
// ===========================================
// Users API
// ===========================================
app.get('/api/users', (req, res) => {
res.json({
success: true,
data: database.users,
total: database.users.length,
});
});
app.get('/api/users/:id', (req, res) => {
const user = database.users.find(u => u.id === req.params.id);
if (!user) {
return res.status(404).json({ success: false, error: 'User not found' });
}
res.json({ success: true, data: user });
});
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ success: false, error: 'Name and email are required' });
}
const newUser = {
id: uuidv4(),
name,
email,
createdAt: new Date().toISOString(),
};
database.users.push(newUser);
res.status(201).json({ success: true, data: newUser });
});
app.put('/api/users/:id', (req, res) => {
const index = database.users.findIndex(u => u.id === req.params.id);
if (index === -1) {
return res.status(404).json({ success: false, error: 'User not found' });
}
database.users[index] = { ...database.users[index], ...req.body };
res.json({ success: true, data: database.users[index] });
});
app.delete('/api/users/:id', (req, res) => {
const index = database.users.findIndex(u => u.id === req.params.id);
if (index === -1) {
return res.status(404).json({ success: false, error: 'User not found' });
}
const deleted = database.users.splice(index, 1);
res.json({ success: true, data: deleted[0] });
});
// ===========================================
// Tasks API
// ===========================================
app.get('/api/tasks', (req, res) => {
const { userId, status } = req.query;
let tasks = [...database.tasks];
if (userId) tasks = tasks.filter(t => t.userId === userId);
if (status) tasks = tasks.filter(t => t.status === status);
res.json({
success: true,
data: tasks,
total: tasks.length,
});
});
app.post('/api/tasks', (req, res) => {
const { userId, title } = req.body;
if (!userId || !title) {
return res.status(400).json({ success: false, error: 'userId and title are required' });
}
const newTask = {
id: uuidv4(),
userId,
title,
status: 'pending',
createdAt: new Date().toISOString(),
};
database.tasks.push(newTask);
res.status(201).json({ success: true, data: newTask });
});
app.patch('/api/tasks/:id/status', (req, res) => {
const { status } = req.body;
const validStatuses = ['pending', 'in-progress', 'done'];
if (!status || !validStatuses.includes(status)) {
return res.status(400).json({
success: false,
error: `Invalid status. Must be one of: ${validStatuses.join(', ')}`
});
}
const task = database.tasks.find(t => t.id === req.params.id);
if (!task) {
return res.status(404).json({ success: false, error: 'Task not found' });
}
task.status = status;
res.json({ success: true, data: task });
});
// ===========================================
// Echo/Debug Route (for testing)
// ===========================================
app.all('/api/echo', (req, res) => {
res.json({
method: req.method,
path: req.path,
query: req.query,
headers: req.headers,
body: req.body,
timestamp: new Date().toISOString(),
});
});
// ===========================================
// 404 Handler
// ===========================================
app.use((req, res) => {
res.status(404).json({
success: false,
error: 'Not Found',
path: req.path,
});
});
// ===========================================
// Error Handler
// ===========================================
app.use((err, req, res, next) => {
console.error('Error:', err);
res.status(500).json({
success: false,
error: NODE_ENV === 'production' ? 'Internal Server Error' : err.message,
});
});
// ===========================================
// Start Server
// ===========================================
app.listen(PORT, '0.0.0.0', () => {
console.log('========================================');
console.log(`🚀 Server started successfully!`);
console.log(`📍 Environment: ${NODE_ENV}`);
console.log(`🌐 Listening on: http://0.0.0.0:${PORT}`);
console.log(`💚 Health check: http://0.0.0.0:${PORT}/health`);
console.log('========================================');
});
module.exports = app;