mirror of
https://github.com/fosrl/pangolin.git
synced 2025-07-31 08:04:54 +02:00
Merge branch 'dev' into clients-pops
This commit is contained in:
commit
0e87b6e48b
21 changed files with 231 additions and 208 deletions
|
@ -5,6 +5,12 @@ import { withReplicas } from "drizzle-orm/pg-core";
|
|||
function createDb() {
|
||||
const config = readConfigFile();
|
||||
|
||||
if (!config.postgres) {
|
||||
throw new Error(
|
||||
"Postgres configuration is missing in the configuration file."
|
||||
);
|
||||
}
|
||||
|
||||
const connectionString = config.postgres?.connection_string;
|
||||
const replicaConnections = config.postgres?.replicas || [];
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import path from "path";
|
|||
import fs from "fs/promises";
|
||||
import { APP_PATH } from "@server/lib/consts";
|
||||
import { existsSync, mkdirSync } from "fs";
|
||||
import { readConfigFile } from "@server/lib/readConfigFile";
|
||||
|
||||
export const location = path.join(APP_PATH, "db", "db.sqlite");
|
||||
export const exists = await checkFileExists(location);
|
||||
|
@ -13,8 +12,6 @@ export const exists = await checkFileExists(location);
|
|||
bootstrapVolume();
|
||||
|
||||
function createDb() {
|
||||
const config = readConfigFile();
|
||||
|
||||
const sqlite = new Database(location);
|
||||
return DrizzleSqlite(sqlite, { schema });
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { SupporterKey, supporterKey } from "@server/db";
|
|||
import { eq } from "drizzle-orm";
|
||||
import { license } from "@server/license/license";
|
||||
import { configSchema, readConfigFile } from "./readConfigFile";
|
||||
import { fromError } from "zod-validation-error";
|
||||
|
||||
export class Config {
|
||||
private rawConfig!: z.infer<typeof configSchema>;
|
||||
|
@ -20,7 +21,35 @@ export class Config {
|
|||
}
|
||||
|
||||
public load() {
|
||||
const parsedConfig = readConfigFile();
|
||||
const environment = readConfigFile();
|
||||
|
||||
const {
|
||||
data: parsedConfig,
|
||||
success,
|
||||
error
|
||||
} = configSchema.safeParse(environment);
|
||||
|
||||
if (!success) {
|
||||
const errors = fromError(error);
|
||||
throw new Error(`Invalid configuration file: ${errors}`);
|
||||
}
|
||||
|
||||
if (process.env.APP_BASE_DOMAIN) {
|
||||
console.log(
|
||||
"WARNING: You're using deprecated environment variables. Transition to the configuration file. https://docs.fossorial.io/"
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
// @ts-ignore
|
||||
parsedConfig.users ||
|
||||
process.env.USERS_SERVERADMIN_EMAIL ||
|
||||
process.env.USERS_SERVERADMIN_PASSWORD
|
||||
) {
|
||||
console.log(
|
||||
"WARNING: Your admin credentials are still in the config file or environment variables. This method of setting admin credentials is no longer supported. It is recommended to remove them."
|
||||
);
|
||||
}
|
||||
|
||||
process.env.APP_VERSION = APP_VERSION;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import path from "path";
|
|||
import { fileURLToPath } from "url";
|
||||
|
||||
// This is a placeholder value replaced by the build process
|
||||
export const APP_VERSION = "1.5.1";
|
||||
export const APP_VERSION = "1.6.0";
|
||||
|
||||
export const __FILENAME = fileURLToPath(import.meta.url);
|
||||
export const __DIRNAME = path.dirname(__FILENAME);
|
||||
|
|
|
@ -295,24 +295,11 @@ export function readConfigFile() {
|
|||
environment = loadConfig(configFilePath2);
|
||||
}
|
||||
|
||||
if (process.env.APP_BASE_DOMAIN) {
|
||||
console.log(
|
||||
"You're using deprecated environment variables. Transition to the configuration file. https://docs.fossorial.io/"
|
||||
);
|
||||
}
|
||||
|
||||
if (!environment) {
|
||||
throw new Error(
|
||||
"No configuration file found. Please create one. https://docs.fossorial.io/"
|
||||
);
|
||||
}
|
||||
|
||||
const parsedConfig = configSchema.safeParse(environment);
|
||||
|
||||
if (!parsedConfig.success) {
|
||||
const errors = fromError(parsedConfig.error);
|
||||
throw new Error(`Invalid configuration file: ${errors}`);
|
||||
}
|
||||
|
||||
return parsedConfig.data;
|
||||
return environment;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { rateLimit } from "express-rate-limit";
|
||||
import { MemoryStore, rateLimit, Store } from "express-rate-limit";
|
||||
import createHttpError from "http-errors";
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import logger from "@server/logger";
|
||||
|
@ -6,7 +6,15 @@ import HttpCode from "@server/types/HttpCode";
|
|||
import config from "@server/lib/config";
|
||||
import { RedisStore } from "rate-limit-redis";
|
||||
import redisManager from "@server/db/redis";
|
||||
import { Command as RedisCommand } from "ioredis";
|
||||
|
||||
export let rateLimitStore: Store = new MemoryStore();
|
||||
if (config.getRawConfig().flags?.enable_redis) {
|
||||
const client = redisManager.client!;
|
||||
rateLimitStore = new RedisStore({
|
||||
sendCommand: async (command: string, ...args: string[]) =>
|
||||
(await client.call(command, args)) as any
|
||||
});
|
||||
}
|
||||
|
||||
export function rateLimitMiddleware({
|
||||
windowMin,
|
||||
|
@ -19,11 +27,8 @@ export function rateLimitMiddleware({
|
|||
type: "IP_ONLY" | "IP_AND_PATH";
|
||||
skipCondition?: (req: Request, res: Response) => boolean;
|
||||
}) {
|
||||
const enableRedis = config.getRawConfig().flags?.enable_redis;
|
||||
|
||||
let opts;
|
||||
if (type === "IP_AND_PATH") {
|
||||
opts = {
|
||||
return rateLimit({
|
||||
windowMs: windowMin * 60 * 1000,
|
||||
max,
|
||||
skip: skipCondition,
|
||||
|
@ -38,10 +43,11 @@ export function rateLimitMiddleware({
|
|||
return next(
|
||||
createHttpError(HttpCode.TOO_MANY_REQUESTS, message)
|
||||
);
|
||||
}
|
||||
} as any;
|
||||
},
|
||||
store: rateLimitStore
|
||||
});
|
||||
} else {
|
||||
opts = {
|
||||
return rateLimit({
|
||||
windowMs: windowMin * 60 * 1000,
|
||||
max,
|
||||
skip: skipCondition,
|
||||
|
@ -52,18 +58,8 @@ export function rateLimitMiddleware({
|
|||
createHttpError(HttpCode.TOO_MANY_REQUESTS, message)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (enableRedis) {
|
||||
const client = redisManager.client!;
|
||||
opts.store = new RedisStore({
|
||||
sendCommand: async (command: string, ...args: string[]) =>
|
||||
(await client.call(command, args)) as any
|
||||
});
|
||||
}
|
||||
|
||||
return rateLimit(opts);
|
||||
}
|
||||
|
||||
export default rateLimitMiddleware;
|
||||
|
|
|
@ -32,6 +32,7 @@ import {
|
|||
verifyIsLoggedInUser,
|
||||
verifyClientAccess,
|
||||
verifyApiKeyAccess,
|
||||
rateLimitStore,
|
||||
} from "@server/middlewares";
|
||||
import { verifyUserHasAction } from "../middlewares/verifyUserHasAction";
|
||||
import { ActionsEnum } from "@server/auth/actions";
|
||||
|
@ -782,7 +783,8 @@ authRouter.post(
|
|||
handler: (req, res, next) => {
|
||||
const message = `You can only request an email verification code ${3} times every ${15} minutes. Please try again later.`;
|
||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||
}
|
||||
},
|
||||
store: rateLimitStore
|
||||
}),
|
||||
auth.requestEmailVerificationCode
|
||||
);
|
||||
|
@ -802,7 +804,8 @@ authRouter.post(
|
|||
handler: (req, res, next) => {
|
||||
const message = `You can only request a password reset ${3} times every ${15} minutes. Please try again later.`;
|
||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||
}
|
||||
},
|
||||
store: rateLimitStore
|
||||
}),
|
||||
auth.requestPasswordReset
|
||||
);
|
||||
|
@ -821,7 +824,8 @@ authRouter.post(
|
|||
handler: (req, res, next) => {
|
||||
const message = `You can only request an email OTP ${10} times every ${15} minutes. Please try again later.`;
|
||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||
}
|
||||
},
|
||||
store: rateLimitStore
|
||||
}),
|
||||
resource.authWithWhitelist
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue