add testing libs, separate app from server
This commit is contained in:
parent
3086289d68
commit
0efd3f4056
1091
server/package-lock.json
generated
1091
server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,8 +4,9 @@
|
||||
"description": "",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"test": "echo 'No tests set up for the server.'",
|
||||
"coverage": "echo 'No coverage set up for the server.'",
|
||||
"test-watch": "vitest",
|
||||
"test": "vitest run",
|
||||
"coverage": "vitest run --coverage",
|
||||
"build": "npx tsc",
|
||||
"dev": "npx nodemon ./server.ts | npx pino-colada"
|
||||
},
|
||||
@ -31,6 +32,7 @@
|
||||
"prisma": "^4.0.0",
|
||||
"supertest": "^6.2.3",
|
||||
"ts-node": "^10.8.1",
|
||||
"typescript": "^4.7.4"
|
||||
"typescript": "^4.7.4",
|
||||
"vitest": "^0.17.1"
|
||||
}
|
||||
}
|
||||
|
@ -1,98 +1,8 @@
|
||||
import "dotenv/config";
|
||||
import express, { Express, Request } from "express";
|
||||
import { PrismaClient, EncryptedNote } from "@prisma/client";
|
||||
import { addDays } from "./util";
|
||||
import helmet from "helmet";
|
||||
import rateLimit from "express-rate-limit";
|
||||
import pinoHttp from "pino-http";
|
||||
import logger from "./logger";
|
||||
|
||||
// Initialize middleware clients
|
||||
const prisma = new PrismaClient();
|
||||
const app: Express = express();
|
||||
app.use(express.json());
|
||||
|
||||
// configure logging
|
||||
app.use(
|
||||
pinoHttp({
|
||||
logger: logger,
|
||||
})
|
||||
);
|
||||
|
||||
// configure Helmet and CORS
|
||||
app.use(
|
||||
helmet({
|
||||
crossOriginResourcePolicy: {
|
||||
policy: process.env.ENVIRONMENT == "dev" ? "cross-origin" : "same-origin",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
// Apply rate limiting
|
||||
const postLimiter = rateLimit({
|
||||
windowMs: parseInt(process.env.POST_LIMIT_WINDOW_SECONDS as string) * 1000,
|
||||
max: parseInt(process.env.POST_LIMIT as string), // Limit each IP to X requests per window
|
||||
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
||||
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
||||
});
|
||||
import logger from "./src/logger";
|
||||
import app from "./src/app";
|
||||
|
||||
// start the Express server
|
||||
app.listen(process.env.PORT, () => {
|
||||
logger.info(`server started at port ${process.env.PORT}`);
|
||||
});
|
||||
|
||||
// Post new encrypted note
|
||||
app.post(
|
||||
"/api/note/",
|
||||
postLimiter,
|
||||
(req: Request<{}, {}, EncryptedNote>, res, next) => {
|
||||
const note = req.body;
|
||||
prisma.encryptedNote
|
||||
.create({
|
||||
data: { ...note, expire_time: addDays(new Date(), 30) },
|
||||
})
|
||||
.then((savedNote) => {
|
||||
res.json({
|
||||
view_url: `${process.env.FRONTEND_URL}/note/${savedNote.id}`,
|
||||
expire_time: savedNote.expire_time,
|
||||
});
|
||||
})
|
||||
.catch(next);
|
||||
}
|
||||
);
|
||||
|
||||
// Get encrypted note
|
||||
app.get("/api/note/:id", (req, res, next) => {
|
||||
prisma.encryptedNote
|
||||
.findUnique({
|
||||
where: { id: req.params.id },
|
||||
})
|
||||
.then((note) => {
|
||||
if (note != null) {
|
||||
res.send(note);
|
||||
}
|
||||
res.status(404).send();
|
||||
})
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
// Clean up expired notes periodically
|
||||
const interval =
|
||||
Math.max(parseInt(<string>process.env.CLEANUP_INTERVAL_SECONDS) || 1, 1) *
|
||||
1000;
|
||||
setInterval(async () => {
|
||||
try {
|
||||
logger.info("[Cleanup] Cleaning up expired notes...");
|
||||
const deleted = await prisma.encryptedNote.deleteMany({
|
||||
where: {
|
||||
expire_time: {
|
||||
lte: new Date(),
|
||||
},
|
||||
},
|
||||
});
|
||||
logger.info(`[Cleanup] Deleted ${deleted.count} expired notes.`);
|
||||
} catch (err) {
|
||||
logger.error(`[Cleanup] Error cleaning expired notes:`);
|
||||
logger.error(err);
|
||||
}
|
||||
}, interval);
|
||||
|
95
server/src/app.ts
Normal file
95
server/src/app.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import "dotenv/config";
|
||||
import express, { Express, Request } from "express";
|
||||
import { PrismaClient, EncryptedNote } from "@prisma/client";
|
||||
import { addDays } from "./util";
|
||||
import helmet from "helmet";
|
||||
import rateLimit from "express-rate-limit";
|
||||
import pinoHttp from "pino-http";
|
||||
import logger from "./logger";
|
||||
|
||||
// Initialize middleware clients
|
||||
const prisma = new PrismaClient();
|
||||
const app: Express = express();
|
||||
app.use(express.json());
|
||||
|
||||
// configure logging
|
||||
app.use(
|
||||
pinoHttp({
|
||||
logger: logger,
|
||||
})
|
||||
);
|
||||
|
||||
// configure Helmet and CORS
|
||||
app.use(
|
||||
helmet({
|
||||
crossOriginResourcePolicy: {
|
||||
policy: process.env.ENVIRONMENT == "dev" ? "cross-origin" : "same-origin",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
// Apply rate limiting
|
||||
const postLimiter = rateLimit({
|
||||
windowMs: parseInt(process.env.POST_LIMIT_WINDOW_SECONDS as string) * 1000,
|
||||
max: parseInt(process.env.POST_LIMIT as string), // Limit each IP to X requests per window
|
||||
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
|
||||
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
|
||||
});
|
||||
|
||||
// Post new encrypted note
|
||||
app.post(
|
||||
"/api/note/",
|
||||
postLimiter,
|
||||
(req: Request<{}, {}, EncryptedNote>, res, next) => {
|
||||
const note = req.body;
|
||||
prisma.encryptedNote
|
||||
.create({
|
||||
data: { ...note, expire_time: addDays(new Date(), 30) },
|
||||
})
|
||||
.then((savedNote) => {
|
||||
res.json({
|
||||
view_url: `${process.env.FRONTEND_URL}/note/${savedNote.id}`,
|
||||
expire_time: savedNote.expire_time,
|
||||
});
|
||||
})
|
||||
.catch(next);
|
||||
}
|
||||
);
|
||||
|
||||
// Get encrypted note
|
||||
app.get("/api/note/:id", (req, res, next) => {
|
||||
prisma.encryptedNote
|
||||
.findUnique({
|
||||
where: { id: req.params.id },
|
||||
})
|
||||
.then((note) => {
|
||||
if (note != null) {
|
||||
res.send(note);
|
||||
}
|
||||
res.status(404).send();
|
||||
})
|
||||
.catch(next);
|
||||
});
|
||||
|
||||
// Clean up expired notes periodically
|
||||
const interval =
|
||||
Math.max(parseInt(<string>process.env.CLEANUP_INTERVAL_SECONDS) || 1, 1) *
|
||||
1000;
|
||||
setInterval(async () => {
|
||||
try {
|
||||
logger.info("[Cleanup] Cleaning up expired notes...");
|
||||
const deleted = await prisma.encryptedNote.deleteMany({
|
||||
where: {
|
||||
expire_time: {
|
||||
lte: new Date(),
|
||||
},
|
||||
},
|
||||
});
|
||||
logger.info(`[Cleanup] Deleted ${deleted.count} expired notes.`);
|
||||
} catch (err) {
|
||||
logger.error(`[Cleanup] Error cleaning expired notes:`);
|
||||
logger.error(err);
|
||||
}
|
||||
}, interval);
|
||||
|
||||
export default app;
|
@ -9,6 +9,7 @@
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
"noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied 'any' type. */,
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */,
|
||||
"types": ["vitest/globals"]
|
||||
}
|
||||
}
|
||||
|
7
server/vite.config.ts
Normal file
7
server/vite.config.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from "vitest/config";
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
// ...
|
||||
},
|
||||
});
|
Loading…
Reference in New Issue
Block a user