feat:UI及部分后端

This commit is contained in:
Tianpao
2025-09-13 21:05:14 +08:00
parent d32768afea
commit c61563c484
29 changed files with 2952 additions and 237 deletions

16
backend/src/main.ts Normal file
View File

@@ -0,0 +1,16 @@
import config,{ Config } from "./utils/config.js";
const input = process.argv[2];
switch (input) {
case 'getconfig': //读取配置
process.stdout.write(JSON.stringify(config));
break;
case 'writeconfig': //写入配置
if(process.argv.length < 4){
process.exit(1);
}
Config.write_config(JSON.parse(process.argv[3]));
break;
case 'start':
}

View File

@@ -0,0 +1,73 @@
import got from "got";
import { join } from "node:path";
import { fastdownload, usemirror } from "../utils/utils.js";
import { modpack_info, XPlatform } from "./index.js";
const cf_url = (() => {
if (usemirror) {
return "https://mod.mcimirror.top/curseforge";
} else {
return "https://api.curseforge.com";
}
})();
export interface CurseForgeManifest {
minecraft: {
version: string;
modLoaders: Array<{ id: string }>;
};
files: Array<{ projectID: number; fileID: number }>;
}
export class CurseForge implements XPlatform {
async getinfo(manifest: object): Promise<modpack_info> {
let result: modpack_info = Object.create({});
const local_manifest = manifest as CurseForgeManifest;
if (result && local_manifest)
result.minecraft = local_manifest.minecraft.version;
const id = local_manifest.minecraft.modLoaders[0].id;
const loader_all = id.match(/(.*)-/) as RegExpMatchArray;
result.loader = loader_all[1];
result.loader_version = id.replace(loader_all[0], "");
return result;
}
async downloadfile(manifest: object, path: string): Promise<void> {
const local_manifest = manifest as CurseForgeManifest;
const FileID = JSON.stringify({
fileIds: local_manifest.files.map(
(file: { fileID: number }) => file.fileID
),
});
let tmp: [string, string] | string[][] = [];
await got
.post(cf_url + "/v1/mods/files", {
body: FileID,
headers: {
"Content-Type": "application/json",
"x-api-key":
"$2a$10$ydk0TLDG/Gc6uPMdz7mad.iisj2TaMDytVcIW4gcVP231VKngLBKy",
"User-Agent": "DeEarthX",
},
})
.json()
.then((res: any) => {
res.data.forEach(
(e: { fileName: string; downloadUrl: null | string }) => {
if (e.fileName.endsWith(".zip") || e.downloadUrl == null) {
return;
}
const unpath = join(path + "/mods/", e.fileName);
if (usemirror) {
tmp.push([
"https://mod.mcimirror.top" + new URL(e.downloadUrl).pathname,
unpath,
]);
} else {
tmp.push([e.downloadUrl, unpath]);
}
}
);
});
await fastdownload(tmp as unknown as [string, string]); //下载文件
}
}

View File

@@ -0,0 +1,42 @@
import { CurseForge } from "./curseforge.js";
import { MCBBS } from "./mcbbs.js";
import { Modrinth } from "./modrinth.js";
export interface XPlatform {
getinfo(manifest: object): Promise<modpack_info>;
downloadfile(manifest: object,path:string): Promise<void>;
}
export interface modpack_info {
minecraft: string;
loader: string;
loader_version: string;
}
export function platform(plat: string | undefined): XPlatform {
let platform: XPlatform = Object.create({});
switch (plat) {
case "curseforge":
platform = new CurseForge();
break;
case "modrinth":
platform = new Modrinth();
break;
case "mcbbs":
platform = new MCBBS();
break;
}
return platform;
}
export function what_platform(dud_files: Array<string>) {
if (dud_files.includes("mcbbs.packmeta")) {
return "mcbbs";
} else if (dud_files.includes("manifest.json")) {
return "curseforge";
} else if (dud_files.includes("modrinth.index.json")) {
return "modrinth";
} else {
return undefined;
}
}

View File

@@ -0,0 +1,20 @@
import { CurseForgeManifest } from "./curseforge.js";
import { modpack_info, XPlatform } from "./index.js";
interface MCBBSManifest extends CurseForgeManifest {}
export class MCBBS implements XPlatform {
async getinfo(manifest: object): Promise<modpack_info> {
const result: modpack_info = Object.create({});
const local_manifest = manifest as MCBBSManifest;
if (result && local_manifest)
result.minecraft = local_manifest.minecraft.version;
const id = local_manifest.minecraft.modLoaders[0].id;
const loader_all = id.match(/(.*)-/) as RegExpMatchArray;
result.loader = loader_all[1];
result.loader_version = id.replace(loader_all[0], "");
return result;
}
async downloadfile(urls: [string, string]): Promise<void> {}
}

View File

@@ -0,0 +1,49 @@
import fs from "node:fs";
import { mr_fastdownload, usemirror } from "../utils/utils.js";
import { modpack_info, XPlatform } from "./index.js";
import { join } from "node:path";
interface ModrinthManifest {
files: Array<{ path: string; downloads: string[]; fileSize: number; }>;
dependencies: {
minecraft: string;
forge: string;
neoforge: string;
"fabric-loader": string;
[key: string]: string;
};
}
export class Modrinth implements XPlatform {
async getinfo(manifest: object): Promise<modpack_info> {
let result: modpack_info = Object.create({});
const local_manifest = manifest as ModrinthManifest;
const depkey = Object.keys(local_manifest.dependencies);
const loader = ["forge", "neoforge", "fabric-loader"];
result.minecraft = local_manifest.dependencies.minecraft;
for (let i = 0; i < depkey.length; i++) {
const key = depkey[i];
if (key !== "minecraft" && loader.includes(key)) {
result.loader = key;
result.loader_version = local_manifest.dependencies[key];
}
}
return result;
}
async downloadfile(manifest: object,path:string): Promise<void> {
const index = manifest as ModrinthManifest;
let tmp: [string, string, string][] = []
index.files.forEach(async (e: { path: string; downloads: string[]; fileSize: number;}) => {
if (e.path.endsWith(".zip")) {
return;
}
const unpath = join(path,e.path)
if (usemirror){
tmp.push(["https://mod.mcimirror.top"+new URL(e.downloads[0]).pathname,unpath,String(e.fileSize)])
}else{
tmp.push([e.downloads[0],unpath,String(e.fileSize)])
}
});
await mr_fastdownload(tmp as unknown as [string, string, string])
}
}

View File

@@ -0,0 +1,38 @@
import fs from "fs";
interface IConfig {
mirror: {
bmclapi: boolean;
mcimirror: boolean;
};
filter: {
hashes: boolean;
dexpub: boolean;
mixins: boolean;
};
}
export class Config {
private readonly default_config: IConfig = {
mirror: {
bmclapi: true,
mcimirror: true,
},
filter: {
hashes: true,
dexpub: false,
mixins: true,
},
};
config(): IConfig {
if (!fs.existsSync("./config.json")) {
fs.writeFileSync("./config.json", JSON.stringify(this.default_config));
return this.default_config;
}
return JSON.parse(fs.readFileSync("./config.json", "utf-8"));
}
static write_config(config: IConfig) {
fs.writeFileSync("./config.json", JSON.stringify(config));
}
}
export default new Config().config();

View File

@@ -0,0 +1,14 @@
import config from "./config.js";
export class Utils{
public modrinth_url: string;
public curseforge_url: string;
constructor(){
this.modrinth_url = "https://api.modrinth.com"
this.curseforge_url = "https://api.curseforge.com"
if(config.mirror.mcimirror){
this.modrinth_url = "https://mod.mcimirror.top/modrinth"
this.curseforge_url = "https://mod.mcimirror.top/curseforge"
}
}
}