Merge branch 'dev' into clients-pops

This commit is contained in:
miloschwartz 2025-06-22 17:50:39 -04:00
commit 0e87b6e48b
No known key found for this signature in database
21 changed files with 231 additions and 208 deletions

View file

@ -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 || [];

View file

@ -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 });
}

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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
);