refactor(config): ♻️ Redo config structure from scratch, simplify validation code, improve checks, add support for loading sensitive data from paths

This commit is contained in:
Jesse Wierzbinski 2025-02-15 02:47:29 +01:00
parent d4afd84019
commit 54fd81f076
No known key found for this signature in database
118 changed files with 3892 additions and 5291 deletions

View file

@ -12,7 +12,7 @@ import {
eq,
inArray,
} from "drizzle-orm";
import { config } from "~/packages/config-manager/index.ts";
import { config } from "~/config.ts";
import { ApiError } from "../errors/api-error.ts";
import { BaseInterface } from "./base.ts";
import { User } from "./user.ts";
@ -147,7 +147,7 @@ export class Instance extends BaseInterface<typeof Instances> {
const { ok, raw, data } = await requester
.get(wellKnownUrl, {
// @ts-expect-error Bun extension
proxy: config.http.proxy.address,
proxy: config.http.proxy_address,
})
.catch((e) => ({
...(e as ResponseError).response,
@ -204,7 +204,7 @@ export class Instance extends BaseInterface<typeof Instances> {
links: { rel: string; href: string }[];
}>(wellKnownUrl, {
// @ts-expect-error Bun extension
proxy: config.http.proxy.address,
proxy: config.http.proxy_address,
})
.catch((e) => ({
...(
@ -256,7 +256,7 @@ export class Instance extends BaseInterface<typeof Instances> {
software: { version: string };
}>(metadataUrl.href, {
// @ts-expect-error Bun extension
proxy: config.http.proxy.address,
proxy: config.http.proxy_address,
})
.catch((e) => ({
...(

View file

@ -17,7 +17,7 @@ import {
eq,
inArray,
} from "drizzle-orm";
import { config } from "~/packages/config-manager/index.ts";
import { config } from "~/config.ts";
import { BaseInterface } from "./base.ts";
import { Note } from "./note.ts";
import { User } from "./user.ts";

View file

@ -15,9 +15,9 @@ import {
inArray,
} from "drizzle-orm";
import sharp from "sharp";
import { MediaBackendType } from "~/classes/config/schema.ts";
import type { Attachment as AttachmentSchema } from "~/classes/schemas/attachment.ts";
import { MediaBackendType } from "~/packages/config-manager/config.type";
import { config } from "~/packages/config-manager/index.ts";
import { config } from "~/config.ts";
import { ApiError } from "../errors/api-error.ts";
import { getMediaHash } from "../media/media-hasher.ts";
import { MediaJobType, mediaQueue } from "../queues/media.ts";
@ -135,11 +135,7 @@ export class Media extends BaseInterface<typeof Medias> {
switch (config.media.backend) {
case MediaBackendType.Local: {
const path = join(
config.media.local_uploads_folder,
hash,
fileName,
);
const path = join(config.media.uploads_path, hash, fileName);
await write(path, file);
@ -154,7 +150,7 @@ export class Media extends BaseInterface<typeof Medias> {
}
const client = new S3Client({
endpoint: config.s3.endpoint,
endpoint: config.s3.endpoint.origin,
region: config.s3.region,
bucket: config.s3.bucket_name,
accessKeyId: config.s3.access_key,
@ -260,21 +256,21 @@ export class Media extends BaseInterface<typeof Medias> {
}
private static checkFile(file: File): void {
if (file.size > config.validation.max_media_size) {
if (file.size > config.validation.media.max_bytes) {
throw new ApiError(
413,
`File too large, max size is ${config.validation.max_media_size} bytes`,
`File too large, max size is ${config.validation.media.max_bytes} bytes`,
);
}
if (
config.validation.enforce_mime_types &&
!config.validation.allowed_mime_types.includes(file.type)
config.validation.media.allowed_mime_types.length > 0 &&
!config.validation.media.allowed_mime_types.includes(file.type)
) {
throw new ApiError(
415,
`File type ${file.type} is not allowed`,
`Allowed types: ${config.validation.allowed_mime_types.join(", ")}`,
`Allowed types: ${config.validation.media.allowed_mime_types.join(", ")}`,
);
}
}

View file

@ -38,7 +38,7 @@ import {
parseTextMentions,
} from "~/classes/functions/status";
import type { Status as StatusSchema } from "~/classes/schemas/status.ts";
import { config } from "~/packages/config-manager";
import { config } from "~/config.ts";
import { DeliveryJobType, deliveryQueue } from "../queues/delivery.ts";
import type { Status } from "../schemas/status.ts";
import { Application } from "./application.ts";
@ -594,7 +594,7 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
const { data } = await requester.get(uri, {
// @ts-expect-error Bun extension
proxy: config.http.proxy.address,
proxy: config.http.proxy_address,
});
const note = await new EntityValidator().Note(data);

View file

@ -9,7 +9,7 @@ import {
eq,
inArray,
} from "drizzle-orm";
import { config } from "~/packages/config-manager/index.ts";
import { config } from "~/config.ts";
import { BaseInterface } from "./base.ts";
type ReactionType = InferSelectModel<typeof Reactions> & {

View file

@ -14,7 +14,7 @@ import {
eq,
inArray,
} from "drizzle-orm";
import { config } from "~/packages/config-manager/index.ts";
import { config } from "~/config.ts";
import { BaseInterface } from "./base.ts";
type RoleType = InferSelectModel<typeof Roles>;

View file

@ -1,6 +1,6 @@
import { Notes, Notifications, Users } from "@versia/kit/tables";
import { type SQL, gt } from "drizzle-orm";
import { config } from "~/packages/config-manager";
import { config } from "~/config.ts";
import { Note } from "./note.ts";
import { Notification } from "./notification.ts";
import { User } from "./user.ts";

View file

@ -47,7 +47,7 @@ import {
import { htmlToText } from "html-to-text";
import { findManyUsers } from "~/classes/functions/user";
import { searchManager } from "~/classes/search/search-manager";
import { type Config, config } from "~/packages/config-manager";
import { config } from "~/config.ts";
import type { KnownEntity } from "~/types/api.ts";
import { DeliveryJobType, deliveryQueue } from "../queues/delivery.ts";
import { PushJobType, pushQueue } from "../queues/push.ts";
@ -522,7 +522,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
});
// Also do push notifications
if (config.notifications.push.enabled) {
if (config.notifications.push) {
await this.notifyPush(notification.id, type, relatedUser, note);
}
}
@ -603,7 +603,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
}
if (instance.data.protocol === "activitypub") {
if (!config.federation.bridge.enabled) {
if (!config.federation.bridge) {
throw new Error("ActivityPub bridge is not enabled");
}
@ -627,7 +627,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
const requester = await User.getFederationRequester();
const output = await requester.get<Partial<VersiaUser>>(uri, {
// @ts-expect-error Bun extension
proxy: config.http.proxy.address,
proxy: config.http.proxy_address,
});
const { data: json } = output;
@ -815,10 +815,9 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
/**
* Get the user's avatar in raw URL format
* @param config The config to use
* @returns The raw URL for the user's avatar
*/
public getAvatarUrl(config: Config): URL {
public getAvatarUrl(): URL {
if (!this.avatar) {
return (
config.defaults.avatar ||
@ -912,10 +911,9 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
/**
* Get the user's header in raw URL format
* @param config The config to use
* @returns The raw URL for the user's header
*/
public getHeaderUrl(config: Config): URL | null {
public getHeaderUrl(): URL | null {
if (!this.header) {
return config.defaults.header ?? null;
}
@ -996,7 +994,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
JSON.stringify(entity),
);
if (config.debug.federation) {
if (config.debug?.federation) {
const logger = getLogger("federation");
// Log public key
@ -1014,8 +1012,8 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
*
* @returns The requester
*/
public static async getFederationRequester(): Promise<FederationRequester> {
const signatureConstructor = await SignatureConstructor.fromStringKey(
public static getFederationRequester(): FederationRequester {
const signatureConstructor = new SignatureConstructor(
config.instance.keys.private,
config.http.base_url,
);
@ -1087,7 +1085,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
try {
await new FederationRequester().post(inbox, entity, {
// @ts-expect-error Bun extension
proxy: config.http.proxy.address,
proxy: config.http.proxy_address,
headers: {
...headers.toJSON(),
"Content-Type": "application/json; charset=utf-8",
@ -1117,9 +1115,9 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
url:
user.uri ||
new URL(`/@${user.username}`, config.http.base_url).toString(),
avatar: proxyUrl(this.getAvatarUrl(config)).toString(),
header: this.getHeaderUrl(config)
? proxyUrl(this.getHeaderUrl(config) as URL).toString()
avatar: proxyUrl(this.getAvatarUrl()).toString(),
header: this.getHeaderUrl()
? proxyUrl(this.getHeaderUrl() as URL).toString()
: "",
locked: user.isLocked,
created_at: new Date(user.createdAt).toISOString(),
@ -1135,9 +1133,9 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
bot: user.isBot,
source: isOwnAccount ? user.source : undefined,
// TODO: Add static avatar and header
avatar_static: proxyUrl(this.getAvatarUrl(config)).toString(),
header_static: this.getHeaderUrl(config)
? proxyUrl(this.getHeaderUrl(config) as URL).toString()
avatar_static: proxyUrl(this.getAvatarUrl()).toString(),
header_static: this.getHeaderUrl()
? proxyUrl(this.getHeaderUrl() as URL).toString()
: "",
acct: this.getAcct(),
// TODO: Add these fields