organized routes and routes and added rate limiter

This commit is contained in:
Milo Schwartz 2024-10-02 00:04:40 -04:00
parent f1e77dfe42
commit 1a91dbb89c
No known key found for this signature in database
45 changed files with 241 additions and 181 deletions

46
package-lock.json generated
View file

@ -12,9 +12,11 @@
"@node-rs/argon2": "1.8.3", "@node-rs/argon2": "1.8.3",
"axios": "1.7.7", "axios": "1.7.7",
"better-sqlite3": "11.3.0", "better-sqlite3": "11.3.0",
"cookie-parser": "1.4.6",
"cors": "2.8.5", "cors": "2.8.5",
"drizzle-orm": "0.33.0", "drizzle-orm": "0.33.0",
"express": "4.21.0", "express": "4.21.0",
"express-rate-limit": "7.4.0",
"helmet": "7.1.0", "helmet": "7.1.0",
"http-errors": "2.0.0", "http-errors": "2.0.0",
"lucia": "3.2.0", "lucia": "3.2.0",
@ -29,6 +31,7 @@
"devDependencies": { "devDependencies": {
"@dotenvx/dotenvx": "1.14.2", "@dotenvx/dotenvx": "1.14.2",
"@types/better-sqlite3": "7.6.11", "@types/better-sqlite3": "7.6.11",
"@types/cookie-parser": "1.4.7",
"@types/cors": "2.8.17", "@types/cors": "2.8.17",
"@types/express": "5.0.0", "@types/express": "5.0.0",
"@types/node": "^20", "@types/node": "^20",
@ -5865,6 +5868,15 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/cookie-parser": {
"version": "1.4.7",
"resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.7.tgz",
"integrity": "sha512-Fvuyi354Z+uayxzIGCwYTayFKocfV7TuDYZClCdIP9ckhvAu/ixDtCB6qx2TT0FKjPLf1f3P/J1rgf6lPs64mw==",
"dev": true,
"dependencies": {
"@types/express": "*"
}
},
"node_modules/@types/cors": { "node_modules/@types/cors": {
"version": "2.8.17", "version": "2.8.17",
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
@ -7630,6 +7642,26 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/cookie-parser": {
"version": "1.4.6",
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
"dependencies": {
"cookie": "0.4.1",
"cookie-signature": "1.0.6"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/cookie-parser/node_modules/cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": { "node_modules/cookie-signature": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
@ -9355,6 +9387,20 @@
"node": ">= 0.10.0" "node": ">= 0.10.0"
} }
}, },
"node_modules/express-rate-limit": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.4.0.tgz",
"integrity": "sha512-v1204w3cXu5gCDmAvgvzI6qjzZzoMWKnyVDk3ACgfswTQLYiGen+r8w0VnXnGMmzEN/g8fwIQ4JrFFd4ZP6ssg==",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/express-rate-limit"
},
"peerDependencies": {
"express": "4 || 5 || ^5.0.0-beta.1"
}
},
"node_modules/express/node_modules/debug": { "node_modules/express/node_modules/debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",

View file

@ -5,7 +5,7 @@
"scripts": { "scripts": {
"dev": "dotenvx run -- tsx watch server/index.ts", "dev": "dotenvx run -- tsx watch server/index.ts",
"db:generate": "drizzle-kit generate", "db:generate": "drizzle-kit generate",
"db:push": "npx tsx scripts/migrate.ts", "db:push": "npx tsx db/migrate.ts",
"db:hydrate": "npx tsx scripts/hydrate.ts", "db:hydrate": "npx tsx scripts/hydrate.ts",
"db:studio": "drizzle-kit studio", "db:studio": "drizzle-kit studio",
"build": "next build && tsc --project tsconfig.server.json && tsc-alias -p tsconfig.server.json", "build": "next build && tsc --project tsconfig.server.json && tsc-alias -p tsconfig.server.json",
@ -16,9 +16,11 @@
"@node-rs/argon2": "1.8.3", "@node-rs/argon2": "1.8.3",
"axios": "1.7.7", "axios": "1.7.7",
"better-sqlite3": "11.3.0", "better-sqlite3": "11.3.0",
"cookie-parser": "1.4.6",
"cors": "2.8.5", "cors": "2.8.5",
"drizzle-orm": "0.33.0", "drizzle-orm": "0.33.0",
"express": "4.21.0", "express": "4.21.0",
"express-rate-limit": "7.4.0",
"helmet": "7.1.0", "helmet": "7.1.0",
"http-errors": "2.0.0", "http-errors": "2.0.0",
"lucia": "3.2.0", "lucia": "3.2.0",
@ -33,6 +35,7 @@
"devDependencies": { "devDependencies": {
"@dotenvx/dotenvx": "1.14.2", "@dotenvx/dotenvx": "1.14.2",
"@types/better-sqlite3": "7.6.11", "@types/better-sqlite3": "7.6.11",
"@types/cookie-parser": "1.4.7",
"@types/cors": "2.8.17", "@types/cors": "2.8.17",
"@types/express": "5.0.0", "@types/express": "5.0.0",
"@types/node": "^20", "@types/node": "^20",

View file

@ -9,7 +9,7 @@ const adapter = new DrizzleSQLiteAdapter(db, sessions, users);
export const lucia = new Lucia(adapter, { export const lucia = new Lucia(adapter, {
getUserAttributes: (attributes) => { getUserAttributes: (attributes) => {
return { return {
username: attributes.username, email: attributes.email,
}; };
}, },
// getSessionAttributes: (attributes) => { // getSessionAttributes: (attributes) => {
@ -19,7 +19,7 @@ export const lucia = new Lucia(adapter, {
// }, // },
sessionCookie: { sessionCookie: {
name: "session", name: "session",
expires: false, // session cookies have very long lifespan (2 years) expires: false,
attributes: { attributes: {
secure: environment.ENVIRONMENT === "prod", secure: environment.ENVIRONMENT === "prod",
sameSite: "strict", sameSite: "strict",
@ -31,7 +31,6 @@ export const lucia = new Lucia(adapter, {
export default lucia; export default lucia;
// IMPORTANT!
declare module "lucia" { declare module "lucia" {
interface Register { interface Register {
Lucia: typeof lucia; Lucia: typeof lucia;
@ -41,7 +40,7 @@ declare module "lucia" {
} }
interface DatabaseUserAttributes { interface DatabaseUserAttributes {
username: string; email: string;
passwordHash: string; passwordHash: string;
} }

View file

@ -5,46 +5,60 @@ import environment from "@server/environment";
import logger from "@server/logger"; import logger from "@server/logger";
import helmet from "helmet"; import helmet from "helmet";
import cors from "cors"; import cors from "cors";
import {
errorHandlerMiddleware,
rateLimitMiddleware,
} from "@server/middlewares";
import internal from "@server/routers/internal"; import internal from "@server/routers/internal";
import external from "@server/routers/external"; import { authenticated, unauthenticated } from "@server/routers/external";
import notFoundMiddleware from "./middlewares/notFound"; import cookieParser from "cookie-parser";
import { errorHandlerMiddleware } from "./middlewares/formatError";
const dev = environment.ENVIRONMENT !== "prod"; const dev = environment.ENVIRONMENT !== "prod";
const app = next({ dev }); const app = next({ dev });
const handle = app.getRequestHandler(); const handle = app.getRequestHandler();
const mainPort = environment.EXTERNAL_PORT;
const externalPort = environment.EXTERNAL_PORT;
const internalPort = environment.INTERNAL_PORT; const internalPort = environment.INTERNAL_PORT;
app.prepare().then(() => { app.prepare().then(() => {
// Main server // External server
const mainServer = express(); const externalServer = express();
mainServer.use(helmet());
mainServer.use(cors()); externalServer.use(helmet());
externalServer.use(cors());
externalServer.use(cookieParser());
externalServer.use(express.json());
externalServer.use(rateLimitMiddleware);
const prefix = `/api/v1`; const prefix = `/api/v1`;
mainServer.use(prefix, express.json(), external); externalServer.use(prefix, unauthenticated);
externalServer.use(prefix, authenticated);
// We are using NEXT from here on // We are using NEXT from here on
mainServer.all("*", (req: Request, res: Response) => { externalServer.all("*", (req: Request, res: Response) => {
const parsedUrl = parse(req.url!, true); const parsedUrl = parse(req.url!, true);
handle(req, res, parsedUrl); handle(req, res, parsedUrl);
}); });
mainServer.listen(mainPort, (err?: any) => { externalServer.listen(externalPort, (err?: any) => {
if (err) throw err; if (err) throw err;
logger.info(`Main server is running on http://localhost:${mainPort}`); logger.info(
`Main server is running on http://localhost:${externalPort}`,
);
}); });
mainServer.use(notFoundMiddleware); externalServer.use(errorHandlerMiddleware);
mainServer.use(errorHandlerMiddleware);
// Internal server // Internal server
const internalServer = express(); const internalServer = express();
internalServer.use(helmet()); internalServer.use(helmet());
internalServer.use(cors()); internalServer.use(cors());
internalServer.use(cookieParser());
internalServer.use(express.json());
internalServer.use(prefix, express.json(), internal); internalServer.use(prefix, internal);
internalServer.listen(internalPort, (err?: any) => { internalServer.listen(internalPort, (err?: any) => {
if (err) throw err; if (err) throw err;
@ -53,7 +67,3 @@ app.prepare().then(() => {
); );
}); });
}); });
process.on("SIGINT", () => {
process.exit(0);
});

View file

@ -10,7 +10,6 @@ export const errorHandlerMiddleware: ErrorRequestHandler = (
res: Response<ErrorResponse>, res: Response<ErrorResponse>,
next: NextFunction, next: NextFunction,
) => { ) => {
logger.error(error);
const statusCode = error.statusCode || HttpCode.INTERNAL_SERVER_ERROR; const statusCode = error.statusCode || HttpCode.INTERNAL_SERVER_ERROR;
res?.status(statusCode).send({ res?.status(statusCode).send({
data: null, data: null,

View file

@ -0,0 +1,3 @@
export * from "./notFound";
export * from "./rateLimit";
export * from "./formatError";

View file

@ -0,0 +1,20 @@
import { rateLimit } from "express-rate-limit";
import createHttpError from "http-errors";
import { NextFunction, Request, Response } from "express";
import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode";
const limit = 100;
const minutes = 1;
export const rateLimitMiddleware = rateLimit({
windowMs: minutes * 60 * 1000,
limit,
handler: (req: Request, res: Response, next: NextFunction) => {
const message = `Rate limit exceeded. You can make ${limit} requests every ${minutes} minute(s).`;
logger.warn(`Rate limit exceeded for IP ${req.ip}`);
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
},
});
export default rateLimitMiddleware;

View file

@ -0,0 +1,2 @@
export * from "./login";
export * from "./signup";

View file

@ -2,6 +2,7 @@ import { verify } from "@node-rs/argon2";
import lucia from "@server/auth"; import lucia from "@server/auth";
import db from "@server/db"; import db from "@server/db";
import { users } from "@server/db/schema"; import { users } from "@server/db/schema";
import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import response from "@server/utils/response"; import response from "@server/utils/response";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
@ -15,7 +16,11 @@ export const loginBodySchema = z.object({
password: z.string(), password: z.string(),
}); });
export async function login(req: Request, res: Response, next: NextFunction) { export async function login(
req: Request,
res: Response,
next: NextFunction,
): Promise<any> {
const parsedBody = loginBodySchema.safeParse(req.body); const parsedBody = loginBodySchema.safeParse(req.body);
if (!parsedBody.success) { if (!parsedBody.success) {
@ -29,6 +34,20 @@ export async function login(req: Request, res: Response, next: NextFunction) {
const { email, password } = parsedBody.data; const { email, password } = parsedBody.data;
const sessionId = req.cookies[lucia.sessionCookieName];
const { session: existingSession } = await lucia.validateSession(sessionId);
if (existingSession) {
return res.status(HttpCode.OK).send(
response<null>({
data: null,
success: true,
error: false,
message: "Already logged in",
status: HttpCode.OK,
}),
);
}
const existingUserRes = await db const existingUserRes = await db
.select() .select()
.from(users) .from(users)

View file

@ -28,7 +28,7 @@ export const signupBodySchema = z.object({
export type SignUpBody = z.infer<typeof signupBodySchema>; export type SignUpBody = z.infer<typeof signupBodySchema>;
export async function signup(req: Request, res: Response, next: NextFunction) { export async function signup(req: Request, res: Response, next: NextFunction): Promise<any> {
const parsedBody = signupBodySchema.safeParse(req.body); const parsedBody = signupBodySchema.safeParse(req.body);
if (!parsedBody.success) { if (!parsedBody.success) {

View file

@ -1,9 +0,0 @@
import { Router } from "express";
const badger = Router();
badger.get("/", (_, res) => {
res.status(200).json({ message: "Healthy" });
});
export default badger;

View file

@ -1,17 +1,48 @@
import { Router } from "express"; import { Router } from "express";
import gerbil from "./gerbil/gerbil"; import * as site from "./site";
import pangolin from "./pangolin/pangolin"; import * as org from "./org";
import global from "./global/global"; import * as resource from "./resource";
import * as target from "./target";
import * as user from "./user";
import * as auth from "./auth";
import HttpCode from "@server/types/HttpCode";
const unauth = Router(); // Root routes
export const unauthenticated = Router();
unauth.get("/", (_, res) => { unauthenticated.get("/", (_, res) => {
res.status(200).json({ message: "Healthy" }); res.status(HttpCode.OK).json({ message: "Healthy" });
}); });
unauth.use("/newt", gerbil); // Authenticated Root routes
unauth.use("/pangolin", pangolin); export const authenticated = Router();
unauth.use("/", global) authenticated.put("/site", site.createSite);
authenticated.get("/site/:siteId", site.getSite);
authenticated.post("/site/:siteId", site.updateSite);
authenticated.delete("/site/:siteId", site.deleteSite);
export default unauth; authenticated.put("/org", org.createOrg);
authenticated.get("/org/:orgId", org.getOrg);
authenticated.post("/org/:orgId", org.updateOrg);
authenticated.delete("/org/:orgId", org.deleteOrg);
authenticated.put("/resource", resource.createResource);
authenticated.get("/resource/:resourceId", resource.getResource);
authenticated.post("/resource/:resourceId", resource.updateResource);
authenticated.delete("/resource/:resourceId", resource.deleteResource);
authenticated.put("/target", target.createTarget);
authenticated.get("/target/:targetId", target.getTarget);
authenticated.post("/target/:targetId", target.updateTarget);
authenticated.delete("/target/:targetId", target.deleteTarget);
authenticated.get("/user/:userId", user.getUser);
authenticated.delete("/user/:userId", user.deleteUser);
// Auth routes
const authRouter = Router();
unauthenticated.use("/auth", authRouter);
authRouter.put("/signup", auth.signup);
authRouter.post("/login", auth.login);

View file

@ -1,14 +0,0 @@
import { Router } from "express";
import { getConfig } from "./getConfig";
import { receiveBandwidth } from "./receiveBandwidth";
const gerbil = Router();
gerbil.get("/", (_, res) => {
res.status(200).json({ message: "Healthy" });
});
gerbil.get("/get-config", getConfig);
gerbil.post("/receive-bandwidth", receiveBandwidth);
export default gerbil;

View file

@ -0,0 +1,2 @@
export * from "./getConfig";
export * from "./receiveBandwidth";

View file

@ -1,58 +0,0 @@
import { Router } from "express";
import { signup } from "@server/auth/signup";
import { login } from "@server/auth/login";
import { getSite } from "./site/getSite";
import { createSite } from "./site/createSite";
import { updateSite } from "./site/updateSite";
import { deleteSite } from "./site/deleteSite";
import { getOrg } from "./org/getOrg";
import { createOrg } from "./org/createOrg";
import { updateOrg } from "./org/updateOrg";
import { deleteOrg } from "./org/deleteOrg";
import { getResource } from "./resource/getResource";
import { createResource } from "./resource/createResource";
import { updateResource } from "./resource/updateResource";
import { deleteResource } from "./resource/deleteResource";
import { getTarget } from "./target/getTarget";
import { createTarget } from "./target/createTarget";
import { updateTarget } from "./target/updateTarget";
import { deleteTarget } from "./target/deleteTarget";
import { getUser } from "./user/getUser";
import { createUser } from "./user/createUser";
import { updateUser } from "./user/updateUser";
import { deleteUser } from "./user/deleteUser";
const global = Router();
global.get("/", (_, res) => {
res.status(200).json({ message: "Healthy" });
});
global.put("/site", createSite);
global.get("/site/:siteId", getSite);
global.post("/site/:siteId", updateSite);
global.delete("/site/:siteId", deleteSite);
global.put("/org", createOrg);
global.get("/org/:orgId", getOrg);
global.post("/org/:orgId", updateOrg);
global.delete("/org/:orgId", deleteOrg);
global.put("/resource", createResource);
global.get("/resource/resourceId", getResource);
global.post("/resource/resourceId", updateResource);
global.delete("/resource/resourceId", deleteResource);
global.put("/target", createTarget);
global.get("/target/:targetId", getTarget);
global.post("/target/:targetId", updateTarget);
global.delete("/target/:targetId", deleteTarget);
global.get("/user/:userId", getUser);
global.delete("/user/:userId", deleteUser);
// auth
global.post("/signup", signup);
global.post("/login", login);
export default global;

View file

@ -1,17 +1,23 @@
import { Router } from "express"; import { Router } from "express";
import gerbil from "./gerbil/gerbil"; import * as gerbil from "@server/routers/gerbil";
import badger from "./badger/badger"; import * as traefik from "@server/routers/traefik";
import { traefikConfigProvider } from "@server/traefik-config-provider"; import HttpCode from "@server/types/HttpCode";
const unauth = Router(); // Root routes
const internalRouter = Router();
unauth.get("/", (_, res) => { internalRouter.get("/", (_, res) => {
res.status(200).json({ message: "Healthy" }); res.status(HttpCode.OK).json({ message: "Healthy" });
}); });
unauth.use("/badger", badger); internalRouter.get("/traefik-config", traefik.traefikConfigProvider);
unauth.use("/gerbil", gerbil);
unauth.get("/traefik-config-provider", traefikConfigProvider); // Gerbil routes
const gerbilRouter = Router();
export default unauth; gerbilRouter.get("/get-config", gerbil.getConfig);
gerbilRouter.post("/receive-bandwidth", gerbil.receiveBandwidth);
internalRouter.use("/gerbil", gerbilRouter);
export default internalRouter;

View file

@ -1,9 +0,0 @@
import { Router } from "express";
const newt = Router();
newt.get("/", (_, res) => {
res.status(200).json({ message: "Healthy" });
});
export default newt;

View file

@ -11,7 +11,7 @@ const createOrgSchema = z.object({
domain: z.string().min(1).max(255), domain: z.string().min(1).max(255),
}); });
export async function createOrg(req: Request, res: Response, next: NextFunction) { export async function createOrg(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
const parsedBody = createOrgSchema.safeParse(req.body); const parsedBody = createOrgSchema.safeParse(req.body);
if (!parsedBody.success) { if (!parsedBody.success) {

View file

@ -11,7 +11,7 @@ const deleteOrgSchema = z.object({
orgId: z.string().transform(Number).pipe(z.number().int().positive()) orgId: z.string().transform(Number).pipe(z.number().int().positive())
}); });
export async function deleteOrg(req: Request, res: Response, next: NextFunction) { export async function deleteOrg(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
const parsedParams = deleteOrgSchema.safeParse(req.params); const parsedParams = deleteOrgSchema.safeParse(req.params);
if (!parsedParams.success) { if (!parsedParams.success) {

View file

@ -11,7 +11,7 @@ const getOrgSchema = z.object({
orgId: z.string().transform(Number).pipe(z.number().int().positive()) orgId: z.string().transform(Number).pipe(z.number().int().positive())
}); });
export async function getOrg(req: Request, res: Response, next: NextFunction) { export async function getOrg(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
const parsedParams = getOrgSchema.safeParse(req.params); const parsedParams = getOrgSchema.safeParse(req.params);
if (!parsedParams.success) { if (!parsedParams.success) {

View file

@ -0,0 +1,4 @@
export * from "./getOrg";
export * from "./createOrg";
export * from "./deleteOrg";
export * from "./updateOrg";

View file

@ -18,7 +18,7 @@ const updateOrgBodySchema = z.object({
message: "At least one field must be provided for update" message: "At least one field must be provided for update"
}); });
export async function updateOrg(req: Request, res: Response, next: NextFunction) { export async function updateOrg(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
const parsedParams = updateOrgParamsSchema.safeParse(req.params); const parsedParams = updateOrgParamsSchema.safeParse(req.params);
if (!parsedParams.success) { if (!parsedParams.success) {

View file

@ -1,9 +0,0 @@
import { Router } from "express";
const pangolin = Router();
pangolin.get("/", (_, res) => {
res.status(200).json({ message: "Healthy" });
});
export default pangolin;

View file

@ -13,7 +13,7 @@ const createResourceSchema = z.object({
subdomain: z.string().min(1).max(255).optional(), subdomain: z.string().min(1).max(255).optional(),
}); });
export async function createResource(req: Request, res: Response, next: NextFunction) { export async function createResource(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
// Validate request body // Validate request body
const parsedBody = createResourceSchema.safeParse(req.body); const parsedBody = createResourceSchema.safeParse(req.body);

View file

@ -12,7 +12,7 @@ const deleteResourceSchema = z.object({
resourceId: z.string().uuid() resourceId: z.string().uuid()
}); });
export async function deleteResource(req: Request, res: Response, next: NextFunction) { export async function deleteResource(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
// Validate request parameters // Validate request parameters
const parsedParams = deleteResourceSchema.safeParse(req.params); const parsedParams = deleteResourceSchema.safeParse(req.params);

View file

@ -12,7 +12,7 @@ const getResourceSchema = z.object({
resourceId: z.string().uuid() resourceId: z.string().uuid()
}); });
export async function getResource(req: Request, res: Response, next: NextFunction) { export async function getResource(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
// Validate request parameters // Validate request parameters
const parsedParams = getResourceSchema.safeParse(req.params); const parsedParams = getResourceSchema.safeParse(req.params);

View file

@ -0,0 +1,4 @@
export * from "./getResource";
export * from "./createResource";
export * from "./deleteResource";
export * from "./updateResource";

View file

@ -20,7 +20,7 @@ const updateResourceBodySchema = z.object({
message: "At least one field must be provided for update" message: "At least one field must be provided for update"
}); });
export async function updateResource(req: Request, res: Response, next: NextFunction) { export async function updateResource(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
// Validate request parameters // Validate request parameters
const parsedParams = updateResourceParamsSchema.safeParse(req.params); const parsedParams = updateResourceParamsSchema.safeParse(req.params);

View file

@ -4,7 +4,7 @@ import HttpCode from '@server/types/HttpCode';
// define zod type here // define zod type here
export async function createSite(req: Request, res: Response, next: NextFunction) { export async function createSite(req: Request, res: Response, next: NextFunction): Promise<any> {
return res.status(HttpCode.OK).send( return res.status(HttpCode.OK).send(
response<null>({ response<null>({
data: null, data: null,

View file

@ -12,7 +12,7 @@ const deleteSiteSchema = z.object({
siteId: z.string().transform(Number).pipe(z.number().int().positive()) siteId: z.string().transform(Number).pipe(z.number().int().positive())
}); });
export async function deleteSite(req: Request, res: Response, next: NextFunction) { export async function deleteSite(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
// Validate request parameters // Validate request parameters
const parsedParams = deleteSiteSchema.safeParse(req.params); const parsedParams = deleteSiteSchema.safeParse(req.params);

View file

@ -12,7 +12,7 @@ const getSiteSchema = z.object({
siteId: z.string().transform(Number).pipe(z.number().int().positive()) siteId: z.string().transform(Number).pipe(z.number().int().positive())
}); });
export async function getSite(req: Request, res: Response, next: NextFunction) { export async function getSite(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
// Validate request parameters // Validate request parameters
const parsedParams = getSiteSchema.safeParse(req.params); const parsedParams = getSiteSchema.safeParse(req.params);

View file

@ -0,0 +1,4 @@
export * from "./getSite";
export * from "./createSite";
export * from "./deleteSite";
export * from "./updateSite";

View file

@ -25,7 +25,7 @@ const updateSiteBodySchema = z.object({
message: "At least one field must be provided for update" message: "At least one field must be provided for update"
}); });
export async function updateSite(req: Request, res: Response, next: NextFunction) { export async function updateSite(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
// Validate request parameters // Validate request parameters
const parsedParams = updateSiteParamsSchema.safeParse(req.params); const parsedParams = updateSiteParamsSchema.safeParse(req.params);

View file

@ -15,7 +15,7 @@ const createTargetSchema = z.object({
enabled: z.boolean().default(true), enabled: z.boolean().default(true),
}); });
export async function createTarget(req: Request, res: Response, next: NextFunction) { export async function createTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
const parsedBody = createTargetSchema.safeParse(req.body); const parsedBody = createTargetSchema.safeParse(req.body);
if (!parsedBody.success) { if (!parsedBody.success) {

View file

@ -11,7 +11,7 @@ const deleteTargetSchema = z.object({
targetId: z.string().transform(Number).pipe(z.number().int().positive()) targetId: z.string().transform(Number).pipe(z.number().int().positive())
}); });
export async function deleteTarget(req: Request, res: Response, next: NextFunction) { export async function deleteTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
const parsedParams = deleteTargetSchema.safeParse(req.params); const parsedParams = deleteTargetSchema.safeParse(req.params);
if (!parsedParams.success) { if (!parsedParams.success) {

View file

@ -11,7 +11,7 @@ const getTargetSchema = z.object({
targetId: z.string().transform(Number).pipe(z.number().int().positive()) targetId: z.string().transform(Number).pipe(z.number().int().positive())
}); });
export async function getTarget(req: Request, res: Response, next: NextFunction) { export async function getTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
const parsedParams = getTargetSchema.safeParse(req.params); const parsedParams = getTargetSchema.safeParse(req.params);
if (!parsedParams.success) { if (!parsedParams.success) {

View file

@ -0,0 +1,4 @@
export * from "./getTarget";
export * from "./createTarget";
export * from "./deleteTarget";
export * from "./updateTarget";

View file

@ -21,7 +21,7 @@ const updateTargetBodySchema = z.object({
message: "At least one field must be provided for update" message: "At least one field must be provided for update"
}); });
export async function updateTarget(req: Request, res: Response, next: NextFunction) { export async function updateTarget(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
const parsedParams = updateTargetParamsSchema.safeParse(req.params); const parsedParams = updateTargetParamsSchema.safeParse(req.params);
if (!parsedParams.success) { if (!parsedParams.success) {

View file

@ -0,0 +1 @@
export * from "./getTraefikConfig";

View file

@ -11,7 +11,7 @@ const deleteUserSchema = z.object({
userId: z.string().uuid() userId: z.string().uuid()
}); });
export async function deleteUser(req: Request, res: Response, next: NextFunction) { export async function deleteUser(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
const parsedParams = deleteUserSchema.safeParse(req.params); const parsedParams = deleteUserSchema.safeParse(req.params);
if (!parsedParams.success) { if (!parsedParams.success) {

View file

@ -11,7 +11,7 @@ const getUserSchema = z.object({
userId: z.string().uuid() userId: z.string().uuid()
}); });
export async function getUser(req: Request, res: Response, next: NextFunction) { export async function getUser(req: Request, res: Response, next: NextFunction): Promise<any> {
try { try {
const parsedParams = getUserSchema.safeParse(req.params); const parsedParams = getUserSchema.safeParse(req.params);
if (!parsedParams.success) { if (!parsedParams.success) {

View file

@ -0,0 +1,2 @@
export * from "./getUser";
export * from "./deleteUser";