Initial commit - Church Music Database
This commit is contained in:
149
new-site/backend/server.js
Normal file
149
new-site/backend/server.js
Normal file
@@ -0,0 +1,149 @@
|
||||
require("dotenv").config();
|
||||
|
||||
const express = require("express");
|
||||
const cors = require("cors");
|
||||
const helmet = require("helmet");
|
||||
const morgan = require("morgan");
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const { query } = require("./db");
|
||||
const {
|
||||
cacheMiddleware,
|
||||
invalidationMiddleware,
|
||||
getCacheStats,
|
||||
startCacheCleanup,
|
||||
} = require("./middleware/cache");
|
||||
|
||||
// Import routes
|
||||
const authRoutes = require("./routes/auth");
|
||||
const songsRoutes = require("./routes/songs");
|
||||
const listsRoutes = require("./routes/lists");
|
||||
const profilesRoutes = require("./routes/profiles");
|
||||
const adminRoutes = require("./routes/admin");
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 8080;
|
||||
|
||||
// Start cache cleanup (every 60 seconds)
|
||||
startCacheCleanup(60000);
|
||||
|
||||
// Security middleware
|
||||
app.use(
|
||||
helmet({
|
||||
contentSecurityPolicy: false, // Disable for development
|
||||
}),
|
||||
);
|
||||
|
||||
// CORS configuration
|
||||
const allowedOrigins = [
|
||||
"http://localhost:5100",
|
||||
"http://localhost:3000",
|
||||
"https://houseofprayer.ddns.net",
|
||||
"http://houseofprayer.ddns.net",
|
||||
];
|
||||
|
||||
app.use(
|
||||
cors({
|
||||
origin: (origin, callback) => {
|
||||
// Allow requests with no origin (like mobile apps or curl requests)
|
||||
if (!origin) return callback(null, true);
|
||||
|
||||
if (allowedOrigins.includes(origin)) {
|
||||
callback(null, true);
|
||||
} else {
|
||||
// For development, allow all origins
|
||||
callback(null, true);
|
||||
}
|
||||
},
|
||||
credentials: true,
|
||||
methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
|
||||
allowedHeaders: ["Content-Type", "Authorization", "If-None-Match"],
|
||||
exposedHeaders: ["ETag", "X-Cache", "Cache-Control"],
|
||||
}),
|
||||
);
|
||||
|
||||
// Explicit OPTIONS handler for preflight requests
|
||||
app.options("*", cors());
|
||||
|
||||
// Rate limiting
|
||||
const limiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 1000, // Generous limit for development
|
||||
message: { error: "Too many requests, please try again later." },
|
||||
});
|
||||
app.use("/api/", limiter);
|
||||
|
||||
// Body parsing
|
||||
app.use(express.json({ limit: "10mb" }));
|
||||
app.use(express.urlencoded({ extended: true, limit: "10mb" }));
|
||||
|
||||
// Response caching middleware (applies to GET requests)
|
||||
app.use("/api/", cacheMiddleware());
|
||||
|
||||
// Cache invalidation middleware (handles POST, PUT, DELETE)
|
||||
app.use("/api/", invalidationMiddleware);
|
||||
|
||||
// Logging (compact format for production-like environment)
|
||||
app.use(morgan("dev"));
|
||||
|
||||
// Health check
|
||||
app.get("/health", (req, res) => {
|
||||
res.json({ status: "ok", timestamp: new Date().toISOString() });
|
||||
});
|
||||
|
||||
// Cache stats endpoint for monitoring
|
||||
app.get("/api/cache-stats", (req, res) => {
|
||||
res.json({
|
||||
success: true,
|
||||
cache: getCacheStats(),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
|
||||
// Stats endpoint for dashboard
|
||||
app.get("/api/stats", async (req, res) => {
|
||||
try {
|
||||
const [songsResult, profilesResult, listsResult] = await Promise.all([
|
||||
query("SELECT COUNT(*) as count FROM songs"),
|
||||
query("SELECT COUNT(*) as count FROM profiles"),
|
||||
query("SELECT COUNT(*) as count FROM plans"),
|
||||
]);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
stats: {
|
||||
songs: parseInt(songsResult.rows[0].count),
|
||||
profiles: parseInt(profilesResult.rows[0].count),
|
||||
lists: parseInt(listsResult.rows[0].count),
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Stats error:", err);
|
||||
res.status(500).json({ success: false, message: "Failed to fetch stats" });
|
||||
}
|
||||
});
|
||||
|
||||
// API Routes
|
||||
app.use("/api/auth", authRoutes);
|
||||
app.use("/api/songs", songsRoutes);
|
||||
app.use("/api/lists", listsRoutes);
|
||||
app.use("/api/profiles", profilesRoutes);
|
||||
app.use("/api/admin", adminRoutes);
|
||||
|
||||
// 404 handler for API routes
|
||||
app.use((req, res) => {
|
||||
res.status(404).json({ error: "Not found" });
|
||||
});
|
||||
|
||||
// Error handler
|
||||
app.use((err, req, res, next) => {
|
||||
console.error("Server error:", err);
|
||||
res.status(500).json({ error: "Internal server error" });
|
||||
});
|
||||
|
||||
// Start server
|
||||
app.listen(PORT, () => {
|
||||
console.log("🚀 Server running on http://localhost:" + PORT);
|
||||
console.log("📊 Health check: http://localhost:" + PORT + "/health");
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
Reference in New Issue
Block a user