Skip to content

Commit 5112459

Browse files
authored
chore(config): Harden runtime config and audit fixes (#181)
* chore(server): Harden runtime configuration * chore(deps): Refresh safe audit fixes
1 parent 763a81a commit 5112459

8 files changed

Lines changed: 173 additions & 76 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,16 @@ MONGODB_URI="<uri>"
4848
```
4949

5050
4. Save the file.
51+
5. Optional: add `CORS_ORIGIN="<origin>"` if the client is not served from
52+
`http://localhost:3000`.
5153

5254
### Running the Server
5355

5456
1. Open a terminal window.
5557
2. Ensure that you're in the root directory: `fairsplit`
5658
3. Navigate to the server directory: `cd server`
5759
4. Install dependencies: `npm install`
58-
5. Run the server: `node app`
60+
5. Run the server: `npm start`
5961

6062
### Running the Client
6163

client/package-lock.json

Lines changed: 59 additions & 33 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/app.js

Lines changed: 15 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,23 @@
11
const express = require("express");
2-
const mongoose = require("mongoose");
3-
require("dotenv").config();
42
const cors = require("cors");
53
const usersRouter = require("./routes/users.js");
64
const expensesRouter = require("./routes/expenses.js");
75
const debtsRouter = require("./routes/debts.js");
86
const { errorHandler } = require("./middleware/errors");
97

10-
const app = express();
11-
app.use(express.json());
12-
app.use(
13-
cors({
14-
origin: "http://localhost:3000",
15-
}),
16-
);
17-
app.use(usersRouter);
18-
app.use(expensesRouter);
19-
app.use(debtsRouter);
20-
app.use(errorHandler);
8+
function createApp({ corsOrigin = "http://localhost:3000" } = {}) {
9+
const app = express();
10+
app.use(express.json());
11+
app.use(
12+
cors({
13+
origin: corsOrigin,
14+
}),
15+
);
16+
app.use(usersRouter);
17+
app.use(expensesRouter);
18+
app.use(debtsRouter);
19+
app.use(errorHandler);
20+
return app;
21+
}
2122

22-
// !IMPORTANT: Create .env file with password
23-
const password = process.env.PASSWORD || "changePasswordHere";
24-
let devUrl = `mongodb+srv://admin:${password}@fairsplit.fjvgxmg.mongodb.net/?retryWrites=true&w=majority`;
25-
const mongoDB = process.env.MONGODB_URI || devUrl;
26-
// Set up the Mongoose connection.
27-
mongoose
28-
.connect(mongoDB)
29-
.catch((error) => console.error("MongoDB connection error:", error));
30-
let db = mongoose.connection;
31-
32-
// Bind the connection to an error event to get notification of connection
33-
// errors.
34-
db.on("error", console.error.bind(console, "MongoDB connection error:"));
35-
// Displays a success message when the connection is successfully made.
36-
db.once("open", function () {
37-
console.log("Connected successfully.");
38-
});
39-
40-
// Set up the port to listen on.
41-
const port = process.env.PORT || 3001;
42-
app.listen(port, () => {
43-
console.log(`Server is running at port ${port}.`);
44-
});
45-
46-
module.exports = app;
23+
module.exports = { createApp };

server/config.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
function parsePort(port) {
2+
const parsedPort = Number(port);
3+
if (!Number.isInteger(parsedPort) || parsedPort < 1) {
4+
throw new Error("PORT must be a positive integer.");
5+
}
6+
return parsedPort;
7+
}
8+
9+
function getServerConfig(env = process.env) {
10+
if (!env.MONGODB_URI) {
11+
throw new Error("MONGODB_URI must be set.");
12+
}
13+
14+
return {
15+
corsOrigin: env.CORS_ORIGIN || "http://localhost:3000",
16+
mongoDB: env.MONGODB_URI,
17+
port: parsePort(env.PORT || "3001"),
18+
};
19+
}
20+
21+
module.exports = { getServerConfig, parsePort };

server/config.test.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const { getServerConfig, parsePort } = require("./config");
2+
3+
describe("server config", () => {
4+
test("requires MONGODB_URI", () => {
5+
expect(() => getServerConfig({})).toThrow(/MONGODB_URI/);
6+
});
7+
8+
test("uses safe local defaults for optional runtime config", () => {
9+
expect(
10+
getServerConfig({
11+
MONGODB_URI: "mongodb://localhost:27017/fairsplit",
12+
}),
13+
).toEqual({
14+
corsOrigin: "http://localhost:3000",
15+
mongoDB: "mongodb://localhost:27017/fairsplit",
16+
port: 3001,
17+
});
18+
});
19+
20+
test("reads explicit runtime config", () => {
21+
expect(
22+
getServerConfig({
23+
CORS_ORIGIN: "https://example.com",
24+
MONGODB_URI: "mongodb://localhost:27017/fairsplit",
25+
PORT: "8080",
26+
}),
27+
).toEqual({
28+
corsOrigin: "https://example.com",
29+
mongoDB: "mongodb://localhost:27017/fairsplit",
30+
port: 8080,
31+
});
32+
});
33+
34+
test.each(["0", "-1", "abc", "3000.5"])("rejects invalid PORT %s", (port) => {
35+
expect(() => parsePort(port)).toThrow(/PORT/);
36+
});
37+
});

server/package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
"name": "server",
33
"version": "1.0.0",
44
"description": "",
5-
"main": "app.js",
5+
"main": "server.js",
66
"scripts": {
7+
"start": "node server.js",
78
"test": "jest --verbose --coverage"
89
},
910
"keywords": [],

server/server.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const mongoose = require("mongoose");
2+
require("dotenv").config();
3+
4+
const { createApp } = require("./app");
5+
const { getServerConfig } = require("./config");
6+
7+
async function startServer() {
8+
const config = getServerConfig();
9+
const app = createApp({ corsOrigin: config.corsOrigin });
10+
11+
mongoose.connection.on(
12+
"error",
13+
console.error.bind(console, "MongoDB connection error:"),
14+
);
15+
mongoose.connection.once("open", () => {
16+
console.log("Connected successfully.");
17+
});
18+
19+
await mongoose.connect(config.mongoDB);
20+
21+
return app.listen(config.port, () => {
22+
console.log(`Server is running at port ${config.port}.`);
23+
});
24+
}
25+
26+
if (require.main === module) {
27+
startServer().catch((error) => {
28+
console.error("Failed to start server:", error.message);
29+
process.exit(1);
30+
});
31+
}
32+
33+
module.exports = { startServer };

0 commit comments

Comments
 (0)