This repository was archived by the owner on Jul 6, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
126 lines (108 loc) · 4.44 KB
/
Copy pathserver.js
File metadata and controls
126 lines (108 loc) · 4.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
"use strict";
/* ── Secrets must be bootstrapped before anything else reads process.env ── */
require("./services/secrets").bootstrap();
/* ── dotenv is now optional — only needed for PORT / NODE_ENV / TRUST_PROXY ── */
try {
require("dotenv").config({ override: false });
} catch {
/* dotenv not installed — fine */
}
const express = require("express");
const helmet = require("helmet");
const cookieParser = require("cookie-parser");
const path = require("path");
const rateLimit = require("express-rate-limit");
const authRoutes = require("./routes/auth");
const profileRoutes = require("./routes/profiles");
const brokerRoutes = require("./routes/brokers");
const scanRoutes = require("./routes/scan");
const exposureRoutes = require("./routes/exposures");
const removalRoutes = require("./routes/removals");
const settingsRoutes = require("./routes/settings");
const aiRoutes = require("./routes/ai");
const { initDb } = require("./db/database");
const { startScheduler } = require("./services/scheduler");
const { printStatus } = require("./services/secrets");
const app = express();
const PORT = parseInt(process.env.PORT, 10) || 3060;
/* ── Security headers ─────────────────────────────────────── */
app.use(
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
fontSrc: ["'self'", "data:"],
imgSrc: ["'self'", "data:", "blob:"],
connectSrc: ["'self'"],
objectSrc: ["'none'"],
frameAncestors: ["'none'"],
frameSrc: ["'none'"],
formAction: ["'self'"],
baseUri: ["'self'"],
workerSrc: ["'none'"],
},
},
}),
);
if (process.env.TRUST_PROXY === "1") app.set("trust proxy", 1);
app.use(cookieParser());
app.use(express.json({ limit: "512kb" }));
app.use(express.urlencoded({ extended: false, limit: "512kb" }));
/* ── Rate limiting ────────────────────────────────────────── */
const rlKey =
process.env.TRUST_PROXY === "1"
? undefined
: (req) => req.socket.remoteAddress || "unknown";
app.use(
"/api",
rateLimit({
windowMs: 60 * 1000,
max: 300,
standardHeaders: true,
legacyHeaders: false,
keyGenerator: rlKey,
}),
);
/* ── Never cache JS / CSS ─────────────────────────────────── */
app.use((req, res, next) => {
if (/\.(js|css)$/.test(req.path)) res.set("Cache-Control", "no-store");
next();
});
/* ── Static files ─────────────────────────────────────────── */
app.use(express.static(path.join(__dirname, "public")));
/* ── API routes ───────────────────────────────────────────── */
app.use("/api/auth", authRoutes);
app.use("/api/profiles", profileRoutes);
app.use("/api/brokers", brokerRoutes);
app.use("/api/scan", scanRoutes);
app.use("/api/exposures", exposureRoutes);
app.use("/api/removals", removalRoutes);
app.use("/api/settings", settingsRoutes);
app.use("/api/ai", aiRoutes);
/* ── SPA fallback ─────────────────────────────────────────── */
app.get("*", (req, res) => {
if (req.path.startsWith("/api")) {
return res.status(404).json({ error: "Not found" });
}
res.sendFile(path.join(__dirname, "public", "index.html"));
});
/* ── Global error handler ─────────────────────────────────── */
app.use((err, req, res, _next) => {
console.error(err);
const isProd = process.env.NODE_ENV === "production";
res.status(err.status || 500).json({
error: isProd ? "Internal error" : err.message || "Internal error",
});
});
/* ── Boot ─────────────────────────────────────────────────── */
initDb();
startScheduler();
printStatus();
app.listen(PORT, () => {
console.log(`NeoDataRemoval running on http://localhost:${PORT}`);
console.log(
"No .env file required — configure everything via Settings → System.",
);
});