mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
Slight refactoring, begin work on major moderation overhaul
This commit is contained in:
parent
e05dca9fc1
commit
2bc9ff51ea
|
|
@ -2,7 +2,7 @@
|
|||
* Usage: TOKEN=your_token_here bun benchmark:timeline <request_count>
|
||||
*/
|
||||
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import chalk from "chalk";
|
||||
|
||||
const config = getConfig();
|
||||
|
|
|
|||
443
classes/configmanager.ts
Normal file
443
classes/configmanager.ts
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
/**
|
||||
* @file configmanager.ts
|
||||
* @summary ConfigManager system to retrieve and modify system configuration
|
||||
* @description Can read from a hand-written file, config.toml, or from a machine-saved file, config.internal.toml
|
||||
* Fuses both and provides a way to retrieve individual values
|
||||
*/
|
||||
|
||||
import { parse, stringify } from "@iarna/toml";
|
||||
import { deepMerge, deepMergeArray } from "@merge";
|
||||
import chalk from "chalk";
|
||||
|
||||
const scanConfig = async () => {
|
||||
const config = Bun.file(process.cwd() + "/config/config.toml");
|
||||
|
||||
if (!(await config.exists())) {
|
||||
console.error(
|
||||
`${chalk.red(`✗`)} ${chalk.bold(
|
||||
"Error while reading config: "
|
||||
)} Config file not found`
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return parse(await config.text()) as ConfigType;
|
||||
};
|
||||
|
||||
// Creates the internal config with nothing in it if it doesnt exist
|
||||
const scanInternalConfig = async () => {
|
||||
const config = Bun.file(process.cwd() + "/config/config.internal.toml");
|
||||
|
||||
if (!(await config.exists())) {
|
||||
await Bun.write(config, "");
|
||||
}
|
||||
|
||||
return parse(await config.text()) as ConfigType;
|
||||
};
|
||||
|
||||
let config = await scanConfig();
|
||||
const internalConfig = await scanInternalConfig();
|
||||
|
||||
export interface ConfigType {
|
||||
database: {
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
password: string;
|
||||
database: string;
|
||||
};
|
||||
|
||||
redis: {
|
||||
queue: {
|
||||
host: string;
|
||||
port: number;
|
||||
password: string;
|
||||
database: number | null;
|
||||
};
|
||||
cache: {
|
||||
host: string;
|
||||
port: number;
|
||||
password: string;
|
||||
database: number | null;
|
||||
enabled: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
meilisearch: {
|
||||
host: string;
|
||||
port: number;
|
||||
api_key: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
signups: {
|
||||
tos_url: string;
|
||||
rules: string[];
|
||||
registration: boolean;
|
||||
};
|
||||
|
||||
oidc: {
|
||||
providers: {
|
||||
name: string;
|
||||
id: string;
|
||||
url: string;
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
icon: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
http: {
|
||||
base_url: string;
|
||||
bind: string;
|
||||
bind_port: string;
|
||||
banned_ips: string[];
|
||||
};
|
||||
|
||||
instance: {
|
||||
name: string;
|
||||
description: string;
|
||||
banner: string;
|
||||
logo: string;
|
||||
};
|
||||
|
||||
smtp: {
|
||||
server: string;
|
||||
port: number;
|
||||
username: string;
|
||||
password: string;
|
||||
tls: boolean;
|
||||
};
|
||||
|
||||
validation: {
|
||||
max_displayname_size: number;
|
||||
max_bio_size: number;
|
||||
max_username_size: number;
|
||||
max_note_size: number;
|
||||
max_avatar_size: number;
|
||||
max_header_size: number;
|
||||
max_media_size: number;
|
||||
max_media_attachments: number;
|
||||
max_media_description_size: number;
|
||||
max_poll_options: number;
|
||||
max_poll_option_size: number;
|
||||
min_poll_duration: number;
|
||||
max_poll_duration: number;
|
||||
|
||||
username_blacklist: string[];
|
||||
blacklist_tempmail: boolean;
|
||||
email_blacklist: string[];
|
||||
url_scheme_whitelist: string[];
|
||||
|
||||
enforce_mime_types: boolean;
|
||||
allowed_mime_types: string[];
|
||||
};
|
||||
|
||||
media: {
|
||||
backend: string;
|
||||
deduplicate_media: boolean;
|
||||
conversion: {
|
||||
convert_images: boolean;
|
||||
convert_to: string;
|
||||
};
|
||||
};
|
||||
|
||||
s3: {
|
||||
endpoint: string;
|
||||
access_key: string;
|
||||
secret_access_key: string;
|
||||
region: string;
|
||||
bucket_name: string;
|
||||
public_url: string;
|
||||
};
|
||||
|
||||
defaults: {
|
||||
visibility: string;
|
||||
language: string;
|
||||
avatar: string;
|
||||
header: string;
|
||||
};
|
||||
|
||||
email: {
|
||||
send_on_report: boolean;
|
||||
send_on_suspend: boolean;
|
||||
send_on_unsuspend: boolean;
|
||||
};
|
||||
|
||||
activitypub: {
|
||||
use_tombstones: boolean;
|
||||
reject_activities: string[];
|
||||
force_followers_only: string[];
|
||||
discard_reports: string[];
|
||||
discard_deletes: string[];
|
||||
discard_banners: string[];
|
||||
discard_avatars: string[];
|
||||
discard_updates: string[];
|
||||
discard_follows: string[];
|
||||
force_sensitive: string[];
|
||||
remove_media: string[];
|
||||
fetch_all_collection_members: boolean;
|
||||
authorized_fetch: boolean;
|
||||
};
|
||||
|
||||
filters: {
|
||||
note_filters: string[];
|
||||
username_filters: string[];
|
||||
displayname_filters: string[];
|
||||
bio_filters: string[];
|
||||
emoji_filters: string[];
|
||||
};
|
||||
|
||||
logging: {
|
||||
log_requests: boolean;
|
||||
log_requests_verbose: boolean;
|
||||
log_filters: boolean;
|
||||
};
|
||||
|
||||
ratelimits: {
|
||||
duration_coeff: number;
|
||||
max_coeff: number;
|
||||
};
|
||||
|
||||
custom_ratelimits: Record<
|
||||
string,
|
||||
{
|
||||
duration: number;
|
||||
max: number;
|
||||
}
|
||||
>;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export const configDefaults: ConfigType = {
|
||||
http: {
|
||||
bind: "http://0.0.0.0",
|
||||
bind_port: "8000",
|
||||
base_url: "http://lysand.localhost:8000",
|
||||
banned_ips: [],
|
||||
},
|
||||
database: {
|
||||
host: "localhost",
|
||||
port: 5432,
|
||||
username: "postgres",
|
||||
password: "postgres",
|
||||
database: "lysand",
|
||||
},
|
||||
redis: {
|
||||
queue: {
|
||||
host: "localhost",
|
||||
port: 6379,
|
||||
password: "",
|
||||
database: 0,
|
||||
},
|
||||
cache: {
|
||||
host: "localhost",
|
||||
port: 6379,
|
||||
password: "",
|
||||
database: 1,
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
meilisearch: {
|
||||
host: "localhost",
|
||||
port: 1491,
|
||||
api_key: "",
|
||||
enabled: false,
|
||||
},
|
||||
signups: {
|
||||
tos_url: "",
|
||||
rules: [],
|
||||
registration: false,
|
||||
},
|
||||
oidc: {
|
||||
providers: [],
|
||||
},
|
||||
instance: {
|
||||
banner: "",
|
||||
description: "",
|
||||
logo: "",
|
||||
name: "",
|
||||
},
|
||||
smtp: {
|
||||
password: "",
|
||||
port: 465,
|
||||
server: "",
|
||||
tls: true,
|
||||
username: "",
|
||||
},
|
||||
media: {
|
||||
backend: "local",
|
||||
deduplicate_media: true,
|
||||
conversion: {
|
||||
convert_images: false,
|
||||
convert_to: "webp",
|
||||
},
|
||||
},
|
||||
email: {
|
||||
send_on_report: false,
|
||||
send_on_suspend: false,
|
||||
send_on_unsuspend: false,
|
||||
},
|
||||
s3: {
|
||||
access_key: "",
|
||||
bucket_name: "",
|
||||
endpoint: "",
|
||||
public_url: "",
|
||||
region: "",
|
||||
secret_access_key: "",
|
||||
},
|
||||
validation: {
|
||||
max_displayname_size: 50,
|
||||
max_bio_size: 6000,
|
||||
max_note_size: 5000,
|
||||
max_avatar_size: 5_000_000,
|
||||
max_header_size: 5_000_000,
|
||||
max_media_size: 40_000_000,
|
||||
max_media_attachments: 10,
|
||||
max_media_description_size: 1000,
|
||||
max_poll_options: 20,
|
||||
max_poll_option_size: 500,
|
||||
min_poll_duration: 60,
|
||||
max_poll_duration: 1893456000,
|
||||
max_username_size: 30,
|
||||
|
||||
username_blacklist: [
|
||||
".well-known",
|
||||
"~",
|
||||
"about",
|
||||
"activities",
|
||||
"api",
|
||||
"auth",
|
||||
"dev",
|
||||
"inbox",
|
||||
"internal",
|
||||
"main",
|
||||
"media",
|
||||
"nodeinfo",
|
||||
"notice",
|
||||
"oauth",
|
||||
"objects",
|
||||
"proxy",
|
||||
"push",
|
||||
"registration",
|
||||
"relay",
|
||||
"settings",
|
||||
"status",
|
||||
"tag",
|
||||
"users",
|
||||
"web",
|
||||
"search",
|
||||
"mfa",
|
||||
],
|
||||
|
||||
blacklist_tempmail: false,
|
||||
|
||||
email_blacklist: [],
|
||||
|
||||
url_scheme_whitelist: [
|
||||
"http",
|
||||
"https",
|
||||
"ftp",
|
||||
"dat",
|
||||
"dweb",
|
||||
"gopher",
|
||||
"hyper",
|
||||
"ipfs",
|
||||
"ipns",
|
||||
"irc",
|
||||
"xmpp",
|
||||
"ircs",
|
||||
"magnet",
|
||||
"mailto",
|
||||
"mumble",
|
||||
"ssb",
|
||||
],
|
||||
|
||||
enforce_mime_types: false,
|
||||
allowed_mime_types: [],
|
||||
},
|
||||
defaults: {
|
||||
visibility: "public",
|
||||
language: "en",
|
||||
avatar: "",
|
||||
header: "",
|
||||
},
|
||||
activitypub: {
|
||||
use_tombstones: true,
|
||||
reject_activities: [],
|
||||
force_followers_only: [],
|
||||
discard_reports: [],
|
||||
discard_deletes: [],
|
||||
discard_banners: [],
|
||||
discard_avatars: [],
|
||||
force_sensitive: [],
|
||||
discard_updates: [],
|
||||
discard_follows: [],
|
||||
remove_media: [],
|
||||
fetch_all_collection_members: false,
|
||||
authorized_fetch: false,
|
||||
},
|
||||
filters: {
|
||||
note_filters: [],
|
||||
username_filters: [],
|
||||
displayname_filters: [],
|
||||
bio_filters: [],
|
||||
emoji_filters: [],
|
||||
},
|
||||
logging: {
|
||||
log_requests: false,
|
||||
log_requests_verbose: false,
|
||||
log_filters: true,
|
||||
},
|
||||
ratelimits: {
|
||||
duration_coeff: 1,
|
||||
max_coeff: 1,
|
||||
},
|
||||
custom_ratelimits: {},
|
||||
};
|
||||
|
||||
export const getConfig = () => {
|
||||
// Deeply merge configDefaults, config and internalConfig
|
||||
return deepMergeArray([
|
||||
configDefaults,
|
||||
config,
|
||||
internalConfig,
|
||||
]) as any as ConfigType;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the internal config
|
||||
* @param newConfig Any part of ConfigType
|
||||
*/
|
||||
export const setConfig = async (newConfig: Partial<ConfigType>) => {
|
||||
const newInternalConfig = deepMerge(internalConfig, newConfig);
|
||||
|
||||
// Prepend a warning comment and write the new TOML to the file
|
||||
await Bun.write(
|
||||
Bun.file(process.cwd() + "/config/config.internal.toml"),
|
||||
`# This file is automatically generated. Do not modify it manually.\n${stringify(
|
||||
newInternalConfig
|
||||
)}`
|
||||
);
|
||||
};
|
||||
|
||||
export const getHost = () => {
|
||||
const url = new URL(getConfig().http.base_url);
|
||||
|
||||
return url.host;
|
||||
};
|
||||
|
||||
// Refresh config every 5 seconds
|
||||
setInterval(() => {
|
||||
scanConfig()
|
||||
.then(newConfig => {
|
||||
if (newConfig !== config) {
|
||||
config = newConfig;
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
}, 5000);
|
||||
|
||||
export { config };
|
||||
|
|
@ -4,7 +4,7 @@ import {
|
|||
PutObjectCommand,
|
||||
S3Client,
|
||||
} from "@aws-sdk/client-s3";
|
||||
import type { ConfigType } from "@config";
|
||||
import type { ConfigType } from "~classes/configmanager";
|
||||
import sharp from "sharp";
|
||||
import { exists, mkdir } from "fs/promises";
|
||||
class MediaBackend {
|
||||
|
|
|
|||
2
cli.ts
2
cli.ts
|
|
@ -4,7 +4,7 @@ import { client } from "~database/datasource";
|
|||
import { createNewLocalUser } from "~database/entities/User";
|
||||
import Table from "cli-table";
|
||||
import { rebuildSearchIndexes, MeiliIndexType } from "@meilisearch";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { uploadFile } from "~classes/media";
|
||||
import { getUrl } from "~database/entities/Attachment";
|
||||
import { mkdir, exists } from "fs/promises";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { ConfigType } from "@config";
|
||||
import type { ConfigType } from "~classes/configmanager";
|
||||
import type { Attachment } from "@prisma/client";
|
||||
import type { APIAsyncAttachment } from "~types/entities/async_attachment";
|
||||
import type { APIAttachment } from "~types/entities/attachment";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import type { Like as LysandLike } from "~types/lysand/Object";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import type { Like } from "@prisma/client";
|
||||
import { client } from "~database/datasource";
|
||||
import type { UserWithRelations } from "./User";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { Worker } from "bullmq";
|
||||
import { client, federationQueue } from "~database/datasource";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import type { UserWithRelations } from "./User";
|
||||
import {
|
||||
fetchRemoteUser,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { ConfigType } from "@config";
|
||||
import { getConfig } from "@config";
|
||||
import type { ConfigType } from "~classes/configmanager";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import type { APIAccount } from "~types/entities/account";
|
||||
import type { User as LysandUser } from "~types/lysand/Object";
|
||||
import { htmlToText } from "html-to-text";
|
||||
|
|
|
|||
2
index.ts
2
index.ts
|
|
@ -1,4 +1,4 @@
|
|||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { jsonResponse } from "@response";
|
||||
import chalk from "chalk";
|
||||
import { appendFile } from "fs/promises";
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
// Proxies all `bunx prisma` commands with an environment variable
|
||||
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
process.stdout.write(
|
||||
`postgresql://${config.database.username}:${config.database.password}@${config.database.host}:${config.database.port}/${config.database.database}`
|
||||
`postgresql://${config.database.username}:${config.database.password}@${config.database.host}:${config.database.port}/${config.database.database}\n`
|
||||
);
|
||||
|
||||
// Ends
|
||||
process.exit(0);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "Instance" ADD COLUMN "disableAutomoderation" BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" ADD COLUMN "disableAutomoderation" BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ModerationData" (
|
||||
"id" UUID NOT NULL DEFAULT uuid_generate_v7(),
|
||||
"statusId" UUID NOT NULL,
|
||||
"creatorId" UUID NOT NULL,
|
||||
"note" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "ModerationData_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Flag" (
|
||||
"id" UUID NOT NULL DEFAULT uuid_generate_v7(),
|
||||
"statusId" UUID NOT NULL,
|
||||
"userId" UUID NOT NULL,
|
||||
"flagType" TEXT NOT NULL DEFAULT 'other',
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Flag_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ModerationData" ADD CONSTRAINT "ModerationData_statusId_fkey" FOREIGN KEY ("statusId") REFERENCES "Status"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ModerationData" ADD CONSTRAINT "ModerationData_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Flag" ADD CONSTRAINT "Flag_statusId_fkey" FOREIGN KEY ("statusId") REFERENCES "Status"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Flag" ADD CONSTRAINT "Flag_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
|
@ -45,6 +45,7 @@ model Instance {
|
|||
emojis Emoji[] // One to many relation with Emoji
|
||||
statuses Status[] // One to many relation with Status
|
||||
users User[] // One to many relation with User
|
||||
disableAutomoderation Boolean @default(false)
|
||||
}
|
||||
|
||||
model Like {
|
||||
|
|
@ -125,6 +126,30 @@ model Status {
|
|||
pinnedBy User[] @relation("UserPinnedNotes")
|
||||
attachments Attachment[]
|
||||
relatedNotifications Notification[]
|
||||
flags Flag[]
|
||||
moderationData ModerationData[]
|
||||
}
|
||||
|
||||
model ModerationData {
|
||||
id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid
|
||||
status Status @relation(fields: [statusId], references: [id], onDelete: Cascade)
|
||||
statusId String @db.Uuid
|
||||
creator User @relation(fields: [creatorId], references: [id], onDelete: Cascade)
|
||||
creatorId String @db.Uuid
|
||||
note String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
// Used for moderation purposes
|
||||
model Flag {
|
||||
id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid
|
||||
status Status @relation(fields: [statusId], references: [id], onDelete: Cascade)
|
||||
statusId String @db.Uuid
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
userId String @db.Uuid
|
||||
flagType String @default("other")
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
model Token {
|
||||
|
|
@ -212,6 +237,9 @@ model User {
|
|||
notifications Notification[] // One to many relation with Notification
|
||||
notified Notification[] @relation("NotificationToNotified") // One to many relation with Notification
|
||||
linkedOpenIdAccounts OpenIdAccount[] // One to many relation with OpenIdAccount
|
||||
flags Flag[] @relation
|
||||
moderationData ModerationData[]
|
||||
disableAutomoderation Boolean @default(false)
|
||||
}
|
||||
|
||||
model OpenIdAccount {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { MatchedRoute } from "bun";
|
||||
import { getConfig, getHost } from "@config";
|
||||
import { getConfig, getHost } from "~classes/configmanager";
|
||||
import { xmlResponse } from "@response";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { MatchedRoute } from "bun";
|
||||
import { getConfig, getHost } from "@config";
|
||||
import { getConfig, getHost } from "~classes/configmanager";
|
||||
import { applyConfig } from "@api";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { MatchedRoute } from "bun";
|
||||
import { getConfig, getHost } from "@config";
|
||||
import { getConfig, getHost } from "~classes/configmanager";
|
||||
import { applyConfig } from "@api";
|
||||
import { client } from "~database/datasource";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { parseRequest } from "@request";
|
||||
import { jsonResponse } from "@response";
|
||||
import { tempmailDomains } from "@tempmail";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { applyConfig } from "@api";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { jsonResponse } from "@response";
|
||||
import { client } from "~database/datasource";
|
||||
import { userRelations, userToAPI } from "~database/entities/User";
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { client } from "~database/datasource";
|
|||
import { getFromRequest } from "~database/entities/User";
|
||||
import type { APIRouteMeta } from "~types/api";
|
||||
import { uploadFile } from "~classes/media";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
|
||||
import type { MatchedRoute } from "bun";
|
||||
import { parseRequest } from "@request";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { getFromRequest } from "~database/entities/User";
|
|||
import type { APIRouteMeta } from "~types/api";
|
||||
import sharp from "sharp";
|
||||
import { uploadFile } from "~classes/media";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { applyConfig } from "@api";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { sanitizeHtml } from "@sanitization";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { applyConfig } from "@api";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import type { MatchedRoute } from "bun";
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { applyConfig } from "@api";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import { sanitizeHtml } from "@sanitization";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { getFromRequest } from "~database/entities/User";
|
|||
import type { APIRouteMeta } from "~types/api";
|
||||
import sharp from "sharp";
|
||||
import { uploadFile } from "~classes/media";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { attachmentToAPI, getUrl } from "~database/entities/Attachment";
|
||||
|
||||
export const meta: APIRouteMeta = applyConfig({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { applyConfig } from "@api";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { MeiliIndexType, meilisearch } from "@meilisearch";
|
||||
import { parseRequest } from "@request";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { applyConfig } from "@api";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { oauthRedirectUri } from "@constants";
|
||||
import type { MatchedRoute } from "bun";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { applyConfig } from "@api";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { oauthRedirectUri } from "@constants";
|
||||
import type { MatchedRoute } from "bun";
|
||||
import { randomBytes } from "crypto";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { applyConfig } from "@api";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { jsonResponse } from "@response";
|
||||
|
||||
export const meta = applyConfig({
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { applyConfig } from "@api";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { getBestContentType } from "@content_types";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import type { MatchedRoute } from "bun";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { applyConfig } from "@api";
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { errorResponse, jsonResponse } from "@response";
|
||||
import type { MatchedRoute } from "bun";
|
||||
import { client } from "~database/datasource";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { jsonResponse } from "@response";
|
||||
import type { MatchedRoute } from "bun";
|
||||
import { getConfig, getHost } from "@config";
|
||||
import { getConfig, getHost } from "~classes/configmanager";
|
||||
import { applyConfig } from "@api";
|
||||
import {
|
||||
statusAndUserRelations,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import type { Token } from "@prisma/client";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||
import { client } from "~database/datasource";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import type { Token } from "@prisma/client";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||
import { client } from "~database/datasource";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import type { Token } from "@prisma/client";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||
import { client } from "~database/datasource";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { type ConfigType, getConfig } from "@config";
|
||||
import { type ConfigType, getConfig } from "~classes/configmanager";
|
||||
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
||||
import { LocalBackend, S3Backend } from "~classes/media";
|
||||
import { unlink } from "fs/promises";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import type { Application, Token } from "@prisma/client";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||
import { client } from "~database/datasource";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import type { APIRouteMeta } from "~types/api";
|
||||
|
||||
export const applyConfig = (routeMeta: APIRouteMeta) => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
|
||||
const config = getConfig();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import chalk from "chalk";
|
||||
import { client } from "~database/datasource";
|
||||
import { Meilisearch } from "meilisearch";
|
||||
|
|
|
|||
16
utils/merge.ts
Normal file
16
utils/merge.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
export const deepMerge = (
|
||||
target: Record<string, any>,
|
||||
source: Record<string, any>
|
||||
) => {
|
||||
const result = { ...target, ...source };
|
||||
for (const key of Object.keys(result)) {
|
||||
result[key] =
|
||||
typeof target[key] == "object" && typeof source[key] == "object"
|
||||
? deepMerge(target[key], source[key])
|
||||
: structuredClone(result[key]);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export const deepMergeArray = (array: Record<string, any>[]) =>
|
||||
array.reduce((ci, ni) => deepMerge(ci, ni), {});
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import type { Prisma } from "@prisma/client";
|
||||
import chalk from "chalk";
|
||||
import Redis from "ioredis";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getConfig } from "@config";
|
||||
import { getConfig } from "~classes/configmanager";
|
||||
import { sanitize } from "isomorphic-dompurify";
|
||||
|
||||
export const sanitizeHtml = async (html: string) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue