
- Updated `.env` and added `.env.test` for environment variables. - Introduced API documentation in `API_DOCUMENTATION.md`. - Added authentication setup guide in `AUTHENTICATION_SETUP.md`. - Implemented user authentication with JWT and email verification. - Created new routes for user management and form submissions. - Added middleware for API key authentication and error handling. - Set up Redis for rate limiting and notifications. - Removed obsolete files and configurations related to the previous Rust implementation.
196 lines
5.0 KiB
JavaScript
196 lines
5.0 KiB
JavaScript
require("dotenv").config();
|
|
const express = require("express");
|
|
const path = require("path");
|
|
const fs = require("fs"); // Added for fs operations
|
|
const db = require("./src/config/database"); // SQLite db instance
|
|
const helmet = require("helmet");
|
|
const session = require("express-session");
|
|
const passport = require("./src/config/passport");
|
|
const logger = require("./config/logger");
|
|
const errorHandler = require("./middleware/errorHandler");
|
|
const { connectRedis, closeRedis } = require("./src/config/redis");
|
|
|
|
// Import routes
|
|
const publicRoutes = require("./src/routes/public");
|
|
const authRoutes = require("./src/routes/auth");
|
|
const dashboardRoutes = require("./src/routes/dashboard");
|
|
const apiV1Routes = require("./src/routes/api_v1");
|
|
|
|
const app = express();
|
|
const PORT = process.env.PORT || 3000;
|
|
|
|
// Function to initialize the database
|
|
async function initializeDatabase() {
|
|
const dbPath = path.resolve(__dirname, "formies.sqlite");
|
|
const dbExists = fs.existsSync(dbPath);
|
|
|
|
if (!dbExists) {
|
|
logger.info("Database file not found, creating and initializing...");
|
|
try {
|
|
// The 'db' instance from './src/config/database' should already create the file.
|
|
// Now, run the init.sql script.
|
|
const initSql = fs.readFileSync(
|
|
path.resolve(__dirname, "init.sql"),
|
|
"utf8"
|
|
);
|
|
// SQLite driver's `exec` method can run multiple statements
|
|
await new Promise((resolve, reject) => {
|
|
db.exec(initSql, (err) => {
|
|
if (err) {
|
|
logger.error("Failed to initialize database:", err);
|
|
return reject(err);
|
|
}
|
|
logger.info("Database initialized successfully.");
|
|
resolve();
|
|
});
|
|
});
|
|
} catch (error) {
|
|
logger.error("Error during database initialization:", error);
|
|
process.exit(1); // Exit if DB initialization fails
|
|
}
|
|
} else {
|
|
logger.info("Database file found.");
|
|
}
|
|
}
|
|
|
|
// Initialize Redis connection and Database
|
|
async function initializeApp() {
|
|
// Initialize Redis first, but don't block on failure
|
|
connectRedis().catch(() => {
|
|
logger.warn(
|
|
"Redis connection failed, continuing with in-memory rate limiting"
|
|
);
|
|
});
|
|
|
|
try {
|
|
await initializeDatabase(); // Initialize SQLite database
|
|
} catch (error) {
|
|
logger.error("Failed to initialize database:", error);
|
|
process.exit(1); // Exit if DB initialization fails
|
|
}
|
|
|
|
// Middleware
|
|
app.use(
|
|
helmet({
|
|
contentSecurityPolicy: {
|
|
directives: {
|
|
defaultSrc: ["'self'"],
|
|
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
scriptSrc: ["'self'"],
|
|
imgSrc: ["'self'", "data:", "https:"],
|
|
},
|
|
},
|
|
})
|
|
);
|
|
|
|
app.use(express.json({ limit: "10mb" }));
|
|
app.use(express.urlencoded({ extended: true, limit: "10mb" }));
|
|
|
|
// Session configuration (for development only, use Redis in production)
|
|
app.use(
|
|
session({
|
|
secret:
|
|
process.env.SESSION_SECRET || "fallback-secret-change-in-production",
|
|
resave: false,
|
|
saveUninitialized: false,
|
|
cookie: {
|
|
secure: process.env.NODE_ENV === "production",
|
|
httpOnly: true,
|
|
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
|
},
|
|
})
|
|
);
|
|
|
|
// Initialize Passport
|
|
app.use(passport.initialize());
|
|
app.use(passport.session());
|
|
|
|
// Set view engine
|
|
app.set("view engine", "ejs");
|
|
|
|
// API Routes
|
|
app.use("/api/auth", authRoutes);
|
|
|
|
// API V1 Routes
|
|
app.use("/api/v1", apiV1Routes);
|
|
|
|
// User Dashboard Routes
|
|
app.use("/dashboard", dashboardRoutes);
|
|
|
|
// Existing routes (maintaining backward compatibility)
|
|
app.use("/", publicRoutes);
|
|
|
|
// Health check endpoint
|
|
app.get("/health", (req, res) => {
|
|
res.json({
|
|
status: "healthy",
|
|
timestamp: new Date().toISOString(),
|
|
version: "1.0.0",
|
|
});
|
|
});
|
|
|
|
// Global error handler - should be the last middleware
|
|
app.use(errorHandler);
|
|
|
|
// 404 handler
|
|
app.use((req, res) => {
|
|
logger.warn(
|
|
`404 - Endpoint not found: ${req.originalUrl} - Method: ${req.method} - IP: ${req.ip}`
|
|
);
|
|
res.status(404).json({
|
|
error: {
|
|
message: "Endpoint not found",
|
|
code: "NOT_FOUND",
|
|
},
|
|
});
|
|
});
|
|
|
|
// Start server
|
|
app.listen(PORT, () => {
|
|
logger.info(`Server running on http://localhost:${PORT}`);
|
|
|
|
// Environment checks
|
|
if (!process.env.JWT_SECRET) {
|
|
logger.warn(
|
|
"WARNING: JWT_SECRET not set. Authentication will not work properly."
|
|
);
|
|
}
|
|
|
|
if (process.env.NTFY_ENABLED === "true" && process.env.NTFY_TOPIC_URL) {
|
|
logger.info(
|
|
`Ntfy notifications enabled for topic: ${process.env.NTFY_TOPIC_URL}`
|
|
);
|
|
} else {
|
|
logger.info("Ntfy notifications disabled or topic not configured.");
|
|
}
|
|
|
|
// Start cleanup of expired sessions every hour
|
|
setInterval(
|
|
() => {
|
|
const jwtService = require("./src/services/jwtService");
|
|
jwtService.cleanupExpiredSessions();
|
|
},
|
|
60 * 60 * 1000
|
|
);
|
|
});
|
|
|
|
// Graceful shutdown
|
|
process.on("SIGINT", async () => {
|
|
logger.info("Received SIGINT, shutting down gracefully...");
|
|
await closeRedis();
|
|
process.exit(0);
|
|
});
|
|
|
|
process.on("SIGTERM", async () => {
|
|
logger.info("Received SIGTERM, shutting down gracefully...");
|
|
await closeRedis();
|
|
process.exit(0);
|
|
});
|
|
}
|
|
|
|
// Initialize the application
|
|
initializeApp().catch((error) => {
|
|
logger.error("Failed to initialize application:", error);
|
|
process.exit(1);
|
|
});
|