| | |
| | import path from "path"; |
| | import fs from "fs-extra"; |
| | import minimist from "minimist"; |
| | import _ from "lodash"; |
| | var cmdArgs = minimist(process.argv.slice(2)); |
| | var envVars = process.env; |
| | var Environment = class { |
| | |
| | cmdArgs; |
| | |
| | envVars; |
| | |
| | env; |
| | |
| | name; |
| | |
| | host; |
| | |
| | port; |
| | |
| | package; |
| | constructor(options = {}) { |
| | const { cmdArgs: cmdArgs2, envVars: envVars2, package: _package } = options; |
| | this.cmdArgs = cmdArgs2; |
| | this.envVars = envVars2; |
| | this.env = _.defaultTo(cmdArgs2.env || envVars2.SERVER_ENV, "dev"); |
| | this.name = cmdArgs2.name || envVars2.SERVER_NAME || void 0; |
| | this.host = cmdArgs2.host || envVars2.SERVER_HOST || void 0; |
| | this.port = Number(cmdArgs2.port || envVars2.SERVER_PORT) ? Number(cmdArgs2.port || envVars2.SERVER_PORT) : void 0; |
| | this.package = _package; |
| | } |
| | }; |
| | var environment_default = new Environment({ |
| | cmdArgs, |
| | envVars, |
| | package: JSON.parse(fs.readFileSync(path.join(path.resolve(), "package.json")).toString()) |
| | }); |
| |
|
| | |
| | import path3 from "path"; |
| | import fs3 from "fs-extra"; |
| | import yaml from "yaml"; |
| | import _3 from "lodash"; |
| |
|
| | |
| | import os from "os"; |
| | import path2 from "path"; |
| | import crypto from "crypto"; |
| | import { Readable, Writable } from "stream"; |
| | import "colors"; |
| | import mime from "mime"; |
| | import axios from "axios"; |
| | import fs2 from "fs-extra"; |
| | import { v1 as uuid } from "uuid"; |
| | import { format as dateFormat } from "date-fns"; |
| | import CRC32 from "crc-32"; |
| | import randomstring from "randomstring"; |
| | import _2 from "lodash"; |
| | import { CronJob } from "cron"; |
| |
|
| | |
| | var http_status_codes_default = { |
| | CONTINUE: 100, |
| | |
| | SWITCHING_PROTOCOLS: 101, |
| | |
| | PROCESSING: 102, |
| | |
| | OK: 200, |
| | |
| | CREATED: 201, |
| | |
| | ACCEPTED: 202, |
| | |
| | NON_AUTHORITATIVE_INFO: 203, |
| | |
| | NO_CONTENT: 204, |
| | |
| | RESET_CONTENT: 205, |
| | |
| | PARTIAL_CONTENT: 206, |
| | |
| | MULTIPLE_STATUS: 207, |
| | |
| | MULTIPLE_CHOICES: 300, |
| | |
| | MOVED_PERMANENTLY: 301, |
| | |
| | FOUND: 302, |
| | |
| | SEE_OTHER: 303, |
| | |
| | NOT_MODIFIED: 304, |
| | |
| | USE_PROXY: 305, |
| | |
| | UNUSED: 306, |
| | |
| | TEMPORARY_REDIRECT: 307, |
| | |
| | BAD_REQUEST: 400, |
| | |
| | UNAUTHORIZED: 401, |
| | |
| | PAYMENT_REQUIRED: 402, |
| | |
| | FORBIDDEN: 403, |
| | |
| | NOT_FOUND: 404, |
| | |
| | METHOD_NOT_ALLOWED: 405, |
| | |
| | NO_ACCEPTABLE: 406, |
| | |
| | PROXY_AUTHENTICATION_REQUIRED: 407, |
| | |
| | REQUEST_TIMEOUT: 408, |
| | |
| | CONFLICT: 409, |
| | |
| | GONE: 410, |
| | |
| | LENGTH_REQUIRED: 411, |
| | |
| | PRECONDITION_FAILED: 412, |
| | |
| | REQUEST_ENTITY_TOO_LARGE: 413, |
| | |
| | REQUEST_URI_TOO_LONG: 414, |
| | |
| | UNSUPPORTED_MEDIA_TYPE: 415, |
| | |
| | REQUESTED_RANGE_NOT_SATISFIABLE: 416, |
| | |
| | EXPECTION_FAILED: 417, |
| | |
| | TOO_MANY_CONNECTIONS: 421, |
| | |
| | UNPROCESSABLE_ENTITY: 422, |
| | |
| | FAILED_DEPENDENCY: 424, |
| | |
| | UNORDERED_COLLECTION: 425, |
| | |
| | UPGRADE_REQUIRED: 426, |
| | |
| | RETRY_WITH: 449, |
| | |
| | INTERNAL_SERVER_ERROR: 500, |
| | |
| | NOT_IMPLEMENTED: 501, |
| | |
| | BAD_GATEWAY: 502, |
| | |
| | SERVICE_UNAVAILABLE: 503, |
| | |
| | GATEWAY_TIMEOUT: 504, |
| | |
| | HTTP_VERSION_NOT_SUPPORTED: 505, |
| | |
| | VARIANT_ALSO_NEGOTIATES: 506, |
| | |
| | INSUFFICIENT_STORAGE: 507, |
| | |
| | BANDWIDTH_LIMIT_EXCEEDED: 509, |
| | |
| | NOT_EXTENDED: 510 |
| | |
| | }; |
| |
|
| | |
| | var autoIdMap = new Map(); |
| | var util = { |
| | is2DArrays(value) { |
| | return _2.isArray(value) && (!value[0] || _2.isArray(value[0]) && _2.isArray(value[value.length - 1])); |
| | }, |
| | uuid: (separator = true) => separator ? uuid() : uuid().replace(/\-/g, ""), |
| | autoId: (prefix = "") => { |
| | let index = autoIdMap.get(prefix); |
| | if (index > 999999) index = 0; |
| | autoIdMap.set(prefix, (index || 0) + 1); |
| | return `${prefix}${index || 1}`; |
| | }, |
| | ignoreJSONParse(value) { |
| | const result = _2.attempt(() => JSON.parse(value)); |
| | if (_2.isError(result)) return null; |
| | return result; |
| | }, |
| | generateRandomString(options) { |
| | return randomstring.generate(options); |
| | }, |
| | getResponseContentType(value) { |
| | return value.headers ? value.headers["content-type"] || value.headers["Content-Type"] : null; |
| | }, |
| | mimeToExtension(value) { |
| | let extension = mime.getExtension(value); |
| | if (extension == "mpga") return "mp3"; |
| | return extension; |
| | }, |
| | extractURLExtension(value) { |
| | const extname = path2.extname(new URL(value).pathname); |
| | return extname.substring(1).toLowerCase(); |
| | }, |
| | createCronJob(cronPatterns, callback) { |
| | if (!_2.isFunction(callback)) |
| | throw new Error("callback must be an Function"); |
| | return new CronJob( |
| | cronPatterns, |
| | () => callback(), |
| | null, |
| | false, |
| | "Asia/Shanghai" |
| | ); |
| | }, |
| | getDateString(format = "yyyy-MM-dd", date = new Date()) { |
| | return dateFormat(date, format); |
| | }, |
| | getIPAddressesByIPv4() { |
| | const interfaces = os.networkInterfaces(); |
| | const addresses = []; |
| | for (let name in interfaces) { |
| | const networks = interfaces[name]; |
| | const results = networks.filter( |
| | (network) => network.family === "IPv4" && network.address !== "127.0.0.1" && !network.internal |
| | ); |
| | if (results[0] && results[0].address) addresses.push(results[0].address); |
| | } |
| | return addresses; |
| | }, |
| | getMACAddressesByIPv4() { |
| | const interfaces = os.networkInterfaces(); |
| | const addresses = []; |
| | for (let name in interfaces) { |
| | const networks = interfaces[name]; |
| | const results = networks.filter( |
| | (network) => network.family === "IPv4" && network.address !== "127.0.0.1" && !network.internal |
| | ); |
| | if (results[0] && results[0].mac) addresses.push(results[0].mac); |
| | } |
| | return addresses; |
| | }, |
| | generateSSEData(event, data, retry) { |
| | return `event: ${event || "message"} |
| | data: ${(data || "").replace(/\n/g, "\\n").replace(/\s/g, "\\s")} |
| | retry: ${retry || 3e3} |
| | |
| | `; |
| | }, |
| | buildDataBASE64(type, ext, buffer) { |
| | return `data:${type}/${ext.replace("jpg", "jpeg")};base64,${buffer.toString( |
| | "base64" |
| | )}`; |
| | }, |
| | isLinux() { |
| | return os.platform() !== "win32"; |
| | }, |
| | isIPAddress(value) { |
| | return _2.isString(value) && (/^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$/.test( |
| | value |
| | ) || /\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*/.test( |
| | value |
| | )); |
| | }, |
| | isPort(value) { |
| | return _2.isNumber(value) && value > 0 && value < 65536; |
| | }, |
| | isReadStream(value) { |
| | return value && (value instanceof Readable || "readable" in value || value.readable); |
| | }, |
| | isWriteStream(value) { |
| | return value && (value instanceof Writable || "writable" in value || value.writable); |
| | }, |
| | isHttpStatusCode(value) { |
| | return _2.isNumber(value) && Object.values(http_status_codes_default).includes(value); |
| | }, |
| | isURL(value) { |
| | return !_2.isUndefined(value) && /^(http|https)/.test(value); |
| | }, |
| | isSrc(value) { |
| | return !_2.isUndefined(value) && /^\/.+\.[0-9a-zA-Z]+(\?.+)?$/.test(value); |
| | }, |
| | isBASE64(value) { |
| | return !_2.isUndefined(value) && /^[a-zA-Z0-9\/\+]+(=?)+$/.test(value); |
| | }, |
| | isBASE64Data(value) { |
| | return /^data:/.test(value); |
| | }, |
| | extractBASE64DataFormat(value) { |
| | const match = value.trim().match(/^data:(.+);base64,/); |
| | if (!match) return null; |
| | return match[1]; |
| | }, |
| | removeBASE64DataHeader(value) { |
| | return value.replace(/^data:(.+);base64,/, ""); |
| | }, |
| | isDataString(value) { |
| | return /^(base64|json):/.test(value); |
| | }, |
| | isStringNumber(value) { |
| | return _2.isFinite(Number(value)); |
| | }, |
| | isUnixTimestamp(value) { |
| | return /^[0-9]{10}$/.test(`${value}`); |
| | }, |
| | isTimestamp(value) { |
| | return /^[0-9]{13}$/.test(`${value}`); |
| | }, |
| | isEmail(value) { |
| | return /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/.test( |
| | value |
| | ); |
| | }, |
| | isAsyncFunction(value) { |
| | return Object.prototype.toString.call(value) === "[object AsyncFunction]"; |
| | }, |
| | async isAPNG(filePath) { |
| | let head; |
| | const readStream = fs2.createReadStream(filePath, { start: 37, end: 40 }); |
| | const readPromise = new Promise((resolve, reject) => { |
| | readStream.once("end", resolve); |
| | readStream.once("error", reject); |
| | }); |
| | readStream.once("data", (data) => head = data); |
| | await readPromise; |
| | return head.compare(Buffer.from([97, 99, 84, 76])) === 0; |
| | }, |
| | unixTimestamp() { |
| | return parseInt(`${Date.now() / 1e3}`); |
| | }, |
| | timestamp() { |
| | return Date.now(); |
| | }, |
| | urlJoin(...values) { |
| | let url = ""; |
| | for (let i = 0; i < values.length; i++) |
| | url += `${i > 0 ? "/" : ""}${values[i].replace(/^\/*/, "").replace(/\/*$/, "")}`; |
| | return url; |
| | }, |
| | millisecondsToHmss(milliseconds) { |
| | if (_2.isString(milliseconds)) return milliseconds; |
| | milliseconds = parseInt(milliseconds); |
| | const sec = Math.floor(milliseconds / 1e3); |
| | const hours = Math.floor(sec / 3600); |
| | const minutes = Math.floor((sec - hours * 3600) / 60); |
| | const seconds = sec - hours * 3600 - minutes * 60; |
| | const ms = milliseconds % 6e4 - seconds * 1e3; |
| | return `${hours > 9 ? hours : "0" + hours}:${minutes > 9 ? minutes : "0" + minutes}:${seconds > 9 ? seconds : "0" + seconds}.${ms}`; |
| | }, |
| | millisecondsToTimeString(milliseconds) { |
| | if (milliseconds < 1e3) return `${milliseconds}ms`; |
| | if (milliseconds < 6e4) |
| | return `${parseFloat((milliseconds / 1e3).toFixed(2))}s`; |
| | return `${Math.floor(milliseconds / 1e3 / 60)}m${Math.floor( |
| | milliseconds / 1e3 % 60 |
| | )}s`; |
| | }, |
| | rgbToHex(r, g, b) { |
| | return ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); |
| | }, |
| | hexToRgb(hex) { |
| | const value = parseInt(hex.replace(/^#/, ""), 16); |
| | return [value >> 16 & 255, value >> 8 & 255, value & 255]; |
| | }, |
| | md5(value) { |
| | return crypto.createHash("md5").update(value).digest("hex"); |
| | }, |
| | crc32(value) { |
| | return _2.isBuffer(value) ? CRC32.buf(value) : CRC32.str(value); |
| | }, |
| | arrayParse(value) { |
| | return _2.isArray(value) ? value : [value]; |
| | }, |
| | booleanParse(value) { |
| | return value === "true" || value === true ? true : false; |
| | }, |
| | encodeBASE64(value) { |
| | return Buffer.from(value).toString("base64"); |
| | }, |
| | decodeBASE64(value) { |
| | return Buffer.from(value, "base64").toString(); |
| | }, |
| | async fetchFileBASE64(url) { |
| | const result = await axios.get(url, { |
| | responseType: "arraybuffer" |
| | }); |
| | return result.data.toString("base64"); |
| | } |
| | }; |
| | var util_default = util; |
| |
|
| | |
| | var CONFIG_PATH = path3.join(path3.resolve(), "configs/", environment_default.env, "/service.yml"); |
| | var ServiceConfig = class _ServiceConfig { |
| | |
| | name; |
| | |
| | host; |
| | |
| | port; |
| | |
| | urlPrefix; |
| | |
| | bindAddress; |
| | constructor(options) { |
| | const { name, host, port, urlPrefix, bindAddress } = options || {}; |
| | this.name = _3.defaultTo(name, "deepseek-free-api"); |
| | this.host = _3.defaultTo(host, "0.0.0.0"); |
| | this.port = _3.defaultTo(port, 5566); |
| | this.urlPrefix = _3.defaultTo(urlPrefix, ""); |
| | this.bindAddress = bindAddress; |
| | } |
| | get addressHost() { |
| | if (this.bindAddress) return this.bindAddress; |
| | const ipAddresses = util_default.getIPAddressesByIPv4(); |
| | for (let ipAddress of ipAddresses) { |
| | if (ipAddress === this.host) |
| | return ipAddress; |
| | } |
| | return ipAddresses[0] || "127.0.0.1"; |
| | } |
| | get address() { |
| | return `${this.addressHost}:${this.port}`; |
| | } |
| | get pageDirUrl() { |
| | return `http://127.0.0.1:${this.port}/page`; |
| | } |
| | get publicDirUrl() { |
| | return `http://127.0.0.1:${this.port}/public`; |
| | } |
| | static load() { |
| | const external = _3.pickBy(environment_default, (v, k) => ["name", "host", "port"].includes(k) && !_3.isUndefined(v)); |
| | if (!fs3.pathExistsSync(CONFIG_PATH)) return new _ServiceConfig(external); |
| | const data = yaml.parse(fs3.readFileSync(CONFIG_PATH).toString()); |
| | return new _ServiceConfig({ ...data, ...external }); |
| | } |
| | }; |
| | var service_config_default = ServiceConfig.load(); |
| |
|
| | |
| | import path4 from "path"; |
| | import fs4 from "fs-extra"; |
| | import yaml2 from "yaml"; |
| | import _4 from "lodash"; |
| | var CONFIG_PATH2 = path4.join(path4.resolve(), "configs/", environment_default.env, "/system.yml"); |
| | var SystemConfig = class _SystemConfig { |
| | |
| | requestLog; |
| | |
| | tmpDir; |
| | |
| | logDir; |
| | |
| | logWriteInterval; |
| | |
| | logFileExpires; |
| | |
| | publicDir; |
| | |
| | tmpFileExpires; |
| | |
| | requestBody; |
| | |
| | debug; |
| | constructor(options) { |
| | const { requestLog, tmpDir, logDir, logWriteInterval, logFileExpires, publicDir, tmpFileExpires, requestBody, debug } = options || {}; |
| | this.requestLog = _4.defaultTo(requestLog, false); |
| | this.tmpDir = _4.defaultTo(tmpDir, "./tmp"); |
| | this.logDir = _4.defaultTo(logDir, "./logs"); |
| | this.logWriteInterval = _4.defaultTo(logWriteInterval, 200); |
| | this.logFileExpires = _4.defaultTo(logFileExpires, 262656e4); |
| | this.publicDir = _4.defaultTo(publicDir, "./public"); |
| | this.tmpFileExpires = _4.defaultTo(tmpFileExpires, 864e5); |
| | this.requestBody = Object.assign(requestBody || {}, { |
| | enableTypes: ["json", "form", "text", "xml"], |
| | encoding: "utf-8", |
| | formLimit: "100mb", |
| | jsonLimit: "100mb", |
| | textLimit: "100mb", |
| | xmlLimit: "100mb", |
| | formidable: { |
| | maxFileSize: "100mb" |
| | }, |
| | multipart: true, |
| | parsedMethods: ["POST", "PUT", "PATCH"] |
| | }); |
| | this.debug = _4.defaultTo(debug, true); |
| | } |
| | get rootDirPath() { |
| | return path4.resolve(); |
| | } |
| | get tmpDirPath() { |
| | return path4.resolve(this.tmpDir); |
| | } |
| | get logDirPath() { |
| | return path4.resolve(this.logDir); |
| | } |
| | get publicDirPath() { |
| | return path4.resolve(this.publicDir); |
| | } |
| | static load() { |
| | if (!fs4.pathExistsSync(CONFIG_PATH2)) return new _SystemConfig(); |
| | const data = yaml2.parse(fs4.readFileSync(CONFIG_PATH2).toString()); |
| | return new _SystemConfig(data); |
| | } |
| | }; |
| | var system_config_default = SystemConfig.load(); |
| |
|
| | |
| | var Config = class { |
| | |
| | service = service_config_default; |
| | |
| | system = system_config_default; |
| | }; |
| | var config_default = new Config(); |
| |
|
| | |
| | import path5 from "path"; |
| | import _util from "util"; |
| | import "colors"; |
| | import _5 from "lodash"; |
| | import fs5 from "fs-extra"; |
| | import { format as dateFormat2 } from "date-fns"; |
| | var isVercelEnv = process.env.VERCEL; |
| | var LogWriter = class { |
| | #buffers = []; |
| | constructor() { |
| | !isVercelEnv && fs5.ensureDirSync(config_default.system.logDirPath); |
| | !isVercelEnv && this.work(); |
| | } |
| | push(content) { |
| | const buffer = Buffer.from(content); |
| | this.#buffers.push(buffer); |
| | } |
| | writeSync(buffer) { |
| | !isVercelEnv && fs5.appendFileSync(path5.join(config_default.system.logDirPath, `/${util_default.getDateString()}.log`), buffer); |
| | } |
| | async write(buffer) { |
| | !isVercelEnv && await fs5.appendFile(path5.join(config_default.system.logDirPath, `/${util_default.getDateString()}.log`), buffer); |
| | } |
| | flush() { |
| | if (!this.#buffers.length) return; |
| | !isVercelEnv && fs5.appendFileSync(path5.join(config_default.system.logDirPath, `/${util_default.getDateString()}.log`), Buffer.concat(this.#buffers)); |
| | } |
| | work() { |
| | if (!this.#buffers.length) return setTimeout(this.work.bind(this), config_default.system.logWriteInterval); |
| | const buffer = Buffer.concat(this.#buffers); |
| | this.#buffers = []; |
| | this.write(buffer).finally(() => setTimeout(this.work.bind(this), config_default.system.logWriteInterval)).catch((err) => console.error("Log write error:", err)); |
| | } |
| | }; |
| | var LogText = class { |
| | |
| | level; |
| | |
| | text; |
| | |
| | source; |
| | |
| | time = new Date(); |
| | constructor(level, ...params) { |
| | this.level = level; |
| | this.text = _util.format.apply(null, params); |
| | this.source = this.#getStackTopCodeInfo(); |
| | } |
| | #getStackTopCodeInfo() { |
| | const unknownInfo = { name: "unknown", codeLine: 0, codeColumn: 0 }; |
| | const stackArray = new Error().stack.split("\n"); |
| | const text = stackArray[4]; |
| | if (!text) |
| | return unknownInfo; |
| | const match = text.match(/at (.+) \((.+)\)/) || text.match(/at (.+)/); |
| | if (!match || !_5.isString(match[2] || match[1])) |
| | return unknownInfo; |
| | const temp = match[2] || match[1]; |
| | const _match = temp.match(/([a-zA-Z0-9_\-\.]+)\:(\d+)\:(\d+)$/); |
| | if (!_match) |
| | return unknownInfo; |
| | const [, scriptPath, codeLine, codeColumn] = _match; |
| | return { |
| | name: scriptPath ? scriptPath.replace(/.js$/, "") : "unknown", |
| | path: scriptPath || null, |
| | codeLine: parseInt(codeLine || 0), |
| | codeColumn: parseInt(codeColumn || 0) |
| | }; |
| | } |
| | toString() { |
| | return `[${dateFormat2(this.time, "yyyy-MM-dd HH:mm:ss.SSS")}][${this.level}][${this.source.name}<${this.source.codeLine},${this.source.codeColumn}>] ${this.text}`; |
| | } |
| | }; |
| | var Logger = class _Logger { |
| | |
| | config = {}; |
| | |
| | static Level = { |
| | Success: "success", |
| | Info: "info", |
| | Log: "log", |
| | Debug: "debug", |
| | Warning: "warning", |
| | Error: "error", |
| | Fatal: "fatal" |
| | }; |
| | |
| | static LevelColor = { |
| | [_Logger.Level.Success]: "green", |
| | [_Logger.Level.Info]: "brightCyan", |
| | [_Logger.Level.Debug]: "white", |
| | [_Logger.Level.Warning]: "brightYellow", |
| | [_Logger.Level.Error]: "brightRed", |
| | [_Logger.Level.Fatal]: "red" |
| | }; |
| | #writer; |
| | constructor() { |
| | this.#writer = new LogWriter(); |
| | } |
| | header() { |
| | this.#writer.writeSync(Buffer.from(` |
| | |
| | ===================== LOG START ${dateFormat2(/* @__PURE__ */ new Date(), "yyyy-MM-dd HH:mm:ss.SSS")} ===================== |
| | |
| | `)); |
| | } |
| | footer() { |
| | this.#writer.flush(); |
| | this.#writer.writeSync(Buffer.from(` |
| | |
| | ===================== LOG END ${dateFormat2(/* @__PURE__ */ new Date(), "yyyy-MM-dd HH:mm:ss.SSS")} ===================== |
| | |
| | `)); |
| | } |
| | success(...params) { |
| | const content = new LogText(_Logger.Level.Success, ...params).toString(); |
| | console.info(content[_Logger.LevelColor[_Logger.Level.Success]]); |
| | this.#writer.push(content + "\n"); |
| | } |
| | info(...params) { |
| | const content = new LogText(_Logger.Level.Info, ...params).toString(); |
| | console.info(content[_Logger.LevelColor[_Logger.Level.Info]]); |
| | this.#writer.push(content + "\n"); |
| | } |
| | log(...params) { |
| | const content = new LogText(_Logger.Level.Log, ...params).toString(); |
| | console.log(content[_Logger.LevelColor[_Logger.Level.Log]]); |
| | this.#writer.push(content + "\n"); |
| | } |
| | debug(...params) { |
| | if (!config_default.system.debug) return; |
| | const content = new LogText(_Logger.Level.Debug, ...params).toString(); |
| | console.debug(content[_Logger.LevelColor[_Logger.Level.Debug]]); |
| | this.#writer.push(content + "\n"); |
| | } |
| | warn(...params) { |
| | const content = new LogText(_Logger.Level.Warning, ...params).toString(); |
| | console.warn(content[_Logger.LevelColor[_Logger.Level.Warning]]); |
| | this.#writer.push(content + "\n"); |
| | } |
| | error(...params) { |
| | const content = new LogText(_Logger.Level.Error, ...params).toString(); |
| | console.error(content[_Logger.LevelColor[_Logger.Level.Error]]); |
| | this.#writer.push(content); |
| | } |
| | fatal(...params) { |
| | const content = new LogText(_Logger.Level.Fatal, ...params).toString(); |
| | console.error(content[_Logger.LevelColor[_Logger.Level.Fatal]]); |
| | this.#writer.push(content); |
| | } |
| | destory() { |
| | this.#writer.destory(); |
| | } |
| | }; |
| | var logger_default = new Logger(); |
| |
|
| | |
| | process.setMaxListeners(Infinity); |
| | process.on("uncaughtException", (err, origin) => { |
| | logger_default.error(`An unhandled error occurred: ${origin}`, err); |
| | }); |
| | process.on("unhandledRejection", (_15, promise) => { |
| | promise.catch((err) => logger_default.error("An unhandled rejection occurred:", err)); |
| | }); |
| | process.on("warning", (warning) => logger_default.warn("System warning: ", warning)); |
| | process.on("exit", () => { |
| | logger_default.info("Service exit"); |
| | logger_default.footer(); |
| | }); |
| | process.on("SIGTERM", () => { |
| | logger_default.warn("received kill signal"); |
| | process.exit(2); |
| | }); |
| | process.on("SIGINT", () => { |
| | process.exit(0); |
| | }); |
| |
|
| | |
| | import Koa from "koa"; |
| | import KoaRouter from "koa-router"; |
| | import koaRange from "koa-range"; |
| | import koaCors from "koa2-cors"; |
| | import koaBody from "koa-body"; |
| | import _11 from "lodash"; |
| |
|
| | |
| | import _7 from "lodash"; |
| |
|
| | |
| | import assert from "assert"; |
| | import _6 from "lodash"; |
| | var Exception = class extends Error { |
| | |
| | errcode; |
| | |
| | errmsg; |
| | |
| | data; |
| | |
| | httpStatusCode; |
| | |
| | |
| | |
| | |
| | |
| | |
| | constructor(exception, _errmsg) { |
| | assert(_6.isArray(exception), "Exception must be Array"); |
| | const [errcode, errmsg] = exception; |
| | assert(_6.isFinite(errcode), "Exception errcode invalid"); |
| | assert(_6.isString(errmsg), "Exception errmsg invalid"); |
| | super(_errmsg || errmsg); |
| | this.errcode = errcode; |
| | this.errmsg = _errmsg || errmsg; |
| | } |
| | compare(exception) { |
| | const [errcode] = exception; |
| | return this.errcode == errcode; |
| | } |
| | setHTTPStatusCode(value) { |
| | this.httpStatusCode = value; |
| | return this; |
| | } |
| | setData(value) { |
| | this.data = _6.defaultTo(value, null); |
| | return this; |
| | } |
| | }; |
| |
|
| | |
| | var APIException = class extends Exception { |
| | |
| | |
| | |
| | |
| | |
| | constructor(exception, errmsg) { |
| | super(exception, errmsg); |
| | } |
| | }; |
| |
|
| | |
| | var exceptions_default = { |
| | API_TEST: [-9999, "API\u5F02\u5E38\u9519\u8BEF"], |
| | API_REQUEST_PARAMS_INVALID: [-2e3, "\u8BF7\u6C42\u53C2\u6570\u975E\u6CD5"], |
| | API_REQUEST_FAILED: [-2001, "\u8BF7\u6C42\u5931\u8D25"], |
| | API_TOKEN_EXPIRES: [-2002, "Token\u5DF2\u5931\u6548"], |
| | API_FILE_URL_INVALID: [-2003, "\u8FDC\u7A0B\u6587\u4EF6URL\u975E\u6CD5"], |
| | API_FILE_EXECEEDS_SIZE: [-2004, "\u8FDC\u7A0B\u6587\u4EF6\u8D85\u51FA\u5927\u5C0F"], |
| | API_CHAT_STREAM_PUSHING: [-2005, "\u5DF2\u6709\u5BF9\u8BDD\u6D41\u6B63\u5728\u8F93\u51FA"], |
| | API_CONTENT_FILTERED: [-2006, "\u5185\u5BB9\u7531\u4E8E\u5408\u89C4\u95EE\u9898\u5DF2\u88AB\u963B\u6B62\u751F\u6210"], |
| | API_IMAGE_GENERATION_FAILED: [-2007, "\u56FE\u50CF\u751F\u6210\u5931\u8D25"] |
| | }; |
| |
|
| | |
| | var Request = class { |
| | |
| | method; |
| | |
| | url; |
| | |
| | path; |
| | |
| | type; |
| | |
| | headers; |
| | |
| | search; |
| | |
| | query; |
| | |
| | params; |
| | |
| | body; |
| | |
| | files; |
| | |
| | remoteIP; |
| | |
| | time; |
| | constructor(ctx, options = {}) { |
| | const { time } = options; |
| | this.method = ctx.request.method; |
| | this.url = ctx.request.url; |
| | this.path = ctx.request.path; |
| | this.type = ctx.request.type; |
| | this.headers = ctx.request.headers || {}; |
| | this.search = ctx.request.search; |
| | this.query = ctx.query || {}; |
| | this.params = ctx.params || {}; |
| | this.body = ctx.request.body || {}; |
| | this.files = ctx.request.files || {}; |
| | this.remoteIP = this.headers["X-Real-IP"] || this.headers["x-real-ip"] || this.headers["X-Forwarded-For"] || this.headers["x-forwarded-for"] || ctx.ip || null; |
| | this.time = Number(_7.defaultTo(time, util_default.timestamp())); |
| | } |
| | validate(key, fn) { |
| | try { |
| | const value = _7.get(this, key); |
| | if (fn) { |
| | if (fn(value) === false) |
| | throw `[Mismatch] -> ${fn}`; |
| | } else if (_7.isUndefined(value)) |
| | throw "[Undefined]"; |
| | } catch (err) { |
| | logger_default.warn(`Params ${key} invalid:`, err); |
| | throw new APIException(exceptions_default.API_REQUEST_PARAMS_INVALID, `Params ${key} invalid`); |
| | } |
| | return this; |
| | } |
| | }; |
| |
|
| | |
| | import mime2 from "mime"; |
| | import _9 from "lodash"; |
| |
|
| | |
| | import _8 from "lodash"; |
| | var Body = class _Body { |
| | |
| | code; |
| | |
| | message; |
| | |
| | data; |
| | |
| | statusCode; |
| | constructor(options = {}) { |
| | const { code, message, data, statusCode } = options; |
| | this.code = Number(_8.defaultTo(code, 0)); |
| | this.message = _8.defaultTo(message, "OK"); |
| | this.data = _8.defaultTo(data, null); |
| | this.statusCode = Number(_8.defaultTo(statusCode, 200)); |
| | } |
| | toObject() { |
| | return { |
| | code: this.code, |
| | message: this.message, |
| | data: this.data |
| | }; |
| | } |
| | static isInstance(value) { |
| | return value instanceof _Body; |
| | } |
| | }; |
| |
|
| | |
| | var Response = class _Response { |
| | |
| | statusCode; |
| | |
| | type; |
| | |
| | headers; |
| | |
| | redirect; |
| | |
| | body; |
| | |
| | size; |
| | |
| | time; |
| | constructor(body, options = {}) { |
| | const { statusCode, type, headers, redirect, size, time } = options; |
| | this.statusCode = Number(_9.defaultTo(statusCode, Body.isInstance(body) ? body.statusCode : void 0)); |
| | this.type = type; |
| | this.headers = headers; |
| | this.redirect = redirect; |
| | this.size = size; |
| | this.time = Number(_9.defaultTo(time, util_default.timestamp())); |
| | this.body = body; |
| | } |
| | injectTo(ctx) { |
| | this.redirect && ctx.redirect(this.redirect); |
| | this.statusCode && (ctx.status = this.statusCode); |
| | this.type && (ctx.type = mime2.getType(this.type) || this.type); |
| | const headers = this.headers || {}; |
| | if (this.size && !headers["Content-Length"] && !headers["content-length"]) |
| | headers["Content-Length"] = this.size; |
| | ctx.set(headers); |
| | if (Body.isInstance(this.body)) |
| | ctx.body = this.body.toObject(); |
| | else |
| | ctx.body = this.body; |
| | } |
| | static isInstance(value) { |
| | return value instanceof _Response; |
| | } |
| | }; |
| |
|
| | |
| | import _10 from "lodash"; |
| |
|
| | |
| | var exceptions_default2 = { |
| | SYSTEM_ERROR: [-1e3, "\u7CFB\u7EDF\u5F02\u5E38"], |
| | SYSTEM_REQUEST_VALIDATION_ERROR: [-1001, "\u8BF7\u6C42\u53C2\u6570\u6821\u9A8C\u9519\u8BEF"], |
| | SYSTEM_NOT_ROUTE_MATCHING: [-1002, "\u65E0\u5339\u914D\u7684\u8DEF\u7531"] |
| | }; |
| |
|
| | |
| | var FailureBody = class _FailureBody extends Body { |
| | constructor(error, _data) { |
| | let errcode, errmsg, data = _data, httpStatusCode = http_status_codes_default.OK; |
| | ; |
| | if (_10.isString(error)) |
| | error = new Exception(exceptions_default2.SYSTEM_ERROR, error); |
| | else if (error instanceof APIException || error instanceof Exception) |
| | ({ errcode, errmsg, data, httpStatusCode } = error); |
| | else if (_10.isError(error)) |
| | ({ errcode, errmsg, data, httpStatusCode } = new Exception(exceptions_default2.SYSTEM_ERROR, error.message)); |
| | super({ |
| | code: errcode || -1, |
| | message: errmsg || "Internal error", |
| | data, |
| | statusCode: httpStatusCode |
| | }); |
| | } |
| | static isInstance(value) { |
| | return value instanceof _FailureBody; |
| | } |
| | }; |
| |
|
| | |
| | var Server = class { |
| | app; |
| | router; |
| | constructor() { |
| | this.app = new Koa(); |
| | this.app.use(koaCors()); |
| | this.app.use(koaRange); |
| | this.router = new KoaRouter({ prefix: config_default.service.urlPrefix }); |
| | this.app.use(async (ctx, next) => { |
| | if (ctx.request.type === "application/xml" || ctx.request.type === "application/ssml+xml") |
| | ctx.req.headers["content-type"] = "text/xml"; |
| | try { |
| | await next(); |
| | } catch (err) { |
| | logger_default.error(err); |
| | const failureBody = new FailureBody(err); |
| | new Response(failureBody).injectTo(ctx); |
| | } |
| | }); |
| | this.app.use(koaBody(_11.clone(config_default.system.requestBody))); |
| | this.app.on("error", (err) => { |
| | if (["ECONNRESET", "ECONNABORTED", "EPIPE", "ECANCELED"].includes(err.code)) return; |
| | logger_default.error(err); |
| | }); |
| | logger_default.success("Server initialized"); |
| | } |
| | |
| | |
| | |
| | |
| | |
| | attachRoutes(routes) { |
| | routes.forEach((route) => { |
| | const prefix = route.prefix || ""; |
| | for (let method in route) { |
| | if (method === "prefix") continue; |
| | if (!_11.isObject(route[method])) { |
| | logger_default.warn(`Router ${prefix} ${method} invalid`); |
| | continue; |
| | } |
| | for (let uri in route[method]) { |
| | this.router[method](`${prefix}${uri}`, async (ctx) => { |
| | const { request, response } = await this.#requestProcessing(ctx, route[method][uri]); |
| | if (response != null && config_default.system.requestLog) |
| | logger_default.info(`<- ${request.method} ${request.url} ${response.time - request.time}ms`); |
| | }); |
| | } |
| | } |
| | logger_default.info(`Route ${config_default.service.urlPrefix || ""}${prefix} attached`); |
| | }); |
| | this.app.use(this.router.routes()); |
| | this.app.use((ctx) => { |
| | const request = new Request(ctx); |
| | logger_default.debug(`-> ${ctx.request.method} ${ctx.request.url} request is not supported - ${request.remoteIP || "unknown"}`); |
| | const message = `[\u8BF7\u6C42\u6709\u8BEF]: \u6B63\u786E\u8BF7\u6C42\u4E3A POST -> /v1/chat/completions\uFF0C\u5F53\u524D\u8BF7\u6C42\u4E3A ${ctx.request.method} -> ${ctx.request.url} \u8BF7\u7EA0\u6B63`; |
| | logger_default.warn(message); |
| | const failureBody = new FailureBody(new Error(message)); |
| | const response = new Response(failureBody); |
| | response.injectTo(ctx); |
| | if (config_default.system.requestLog) |
| | logger_default.info(`<- ${request.method} ${request.url} ${response.time - request.time}ms`); |
| | }); |
| | } |
| | |
| | |
| | |
| | |
| | |
| | |
| | #requestProcessing(ctx, routeFn) { |
| | return new Promise((resolve) => { |
| | const request = new Request(ctx); |
| | try { |
| | if (config_default.system.requestLog) |
| | logger_default.info(`-> ${request.method} ${request.url}`); |
| | routeFn(request).then((response) => { |
| | try { |
| | if (!Response.isInstance(response)) { |
| | const _response = new Response(response); |
| | _response.injectTo(ctx); |
| | return resolve({ request, response: _response }); |
| | } |
| | response.injectTo(ctx); |
| | resolve({ request, response }); |
| | } catch (err) { |
| | logger_default.error(err); |
| | const failureBody = new FailureBody(err); |
| | const response2 = new Response(failureBody); |
| | response2.injectTo(ctx); |
| | resolve({ request, response: response2 }); |
| | } |
| | }).catch((err) => { |
| | try { |
| | logger_default.error(err); |
| | const failureBody = new FailureBody(err); |
| | const response = new Response(failureBody); |
| | response.injectTo(ctx); |
| | resolve({ request, response }); |
| | } catch (err2) { |
| | logger_default.error(err2); |
| | const failureBody = new FailureBody(err2); |
| | const response = new Response(failureBody); |
| | response.injectTo(ctx); |
| | resolve({ request, response }); |
| | } |
| | }); |
| | } catch (err) { |
| | logger_default.error(err); |
| | const failureBody = new FailureBody(err); |
| | const response = new Response(failureBody); |
| | response.injectTo(ctx); |
| | resolve({ request, response }); |
| | } |
| | }); |
| | } |
| | |
| | |
| | |
| | async listen() { |
| | const host = config_default.service.host; |
| | const port = config_default.service.port; |
| | await Promise.all([ |
| | new Promise((resolve, reject) => { |
| | if (host === "0.0.0.0" || host === "localhost" || host === "127.0.0.1") |
| | return resolve(null); |
| | this.app.listen(port, "localhost", (err) => { |
| | if (err) return reject(err); |
| | resolve(null); |
| | }); |
| | }), |
| | new Promise((resolve, reject) => { |
| | this.app.listen(port, host, (err) => { |
| | if (err) return reject(err); |
| | resolve(null); |
| | }); |
| | }) |
| | ]); |
| | logger_default.success(`Server listening on port ${port} (${host})`); |
| | } |
| | }; |
| | var server_default = new Server(); |
| |
|
| | |
| | import fs6 from "fs-extra"; |
| |
|
| | |
| | import _13 from "lodash"; |
| |
|
| | |
| | import { PassThrough } from "stream"; |
| | import _12 from "lodash"; |
| | import AsyncLock from "async-lock"; |
| | import axios2 from "axios"; |
| | import { createParser } from "eventsource-parser"; |
| | var MODEL_NAME = "deepseek-chat"; |
| | var ACCESS_TOKEN_EXPIRES = 3600; |
| | var MAX_RETRY_COUNT = 3; |
| | var RETRY_DELAY = 5e3; |
| | var FAKE_HEADERS = { |
| | Accept: "*/*", |
| | "Accept-Encoding": "gzip, deflate, br, zstd", |
| | "Accept-Language": "zh-CN,zh;q=0.9", |
| | Origin: "https://chat.deepseek.com", |
| | Pragma: "no-cache", |
| | Referer: "https://chat.deepseek.com/", |
| | "Sec-Ch-Ua": '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"', |
| | "Sec-Ch-Ua-Mobile": "?0", |
| | "Sec-Ch-Ua-Platform": '"Windows"', |
| | "Sec-Fetch-Dest": "empty", |
| | "Sec-Fetch-Mode": "cors", |
| | "Sec-Fetch-Site": "same-origin", |
| | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", |
| | "X-App-Version": "20240126.0" |
| | }; |
| | var accessTokenMap = new Map(); |
| | var accessTokenRequestQueueMap = {}; |
| | var chatLock = new AsyncLock(); |
| | async function requestToken(refreshToken) { |
| | if (accessTokenRequestQueueMap[refreshToken]) |
| | return new Promise( |
| | (resolve) => accessTokenRequestQueueMap[refreshToken].push(resolve) |
| | ); |
| | accessTokenRequestQueueMap[refreshToken] = []; |
| | logger_default.info(`Refresh token: ${refreshToken}`); |
| | const result = await (async () => { |
| | const result2 = await axios2.get( |
| | "https://chat.deepseek.com/api/v0/users/current", |
| | { |
| | headers: { |
| | Authorization: `Bearer ${refreshToken}`, |
| | ...FAKE_HEADERS |
| | }, |
| | timeout: 15e3, |
| | validateStatus: () => true |
| | } |
| | ); |
| | const { token } = checkResult(result2, refreshToken); |
| | return { |
| | accessToken: token, |
| | refreshToken: token, |
| | refreshTime: util_default.unixTimestamp() + ACCESS_TOKEN_EXPIRES |
| | }; |
| | })().then((result2) => { |
| | if (accessTokenRequestQueueMap[refreshToken]) { |
| | accessTokenRequestQueueMap[refreshToken].forEach( |
| | (resolve) => resolve(result2) |
| | ); |
| | delete accessTokenRequestQueueMap[refreshToken]; |
| | } |
| | logger_default.success(`Refresh successful`); |
| | return result2; |
| | }).catch((err) => { |
| | if (accessTokenRequestQueueMap[refreshToken]) { |
| | accessTokenRequestQueueMap[refreshToken].forEach( |
| | (resolve) => resolve(err) |
| | ); |
| | delete accessTokenRequestQueueMap[refreshToken]; |
| | } |
| | return err; |
| | }); |
| | if (_12.isError(result)) throw result; |
| | return result; |
| | } |
| | async function acquireToken(refreshToken) { |
| | let result = accessTokenMap.get(refreshToken); |
| | if (!result) { |
| | result = await requestToken(refreshToken); |
| | accessTokenMap.set(refreshToken, result); |
| | } |
| | if (util_default.unixTimestamp() > result.refreshTime) { |
| | result = await requestToken(refreshToken); |
| | accessTokenMap.set(refreshToken, result); |
| | } |
| | return result.accessToken; |
| | } |
| | async function clearContext(model, refreshToken) { |
| | const token = await acquireToken(refreshToken); |
| | const result = await axios2.post( |
| | "https://chat.deepseek.com/api/v0/chat/clear_context", |
| | { |
| | model_class: model, |
| | append_welcome_message: false |
| | }, |
| | { |
| | headers: { |
| | Authorization: `Bearer ${token}`, |
| | ...FAKE_HEADERS |
| | }, |
| | timeout: 15e3, |
| | validateStatus: () => true |
| | } |
| | ); |
| | checkResult(result, refreshToken); |
| | } |
| | async function createCompletion(model = MODEL_NAME, messages, refreshToken, retryCount = 0) { |
| | return (async () => { |
| | logger_default.info(messages); |
| | const result = await chatLock.acquire(refreshToken, async () => { |
| | await clearContext(model, refreshToken); |
| | const token = await acquireToken(refreshToken); |
| | return await axios2.post( |
| | "https://chat.deepseek.com/api/v0/chat/completions", |
| | { |
| | message: messagesPrepare(messages), |
| | stream: true, |
| | model_preference: null, |
| | model_class: model, |
| | temperature: 0 |
| | }, |
| | { |
| | headers: { |
| | Authorization: `Bearer ${token}`, |
| | ...FAKE_HEADERS |
| | }, |
| | |
| | timeout: 12e4, |
| | validateStatus: () => true, |
| | responseType: "stream" |
| | } |
| | ); |
| | }); |
| | if (result.headers["content-type"].indexOf("text/event-stream") == -1) { |
| | result.data.on("data", (buffer) => logger_default.error(buffer.toString())); |
| | throw new APIException( |
| | exceptions_default.API_REQUEST_FAILED, |
| | `Stream response Content-Type invalid: ${result.headers["content-type"]}` |
| | ); |
| | } |
| | const streamStartTime = util_default.timestamp(); |
| | const answer = await receiveStream(model, result.data); |
| | logger_default.success( |
| | `Stream has completed transfer ${util_default.timestamp() - streamStartTime}ms` |
| | ); |
| | return answer; |
| | })().catch((err) => { |
| | if (retryCount < MAX_RETRY_COUNT) { |
| | logger_default.error(`Stream response error: ${err.stack}`); |
| | logger_default.warn(`Try again after ${RETRY_DELAY / 1e3}s...`); |
| | return (async () => { |
| | await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY)); |
| | return createCompletion( |
| | model, |
| | messages, |
| | refreshToken, |
| | retryCount + 1 |
| | ); |
| | })(); |
| | } |
| | throw err; |
| | }); |
| | } |
| | async function createCompletionStream(model = MODEL_NAME, messages, refreshToken, retryCount = 0) { |
| | return (async () => { |
| | logger_default.info(messages); |
| | const result = await chatLock.acquire(refreshToken, async () => { |
| | await clearContext(model, refreshToken); |
| | const token = await acquireToken(refreshToken); |
| | return await axios2.post( |
| | "https://chat.deepseek.com/api/v0/chat/completions", |
| | { |
| | message: messagesPrepare(messages), |
| | stream: true, |
| | model_preference: null, |
| | model_class: model, |
| | temperature: 0 |
| | }, |
| | { |
| | headers: { |
| | Authorization: `Bearer ${token}`, |
| | ...FAKE_HEADERS |
| | }, |
| | |
| | timeout: 12e4, |
| | validateStatus: () => true, |
| | responseType: "stream" |
| | } |
| | ); |
| | }); |
| | if (result.headers["content-type"].indexOf("text/event-stream") == -1) { |
| | logger_default.error( |
| | `Invalid response Content-Type:`, |
| | result.headers["content-type"] |
| | ); |
| | result.data.on("data", (buffer) => logger_default.error(buffer.toString())); |
| | const transStream = new PassThrough(); |
| | transStream.end( |
| | `data: ${JSON.stringify({ |
| | id: "", |
| | model: MODEL_NAME, |
| | object: "chat.completion.chunk", |
| | choices: [ |
| | { |
| | index: 0, |
| | delta: { |
| | role: "assistant", |
| | content: "\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u7B2C\u4E09\u65B9\u54CD\u5E94\u9519\u8BEF" |
| | }, |
| | finish_reason: "stop" |
| | } |
| | ], |
| | usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 }, |
| | created: util_default.unixTimestamp() |
| | })} |
| | |
| | ` |
| | ); |
| | return transStream; |
| | } |
| | const streamStartTime = util_default.timestamp(); |
| | return createTransStream(model, result.data, () => { |
| | logger_default.success( |
| | `Stream has completed transfer ${util_default.timestamp() - streamStartTime}ms` |
| | ); |
| | }); |
| | })().catch((err) => { |
| | if (retryCount < MAX_RETRY_COUNT) { |
| | logger_default.error(`Stream response error: ${err.stack}`); |
| | logger_default.warn(`Try again after ${RETRY_DELAY / 1e3}s...`); |
| | return (async () => { |
| | await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY)); |
| | return createCompletionStream( |
| | model, |
| | messages, |
| | refreshToken, |
| | retryCount + 1 |
| | ); |
| | })(); |
| | } |
| | throw err; |
| | }); |
| | } |
| | function messagesPrepare(messages) { |
| | let content; |
| | if (messages.length < 2) { |
| | content = messages.reduce((content2, message) => { |
| | if (_12.isArray(message.content)) { |
| | return message.content.reduce((_content, v) => { |
| | if (!_12.isObject(v) || v["type"] != "text") return _content; |
| | return _content + (v["text"] || "") + "\n"; |
| | }, content2); |
| | } |
| | return content2 + `${message.content} |
| | `; |
| | }, ""); |
| | logger_default.info("\n\u900F\u4F20\u5185\u5BB9\uFF1A\n" + content); |
| | } else { |
| | content = (messages.reduce((content2, message) => { |
| | if (_12.isArray(message.content)) { |
| | return message.content.reduce((_content, v) => { |
| | if (!_12.isObject(v) || v["type"] != "text") return _content; |
| | return _content + (`${message.role}:` + v["text"] || "") + "\n"; |
| | }, content2); |
| | } |
| | return content2 += `${message.role}:${message.content} |
| | `; |
| | }, "") + "assistant:").replace(/\!\[.+\]\(.+\)/g, ""); |
| | logger_default.info("\n\u5BF9\u8BDD\u5408\u5E76\uFF1A\n" + content); |
| | } |
| | return content; |
| | } |
| | function checkResult(result, refreshToken) { |
| | if (!result.data) return null; |
| | const { code, data, msg } = result.data; |
| | if (!_12.isFinite(code)) return result.data; |
| | if (code === 0) return data; |
| | if (code == 40003) accessTokenMap.delete(refreshToken); |
| | throw new APIException(exceptions_default.API_REQUEST_FAILED, `[\u8BF7\u6C42deepseek\u5931\u8D25]: ${msg}`); |
| | } |
| | async function receiveStream(model, stream) { |
| | return new Promise((resolve, reject) => { |
| | const data = { |
| | id: "", |
| | model, |
| | object: "chat.completion", |
| | choices: [ |
| | { |
| | index: 0, |
| | message: { role: "assistant", content: "" }, |
| | finish_reason: "stop" |
| | } |
| | ], |
| | usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 }, |
| | created: util_default.unixTimestamp() |
| | }; |
| | const parser = createParser((event) => { |
| | try { |
| | if (event.type !== "event") return; |
| | const result = _12.attempt(() => JSON.parse(event.data)); |
| | if (_12.isError(result)) |
| | throw new Error(`Stream response invalid: ${event.data}`); |
| | if (!result.choices || !result.choices[0] || !result.choices[0].delta || !result.choices[0].delta.content || result.choices[0].delta.content == " ") |
| | return; |
| | data.choices[0].message.content += result.choices[0].delta.content; |
| | if (result.choices && result.choices[0] && result.choices[0].finish_reason === "stop") |
| | resolve(data); |
| | } catch (err) { |
| | logger_default.error(err); |
| | reject(err); |
| | } |
| | }); |
| | stream.on("data", (buffer) => parser.feed(buffer.toString())); |
| | stream.once("error", (err) => reject(err)); |
| | stream.once("close", () => resolve(data)); |
| | }); |
| | } |
| | function createTransStream(model, stream, endCallback) { |
| | const created = util_default.unixTimestamp(); |
| | const transStream = new PassThrough(); |
| | !transStream.closed && transStream.write( |
| | `data: ${JSON.stringify({ |
| | id: "", |
| | model, |
| | object: "chat.completion.chunk", |
| | choices: [ |
| | { |
| | index: 0, |
| | delta: { role: "assistant", content: "" }, |
| | finish_reason: null |
| | } |
| | ], |
| | created |
| | })} |
| | |
| | ` |
| | ); |
| | const parser = createParser((event) => { |
| | try { |
| | if (event.type !== "event") return; |
| | const result = _12.attempt(() => JSON.parse(event.data)); |
| | if (_12.isError(result)) |
| | throw new Error(`Stream response invalid: ${event.data}`); |
| | if (!result.choices || !result.choices[0] || !result.choices[0].delta || !result.choices[0].delta.content || result.choices[0].delta.content == " ") |
| | return; |
| | result.model = model; |
| | transStream.write(`data: ${JSON.stringify({ |
| | id: result.id, |
| | model: result.model, |
| | object: "chat.completion.chunk", |
| | choices: [ |
| | { |
| | index: 0, |
| | delta: { role: "assistant", content: result.choices[0].delta.content }, |
| | finish_reason: null |
| | } |
| | ], |
| | created |
| | })} |
| | |
| | `); |
| | if (result.choices && result.choices[0] && result.choices[0].finish_reason === "stop") { |
| | transStream.write(`data: ${JSON.stringify({ |
| | id: result.id, |
| | model: result.model, |
| | object: "chat.completion.chunk", |
| | choices: [ |
| | { |
| | index: 0, |
| | delta: { role: "assistant", content: "" }, |
| | finish_reason: "stop" |
| | } |
| | ], |
| | created |
| | })} |
| | |
| | `); |
| | !transStream.closed && transStream.end("data: [DONE]\n\n"); |
| | } |
| | } catch (err) { |
| | logger_default.error(err); |
| | !transStream.closed && transStream.end("data: [DONE]\n\n"); |
| | } |
| | }); |
| | stream.on("data", (buffer) => parser.feed(buffer.toString())); |
| | stream.once( |
| | "error", |
| | () => !transStream.closed && transStream.end("data: [DONE]\n\n") |
| | ); |
| | stream.once( |
| | "close", |
| | () => !transStream.closed && transStream.end("data: [DONE]\n\n") |
| | ); |
| | return transStream; |
| | } |
| | function tokenSplit(authorization) { |
| | return authorization.replace("Bearer ", "").split(","); |
| | } |
| | async function getTokenLiveStatus(refreshToken) { |
| | const token = await acquireToken(refreshToken); |
| | const result = await axios2.get( |
| | "https://chat.deepseek.com/api/v0/users/current", |
| | { |
| | headers: { |
| | Authorization: `Bearer ${token}`, |
| | ...FAKE_HEADERS |
| | }, |
| | timeout: 15e3, |
| | validateStatus: () => true |
| | } |
| | ); |
| | try { |
| | const { token: token2 } = checkResult(result, refreshToken); |
| | return !!token2; |
| | } catch (err) { |
| | return false; |
| | } |
| | } |
| | var chat_default = { |
| | createCompletion, |
| | createCompletionStream, |
| | getTokenLiveStatus, |
| | tokenSplit |
| | }; |
| |
|
| | |
| | var chat_default2 = { |
| | |
| | prefix: "/hf/v1/chat", |
| | post: { |
| | "/completions": async (request) => { |
| | request.validate("body.conversation_id", (v) => _13.isUndefined(v) || _13.isString(v)).validate("body.messages", _13.isArray).validate("headers.authorization", _13.isString); |
| | const tokens = chat_default.tokenSplit(request.headers.authorization); |
| | const token = _13.sample(tokens); |
| | let { model, messages, stream } = request.body; |
| | if (["deepseek_chat", "deepseek_code", "deepseek-chat*", "deepseek-chat", "deepseek-coder"].includes(model)) |
| | model = { |
| | "deepseek-chat*": "deepseek_chat", |
| | "deepseek-chat": "deepseek_chat", |
| | "deepseek-coder": "deepseek_code" |
| | }[model] || model; |
| | else |
| | model = "deepseek_chat"; |
| | if (stream) { |
| | const stream2 = await chat_default.createCompletionStream(model, messages, token); |
| | return new Response(stream2, { |
| | type: "text/event-stream" |
| | }); |
| | } else |
| | return await chat_default.createCompletion(model, messages, token); |
| | } |
| | } |
| | }; |
| |
|
| | |
| | var ping_default = { |
| | |
| | prefix: "/hf/ping", |
| | get: { |
| | "": async () => "pong" |
| | } |
| | }; |
| |
|
| | |
| | import _14 from "lodash"; |
| | var token_default = { |
| | |
| | prefix: "/hf/token", |
| | post: { |
| | "/check": async (request) => { |
| | request.validate("body.token", _14.isString); |
| | const live = await chat_default.getTokenLiveStatus(request.body.token); |
| | return { |
| | live |
| | }; |
| | } |
| | } |
| | }; |
| |
|
| | |
| | var models_default = { |
| | |
| | prefix: "/hf/v1", |
| | get: { |
| | "/models": async () => { |
| | return { |
| | "data": [ |
| | { |
| | "id": "deepseek-chat", |
| | "object": "model", |
| | "owned_by": "deepseek-free-api" |
| | }, |
| | { |
| | "id": "deepseek-coder", |
| | "object": "model", |
| | "owned_by": "deepseek-free-api" |
| | } |
| | ] |
| | }; |
| | } |
| | } |
| | }; |
| |
|
| | |
| | var routes_default = [ |
| | { |
| | get: { |
| | "/": async () => { |
| | const content = await fs6.readFile("public/welcome.html"); |
| | return new Response(content, { |
| | type: "html", |
| | headers: { |
| | Expires: "-1" |
| | } |
| | }); |
| | } |
| | } |
| | }, |
| | chat_default2, |
| | ping_default, |
| | token_default, |
| | models_default |
| | ]; |
| |
|
| | |
| | var startupTime = performance.now(); |
| | (async () => { |
| | logger_default.header(); |
| | logger_default.info("<<<< deepseek free server >>>>"); |
| | logger_default.info("Version:", environment_default.package.version); |
| | logger_default.info("Process id:", process.pid); |
| | logger_default.info("Environment:", environment_default.env); |
| | logger_default.info("Service name:", config_default.service.name); |
| | server_default.attachRoutes(routes_default); |
| | await server_default.listen(); |
| | config_default.service.bindAddress && logger_default.success("Service bind address:", config_default.service.bindAddress); |
| | })().then( |
| | () => logger_default.success( |
| | `Service startup completed (${Math.floor(performance.now() - startupTime)}ms)` |
| | ) |
| | ).catch((err) => console.error(err)); |
| | |