import { Column, Entity } from "typeorm"; import { BaseClassWithoutId, PrimaryIdColumn } from "./BaseClass"; import crypto from "crypto"; import { Snowflake } from "../util/Snowflake"; import { SessionsReplace } from ".."; import { hostname } from "os"; import { Rights } from "../util/Rights"; @Entity("config") export class ConfigEntity extends BaseClassWithoutId { @PrimaryIdColumn() key: string; @Column({ type: "simple-json", nullable: true }) value: number | boolean | null | string | undefined; } export interface RateLimitOptions { bot?: number; count: number; window: number; onyIp?: boolean; } export interface Region { id: string; name: string; endpoint: string; location?: { latitude: number; longitude: number; }; vip: boolean; custom: boolean; deprecated: boolean; } export interface KafkaBroker { ip: string; port: number; } export interface ConfigValue { gateway: { endpointClient: string | null; endpointPrivate: string | null; endpointPublic: string | null; }; cdn: { endpointClient: string | null; endpointPublic: string | null; endpointPrivate: string | null; resizeHeightMax: number | null; resizeWidthMax: number | null; imagorServerUrl: string | null; }; api: { defaultVersion: string; activeVersions: string[]; useFosscordEnhancements: boolean; }; general: { instanceName: string; instanceDescription: string | null; frontPage: string | null; tosPage: string | null; correspondenceEmail: string | null; correspondenceUserID: string | null; image: string | null; instanceId: string; }; limits: { user: { maxGuilds: number; maxUsername: number; maxFriends: number; }; guild: { maxRoles: number; maxEmojis: number; maxMembers: number; maxChannels: number; maxChannelsInCategory: number; hideOfflineMember: number; }; message: { maxCharacters: number; maxTTSCharacters: number; maxReactions: number; maxAttachmentSize: number; maxBulkDelete: number; maxEmbedDownloadSize: number; }; channel: { maxPins: number; maxTopic: number; maxWebhooks: number; }; rate: { disabled: boolean; ip: Omit; global: RateLimitOptions; error: RateLimitOptions; routes: { guild: RateLimitOptions; webhook: RateLimitOptions; channel: RateLimitOptions; auth: { login: RateLimitOptions; register: RateLimitOptions; }; // TODO: rate limit configuration for all routes }; }; }; security: { autoUpdate: boolean | number; requestSignature: string; jwtSecret: string; forwadedFor: string | null; // header to get the real user ip address captcha: { enabled: boolean; service: "recaptcha" | "hcaptcha" | null; // TODO: hcaptcha, custom sitekey: string | null; secret: string | null; }; ipdataApiKey: string | null; defaultRights: string; }; login: { requireCaptcha: boolean; }; register: { email: { required: boolean; allowlist: boolean; blocklist: boolean; domains: string[]; }; dateOfBirth: { required: boolean; minimum: number; // in years }; disabled: boolean; requireCaptcha: boolean; requireInvite: boolean; guestsRequireInvite: boolean; allowNewRegistration: boolean; allowMultipleAccounts: boolean; blockProxies: boolean; password: { required: boolean; minLength: number; minNumbers: number; minUpperCase: number; minSymbols: number; }; incrementingDiscriminators: boolean; // random otherwise }; regions: { default: string; useDefaultAsOptimal: boolean; available: Region[]; }; guild: { discovery: { showAllGuilds: boolean; useRecommendation: boolean; // TODO: Recommendation, privacy concern? offset: number; limit: number; }; autoJoin: { enabled: boolean; guilds: string[]; canLeave: boolean; }; defaultFeatures: string[]; }; gif: { enabled: boolean; provider: "tenor"; // more coming soon apiKey?: string; }; rabbitmq: { host: string | null; }; kafka: { brokers: KafkaBroker[] | null; }; templates: { enabled: Boolean; allowTemplateCreation: Boolean; allowDiscordTemplates: Boolean; allowRaws: Boolean; }; client: { useTestClient: Boolean; releases: { useLocalRelease: Boolean; //TODO upstreamVersion: string; }; }; metrics: { timeout: number; }; sentry: { enabled: boolean; endpoint: string; traceSampleRate: number; environment: string; }; external: { twitter: string | null; } } export const DefaultConfigOptions: ConfigValue = { gateway: { endpointClient: null, endpointPrivate: null, endpointPublic: null, }, cdn: { endpointClient: null, endpointPrivate: null, endpointPublic: null, resizeHeightMax: 1000, resizeWidthMax: 1000, imagorServerUrl: null, }, api: { defaultVersion: "9", activeVersions: ["6", "7", "8", "9"], useFosscordEnhancements: true, }, general: { instanceName: "Fosscord Instance", instanceDescription: "This is a Fosscord instance made in pre-release days", frontPage: null, tosPage: null, correspondenceEmail: "noreply@localhost.local", correspondenceUserID: null, image: null, instanceId: Snowflake.generate(), }, limits: { user: { maxGuilds: 1048576, maxUsername: 127, maxFriends: 5000, }, guild: { maxRoles: 1000, maxEmojis: 2000, maxMembers: 25000000, maxChannels: 65535, maxChannelsInCategory: 65535, hideOfflineMember: 3, }, message: { maxCharacters: 1048576, maxTTSCharacters: 160, maxReactions: 2048, maxAttachmentSize: 1024 * 1024 * 1024, maxEmbedDownloadSize: 1024 * 1024 * 5, maxBulkDelete: 1000, }, channel: { maxPins: 500, maxTopic: 1024, maxWebhooks: 100, }, rate: { disabled: true, ip: { count: 500, window: 5, }, global: { count: 250, window: 5, }, error: { count: 10, window: 5, }, routes: { guild: { count: 5, window: 5, }, webhook: { count: 10, window: 5, }, channel: { count: 10, window: 5, }, auth: { login: { count: 5, window: 60, }, register: { count: 2, window: 60 * 60 * 12, }, }, }, }, }, security: { autoUpdate: true, requestSignature: crypto.randomBytes(32).toString("base64"), jwtSecret: crypto.randomBytes(256).toString("base64"), forwadedFor: null, // forwadedFor: "X-Forwarded-For" // nginx/reverse proxy // forwadedFor: "CF-Connecting-IP" // cloudflare: captcha: { enabled: false, service: null, sitekey: null, secret: null, }, ipdataApiKey: "eca677b284b3bac29eb72f5e496aa9047f26543605efe99ff2ce35c9", defaultRights: "30644591655940", // See `npm run generate:rights` }, login: { requireCaptcha: false, }, register: { email: { required: false, allowlist: false, blocklist: true, domains: [], // TODO: efficiently save domain blocklist in database // domains: fs.readFileSync(__dirname + "/blockedEmailDomains.txt", { encoding: "utf8" }).split("\n"), }, dateOfBirth: { required: true, minimum: 13, }, disabled: false, requireInvite: false, guestsRequireInvite: true, requireCaptcha: true, allowNewRegistration: true, allowMultipleAccounts: true, blockProxies: true, password: { required: false, minLength: 8, minNumbers: 2, minUpperCase: 2, minSymbols: 0, }, incrementingDiscriminators: false, }, regions: { default: "fosscord", useDefaultAsOptimal: true, available: [ { id: "fosscord", name: "Fosscord", endpoint: "127.0.0.1:3004", vip: false, custom: false, deprecated: false, }, ], }, guild: { discovery: { showAllGuilds: false, useRecommendation: false, offset: 0, limit: 24, }, autoJoin: { enabled: true, canLeave: true, guilds: [], }, defaultFeatures: [], }, gif: { enabled: true, provider: "tenor", apiKey: "LIVDSRZULELA", }, rabbitmq: { host: null, }, kafka: { brokers: null, }, templates: { enabled: true, allowTemplateCreation: true, allowDiscordTemplates: true, allowRaws: false, }, client: { useTestClient: true, releases: { useLocalRelease: true, upstreamVersion: "0.0.264", }, }, metrics: { timeout: 30000, }, sentry: { enabled: false, endpoint: "https://05e8e3d005f34b7d97e920ae5870a5e5@sentry.thearcanebrony.net/6", traceSampleRate: 1.0, environment: hostname(), }, external: { twitter: null, } };