server.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. const express = require('express');
  2. const cors = require('cors');
  3. const helmet = require('helmet');
  4. const morgan = require('morgan');
  5. const { Pool } = require('pg');
  6. require('dotenv').config();
  7. const app = express();
  8. const PORT = process.env.PORT || 5000;
  9. // Database configuration
  10. const pool = new Pool({
  11. connectionString: process.env.DATABASE_URL || 'postgresql://postgres:password@localhost:5432/byop_sample',
  12. ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false
  13. });
  14. // Middleware
  15. app.use(helmet());
  16. app.use(cors());
  17. app.use(morgan('combined'));
  18. app.use(express.json());
  19. app.use(express.urlencoded({ extended: true }));
  20. // Health check endpoint
  21. app.get('/health', (req, res) => {
  22. res.status(200).json({
  23. status: 'healthy',
  24. timestamp: new Date().toISOString(),
  25. uptime: process.uptime(),
  26. environment: process.env.NODE_ENV || 'development'
  27. });
  28. });
  29. // Root endpoint
  30. app.get('/', (req, res) => {
  31. res.json({
  32. message: '🚀 BYOP Sample Backend API',
  33. version: '1.0.0',
  34. endpoints: {
  35. health: '/health',
  36. users: '/api/users',
  37. stats: '/api/stats'
  38. },
  39. database: {
  40. status: 'connected',
  41. host: process.env.DATABASE_URL ? 'configured' : 'localhost'
  42. }
  43. });
  44. });
  45. // Initialize database tables
  46. async function initializeDatabase() {
  47. try {
  48. await pool.query(`
  49. CREATE TABLE IF NOT EXISTS users (
  50. id SERIAL PRIMARY KEY,
  51. name VARCHAR(255) NOT NULL,
  52. email VARCHAR(255) UNIQUE NOT NULL,
  53. role VARCHAR(100) DEFAULT 'user',
  54. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  55. updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  56. )
  57. `);
  58. console.log('✅ Database tables initialized');
  59. } catch (error) {
  60. console.error('❌ Database initialization error:', error);
  61. }
  62. }
  63. // Get all users
  64. app.get('/api/users', async (req, res) => {
  65. try {
  66. const result = await pool.query('SELECT * FROM users ORDER BY created_at DESC');
  67. res.json(result.rows);
  68. } catch (error) {
  69. console.error('Error fetching users:', error);
  70. res.status(500).json({ error: 'Failed to fetch users', details: error.message });
  71. }
  72. });
  73. // Get user by ID
  74. app.get('/api/users/:id', async (req, res) => {
  75. try {
  76. const { id } = req.params;
  77. const result = await pool.query('SELECT * FROM users WHERE id = $1', [id]);
  78. if (result.rows.length === 0) {
  79. return res.status(404).json({ error: 'User not found' });
  80. }
  81. res.json(result.rows[0]);
  82. } catch (error) {
  83. console.error('Error fetching user:', error);
  84. res.status(500).json({ error: 'Failed to fetch user', details: error.message });
  85. }
  86. });
  87. // Create new user
  88. app.post('/api/users', async (req, res) => {
  89. try {
  90. const { name, email, role = 'user' } = req.body;
  91. if (!name || !email) {
  92. return res.status(400).json({ error: 'Name and email are required' });
  93. }
  94. const result = await pool.query(
  95. 'INSERT INTO users (name, email, role) VALUES ($1, $2, $3) RETURNING *',
  96. [name, email, role]
  97. );
  98. res.status(201).json(result.rows[0]);
  99. } catch (error) {
  100. console.error('Error creating user:', error);
  101. if (error.code === '23505') { // Unique violation
  102. return res.status(409).json({ error: 'User with this email already exists' });
  103. }
  104. res.status(500).json({ error: 'Failed to create user', details: error.message });
  105. }
  106. });
  107. // Update user
  108. app.put('/api/users/:id', async (req, res) => {
  109. try {
  110. const { id } = req.params;
  111. const { name, email, role } = req.body;
  112. const result = await pool.query(
  113. 'UPDATE users SET name = COALESCE($1, name), email = COALESCE($2, email), role = COALESCE($3, role), updated_at = CURRENT_TIMESTAMP WHERE id = $4 RETURNING *',
  114. [name, email, role, id]
  115. );
  116. if (result.rows.length === 0) {
  117. return res.status(404).json({ error: 'User not found' });
  118. }
  119. res.json(result.rows[0]);
  120. } catch (error) {
  121. console.error('Error updating user:', error);
  122. res.status(500).json({ error: 'Failed to update user', details: error.message });
  123. }
  124. });
  125. // Delete user
  126. app.delete('/api/users/:id', async (req, res) => {
  127. try {
  128. const { id } = req.params;
  129. const result = await pool.query('DELETE FROM users WHERE id = $1 RETURNING *', [id]);
  130. if (result.rows.length === 0) {
  131. return res.status(404).json({ error: 'User not found' });
  132. }
  133. res.json({ message: 'User deleted successfully', user: result.rows[0] });
  134. } catch (error) {
  135. console.error('Error deleting user:', error);
  136. res.status(500).json({ error: 'Failed to delete user', details: error.message });
  137. }
  138. });
  139. // Get application statistics
  140. app.get('/api/stats', async (req, res) => {
  141. try {
  142. const totalUsersResult = await pool.query('SELECT COUNT(*) as count FROM users');
  143. const recentUsersResult = await pool.query('SELECT COUNT(*) as count FROM users WHERE created_at > NOW() - INTERVAL \'7 days\'');
  144. const stats = {
  145. totalUsers: parseInt(totalUsersResult.rows[0].count),
  146. activeUsers: parseInt(recentUsersResult.rows[0].count),
  147. serverStatus: 'Running',
  148. databaseStatus: 'Connected',
  149. uptime: Math.floor(process.uptime()),
  150. timestamp: new Date().toISOString(),
  151. environment: process.env.NODE_ENV || 'development'
  152. };
  153. res.json(stats);
  154. } catch (error) {
  155. console.error('Error fetching stats:', error);
  156. res.status(500).json({
  157. error: 'Failed to fetch stats',
  158. details: error.message,
  159. serverStatus: 'Running',
  160. databaseStatus: 'Error',
  161. totalUsers: 0,
  162. activeUsers: 0
  163. });
  164. }
  165. });
  166. // Error handling middleware
  167. app.use((err, req, res, next) => {
  168. console.error('Unhandled error:', err);
  169. res.status(500).json({
  170. error: 'Internal server error',
  171. details: process.env.NODE_ENV === 'development' ? err.message : 'Something went wrong'
  172. });
  173. });
  174. // 404 handler
  175. app.use('*', (req, res) => {
  176. res.status(404).json({ error: 'Endpoint not found' });
  177. });
  178. // Start server
  179. app.listen(PORT, '0.0.0.0', async () => {
  180. console.log(`🚀 BYOP Sample Backend running on port ${PORT}`);
  181. console.log(`📊 Environment: ${process.env.NODE_ENV || 'development'}`);
  182. console.log(`🔗 Database: ${process.env.DATABASE_URL ? 'Configured' : 'Default local'}`);
  183. // Initialize database
  184. await initializeDatabase();
  185. console.log('✅ Server ready to accept connections');
  186. });
  187. // Graceful shutdown handling
  188. process.on('SIGTERM', () => {
  189. console.log('🛑 SIGTERM received, shutting down gracefully');
  190. server.close(() => {
  191. console.log('💤 Process terminated');
  192. pool.end();
  193. });
  194. });
  195. process.on('SIGINT', () => {
  196. console.log('🛑 SIGINT received, shutting down gracefully');
  197. process.exit(0);
  198. });