Ctrl K
ComunidadGuías…

lib/config.ts

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import 'dotenv/config';

import { ofetch } from 'ofetch';

import type { HostRateLimits } from '@/utils/host-rate-limit';
import { parseHostRateLimits } from '@/utils/host-rate-limit';

type ConfigEnvKeys =
    // App config
    | 'DISALLOW_ROBOT'
    | 'ENABLE_CLUSTER'
    | 'IS_PACKAGE'
    | 'USER_ROUTES_PATH'
    | 'NODE_NAME'
    | 'PLAYWRIGHT_WS_ENDPOINT'
    | 'PUPPETEER_WS_ENDPOINT'
    | 'PLAYWRIGHT_CDP_ENDPOINT'
    | 'CHROMIUM_EXECUTABLE_PATH'
    | 'CI_EN_COOKIE'
    // Network
    | 'PORT'
    | 'SOCKET'
    | 'LISTEN_INADDR_ANY'
    | 'DISABLE_IPV6'
    | 'REQUEST_RETRY'
    | 'REQUEST_TIMEOUT'
    | 'REQUEST_AUTO_SELECT_FAMILY'
    | 'REQUEST_RATE_LIMITS'
    | 'WORDPRESS_ALLOWED_DOMAINS'
    | 'UA'
    | 'NO_RANDOM_UA'
    | 'ALLOW_ORIGIN'
    // Cache
    | 'CACHE_TYPE'
    | 'CACHE_REQUEST_TIMEOUT'
    | 'CACHE_EXPIRE'
    | 'CACHE_CONTENT_EXPIRE'
    | 'MEMORY_MAX'
    | 'REDIS_URL'
    | 'CACHE_HTTP_URL'
    | 'CACHE_HTTP_TOKEN'
    // Proxy
    | 'PROXY_URI'
    | 'PROXY_PROTOCOL'
    | 'PROXY_HOST'
    | 'PROXY_PORT'
    | 'PROXY_AUTH'
    | 'PROXY_URL_REGEX'
    | 'PROXY_STRATEGY'
    | 'PAC_URI'
    | 'PAC_SCRIPT'
    // Access control
    | 'ACCESS_KEY'
    // Logging
    | 'DEBUG_INFO'
    | 'LOGGER_LEVEL'
    | 'NO_LOGFILES'
    | 'OTEL_SECONDS_BUCKET'
    | 'OTEL_MILLISECONDS_BUCKET'
    | 'SHOW_LOGGER_TIMESTAMP'
    | 'HONEYBADGER_API_KEY'
    | 'ERROR_TRACKING_ROUTE_TIMEOUT'
    | 'SENTRY'
    | 'SENTRY_ROUTE_TIMEOUT'
    | 'ENABLE_REMOTE_DEBUGGING'
    // Feed config
    | 'HOTLINK_TEMPLATE'
    | 'HOTLINK_INCLUDE_PATHS'
    | 'HOTLINK_EXCLUDE_PATHS'
    | 'ALLOW_USER_HOTLINK_TEMPLATE'
    | 'FILTER_REGEX_ENGINE'
    | 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN'
    | 'DISABLE_NSFW'
    | 'SUFFIX'
    | 'TITLE_LENGTH_LIMIT'
    | 'FORMAT'
    // OpenAI
    | 'OPENAI_API_KEY'
    | 'OPENAI_MODEL'
    | 'OPENAI_TEMPERATURE'
    | 'OPENAI_MAX_TOKENS'
    | 'OPENAI_API_ENDPOINT'
    | 'OPENAI_INPUT_OPTION'
    | 'OPENAI_PROMPT'
    | 'OPENAI_PROMPT_TITLE'
    // Follow
    | 'FOLLOW_OWNER_USER_ID'
    | 'FOLLOW_DESCRIPTION'
    | 'FOLLOW_PRICE'
    | 'FOLLOW_USER_LIMIT'
    // Route-specific (dynamic cookies with prefixes)
    | 'BAIDU_COOKIE'
    | `BILIBILI_COOKIE_${string}`
    | 'BILIBILI_DM_IMG_LIST'
    | 'BILIBILI_DM_IMG_INTER'
    | 'BILIBILI_EXCLUDE_SUBTITLES'
    | 'BITBUCKET_USERNAME'
    | 'BITBUCKET_PASSWORD'
    | 'BTBYR_HOST'
    | 'BTBYR_COOKIE'
    | 'BUPT_PORTAL_COOKIE'
    | 'BUKENAVI_EMAIL'
    | 'BUKENAVI_PASSWORD'
    | 'CAIXIN_COOKIE'
    | 'CARA_COOKIE'
    | 'CIVITAI_COOKIE'
    | 'COOMER_ASSETS_URL'
    | 'COOMER_ROOT_URL'
    | 'DIANPING_COOKIE'
    | 'DIDA365_USERNAME'
    | 'DIDA365_PASSWORD'
    | 'DISCORD_AUTHORIZATION'
    | `DISCOURSE_CONFIG_${string}`
    | `DISCUZ_COOKIE_${string}`
    | 'DISQUS_API_KEY'
    | 'DOUBAN_COOKIE'
    | 'DOUYIN_COOKIE'
    | 'EH_IPB_MEMBER_ID'
    | 'EH_IPB_PASS_HASH'
    | 'EH_SK'
    | 'EH_IGNEOUS'
    | 'EH_STAR'
    | 'EH_IMG_PROXY'
    | `EMAIL_CONFIG_${string}`
    | 'ETHERSCAN_API_KEY'
    | 'F95ZONE_COOKIE'
    | 'FACEBOOK_COOKIE'
    | 'FANBOX_SESSION_ID'
    | 'FANFOU_CONSUMER_KEY'
    | 'FANFOU_CONSUMER_SECRET'
    | 'FANFOU_USERNAME'
    | 'FANFOU_PASSWORD'
    | 'FANTIA_COOKIE'
    | 'GAME_4399'
    | 'GELBOORU_API_KEY'
    | 'GELBOORU_USER_ID'
    | 'GITHUB_ACCESS_TOKEN'
    | 'GITEE_ACCESS_TOKEN'
    | 'GOOGLE_FONTS_API_KEY'
    | 'GUOZAOKE_COOKIES'
    | 'HEFENG_KEY'
    | 'HEFENG_API_HOST'
    | 'HUITUN_COOKIE'
    | 'INFZM_COOKIE'
    | 'INITIUM_MEMBER_COOKIE'
    | 'IG_COOKIE'
    | 'INSTAGRAM_COOKIE'
    | 'IWARA_USERNAME'
    | 'IWARA_PASSWORD'
    | 'JAVDB_SESSION'
    | 'JUMEILI_COOKIE'
    | 'KEMONO_ASSETS_URL'
    | 'KEMONO_ROOT_URL'
    | 'KEYLOL_COOKIE'
    | 'LASTFM_API_KEY'
    | 'LOCALS_SESSION'
    | 'LOFTER_COOKIE'
    | 'LORIENTLEJOUR_TOKEN'
    | 'LORIENTLEJOUR_USERNAME'
    | 'LORIENTLEJOUR_PASSWORD'
    | 'LOWENDTALK_COOKIE'
    | 'MALAYSIAKINI_EMAIL'
    | 'MALAYSIAKINI_PASSWORD'
    | 'MALAYSIAKINI_REFRESHTOKEN'
    | 'MANGADEX_USERNAME'
    | 'MANGADEX_PASSWORD'
    | 'MANGADEX_CLIENT_ID'
    | 'MANGADEX_CLIENT_SECRET'
    | 'MANGADEX_REFRESH_TOKEN'
    | 'MHGUI_COOKIE'
    | 'MASTODON_API_HOST'
    | 'MASTODON_API_ACCESS_TOKEN'
    | 'MASTODON_API_ACCT_DOMAIN'
    | `MEDIUM_COOKIE_${string}`
    | 'MEDIUM_ARTICLE_COOKIE'
    | 'MIHOYO_COOKIE'
    | 'MINIFLUX_INSTANCE'
    | 'MINIFLUX_TOKEN'
    | 'MISSKEY_ACCESS_TOKEN'
    | 'MIXI2_AUTH_TOKEN'
    | 'MIXI2_AUTH_KEY'
    | 'MOX_COOKIE'
    | 'NCM_COOKIES'
    | 'NEWRANK_COOKIE'
    | 'NGA_PASSPORT_UID'
    | 'NGA_PASSPORT_CID'
    | 'NHENTAI_USERNAME'
    | 'NHENTAI_PASSWORD'
    | 'NOTION_TOKEN'
    | 'ONLYFANS_COOKIE'
    | 'PATREON_SESSION_ID'
    | 'PIANYUAN_COOKIE'
    | 'PIXABAY_KEY'
    | 'PIXIV_REFRESHTOKEN'
    | 'PIXIV_BYPASS_CDN'
    | 'PIXIV_BYPASS_HOSTNAME'
    | 'PIXIV_BYPASS_DOH'
    | 'PIXIV_IMG_PROXY'
    | 'PKUBBS_COOKIE'
    | 'QINGTING_ID'
    | 'READWISE_ACCESS_TOKEN'
    | 'REUTERS_AUTH_TOKEN'
    | 'SARABA1ST_COOKIE'
    | 'SARABA1ST_HOST'
    | 'SEHUATANG_COOKIE'
    | 'SCBOY_BBS_TOKEN'
    | 'SCIHUB_HOST'
    | 'SDO_FF14RISINGSTONES'
    | 'SDO_UA'
    | 'SECURITY_KEY'
    | 'SIS001_BASE_URL'
    | 'SKEB_BEARER_TOKEN'
    | 'SORRYCC_COOKIES'
    | 'SOUTHPLUS_COOKIE'
    | 'SOUTHPLUS_UA'
    | 'SPOTIFY_CLIENT_ID'
    | 'SPOTIFY_CLIENT_SECRET'
    | 'SPOTIFY_REFRESHTOKEN'
    | 'SSPAI_BEARERTOKEN'
    | 'TAOGUBA_COOKIE'
    | 'TELEGRAM_TOKEN'
    | 'TELEGRAM_SESSION'
    | 'TELEGRAM_API_ID'
    | 'TELEGRAM_API_HASH'
    | 'TELEGRAM_MAX_CONCURRENT_DOWNLOADS'
    | 'TELEGRAM_PROXY_HOST'
    | 'TELEGRAM_PROXY_PORT'
    | 'TELEGRAM_PROXY_SECRET'
    | 'TOPHUB_COOKIE'
    | 'TSDM39_COOKIES'
    | 'TUMBLR_CLIENT_ID'
    | 'TUMBLR_CLIENT_SECRET'
    | 'TUMBLR_REFRESH_TOKEN'
    | 'TWITTER_CONSUMER_KEY'
    | 'TWITTER_CONSUMER_SECRET'
    | 'TWITTER_ACCESS_TOKEN'
    | 'TWITTER_ACCESS_SECRET'
    // | 'TWITTER_USERNAME'
    // | 'TWITTER_PASSWORD'
    // | 'TWITTER_AUTHENTICATION_SECRET'
    // | 'TWITTER_PHONE_OR_EMAIL'
    | 'TWITTER_AUTH_TOKEN'
    | 'TWITTER_THIRD_PARTY_API'
    | 'UESTC_BBS_COOKIE'
    | 'UESTC_BBS_AUTH_STR'
    | 'WEIBO_APP_KEY'
    | 'WEIBO_APP_SECRET'
    | 'WEIBO_COOKIES'
    | 'WEIBO_REDIRECT_URL'
    | 'WENKU8_COOKIE'
    | 'WORDPRESS_CDN'
    | 'XIAOYUZHOU_ID'
    | 'XIAOYUZHOU_TOKEN'
    | 'XIAOHONGSHU_COOKIE'
    | 'XIAOHONGSHU_PROXY'
    | 'XIMALAYA_TOKEN'
    | 'XSIJISHE_COOKIE'
    | 'XUEQIU_COOKIES'
    | 'YAMIBO_SALT'
    | 'YAMIBO_AUTH'
    | 'YOUTUBE_KEY'
    | 'YOUTUBE_CLIENT_ID'
    | 'YOUTUBE_CLIENT_SECRET'
    | 'YOUTUBE_REFRESH_TOKEN'
    | 'YOUTUBE_VIDEO_EMBED_URL'
    | 'ZAIMANHUA_TOKEN'
    | 'ZHIHU_COOKIES'
    | 'ZODGAME_COOKIE'
    | 'ZSXQ_ACCESS_TOKEN'
    | 'SMZDM_COOKIE'
    | 'REMOTE_CONFIG'
    | 'REMOTE_CONFIG_AUTH';

export type ConfigEnv = Partial<Record<ConfigEnvKeys, string | undefined>>;

let envs: ConfigEnv = process.env;

export type Config = {
    userRoutesPath?: string;
    // app config
    disallowRobot: boolean;
    enableCluster?: string;
    isPackage: boolean;
    nodeName?: string;
    playwrightWSEndpoint?: string;
    playwrightCDPEndpoint?: string;
    chromiumExecutablePath?: string;
    // network
    connect: {
        port: number;
        socket?: string;
    };
    listenInaddrAny: boolean;
    disableIPv6: boolean;
    requestRetry: number;
    requestTimeout: number;
    requestAutoSelectFamily?: boolean;
    requestRateLimits: HostRateLimits;
    ua: string;
    isDefaultUA: boolean;
    trueUA: string;
    allowOrigin?: string;
    // cache
    cache: {
        type: string;
        requestTimeout: number;
        routeExpire: number;
        contentExpire: number;
    };
    memory: {
        max: number;
    };
    redis: {
        url: string;
    };
    httpCache: {
        url?: string;
        token?: string;
    };
    // proxy
    proxyUri?: string;
    proxy: {
        protocol?: string;
        host?: string;
        port?: string;
        auth?: string;
        url_regex: string;
        strategy: 'on_retry' | 'all';
    };
    pacUri?: string;
    pacScript?: string;
    // access control
    accessKey?: string;
    // logging
    debugInfo: string;
    loggerLevel: string;
    noLogfiles?: boolean;
    otel: {
        seconds_bucket?: string;
        milliseconds_bucket?: string;
    };
    showLoggerTimestamp?: boolean;
    honeybadger: {
        apiKey?: string;
    };
    sentry: {
        dsn?: string;
    };
    errorTrackingRouteTimeout: number;
    enableRemoteDebugging?: boolean;
    // feed config
    hotlink: {
        template?: string;
        includePaths?: string[];
        excludePaths?: string[];
    };
    feature: {
        allow_user_hotlink_template: boolean;
        filter_regex_engine: string;
        allow_user_supply_unsafe_domain: boolean;
        disable_nsfw: boolean;
    };
    suffix?: string;
    titleLengthLimit: number;
    format: string;
    openai: {
        apiKey?: string;
        model?: string;
        temperature?: number;
        maxTokens?: number;
        endpoint: string;
        inputOption: string;
        promptTitle: string;
        promptDescription: string;
    };
    follow: {
        ownerUserId?: string;
        description?: string;
        price?: number;
        userLimit?: number;
    };

    // Route-specific Configurations
    baidu: {
        cookie?: string;
    };
    bilibili: {
        cookies: Record<string, string | undefined>;
        dmImgList?: string;
        dmImgInter?: string;
        excludeSubtitles?: boolean;
    };
    bitbucket: {
        username?: string;
        password?: string;
    };
    btbyr: {
        host?: string;
        cookies?: string;
    };
    bukenavi: {
        email?: string;
        password?: string;
    };
    bupt: {
        portal_cookie?: string;
    };
    caixin: {
        cookie?: string;
    };
    cara: {
        cookie?: string;
    };
    civitai: {
        cookie?: string;
    };
    ciEn: {
        cookie?: string;
    };
    coomer: {
        assetsUrl: string;
        rootUrl: string;
    };
    dianping: {
        cookie?: string;
    };
    dida365: {
        username?: string;
        password?: string;
    };
    discord: {
        authorization?: string;
    };
    discourse: {
        config: Record<string, string | undefined>;
    };
    discuz: {
        cookies: Record<string, string | undefined>;
    };
    disqus: {
        api_key?: string;
    };
    douban: {
        cookie?: string;
    };
    douyin: {
        cookie?: string;
    };
    ehentai: {
        ipb_member_id?: string;
        ipb_pass_hash?: string;
        sk?: string;
        igneous?: string;
        star?: string;
        img_proxy?: string;
    };
    email: {
        config: Record<string, string | undefined>;
    };
    etherscan: {
        apiKey?: string;
    };
    f95zone: {
        cookie?: string;
    };
    facebook: {
        cookie?: string;
    };
    fanbox: {
        session?: string;
    };
    fanfou: {
        consumer_key?: string;
        consumer_secret?: string;
        username?: string;
        password?: string;
    };
    fantia: {
        cookies?: string;
    };
    game4399: {
        cookie?: string;
    };
    gelbooru: {
        apiKey?: string;
        userId?: string;
    };
    github: {
        access_token?: string;
    };
    gitee: {
        access_token?: string;
    };
    google: {
        fontsApiKey?: string;
    };
    guozaoke: {
        cookies?: string;
    };
    hefeng: {
        key?: string;
        apiHost?: string;
    };
    huitun: {
        cookie?: string;
    };
    infzm: {
        cookie?: string;
    };
    initium: {
        memberCookie?: string;
    };
    instagram: {
        cookie?: string;
    };
    iwara: {
        username?: string;
        password?: string;
    };
    javdb: {
        session?: string;
    };
    jumeili: {
        cookie?: string;
    };
    kemono: {
        assetsUrl: string;
        rootUrl: string;
    };
    keylol: {
        cookie?: string;
    };
    lastfm: {
        api_key?: string;
    };
    lightnovel: {
        cookie?: string;
    };
    locals: {
        session?: string;
    };
    lofter: {
        cookies?: string;
    };
    lorientlejour: {
        token?: string;
        username?: string;
        password?: string;
    };
    lowendtalk: {
        cookie?: string;
    };
    malaysiakini: {
        email?: string;
        password?: string;
        refreshToken?: string;
    };
    mangadex: {
        username?: string;
        password?: string;
        clientId?: string;
        clientSecret?: string;
        refreshToken?: string;
    };
    manhuagui: {
        cookie?: string;
    };
    mastodon: {
        apiHost?: string;
        accessToken?: string;
        acctDomain?: string;
    };
    medium: {
        cookies: Record<string, string | undefined>;
        articleCookie?: string;
    };
    mihoyo: {
        cookie?: string;
    };
    miniflux: {
        instance?: string;
        token?: string;
    };
    misskey: {
        accessToken?: string;
    };
    mixi2: {
        authToken?: string;
        authKey?: string;
    };
    mox: {
        cookie: string;
    };
    ncm: {
        cookies?: string;
    };
    newrank: {
        cookie?: string;
    };
    nga: {
        uid?: string;
        cid?: string;
    };
    nhentai: {
        username?: string;
        password?: string;
    };
    notion: {
        key?: string;
    };
    onlyfans: {
        cookie?: string;
    };
    patreon: {
        sessionId?: string;
    };
    pianyuan: {
        cookie?: string;
    };
    pixabay: {
        key?: string;
    };
    pixiv: {
        refreshToken?: string;
        bypassCdn?: boolean;
        bypassCdnHostname?: string;
        bypassCdnDoh?: string;
        imgProxy?: string;
    };
    pkubbs: {
        cookie?: string;
    };
    qingting: {
        id?: string;
    };
    readwise: {
        accessToken?: string;
    };
    reuters: {
        authToken?: string;
    };
    saraba1st: {
        cookie?: string;
        host?: string;
    };
    sehuatang: {
        cookie?: string;
    };
    scboy: {
        token?: string;
    };
    scihub: {
        host?: string;
    };
    sdo: {
        ff14risingstones?: string;
        ua?: string;
    };
    sis001: {
        baseUrl?: string;
    };
    skeb: {
        bearerToken?: string;
    };
    sorrycc: {
        cookie?: string;
    };
    southplus: {
        cookie?: string;
        ua?: string;
    };
    spotify: {
        clientId?: string;
        clientSecret?: string;
        refreshToken?: string;
    };
    sspai: {
        bearertoken?: string;
    };
    taoguba: {
        cookie?: string;
    };
    telegram: {
        token?: string;
        session?: string;
        apiId?: number;
        apiHash?: string;
        maxConcurrentDownloads?: number;
        proxy?: {
            host?: string;
            port?: number;
            secret?: string;
        };
    };
    tophub: {
        cookie?: string;
    };
    tsdm39: {
        cookie: string;
    };
    tumblr: {
        clientId?: string;
        clientSecret?: string;
        refreshToken?: string;
    };
    twitter: {
        consumerKey?: string;
        consumerSecret?: string;
        accessToken?: string;
        accessSecret?: string;
        // username?: string[];
        // password?: string[];
        // authenticationSecret?: string[];
        // phoneOrEmail?: string[];
        authToken?: string[];
        thirdPartyApi?: string;
    };
    uestc: {
        bbsCookie?: string;
        bbsAuthStr?: string;
    };
    weibo: {
        app_key?: string;
        app_secret?: string;
        cookies?: string;
        redirect_url?: string;
    };
    wenku8: {
        cookie?: string;
    };
    wordpress: {
        allowedDomains: string[];
        cdnUrl?: string;
    };
    xiaoyuzhou: {
        device_id?: string;
        refresh_token?: string;
    };
    xiaohongshu: {
        cookie?: string;
        proxy?: string;
    };
    ximalaya: {
        token?: string;
    };
    xsijishe: {
        cookie?: string;
    };
    xueqiu: {
        cookies?: string;
    };
    yamibo: {
        salt?: string;
        auth?: string;
    };
    youtube: {
        key?: string;
        clientId?: string;
        clientSecret?: string;
        refreshToken?: string;
        videoEmbedUrl?: string;
    };
    zaimanhua: {
        token?: string;
    };
    zhihu: {
        cookies?: string;
    };
    zodgame: {
        cookie?: string;
    };
    zsxq: {
        accessToken?: string;
    };
    smzdm: {
        cookie?: string;
    };
};

const value = {} as Config;

const TRUE_UA = 'RSSHub/1.0 (+http://github.com/DIYgod/RSSHub; like FeedFetcher-Google)';

const toBoolean = (value: string | undefined, defaultValue: boolean) => {
    if (value === undefined) {
        return defaultValue;
    }
    return ['', '0', 'false'].includes(value) ? false : !!value;
};

const toInt = (value: string | undefined, defaultValue?: number) => (value === undefined ? defaultValue : Number.parseInt(value));

const getAssetsUrl = (rootUrl: string, assetsUrl?: string) => {
    if (assetsUrl) {
        return assetsUrl.replace(/\/+$/, '');
    }

    const url = new URL(rootUrl);
    url.hostname = `img.${url.hostname}`;
    return url.href.replace(/\/+$/, '');
};

const calculateValue = () => {
    const bilibili_cookies: Record<string, string | undefined> = {};
    const email_config: Record<string, string | undefined> = {};
    const discuz_cookies: Record<string, string | undefined> = {};
    const medium_cookies: Record<string, string | undefined> = {};
    const discourse_config: Record<string, string | undefined> = {};

    for (const name in envs) {
        if (name.startsWith('BILIBILI_COOKIE_')) {
            const uid = name.slice(16);
            bilibili_cookies[uid] = envs[name];
        } else if (name.startsWith('EMAIL_CONFIG_')) {
            const id = name.slice(13);
            email_config[id] = envs[name];
        } else if (name.startsWith('DISCUZ_COOKIE_')) {
            const cid = name.slice(14);
            discuz_cookies[cid] = envs[name];
        } else if (name.startsWith('MEDIUM_COOKIE_')) {
            const username = name.slice(14).toLowerCase();
            medium_cookies[username] = envs[name];
        } else if (name.startsWith('DISCOURSE_CONFIG_')) {
            const id = name.slice('DISCOURSE_CONFIG_'.length);
            discourse_config[id] = JSON.parse(envs[name] || '{}');
        }
    }

    const _value = {
        // app config
        disallowRobot: toBoolean(envs.DISALLOW_ROBOT, false),
        enableCluster: toBoolean(envs.ENABLE_CLUSTER, false),
        isPackage: !!envs.IS_PACKAGE,
        userRoutesPath: envs.USER_ROUTES_PATH,
        nodeName: envs.NODE_NAME,
        playwrightWSEndpoint: envs.PLAYWRIGHT_WS_ENDPOINT ?? envs.PUPPETEER_WS_ENDPOINT,
        playwrightCDPEndpoint: envs.PLAYWRIGHT_CDP_ENDPOINT,
        chromiumExecutablePath: envs.CHROMIUM_EXECUTABLE_PATH,
        // network
        connect: {
            port: toInt(envs.PORT, 1200), // 监听端口
            socket: envs.SOCKET || undefined, // listen on a unix socket instead of a TCP port
        },
        listenInaddrAny: toBoolean(envs.LISTEN_INADDR_ANY, true), // 是否允许公网连接,取值 0 1
        disableIPv6: toBoolean(envs.DISABLE_IPV6, false),
        requestRetry: toInt(envs.REQUEST_RETRY, 2), // 请求失败重试次数
        requestTimeout: toInt(envs.REQUEST_TIMEOUT, 30000), // Milliseconds to wait for the server to end the response before aborting the request
        requestAutoSelectFamily: envs.REQUEST_AUTO_SELECT_FAMILY === undefined ? undefined : toBoolean(envs.REQUEST_AUTO_SELECT_FAMILY, false),
        requestRateLimits: parseHostRateLimits(envs.REQUEST_RATE_LIMITS),
        ua: envs.UA || (toBoolean(envs.NO_RANDOM_UA, false) ? TRUE_UA : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36'),
        isDefaultUA: !envs.UA && !toBoolean(envs.NO_RANDOM_UA, false),
        trueUA: TRUE_UA,
        allowOrigin: envs.ALLOW_ORIGIN,
        // cache
        cache: {
            type: envs.CACHE_TYPE || (envs.CACHE_TYPE === '' ? '' : 'memory'), // Cache type; supports 'memory', 'redis', and 'http'. Set to empty string to disable cache.
            requestTimeout: toInt(envs.CACHE_REQUEST_TIMEOUT, 60),
            routeExpire: toInt(envs.CACHE_EXPIRE, 5 * 60), // 路由缓存时间,单位为秒
            contentExpire: toInt(envs.CACHE_CONTENT_EXPIRE, 1 * 60 * 60), // 不变内容缓存时间,单位为秒
        },
        memory: {
            max: toInt(envs.MEMORY_MAX, Math.pow(2, 8)), // The maximum number of items that remain in the cache. This must be a positive finite intger.
            // https://github.com/isaacs/node-lru-cache#options
        },
        redis: {
            url: envs.REDIS_URL || 'redis://localhost:6379/',
        },
        httpCache: {
            url: envs.CACHE_HTTP_URL,
            token: envs.CACHE_HTTP_TOKEN,
        },
        // proxy
        proxyUri: envs.PROXY_URI,
        proxy: {
            protocol: envs.PROXY_PROTOCOL,
            host: envs.PROXY_HOST,
            port: envs.PROXY_PORT,
            auth: envs.PROXY_AUTH,
            url_regex: envs.PROXY_URL_REGEX || '.*',
            strategy: envs.PROXY_STRATEGY || 'all', // all / on_retry
        },
        pacUri: envs.PAC_URI,
        pacScript: envs.PAC_SCRIPT,
        // access control
        accessKey: envs.ACCESS_KEY,
        // logging
        // 是否显示 Debug 信息,取值 'true' 'false' 'some_string' ,取值为 'true' 时永久显示,取值为 'false' 时永远隐藏,取值为 'some_string' 时请求带上 '?debug=some_string' 显示
        debugInfo: envs.DEBUG_INFO || 'true',
        loggerLevel: envs.LOGGER_LEVEL || 'info',
        noLogfiles: toBoolean(envs.NO_LOGFILES, false),
        otel: {
            seconds_bucket: envs.OTEL_SECONDS_BUCKET || '0.01,0.1,1,2,5,15,30,60',
            milliseconds_bucket: envs.OTEL_MILLISECONDS_BUCKET || '10,20,50,100,250,500,1000,5000,15000',
        },
        showLoggerTimestamp: toBoolean(envs.SHOW_LOGGER_TIMESTAMP, false),
        honeybadger: {
            apiKey: envs.HONEYBADGER_API_KEY,
        },
        sentry: {
            dsn: envs.SENTRY,
        },
        errorTrackingRouteTimeout: toInt(envs.ERROR_TRACKING_ROUTE_TIMEOUT || envs.SENTRY_ROUTE_TIMEOUT, 30000),
        enableRemoteDebugging: toBoolean(envs.ENABLE_REMOTE_DEBUGGING, false),
        // feed config
        hotlink: {
            template: envs.HOTLINK_TEMPLATE,
            includePaths: envs.HOTLINK_INCLUDE_PATHS ? envs.HOTLINK_INCLUDE_PATHS.split(',') : undefined,
            excludePaths: envs.HOTLINK_EXCLUDE_PATHS ? envs.HOTLINK_EXCLUDE_PATHS.split(',') : undefined,
        },
        feature: {
            allow_user_hotlink_template: toBoolean(envs.ALLOW_USER_HOTLINK_TEMPLATE, false),
            filter_regex_engine: envs.FILTER_REGEX_ENGINE || 're2',
            allow_user_supply_unsafe_domain: toBoolean(envs.ALLOW_USER_SUPPLY_UNSAFE_DOMAIN, false),
            disable_nsfw: toBoolean(envs.DISABLE_NSFW, false),
        },
        suffix: envs.SUFFIX,
        titleLengthLimit: toInt(envs.TITLE_LENGTH_LIMIT, 150),
        format: envs.FORMAT || 'rss',
        openai: {
            apiKey: envs.OPENAI_API_KEY,
            model: envs.OPENAI_MODEL || 'gpt-3.5-turbo-16k',
            temperature: toInt(envs.OPENAI_TEMPERATURE, 0.2),
            maxTokens: toInt(envs.OPENAI_MAX_TOKENS, 0) || undefined,
            endpoint: envs.OPENAI_API_ENDPOINT || 'https://api.openai.com/v1',
            inputOption: envs.OPENAI_INPUT_OPTION || 'description',
            promptDescription: envs.OPENAI_PROMPT || 'Please summarize the following article and reply with markdown format.',
            promptTitle: envs.OPENAI_PROMPT_TITLE || 'Please translate the following title into Simplified Chinese and reply only translated text.',
        },
        follow: {
            ownerUserId: envs.FOLLOW_OWNER_USER_ID,
            description: envs.FOLLOW_DESCRIPTION,
            price: toInt(envs.FOLLOW_PRICE),
            userLimit: toInt(envs.FOLLOW_USER_LIMIT),
        },

        // Route-specific Configurations
        baidu: {
            cookie: envs.BAIDU_COOKIE,
        },
        bilibili: {
            cookies: bilibili_cookies,
            dmImgList: envs.BILIBILI_DM_IMG_LIST,
            dmImgInter: envs.BILIBILI_DM_IMG_INTER,
            excludeSubtitles: toBoolean(envs.BILIBILI_EXCLUDE_SUBTITLES, false),
        },
        bitbucket: {
            username: envs.BITBUCKET_USERNAME,
            password: envs.BITBUCKET_PASSWORD,
        },
        btbyr: {
            host: envs.BTBYR_HOST,
            cookies: envs.BTBYR_COOKIE,
        },
        bupt: {
            portal_cookie: envs.BUPT_PORTAL_COOKIE,
        },
        bukenavi: {
            email: envs.BUKENAVI_EMAIL,
            password: envs.BUKENAVI_PASSWORD,
        },
        caixin: {
            cookie: envs.CAIXIN_COOKIE,
        },
        cara: {
            cookie: envs.CARA_COOKIE,
        },
        civitai: {
            cookie: envs.CIVITAI_COOKIE,
        },
        ciEn: {
            cookie: envs.CI_EN_COOKIE,
        },
        coomer: {
            assetsUrl: getAssetsUrl(envs.COOMER_ROOT_URL || 'https://coomer.st', envs.COOMER_ASSETS_URL),
            rootUrl: (envs.COOMER_ROOT_URL || 'https://coomer.st').replace(/\/+$/, ''),
        },
        dianping: {
            cookie: envs.DIANPING_COOKIE,
        },
        dida365: {
            username: envs.DIDA365_USERNAME,
            password: envs.DIDA365_PASSWORD,
        },
        discord: {
            authorization: envs.DISCORD_AUTHORIZATION,
        },
        discourse: {
            config: discourse_config,
        },
        discuz: {
            cookies: discuz_cookies,
        },
        disqus: {
            api_key: envs.DISQUS_API_KEY,
        },
        douban: {
            cookie: envs.DOUBAN_COOKIE,
        },
        douyin: {
            cookie: envs.DOUYIN_COOKIE,
        },
        ehentai: {
            ipb_member_id: envs.EH_IPB_MEMBER_ID,
            ipb_pass_hash: envs.EH_IPB_PASS_HASH,
            sk: envs.EH_SK,
            igneous: envs.EH_IGNEOUS,
            star: envs.EH_STAR,
            img_proxy: envs.EH_IMG_PROXY,
        },
        email: {
            config: email_config,
        },
        etherscan: {
            apiKey: envs.ETHERSCAN_API_KEY,
        },
        f95zone: {
            cookie: envs.F95ZONE_COOKIE,
        },
        facebook: {
            cookie: envs.FACEBOOK_COOKIE,
        },
        fanbox: {
            session: envs.FANBOX_SESSION_ID,
        },
        fanfou: {
            consumer_key: envs.FANFOU_CONSUMER_KEY,
            consumer_secret: envs.FANFOU_CONSUMER_SECRET,
            username: envs.FANFOU_USERNAME,
            password: envs.FANFOU_PASSWORD,
        },
        fantia: {
            cookies: envs.FANTIA_COOKIE,
        },
        game4399: {
            cookie: envs.GAME_4399,
        },
        gelbooru: {
            apiKey: envs.GELBOORU_API_KEY,
            userId: envs.GELBOORU_USER_ID,
        },
        github: {
            access_token: envs.GITHUB_ACCESS_TOKEN,
        },
        gitee: {
            access_token: envs.GITEE_ACCESS_TOKEN,
        },
        google: {
            fontsApiKey: envs.GOOGLE_FONTS_API_KEY,
        },
        guozaoke: {
            cookies: envs.GUOZAOKE_COOKIES,
        },
        hefeng: {
            key: envs.HEFENG_KEY,
            apiHost: envs.HEFENG_API_HOST,
        },
        huitun: {
            cookie: envs.HUITUN_COOKIE,
        },
        infzm: {
            cookie: envs.INFZM_COOKIE,
        },
        initium: {
            memberCookie: envs.INITIUM_MEMBER_COOKIE,
        },
        instagram: {
            cookie: envs.INSTAGRAM_COOKIE ?? envs.IG_COOKIE,
        },
        iwara: {
            username: envs.IWARA_USERNAME,
            password: envs.IWARA_PASSWORD,
        },
        javdb: {
            session: envs.JAVDB_SESSION,
        },
        jumeili: {
            cookie: envs.JUMEILI_COOKIE,
        },
        kemono: {
            assetsUrl: getAssetsUrl(envs.KEMONO_ROOT_URL || 'https://kemono.cr', envs.KEMONO_ASSETS_URL),
            rootUrl: (envs.KEMONO_ROOT_URL || 'https://kemono.cr').replace(/\/+$/, ''),
        },
        keylol: {
            cookie: envs.KEYLOL_COOKIE,
        },
        lastfm: {
            api_key: envs.LASTFM_API_KEY,
        },
        lightnovel: {
            cookie: envs.SECURITY_KEY,
        },
        locals: {
            session: envs.LOCALS_SESSION,
        },
        lofter: {
            cookies: envs.LOFTER_COOKIE,
        },
        lorientlejour: {
            token: envs.LORIENTLEJOUR_TOKEN,
            username: envs.LORIENTLEJOUR_USERNAME,
            password: envs.LORIENTLEJOUR_PASSWORD,
        },
        lowendtalk: {
            cookie: envs.LOWENDTALK_COOKIE,
        },
        malaysiakini: {
            email: envs.MALAYSIAKINI_EMAIL,
            password: envs.MALAYSIAKINI_PASSWORD,
            refreshToken: envs.MALAYSIAKINI_REFRESHTOKEN,
        },
        mangadex: {
            username: envs.MANGADEX_USERNAME, // required when refresh-token is not set
            password: envs.MANGADEX_PASSWORD, // required when refresh-token is not set
            clientId: envs.MANGADEX_CLIENT_ID,
            clientSecret: envs.MANGADEX_CLIENT_SECRET,
            refreshToken: envs.MANGADEX_REFRESH_TOKEN,
        },
        manhuagui: {
            cookie: envs.MHGUI_COOKIE,
        },
        mastodon: {
            apiHost: envs.MASTODON_API_HOST,
            accessToken: envs.MASTODON_API_ACCESS_TOKEN,
            acctDomain: envs.MASTODON_API_ACCT_DOMAIN,
        },
        medium: {
            cookies: medium_cookies,
            articleCookie: envs.MEDIUM_ARTICLE_COOKIE || '',
        },
        mihoyo: {
            cookie: envs.MIHOYO_COOKIE,
        },
        miniflux: {
            instance: envs.MINIFLUX_INSTANCE || 'https://reader.miniflux.app',
            token: envs.MINIFLUX_TOKEN || '',
        },
        misskey: {
            accessToken: envs.MISSKEY_ACCESS_TOKEN,
        },
        mixi2: {
            authToken: envs.MIXI2_AUTH_TOKEN,
            authKey: envs.MIXI2_AUTH_KEY,
        },
        mox: {
            cookie: envs.MOX_COOKIE,
        },
        ncm: {
            cookies: envs.NCM_COOKIES || '',
        },
        newrank: {
            cookie: envs.NEWRANK_COOKIE,
        },
        nga: {
            uid: envs.NGA_PASSPORT_UID,
            cid: envs.NGA_PASSPORT_CID,
        },
        nhentai: {
            username: envs.NHENTAI_USERNAME,
            password: envs.NHENTAI_PASSWORD,
        },
        notion: {
            key: envs.NOTION_TOKEN,
        },
        onlyfans: {
            cookie: envs.ONLYFANS_COOKIE,
        },
        patreon: {
            sessionId: envs.PATREON_SESSION_ID,
        },
        pianyuan: {
            cookie: envs.PIANYUAN_COOKIE,
        },
        pixabay: {
            key: envs.PIXABAY_KEY,
        },
        pixiv: {
            refreshToken: envs.PIXIV_REFRESHTOKEN,
            bypassCdn: toBoolean(envs.PIXIV_BYPASS_CDN, false),
            bypassCdnHostname: envs.PIXIV_BYPASS_HOSTNAME || 'public-api.secure.pixiv.net',
            bypassCdnDoh: envs.PIXIV_BYPASS_DOH || 'https://1.1.1.1/dns-query',
            imgProxy: envs.PIXIV_IMG_PROXY || 'https://i.pixiv.re',
        },
        pkubbs: {
            cookie: envs.PKUBBS_COOKIE,
        },
        qingting: {
            id: envs.QINGTING_ID,
        },
        readwise: {
            accessToken: envs.READWISE_ACCESS_TOKEN,
        },
        reuters: {
            authToken: envs.REUTERS_AUTH_TOKEN,
        },
        saraba1st: {
            cookie: envs.SARABA1ST_COOKIE,
            host: envs.SARABA1ST_HOST || 'https://stage1st.com',
        },
        sehuatang: {
            cookie: envs.SEHUATANG_COOKIE,
        },
        scboy: {
            token: envs.SCBOY_BBS_TOKEN,
        },
        scihub: {
            host: envs.SCIHUB_HOST || 'https://sci-hub.se/',
        },
        sdo: {
            ff14risingstones: envs.SDO_FF14RISINGSTONES,
            ua: envs.SDO_UA,
        },
        sis001: {
            baseUrl: envs.SIS001_BASE_URL || 'https://sis001.com',
        },
        skeb: {
            bearerToken: envs.SKEB_BEARER_TOKEN,
        },
        sorrycc: {
            cookie: envs.SORRYCC_COOKIES,
        },
        southplus: {
            cookie: envs.SOUTHPLUS_COOKIE,
            ua: envs.SOUTHPLUS_UA,
        },
        spotify: {
            clientId: envs.SPOTIFY_CLIENT_ID,
            clientSecret: envs.SPOTIFY_CLIENT_SECRET,
            refreshToken: envs.SPOTIFY_REFRESHTOKEN,
        },
        sspai: {
            bearertoken: envs.SSPAI_BEARERTOKEN,
        },
        taoguba: {
            cookie: envs.TAOGUBA_COOKIE,
        },
        telegram: {
            token: envs.TELEGRAM_TOKEN,
            session: envs.TELEGRAM_SESSION,
            apiId: toInt(envs.TELEGRAM_API_ID),
            apiHash: envs.TELEGRAM_API_HASH,
            maxConcurrentDownloads: toInt(envs.TELEGRAM_MAX_CONCURRENT_DOWNLOADS),
            proxy: {
                host: envs.TELEGRAM_PROXY_HOST,
                port: toInt(envs.TELEGRAM_PROXY_PORT),
                secret: envs.TELEGRAM_PROXY_SECRET,
            },
        },
        tophub: {
            cookie: envs.TOPHUB_COOKIE,
        },
        tsdm39: {
            cookie: envs.TSDM39_COOKIES,
        },
        tumblr: {
            clientId: envs.TUMBLR_CLIENT_ID,
            clientSecret: envs.TUMBLR_CLIENT_SECRET,
            refreshToken: envs.TUMBLR_REFRESH_TOKEN,
        },
        twitter: {
            consumerKey: envs.TWITTER_CONSUMER_KEY,
            consumerSecret: envs.TWITTER_CONSUMER_SECRET,
            accessToken: envs.TWITTER_ACCESS_TOKEN,
            accessSecret: envs.TWITTER_ACCESS_SECRET,
            // username: envs.TWITTER_USERNAME?.split(','),
            // password: envs.TWITTER_PASSWORD?.split(','),
            // authenticationSecret: envs.TWITTER_AUTHENTICATION_SECRET?.split(','),
            // phoneOrEmail: envs.TWITTER_PHONE_OR_EMAIL?.split(','),
            authToken: envs.TWITTER_AUTH_TOKEN?.split(','),
            thirdPartyApi: envs.TWITTER_THIRD_PARTY_API,
        },
        uestc: {
            bbsCookie: envs.UESTC_BBS_COOKIE,
            bbsAuthStr: envs.UESTC_BBS_AUTH_STR,
        },
        weibo: {
            app_key: envs.WEIBO_APP_KEY,
            app_secret: envs.WEIBO_APP_SECRET,
            cookies: envs.WEIBO_COOKIES,
            redirect_url: envs.WEIBO_REDIRECT_URL,
        },
        wenku8: {
            cookie: envs.WENKU8_COOKIE,
        },
        wordpress: {
            allowedDomains:
                envs.WORDPRESS_ALLOWED_DOMAINS?.split(',')
                    .map((domain) => domain.trim().toLowerCase())
                    .filter(Boolean) ?? [],
            cdnUrl: envs.WORDPRESS_CDN,
        },
        xiaoyuzhou: {
            device_id: envs.XIAOYUZHOU_ID,
            refresh_token: envs.XIAOYUZHOU_TOKEN,
        },
        xiaohongshu: {
            cookie: envs.XIAOHONGSHU_COOKIE,
            proxy: envs.XIAOHONGSHU_PROXY,
        },
        ximalaya: {
            token: envs.XIMALAYA_TOKEN,
        },
        xsijishe: {
            cookie: envs.XSIJISHE_COOKIE,
        },
        xueqiu: {
            cookies: envs.XUEQIU_COOKIES,
        },
        yamibo: {
            salt: envs.YAMIBO_SALT,
            auth: envs.YAMIBO_AUTH,
        },
        youtube: {
            key: envs.YOUTUBE_KEY,
            clientId: envs.YOUTUBE_CLIENT_ID,
            clientSecret: envs.YOUTUBE_CLIENT_SECRET,
            refreshToken: envs.YOUTUBE_REFRESH_TOKEN,
            videoEmbedUrl: envs.YOUTUBE_VIDEO_EMBED_URL || 'https://www.youtube-nocookie.com/embed/',
        },
        zaimanhua: {
            token: envs.ZAIMANHUA_TOKEN,
        },
        zhihu: {
            cookies: envs.ZHIHU_COOKIES,
        },
        zodgame: {
            cookie: envs.ZODGAME_COOKIE,
        },
        zsxq: {
            accessToken: envs.ZSXQ_ACCESS_TOKEN,
        },
        smzdm: {
            cookie: envs.SMZDM_COOKIE,
        },
    };

    Object.assign(value, _value);
};
calculateValue();
(async () => {
    if (!envs.REMOTE_CONFIG) {
        return;
    }
    const { default: logger } = await import('@/utils/logger');
    try {
        const data = await ofetch(envs.REMOTE_CONFIG, {
            headers: {
                Authorization: `Basic ${envs.REMOTE_CONFIG_AUTH}`,
            },
        });
        if (data) {
            envs = Object.assign(envs, data);
            calculateValue();
            logger.info('Remote config loaded.');
        } else {
            logger.error('Remote config load failed.');
        }
    } catch (error) {
        logger.error('Remote config load failed.', error);
    }
})();

export const config: Config = value;

export const setConfig = (env: ConfigEnv) => {
    envs = Object.assign(process.env, env);
    calculateValue();
};