chore:AI大修改

This commit is contained in:
Tianpao
2025-12-27 14:54:04 +08:00
parent 03ed0a4cb7
commit 419c40c794
7 changed files with 500 additions and 260 deletions

View File

@@ -1,4 +1,8 @@
import fs from "node:fs";
/**
* 应用配置接口
*/
export interface IConfig {
mirror: {
bmclapi: boolean;
@@ -9,32 +13,65 @@ export interface IConfig {
dexpub: boolean;
mixins: boolean;
};
oaf: boolean
oaf: boolean;
}
/**
* 默认配置
*/
const DEFAULT_CONFIG: IConfig = {
mirror: {
bmclapi: true,
mcimirror: true,
},
filter: {
hashes: true,
dexpub: false,
mixins: true,
},
oaf: true
};
/**
* 配置文件路径
*/
const CONFIG_PATH = "./config.json";
/**
* 配置管理器
*/
export class Config {
private readonly default_config: IConfig = {
mirror: {
bmclapi: true,
mcimirror: true,
},
filter: {
hashes: true,
dexpub: false,
mixins: true,
},
oaf:true
};
config(): IConfig {
if (!fs.existsSync("./config.json")) {
fs.writeFileSync("./config.json", JSON.stringify(this.default_config));
return this.default_config;
/**
* 获取配置
* @returns 配置对象
*/
public static getConfig(): IConfig {
if (!fs.existsSync(CONFIG_PATH)) {
fs.writeFileSync(CONFIG_PATH, JSON.stringify(DEFAULT_CONFIG, null, 2));
return DEFAULT_CONFIG;
}
try {
const content = fs.readFileSync(CONFIG_PATH, "utf-8");
return JSON.parse(content);
} catch (err) {
console.error("Failed to read config file, using defaults", err);
return DEFAULT_CONFIG;
}
return JSON.parse(fs.readFileSync("./config.json", "utf-8"));
}
static write_config(config: IConfig) {
fs.writeFileSync("./config.json", JSON.stringify(config));
/**
* 写入配置
* @param config 配置对象
*/
public static writeConfig(config: IConfig): void {
try {
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
} catch (err) {
console.error("Failed to write config file", err);
}
}
}
export default new Config().config();
// 默认导出配置实例
export default Config.getConfig();

View File

@@ -1,17 +1,42 @@
const env = process.env.DEBUG;
const isDebug = env === "true";
export function debug(msg: any){
if (env === "true"){
if(msg instanceof Error){
console.log(`[ERROR] [${new Date().toLocaleString()}] `);
console.log(msg);
}
if (typeof msg === "string"){
console.log(`[DEBUG] [${new Date().toLocaleString()}] ` + msg);
}
if (typeof msg === "object"){
console.log(`[OBJ] [${new Date().toLocaleString()}] `);
console.log(msg);
}
}
// 日志级别枚举
type LogLevel = "debug" | "info" | "warn" | "error";
/**
* 日志记录器
* @param level 日志级别
* @param msg 日志内容
* @param data 附加数据(可选)
*/
function log(level: LogLevel, msg: string | Error, data?: any) {
const timestamp = new Date().toLocaleString();
const prefix = `[${level.toUpperCase()}] [${timestamp}]`;
// 确保只在调试模式下输出debug日志
if (level === "debug" && !isDebug) return;
if (msg instanceof Error) {
console.error(`${prefix} ${msg.message}`);
console.error(msg.stack);
if (data) console.error(`${prefix} Data:`, data);
} else {
const logFunc = level === "error" ? console.error :
level === "warn" ? console.warn :
console.log;
logFunc(`${prefix} ${msg}`);
if (data) logFunc(`${prefix} Data:`, data);
}
}
export const logger = {
debug: (msg: string | Error, data?: any) => log("debug", msg, data),
info: (msg: string | Error, data?: any) => log("info", msg, data),
warn: (msg: string | Error, data?: any) => log("warn", msg, data),
error: (msg: string | Error, data?: any) => log("error", msg, data)
};
// 保持向后兼容
export const debug = logger.debug;

View File

@@ -6,7 +6,29 @@ import fs from "node:fs";
import fse from "fs-extra";
import { WebSocket } from "ws";
import { ExecOptions, exec} from "node:child_process";
/**
* Java版本信息接口
*/
export interface JavaVersion {
major: number;
minor: number;
patch: number;
fullVersion: string;
vendor: string;
runtimeVersion?: string;
}
/**
* Java检测结果接口
*/
export interface JavaCheckResult {
exists: boolean;
version?: JavaVersion;
error?: string;
}
import { MessageWS } from "./ws.js";
import { logger } from "./logger.js";
export class Utils {
public modrinth_url: string;
@@ -50,90 +72,157 @@ export function version_compare(v1: string, v2: string) {
return 0;
}
/**
* 检测Java是否安装并获取版本信息
* @returns Java检测结果
*/
export async function checkJava(): Promise<JavaCheckResult> {
try {
const output = await new Promise<string>((resolve, reject) => {
exec("java -version", (err, stdout, stderr) => {
if (err) {
logger.error("Java check failed", err);
reject(new Error("Java not found"));
return;
}
// Java版本信息输出在stderr中
resolve(stderr);
});
});
logger.debug(`Java version output: ${output}`);
// 解析Java版本信息
const versionRegex = /version "(\d+)(\.(\d+))?(\.(\d+))?/;
const vendorRegex = /(Java\(TM\)|OpenJDK).*Runtime Environment.*by (.*)/;
const versionMatch = output.match(versionRegex);
const vendorMatch = output.match(vendorRegex);
if (!versionMatch) {
return {
exists: true,
error: "Failed to parse Java version"
};
}
const major = parseInt(versionMatch[1], 10);
const minor = versionMatch[3] ? parseInt(versionMatch[3], 10) : 0;
const patch = versionMatch[5] ? parseInt(versionMatch[5], 10) : 0;
const versionInfo: JavaVersion = {
major,
minor,
patch,
fullVersion: versionMatch[0].replace("version ", ""),
vendor: vendorMatch ? vendorMatch[2] : "Unknown"
};
logger.info(`Java detected: ${JSON.stringify(versionInfo)}`);
return {
exists: true,
version: versionInfo
};
} catch (error) {
logger.error("Java check error", error as Error);
return {
exists: false,
error: (error as Error).message
};
}
}
export function execPromise(cmd:string,options?:ExecOptions){
logger.debug(`Executing command: ${cmd}`);
return new Promise((resolve,reject)=>{
exec(cmd,options,(err,stdout,stderr)=>{
if(err){
logger.error(`Command execution failed: ${cmd}`, err);
logger.debug(`Stderr: ${stderr}`);
reject(err)
return;
}
if (stdout) logger.debug(`Command stdout: ${stdout}`);
if (stderr) logger.debug(`Command stderr: ${stderr}`);
}).on('exit',(code)=>{
logger.debug(`Command completed with exit code: ${code}`);
resolve(code)
})
})
}
export async function fastdownload(data: [string, string]|string[][]) {
let _data = undefined;
if(Array.isArray(data[0])){
_data = data
}else{
_data = [data]
}
// 确保downloadList始终是[string, string][]类型
const downloadList: [string, string][] = Array.isArray(data[0])
? (data as string[][]).map(item => item as [string, string])
: [data as [string, string]];
logger.info(`Starting fast download of ${downloadList.length} files`);
return await pMap(
_data,
async (e:any) => {
downloadList,
async (item: [string, string]) => {
const [url, filePath] = item;
try {
await pRetry(
async () => {
if (!fs.existsSync(e[1])) {
await got
.get(e[0], {
responseType: "buffer",
headers: {
"user-agent": "DeEarthX",
},
})
.then((res) => {
fse.outputFileSync(e[1], res.rawBody);
});
if (!fs.existsSync(filePath)) {
logger.debug(`Downloading ${url} to ${filePath}`);
const res = await got.get(url, {
responseType: "buffer",
headers: { "user-agent": "DeEarthX" },
});
fse.outputFileSync(filePath, res.rawBody);
logger.debug(`Downloaded ${url} successfully`);
} else {
logger.debug(`File already exists, skipping: ${filePath}`);
}
},
{ retries: 3 }
{ retries: 3, onFailedAttempt: (error) => {
logger.warn(`Download attempt failed for ${url}, retrying (${error.attemptNumber}/3)`);
}}
);
} catch (e) {
//LOGGER.error({ err: e });
} catch (error) {
logger.error(`Failed to download ${url} after 3 attempts`, error);
throw error;
}
},
{ concurrency: 16 }
);
}
export async function Wfastdownload(data: string[][],ws:MessageWS) {
let index = 1;
export async function Wfastdownload(data: string[][], ws: MessageWS) {
logger.info(`Starting web download of ${data.length} files`);
return await pMap(
data,
async (e:any) => {
async (item: string[], idx: number) => {
const [url, filePath] = item;
try {
await pRetry(
async () => {
if (!fs.existsSync(e[1])) {
await got
.get(e[0], {
responseType: "buffer",
headers: {
"user-agent": "DeEarthX",
},
})
.then((res) => {
fse.outputFileSync(e[1], res.rawBody);
});
if (!fs.existsSync(filePath)) {
logger.debug(`Downloading ${url} to ${filePath}`);
const res = await got.get(url, {
responseType: "buffer",
headers: { "user-agent": "DeEarthX" },
});
fse.outputFileSync(filePath, res.rawBody);
logger.debug(`Downloaded ${url} successfully`);
} else {
logger.debug(`File already exists, skipping: ${filePath}`);
}
ws.download(data.length,index,e[1])
// ws.send(JSON.stringify({
// status:"downloading",
// result:{
// total:data.length,
// index:index,
// name:e[1]
// }
// }))
index++
// 更新下载进度
ws.download(data.length, idx + 1, filePath);
},
{ retries: 3 }
{ retries: 3, onFailedAttempt: (error) => {
logger.warn(`Download attempt failed for ${url}, retrying (${error.attemptNumber}/3)`);
}}
);
} catch (e) {
//LOGGER.error({ err: e });
} catch (error) {
logger.error(`Failed to download ${url} after 3 attempts`, error);
throw error;
}
},
{ concurrency: 16 }

View File

@@ -1,56 +1,83 @@
import websocket, { WebSocketServer } from "ws";
import { logger } from "./logger.js";
export class MessageWS {
ws!: websocket;
private ws!: websocket;
constructor(ws: websocket) {
this.ws = ws;
// 监听WebSocket错误
this.ws.on('error', (err) => {
logger.error("WebSocket error", err);
});
// 监听连接关闭
this.ws.on('close', (code, reason) => {
logger.info("WebSocket connection closed", { code, reason: reason.toString() });
});
}
finish(first: number, latest: number) {
this.ws.send(
JSON.stringify({
status: "finish",
result: latest - first,
})
);
/**
* 发送完成消息
* @param startTime 开始时间
* @param endTime 结束时间
*/
finish(startTime: number, endTime: number) {
this.send("finish", endTime - startTime);
}
/**
* 发送解压进度消息
* @param entryName 文件名
* @param total 总文件数
* @param current 当前文件索引
*/
unzip(entryName: string, total: number, current: number) {
this.ws.send(
JSON.stringify({
status: "unzip",
result: { name: entryName, total, current },
})
);
this.send("unzip", { name: entryName, total, current });
}
/**
* 发送下载进度消息
* @param total 总文件数
* @param index 当前文件索引
* @param name 文件名
*/
download(total: number, index: number, name: string) {
this.ws.send(
JSON.stringify({
status: "downloading",
result: {
total,
index,
name,
},
})
);
this.send("downloading", { total, index, name });
}
/**
* 发送状态变更消息
*/
statusChange() {
this.ws.send(
JSON.stringify({
status: "changed",
result: undefined,
})
);
this.send("changed", undefined);
}
/**
* 发送错误消息
* @param error 错误对象
*/
handleError(error: Error) {
this.ws.send(
JSON.stringify({
status: "error",
result: error.message,
})
);
this.send("error", error.message);
}
/**
* 通用消息发送方法
* @param status 消息状态
* @param result 消息内容
*/
private send(status: string, result: any) {
try {
if (this.ws.readyState === websocket.OPEN) {
const message = JSON.stringify({ status, result });
logger.debug("Sending WebSocket message", { status, result });
this.ws.send(message);
} else {
logger.warn(`WebSocket not open, cannot send message: ${status}`);
}
} catch (err) {
logger.error("Failed to send WebSocket message", err);
}
}
}