256 lines
7.4 KiB
JavaScript
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;
|
|
|