Update http request and move versions to core #483

Merged
Sirherobrine23 merged 5 commits from internalServerVersion into main 2022-11-06 15:42:15 +00:00
21 changed files with 2452 additions and 614 deletions

2214
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,7 @@
"docs": "typedoc --readme none --out docs src/index.ts", "docs": "typedoc --readme none --out docs src/index.ts",
"dev": "nodemon", "dev": "nodemon",
"build": "tsc", "build": "tsc",
"test:partial": "mocha -r ts-node/register --exit", "test:partial": "mocha -r ts-node/register -r tsconfig-paths/register --exit",
"test": "npm run test:partial -- 'tests/**/*.ts'" "test": "npm run test:partial -- 'tests/**/*.ts'"
}, },
"repository": { "repository": {
@ -37,17 +37,18 @@
"cron": "^2.1.0", "cron": "^2.1.0",
"got": "^12.5.2", "got": "^12.5.2",
"prismarine-nbt": "^2.2.1", "prismarine-nbt": "^2.2.1",
"tar": "^6.1.11" "tar": "^6.1.12"
}, },
"devDependencies": { "devDependencies": {
"@types/adm-zip": "^0.5.0", "@types/adm-zip": "^0.5.0",
"@types/cron": "^2.0.0", "@types/cron": "^2.0.0",
"@types/mocha": "^10.0.0", "@types/mocha": "^10.0.0",
"@types/node": "^18.11.0", "@types/node": "^18.11.9",
"@types/tar": "^6.1.3", "@types/tar": "^6.1.3",
"mocha": "^10.1.0", "mocha": "^10.1.0",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typedoc": "^0.23.16", "tsconfig-paths": "^4.1.0",
"typedoc": "^0.23.20",
"typescript": "^4.8.4" "typescript": "^4.8.4"
} }
} }

View File

@ -5,7 +5,7 @@ import * as globalPlatfroms from "./globalPlatfroms";
import { platformManeger } from "@the-bds-maneger/server_versions"; import { platformManeger } from "@the-bds-maneger/server_versions";
import { pathControl, bdsPlatformOptions } from "./platformPathManeger"; import { pathControl, bdsPlatformOptions } from "./platformPathManeger";
import { commendExists } from "./lib/childPromisses"; import { commendExists } from "./lib/childPromisses";
import * as httpRequest from "./lib/httpRequest"; import * as httpLarge from "@http/large";
import extendsFs, { exists, readdirrecursive } from "./lib/extendsFs"; import extendsFs, { exists, readdirrecursive } from "./lib/extendsFs";
import { randomPort } from "./lib/randomPort"; import { randomPort } from "./lib/randomPort";
import { manegerConfigProprieties } from "./configManipulate"; import { manegerConfigProprieties } from "./configManipulate";
@ -28,7 +28,7 @@ export async function installServer(version: string|boolean, platformOptions: bd
await fs.readdir(serverPath).then(files => Promise.all(files.filter(file => !saveFileFolder.test(file)).map(file => fs.rm(path.join(serverPath, file), {recursive: true, force: true})))); await fs.readdir(serverPath).then(files => Promise.all(files.filter(file => !saveFileFolder.test(file)).map(file => fs.rm(path.join(serverPath, file), {recursive: true, force: true}))));
const serverConfigProperties = (await fs.readFile(path.join(serverPath, "server.properties"), "utf8").catch(() => "")).trim(); const serverConfigProperties = (await fs.readFile(path.join(serverPath, "server.properties"), "utf8").catch(() => "")).trim();
await httpRequest.extractZip({url, folderTarget: serverPath}); await httpLarge.extractZip({url, folderTarget: serverPath});
if (serverConfigProperties) await fs.writeFile(path.join(serverPath, "server.properties"), serverConfigProperties); if (serverConfigProperties) await fs.writeFile(path.join(serverPath, "server.properties"), serverConfigProperties);
await fs.writeFile(path.join(serverRoot, "version_installed.json"), JSON.stringify({version: bedrockData.version, date: bedrockData.date, installDate: new Date()})); await fs.writeFile(path.join(serverRoot, "version_installed.json"), JSON.stringify({version: bedrockData.version, date: bedrockData.date, installDate: new Date()}));

View File

@ -4,7 +4,7 @@ import fs from "node:fs/promises";
import tar from "tar"; import tar from "tar";
import { bdsRoot } from "./platformPathManeger"; import { bdsRoot } from "./platformPathManeger";
import { exists } from "./lib/extendsFs"; import { exists } from "./lib/extendsFs";
import { getExternalIP } from "./lib/httpRequest"; import { getExternalIP } from "@http/client";
export type payload = { export type payload = {
httpVersion?: string, httpVersion?: string,

View File

@ -3,7 +3,6 @@ import readline from "node:readline";
import child_process from "node:child_process"; import child_process from "node:child_process";
import { EventEmitter } from "node:events"; import { EventEmitter } from "node:events";
import { bdsPlatform } from "./platformPathManeger"; import { bdsPlatform } from "./platformPathManeger";
import * as process_load from "./lib/processLoad";
export const internalSessions: {[sessionID: string]: serverActionV2} = {}; export const internalSessions: {[sessionID: string]: serverActionV2} = {};
process.once("exit", () => Object.keys(internalSessions).forEach(id => internalSessions[id].stopServer())); process.once("exit", () => Object.keys(internalSessions).forEach(id => internalSessions[id].stopServer()));
@ -45,10 +44,6 @@ export declare interface serverActionsEvent {
once(act: "log", fn: (data: string) => void): this; once(act: "log", fn: (data: string) => void): this;
emit(act: "log", data: string): boolean; emit(act: "log", data: string): boolean;
on(act: "avg", fn: (data: process_load.avg) => void): this;
once(act: "avg", fn: (data: process_load.avg) => void): this;
emit(act: "avg", data: process_load.avg): boolean;
on(act: "exit", fn: (data: {code: number, signal: NodeJS.Signals}) => void): this; on(act: "exit", fn: (data: {code: number, signal: NodeJS.Signals}) => void): this;
once(act: "exit", fn: (data: {code: number, signal: NodeJS.Signals}) => void): this; once(act: "exit", fn: (data: {code: number, signal: NodeJS.Signals}) => void): this;
emit(act: "exit", data: {code: number, signal: NodeJS.Signals}): boolean; emit(act: "exit", data: {code: number, signal: NodeJS.Signals}): boolean;
@ -134,10 +129,6 @@ export async function actionV2(options: {id: string, platform: bdsPlatform, proc
// Add to internal sessions // Add to internal sessions
internalSessions[options.id] = serverObject; internalSessions[options.id] = serverObject;
serverObject.events.on("exit", () => delete internalSessions[options.id]); serverObject.events.on("exit", () => delete internalSessions[options.id]);
const processMetric = new process_load.processLoad(childProcess.pid);
processMetric.on("avg", data => serverObject.events.emit("avg", data));
processMetric.on("error", err => serverObject.events.emit("error", err));
serverObject.events.on("exit", () => processMetric.close());
// Register hooks // Register hooks
// Server avaible to player // Server avaible to player

View File

@ -1,10 +1,15 @@
// HTTP
import * as httpSimples from "@http/simples";
import * as httpLarge from "@http/large";
import * as httpGithub from "@http/github";
import * as httpClient from "@http/client";
const httpRequest = {httpSimples, httpLarge, httpGithub, httpClient};
// Utils // Utils
import * as httpRequest from "./lib/httpRequest";
import * as platformPathManeger from "./platformPathManeger" import * as platformPathManeger from "./platformPathManeger"
import * as globalPlatfroms from "./globalPlatfroms"; import * as globalPlatfroms from "./globalPlatfroms";
import * as pluginManeger from "./plugin/plugin"; import * as pluginManeger from "./plugin/plugin";
import * as export_import from "./export_import"; import * as export_import from "./export_import";
import * as process_load from "./lib/processLoad";
import * as pluginHooks from "./plugin/hook"; import * as pluginHooks from "./plugin/hook";
import * as proxy from "./lib/proxy"; import * as proxy from "./lib/proxy";
@ -16,7 +21,7 @@ import * as Spigot from "./spigot";
import * as Powernukkit from "./pwnuukit"; import * as Powernukkit from "./pwnuukit";
import * as PaperMC from "./paper"; import * as PaperMC from "./paper";
export {platformPathManeger, globalPlatfroms, pluginManeger, export_import, process_load, PocketmineMP, pluginHooks, Powernukkit, httpRequest, PaperMC, Bedrock, Spigot, proxy, Java}; export {platformPathManeger, globalPlatfroms, pluginManeger, export_import, httpRequest, PocketmineMP, pluginHooks, Powernukkit, PaperMC, Bedrock, Spigot, proxy, Java};
export default { export default {
Bedrock, Bedrock,
Java, Java,
@ -29,9 +34,8 @@ export default {
globalPlatfroms, globalPlatfroms,
pluginManeger, pluginManeger,
pluginHooks, pluginHooks,
httpRequest,
export_import, export_import,
process_load, proxy,
proxy httpRequest
} }
}; };

View File

@ -4,7 +4,7 @@ import fsOld from "node:fs";
import os from "node:os"; import os from "node:os";
import { platformManeger } from "@the-bds-maneger/server_versions"; import { platformManeger } from "@the-bds-maneger/server_versions";
import { actionV2, actionsV2 } from "./globalPlatfroms"; import { actionV2, actionsV2 } from "./globalPlatfroms";
import { saveFile } from "./lib/httpRequest"; import { saveFile } from "@http/large";
import { pathControl, bdsPlatformOptions } from "./platformPathManeger"; import { pathControl, bdsPlatformOptions } from "./platformPathManeger";
import { manegerConfigProprieties } from "./configManipulate"; import { manegerConfigProprieties } from "./configManipulate";
import { randomPort } from "./lib/randomPort"; import { randomPort } from "./lib/randomPort";

View File

@ -1,236 +0,0 @@
import type { Method } from "got";
import os from "node:os";
import fs from "node:fs";
import path from "node:path";
import crypto from "node:crypto";
import tar from "tar";
import AdmZip from "adm-zip";
import stream from "node:stream";
import { exists } from "./extendsFs";
let got: (typeof import("got"))["default"];
const gotCjs = async () => got||(await (eval('import("got")') as Promise<typeof import("got")>)).default.extend({enableUnixSockets: true});
gotCjs().then(res => got = res);
export type requestOptions = {
url?: string,
socket?: {
socketPath: string,
path?: string,
}
method?: Method,
headers?: {[headerName: string]: string[]|string},
body?: any,
};
export async function pipeFetch(options: requestOptions & {stream: fs.WriteStream|stream.Writable, waitFinish?: boolean}) {
if (!(options?.url||options?.socket)) throw new Error("Host blank")
const urlRequest = (typeof options.url === "string")?options.url:`http://unix:${options.socket.socketPath}:${options.socket.path||"/"}`;
const gotStream = (await gotCjs()).stream(urlRequest, {
isStream: true,
headers: options.headers||{},
method: options.method||"GET",
json: options.body,
});
await new Promise<void>((done, reject) => {
gotStream.pipe(options.stream);
options.stream.on("error", reject);
gotStream.on("error", reject);
gotStream.once("end", () => {
if (options.waitFinish) return options.stream.once("finish", done);
return done();
});
});
}
export async function bufferFetch(options: string|requestOptions) {
if (typeof options === "string") options = {url: options};
if (!(options.url||options.socket)) throw new Error("Host blank")
const urlRequest = (typeof options.url === "string")?options.url:`http://unix:${options.socket.socketPath}:${options.socket.path||"/"}`;
return (await gotCjs())(urlRequest, {
headers: options.headers||{},
method: options.method||"GET",
json: options.body,
responseType: "buffer",
}).then(res => ({headers: res.headers, data: Buffer.from(res.body), response: res}));
}
export async function getJSON<JSONReturn = any>(request: string|requestOptions): Promise<JSONReturn> {
if (typeof request === "string") request = {url: request};
const res = (await bufferFetch(request)).data.toString("utf8");
return JSON.parse(res) as JSONReturn;
}
export async function saveFile(request: string|requestOptions & {filePath?: string}) {
if (typeof request === "string") request = {url: request};
const filePath = request.filePath||path.join(os.tmpdir(), `raw_bdscore_${Date.now()}_${(path.parse(request.url||request.socket?.path||crypto.randomBytes(16).toString("hex"))).name}`);
await pipeFetch({...request, waitFinish: true, stream: fs.createWriteStream(filePath, {autoClose: false})});
return filePath;
}
export async function tarExtract(request: requestOptions & {folderPath?: string}) {
const folderToExtract = request.folderPath||path.join(os.tmpdir(), `raw_bdscore_${Date.now()}_${(path.parse(request.url||request.socket?.path||crypto.randomBytes(16).toString("hex"))).name}`);
if (!await exists(folderToExtract)) await fs.promises.mkdir(folderToExtract, {recursive: true});
await pipeFetch({
...request,
waitFinish: true,
stream: tar.extract({
cwd: folderToExtract,
noChmod: false,
noMtime: false,
preserveOwner: true,
keep: true,
p: true
})
});
return folderToExtract
}
const githubAchive = /github.com\/[\S\w]+\/[\S\w]+\/archive\//;
export async function extractZip(request: requestOptions & {folderTarget: string}) {
const zip = new AdmZip(await saveFile(request));
const targetFolder = githubAchive.test(request.url)?await fs.promises.mkdtemp(path.join(os.tmpdir(), "githubRoot_"), "utf8"):request.folderTarget;
await new Promise<void>((done, reject) => {
zip.extractAllToAsync(targetFolder, true, true, (err) => {
if (!err) return done();
return reject(err);
})
});
if (githubAchive.test(request.url)) {
const files = await fs.promises.readdir(targetFolder);
if (files.length === 0) throw new Error("Invalid extract");
await fs.promises.cp(path.join(targetFolder, files[0]), request.folderTarget, {recursive: true, force: true, preserveTimestamps: true, verbatimSymlinks: true});
return await fs.promises.rm(targetFolder, {recursive: true, force: true});
}
return;
}
export type testIp<protocolType extends "ipv4"|"ipv6" = "ipv4"> = {
ip: string,
type: protocolType,
subtype: string,
via: string,
padding: string,
asn: string,
asnlist: string,
asn_name: string,
country: string,
protocol: "HTTP/2.0"|"HTTP/1.1"|"HTTP/1.0"
};
export async function getExternalIP(): Promise<{ipv4: string, ipv6?: string, rawRequest?: {ipv4: testIp<"ipv4">, ipv6?: testIp<"ipv6">}}> {
const ipv6: testIp<"ipv6"> = await getJSON("https://ipv6.lookup.test-ipv6.com/ip/").catch(() => undefined);
const ipv4: testIp<"ipv4"> = await getJSON("https://ipv4.lookup.test-ipv6.com/ip/");
return {
ipv4: ipv4.ip,
ipv6: ipv6?.ip,
rawRequest: {ipv4, ipv6}
};
}
export type githubRelease = {
url: string;
assets_url: string;
upload_url: string;
html_url: string;
id: number;
tarball_url: string;
zipball_url: string;
body: string;
author: {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
};
node_id: string;
tag_name: string;
target_commitish: string;
name: string;
draft: boolean;
prerelease: boolean;
created_at: string;
published_at: string;
assets: Array<{
url: string;
id: number;
node_id: string;
name: string;
label: string;
content_type: string;
state: string;
size: number;
download_count: number;
created_at: string;
updated_at: string;
browser_download_url: string;
uploader: {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
};
}>;
};
export async function GithubRelease(username: string, repo: string, releaseTag: string): Promise<githubRelease>;
export async function GithubRelease(username: string, repo: string): Promise<githubRelease[]>;
export async function GithubRelease(username: string): Promise<githubRelease[]>;
export async function GithubRelease(username: string, repo?: string, releaseTag?: string): Promise<githubRelease|githubRelease[]> {
let fullRepo = username;
if (!username) throw new Error("Repository is required, example: GithubRelease(\"Username/repo\") or GithubRelease(\"Username\", \"repo\")");
if (repo) {
if (!/\//.test(fullRepo)) fullRepo += "/"+repo;
}
if (releaseTag) return getJSON<githubRelease>(`https://api.github.com/repos/${fullRepo}/releases/tags/${releaseTag}`);
return getJSON<githubRelease[]>(`https://api.github.com/repos/${fullRepo}/releases?per_page=100`);
}
export type githubTree = {
"sha": string,
"url": string,
"truncated": boolean,
"tree": {
"path": string,
"mode": string,
"type": "blob"|"tree",
"sha": string,
"size": number,
"url": string
}[],
};
export async function githubTree(username: string, repo: string, tree: string = "main") {
const validate = /^[a-zA-Z0-9_\-]+$/;
if (!validate.test(username)) throw new Error("Invalid username");
if (!validate.test(repo)) throw new Error("Invalid repository name");
return getJSON<githubTree>(`https://api.github.com/repos/${username}/${repo}/git/trees/${tree}?recursive=true`);
}

View File

@ -1,203 +0,0 @@
import { EventEmitter } from "node:events";
// import customProcess from "./childPromisses";
import extendsFs from "./extendsFs";
import path from "node:path";
import fs from "node:fs/promises";
// import os from "node:os";
export type pidstat = {
/** The process id. **/
pid: number,
/** The filename of the executable **/
exName: string,
/** 'R' is running, 'S' is sleeping, 'D' is sleeping in an uninterruptible wait, 'Z' is zombie, 'T' is traced or stopped **/
state: "R"|"S"|"D"|"Z"|"T",
/** effective user id **/
euid: number,
/** effective group id */
egid: number,
/** The pid of the parent. **/
ppid: number,
/** The pgrp of the process. **/
pgrp: number,
/** The session id of the process. **/
session: number,
/** The tty the process uses **/
tty: number,
/** (too long) **/
tpgid: number,
/** The flags of the process. **/
flags: number,
/** The number of minor faults **/
minflt: number,
/** The number of minor faults with childs **/
cminflt: number,
/** The number of major faults **/
majflt: number,
/** The number of major faults with childs **/
cmajflt: number,
/** user mode jiffies **/
utime: number,
/** kernel mode jiffies **/
stime: number,
/** user mode jiffies with childs **/
cutime: number,
/** kernel mode jiffies with childs **/
cstime: number,
/** process's next timeslice **/
counter: number,
/** the standard nice value, plus fifteen **/
priority: number,
/** The time in jiffies of the next timeout **/
timeout: number,
/** The time before the next SIGALRM is sent to the process **/
itrealvalue: number,
/** Time the process started after system boot **/
starttime: number,
/** Virtual memory size **/
vsize: number,
/** Resident Set Size **/
rss: number,
/** Current limit in bytes on the rss **/
rlim: number,
/** The address above which program text can run **/
startcode: number,
/** The address below which program text can run **/
endcode: number,
/** The address of the start of the stack **/
startstack: number,
/** The current value of ESP **/
kstkesp: number,
/** The current value of EIP **/
kstkeip: number,
/** The bitmap of pending signals **/
signal: number,
/** The bitmap of blocked signals **/
blocked: number,
/** The bitmap of ignored signals **/
sigignore: number,
/** The bitmap of catched signals **/
sigcatch: number,
/** (too long) **/
wchan: number,
/** scheduler **/
sched: number,
/** scheduler priority **/
sched_priority: any
};
// Receive: bytes packets errs drop fifo frame compressed multicast
// Transmit: bytes packets errs drop fifo colls carrier compressed
export type netDef = {
recive: {
bytes: number,
packets: number,
errs: number,
drop: number,
fifo: number,
frame: number,
compressed: number,
multicast: number
},
transmit: {
bytes: number,
packets: number,
errs: number,
drop: number,
fifo: number,
colls: number,
carrier: number,
compressed: number
}
};
export type avg = {cpuAvg: number, cpuAvgNoAbs: number, cwd: string, cmdline: string[], net: {[name: string]: netDef}}
export declare interface processLoad {
on(act: "error", fn: (data: any) => void): this;
once(act: "error", fn: (data: any) => void): this;
emit(act: "error", data: any): boolean;
on(act: "avg", fn: (data: avg & {latestLoop?: Date}) => void): this;
once(act: "avg", fn: (data: avg & {latestLoop?: Date}) => void): this;
emit(act: "avg", data: avg & {latestLoop?: Date}): boolean;
}
const linuxPlatform: NodeJS.Platform[] = ["android", "linux"];
export class processLoad extends EventEmitter {
pidNumber: number;
private terminateProcess = true;
#procFolder = "/proc";
close(){this.terminateProcess = false; return this;}
async #linux(): Promise<avg> {
const pidFolder = path.join(this.#procFolder, this.pidNumber.toFixed(0));
if (!await extendsFs.exists(pidFolder)) throw new Error("PID not found!");
// cwd
const cwd = await fs.realpath(path.join(pidFolder, "cwd"));
// cmdline
const cmdline = (await fs.readFile(path.join(pidFolder, "cmdline"), "utf8")).trim().split(/\u0000/).filter(a => !!a);
// stat
const [pid, exName, state, euid, egid, ppid, pgrp, session, tty, tpgid, flags, minflt, cminflt, majflt, cmajflt, utime, stime, cutime, cstime, counter, priority, timeout, itrealvalue, starttime, vsize, rss, rlim, startcode, endcode, startstack, kstkesp, kstkeip, signal, blocked, sigignore, sigcatch, wchan, sched, sched_priority] = (await fs.readFile(path.join(pidFolder, "stat"), "utf8")).trim().split(/\s+|[\t]+|\t/);
const cpuAvg: pidstat = {pid: parseInt(pid), exName, state: state as pidstat["state"], euid: parseInt(euid), egid: parseInt(egid), ppid: parseInt(ppid), pgrp: parseInt(pgrp), session: parseInt(session), tty: parseInt(tty), tpgid: parseInt(tpgid), flags: parseInt(flags), minflt: parseInt(minflt), cminflt: parseInt(cminflt), majflt: parseInt(majflt), cmajflt: parseInt(cmajflt), utime: parseInt(utime), stime: parseInt(stime), cutime: parseInt(cutime), cstime: parseInt(cstime), counter: parseInt(counter), priority: parseInt(priority), timeout: parseInt(timeout), itrealvalue: parseInt(itrealvalue), starttime: parseInt(starttime), vsize: parseInt(vsize), rss: parseInt(rss), rlim: parseInt(rlim), startcode: parseInt(startcode), endcode: parseInt(endcode), startstack: parseInt(startstack), kstkesp: parseInt(kstkesp), kstkeip: parseInt(kstkeip), signal: parseInt(signal), blocked: parseInt(blocked), sigignore: parseInt(sigignore), sigcatch: parseInt(sigcatch), wchan: parseInt(wchan), sched: parseInt(sched), sched_priority: sched_priority?parseInt(sched_priority):sched_priority};
// net
const netInterface = /([a-zA-Z0-9_\-]+):\s+(.*)/;
const net: {[name: string]: netDef} = {};
(await fs.readFile(path.join(pidFolder, "net/dev"), "utf8")).split(/\r?\n/).filter(line => netInterface.test(line)).forEach(line => {
const dataMath = line.match(netInterface);
if (!dataMath) return;
const [, interfaceName, data] = dataMath;
const [recive_bytes, recive_packets, recive_errs, recive_drop, recive_fifo, recive_frame, recive_compressed, recive_multicast, transmit_bytes, transmit_packets, transmit_errs, transmit_drop, transmit_fifo, transmit_colls, transmit_carrier, transmit_compressed] = data.split(/\s+/).map(numberString => parseInt(numberString));
net[interfaceName] = {
recive: {
bytes: recive_bytes,
compressed: recive_compressed,
drop: recive_drop,
errs: recive_errs,
fifo: recive_fifo,
frame: recive_frame,
multicast: recive_multicast,
packets: recive_packets
},
transmit: {
bytes: transmit_bytes,
carrier: transmit_carrier,
colls: transmit_colls,
compressed: transmit_compressed,
drop: transmit_drop,
errs: transmit_errs,
fifo: transmit_fifo,
packets: transmit_packets
}
};
return;
});
const cpu_usage = Math.abs((cpuAvg.cmajflt+cpuAvg.utime+cpuAvg.stime+cpuAvg.cutime+cpuAvg.cstime)/30)//Hartz;
return {
cpuAvg: parseFloat(cpu_usage.toFixed(2)),
cpuAvgNoAbs: cpu_usage,
cwd,
cmdline,
net,
};
}
latestLoop: Date;
latestAvg?: avg;
constructor(processPid: number) {
super({captureRejections: false});
this.pidNumber = processPid;
(async () => {
while(this.terminateProcess) {
if (linuxPlatform.includes(process.platform)) await this.#linux().then(data => {this.latestAvg = data; this.latestLoop = new Date(); this.emit("avg", {...data, latestLoop: this.latestLoop});}).catch(err => {this.emit("error", err); this.close()});
await new Promise(done => setTimeout(done, 1800));
}
})();
}
}

View File

@ -5,7 +5,7 @@ import os from "node:os";
import Proprieties from "./lib/Proprieties"; import Proprieties from "./lib/Proprieties";
import * as globalPlatfroms from "./globalPlatfroms"; import * as globalPlatfroms from "./globalPlatfroms";
import { platformManeger } from "@the-bds-maneger/server_versions"; import { platformManeger } from "@the-bds-maneger/server_versions";
import { saveFile } from "./lib/httpRequest"; import { saveFile } from "@http/large";
import { pathControl, bdsPlatformOptions } from "./platformPathManeger"; import { pathControl, bdsPlatformOptions } from "./platformPathManeger";
import { spigotProprieties } from "./spigot"; import { spigotProprieties } from "./spigot";

View File

@ -2,7 +2,7 @@ import path from "node:path";
import fs from "node:fs/promises"; import fs from "node:fs/promises";
import { actionsV2 } from "../globalPlatfroms"; import { actionsV2 } from "../globalPlatfroms";
import { execFileAsync } from "../lib/childPromisses"; import { execFileAsync } from "../lib/childPromisses";
import { saveFile } from "../lib/httpRequest"; import { saveFile } from "@http/large";
export type hooksPlatform = "bedrock"|"java"|"pocketmine"|"spigot"|"powernukkit"|"paper"; export type hooksPlatform = "bedrock"|"java"|"pocketmine"|"spigot"|"powernukkit"|"paper";
export type hooksPlatformGeneric = hooksPlatform|"generic"; export type hooksPlatformGeneric = hooksPlatform|"generic";

View File

@ -1,6 +1,7 @@
import path from "node:path"; import path from "node:path";
import * as http_request from "../lib/httpRequest";
import type { bdsPlatform } from "../platformPathManeger"; import type { bdsPlatform } from "../platformPathManeger";
import { getJSON } from "@http/simples";
import { saveFile } from "@http/large";
export type pluginConfig = { export type pluginConfig = {
version?: 1, version?: 1,
@ -40,11 +41,11 @@ export class pluginManeger {
const urlandbds = /http[s]:\/\/|bdsplugin:\/\//; const urlandbds = /http[s]:\/\/|bdsplugin:\/\//;
if (/bdsplugin:\/\//.test(plugin)) plugin = `https://raw.githubusercontent.com/The-Bds-Maneger/plugin_list/main/plugins/${plugin.replace(urlandbds, "").replace(".json", "")}.json`; if (/bdsplugin:\/\//.test(plugin)) plugin = `https://raw.githubusercontent.com/The-Bds-Maneger/plugin_list/main/plugins/${plugin.replace(urlandbds, "").replace(".json", "")}.json`;
else if (!/http[s]:\/\/\//.test(plugin)) plugin = "bdsplugin://"+plugin; else if (!/http[s]:\/\/\//.test(plugin)) plugin = "bdsplugin://"+plugin;
const info = await http_request.getJSON<pluginConfig|pluginV2>(plugin); const info = await getJSON<pluginConfig|pluginV2>(plugin);
if (info.version === 2) { if (info.version === 2) {
const platformData = info.platform[this.#platform]; const platformData = info.platform[this.#platform];
if (!platformData) throw new Error("Platform not supported to Plugin!"); if (!platformData) throw new Error("Platform not supported to Plugin!");
await http_request.saveFile({ await saveFile({
url: platformData.url, url: platformData.url,
filePath: path.join(this.#pluginFolder, platformData.fileName||path.basename(platformData.url)) filePath: path.join(this.#pluginFolder, platformData.fileName||path.basename(platformData.url))
}); });
@ -52,7 +53,7 @@ export class pluginManeger {
return; return;
} }
if (!info.platforms.includes(this.#platform)) throw new Error("Platform not supported to Plugin!"); if (!info.platforms.includes(this.#platform)) throw new Error("Platform not supported to Plugin!");
await http_request.saveFile({ await saveFile({
url: info.url, url: info.url,
filePath: path.join(this.#pluginFolder, info.fileName||path.basename(info.url)) filePath: path.join(this.#pluginFolder, info.fileName||path.basename(info.url))
}); });

View File

@ -7,8 +7,10 @@ import { existsSync as fsExistsSync, Stats } from "node:fs";
import { promisify } from "node:util"; import { promisify } from "node:util";
import { platformManeger } from "@the-bds-maneger/server_versions"; import { platformManeger } from "@the-bds-maneger/server_versions";
import { execFileAsync, execAsync } from "./lib/childPromisses"; import { execFileAsync, execAsync } from "./lib/childPromisses";
import * as httpRequest from "./lib/httpRequest"; import { extractZip, saveFile, tarExtract } from "@http/large";
import { bufferFetch } from "@http/simples";
import { pathControl, bdsPlatformOptions } from "./platformPathManeger"; import { pathControl, bdsPlatformOptions } from "./platformPathManeger";
import {GithubRelease, githubRelease} from "@http/github";
async function findPhp(serverPath: string, extraPath?: string): Promise<string> { async function findPhp(serverPath: string, extraPath?: string): Promise<string> {
if (!extraPath) extraPath = path.join(serverPath, "bin"); if (!extraPath) extraPath = path.join(serverPath, "bin");
@ -57,7 +59,7 @@ async function buildPhp(serverPath: string, buildFolder: string) {
await promisify((new AdmZip(path.join(buildFolder, (await fs.readdir(buildFolder)).find(file => file.endsWith(".zip"))))).extractAllToAsync)(serverPath, false, true); await promisify((new AdmZip(path.join(buildFolder, (await fs.readdir(buildFolder)).find(file => file.endsWith(".zip"))))).extractAllToAsync)(serverPath, false, true);
} else { } else {
const buildFile = path.join(buildFolder, "build.sh"); const buildFile = path.join(buildFolder, "build.sh");
await fs.writeFile(buildFile, (await httpRequest.bufferFetch({url: "https://raw.githubusercontent.com/pmmp/php-build-scripts/stable/compile.sh"})).data); await fs.writeFile(buildFile, (await bufferFetch({url: "https://raw.githubusercontent.com/pmmp/php-build-scripts/stable/compile.sh"})).data);
await fs.chmod(buildFile, "777"); await fs.chmod(buildFile, "777");
await execFileAsync(buildFile, ["-j"+os.cpus().length], {cwd: buildFolder, stdio: "inherit"}); await execFileAsync(buildFile, ["-j"+os.cpus().length], {cwd: buildFolder, stdio: "inherit"});
await fs.cp(path.join(buildFolder, "bin", (await fs.readdir(path.join(buildFolder, "bin")))[0]), path.join(serverPath, "bin"), {force: true, recursive: true, preserveTimestamps: true, verbatimSymlinks: true}); await fs.cp(path.join(buildFolder, "bin", (await fs.readdir(path.join(buildFolder, "bin")))[0]), path.join(serverPath, "bin"), {force: true, recursive: true, preserveTimestamps: true, verbatimSymlinks: true});
@ -67,20 +69,20 @@ async function buildPhp(serverPath: string, buildFolder: string) {
} }
async function installPhp(serverPath: string, buildFolder: string): Promise<void> { async function installPhp(serverPath: string, buildFolder: string): Promise<void> {
const releases: (httpRequest.githubRelease["assets"][0])[] = []; const releases: (githubRelease["assets"][0])[] = [];
(await httpRequest.GithubRelease("The-Bds-Maneger", "Build-PHP-Bins")).map(re => re.assets).forEach(res => releases.push(...res)); (await GithubRelease("The-Bds-Maneger", "Build-PHP-Bins")).map(re => re.assets).forEach(res => releases.push(...res));
if (fsExistsSync(path.resolve(serverPath, "bin"))) await fs.rm(path.resolve(serverPath, "bin"), {recursive: true}); if (fsExistsSync(path.resolve(serverPath, "bin"))) await fs.rm(path.resolve(serverPath, "bin"), {recursive: true});
await fs.writeFile(path.join(os.tmpdir(), "bds_test.php"), `<?php echo "Hello World";`); await fs.writeFile(path.join(os.tmpdir(), "bds_test.php"), `<?php echo "Hello World";`);
if (process.platform === "win32") { if (process.platform === "win32") {
let url: string = releases.filter(assert => assert.name.endsWith(".zip")).find(assert => /win32|windows/.test(assert.name))?.browser_download_url; let url: string = releases.filter(assert => assert.name.endsWith(".zip")).find(assert => /win32|windows/.test(assert.name))?.browser_download_url;
if (!url) throw new Error("Cannnot get php url"); if (!url) throw new Error("Cannnot get php url");
return promisify((new AdmZip(await httpRequest.saveFile({url}))).extractAllToAsync)(serverPath, false, true); return promisify((new AdmZip(await saveFile({url}))).extractAllToAsync)(serverPath, false, true);
} else { } else {
const fileTest = RegExp(`${process.platform.toLowerCase()}.*${process.arch.toLowerCase()}`); const fileTest = RegExp(`${process.platform.toLowerCase()}.*${process.arch.toLowerCase()}`);
const file = releases.find(re => fileTest.test(re.name.toLowerCase())); const file = releases.find(re => fileTest.test(re.name.toLowerCase()));
if (file) { if (file) {
if (/\.zip/.test(file.name)) await httpRequest.extractZip({url: file.browser_download_url, folderTarget: serverPath}); if (/\.zip/.test(file.name)) await extractZip({url: file.browser_download_url, folderTarget: serverPath});
else await httpRequest.tarExtract({url: file.browser_download_url, folderPath: path.join(serverPath, "bin")}); else await tarExtract({url: file.browser_download_url, folderPath: path.join(serverPath, "bin")});
// Update zts // Update zts
if (process.platform === "linux"||process.platform === "android"||process.platform === "darwin") { if (process.platform === "linux"||process.platform === "android"||process.platform === "darwin") {
@ -107,7 +109,7 @@ export async function installServer(version: string|boolean, platformOptions: bd
const { serverPath, buildFolder, id } = await pathControl("pocketmine", platformOptions); const { serverPath, buildFolder, id } = await pathControl("pocketmine", platformOptions);
await installPhp(serverPath, buildFolder); await installPhp(serverPath, buildFolder);
const info = await platformManeger.pocketmine.find(version); const info = await platformManeger.pocketmine.find(version);
await httpRequest.saveFile({url: info?.url, filePath: path.join(serverPath, "pocketmine.phar")}); await saveFile({url: info?.url, filePath: path.join(serverPath, "pocketmine.phar")});
return { return {
id, id,
version: info.version, version: info.version,

View File

@ -4,7 +4,7 @@ import fs from "node:fs/promises";
import os from "node:os"; import os from "node:os";
import * as globalPlatfroms from "./globalPlatfroms"; import * as globalPlatfroms from "./globalPlatfroms";
import { platformManeger } from "@the-bds-maneger/server_versions"; import { platformManeger } from "@the-bds-maneger/server_versions";
import { saveFile } from "./lib/httpRequest"; import { saveFile } from "@http/large";
import { pathControl, bdsPlatformOptions } from "./platformPathManeger"; import { pathControl, bdsPlatformOptions } from "./platformPathManeger";
export async function installServer(version: string|boolean, platformOptions: bdsPlatformOptions = {id: "default"}) { export async function installServer(version: string|boolean, platformOptions: bdsPlatformOptions = {id: "default"}) {

26
src/request/client.ts Normal file
View File

@ -0,0 +1,26 @@
import { getJSON } from "@http/simples";
export type testIp<protocolType extends "ipv4"|"ipv6" = "ipv4"> = {
ip: string,
type: protocolType,
subtype: string,
via: string,
padding: string,
asn: string,
asnlist: string,
asn_name: string,
country: string,
protocol: "HTTP/2.0"|"HTTP/1.1"|"HTTP/1.0"
};
export async function getExternalIP(): Promise<{ipv4: string, ipv6?: string, rawRequest?: {ipv4: testIp<"ipv4">, ipv6?: testIp<"ipv6">}}> {
const [ipv6, ipv4] = await Promise.all([
await getJSON<testIp<"ipv6">>("https://ipv6.lookup.test-ipv6.com/ip/").catch(() => undefined),
await getJSON<testIp<"ipv4">>("https://ipv4.lookup.test-ipv6.com/ip/")
]);
return {
ipv4: ipv4.ip,
ipv6: ipv6?.ip,
rawRequest: {ipv4, ipv6}
};
}

107
src/request/github.ts Normal file
View File

@ -0,0 +1,107 @@
import { getJSON } from "@http/simples";
export type githubRelease = {
url: string;
assets_url: string;
upload_url: string;
html_url: string;
id: number;
tarball_url: string;
zipball_url: string;
body: string;
author: {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
};
node_id: string;
tag_name: string;
target_commitish: string;
name: string;
draft: boolean;
prerelease: boolean;
created_at: string;
published_at: string;
assets: Array<{
url: string;
id: number;
node_id: string;
name: string;
label: string;
content_type: string;
state: string;
size: number;
download_count: number;
created_at: string;
updated_at: string;
browser_download_url: string;
uploader: {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
};
}>;
};
export async function GithubRelease(username: string, repo: string, releaseTag: string): Promise<githubRelease>;
export async function GithubRelease(username: string, repo: string): Promise<githubRelease[]>;
export async function GithubRelease(username: string): Promise<githubRelease[]>;
export async function GithubRelease(username: string, repo?: string, releaseTag?: string): Promise<githubRelease|githubRelease[]> {
let fullRepo = username;
if (!username) throw new Error("Repository is required, example: GithubRelease(\"Username/repo\") or GithubRelease(\"Username\", \"repo\")");
if (repo) {
if (!/\//.test(fullRepo)) fullRepo += "/"+repo;
}
if (releaseTag) return getJSON<githubRelease>(`https://api.github.com/repos/${fullRepo}/releases/tags/${releaseTag}`);
return getJSON<githubRelease[]>(`https://api.github.com/repos/${fullRepo}/releases?per_page=100`);
}
export type githubTree = {
"sha": string,
"url": string,
"truncated": boolean,
"tree": {
"path": string,
"mode": string,
"type": "blob"|"tree",
"sha": string,
"size": number,
"url": string
}[],
};
export async function githubTree(username: string, repo: string, tree: string = "main") {
const validate = /^[a-zA-Z0-9_\-]+$/;
if (!validate.test(username)) throw new Error("Invalid username");
if (!validate.test(repo)) throw new Error("Invalid repository name");
return getJSON<githubTree>(`https://api.github.com/repos/${username}/${repo}/git/trees/${tree}?recursive=true`);
}

53
src/request/large.ts Normal file
View File

@ -0,0 +1,53 @@
import { requestOptions, pipeFetch } from "@http/simples";
import os from "node:os";
import fs from "node:fs";
import path from "node:path";
import crypto from "node:crypto";
import tar from "tar";
import AdmZip from "adm-zip";
import { exists } from "../lib/extendsFs";
export async function saveFile(request: string|requestOptions & {filePath?: string}) {
if (typeof request === "string") request = {url: request};
const filePath = request.filePath||path.join(os.tmpdir(), `raw_bdscore_${Date.now()}_${(path.parse(request.url||request.socket?.path||crypto.randomBytes(16).toString("hex"))).name}`);
await pipeFetch({...request, waitFinish: true, stream: fs.createWriteStream(filePath, {autoClose: false})});
return filePath;
}
export async function tarExtract(request: requestOptions & {folderPath?: string}) {
const folderToExtract = request.folderPath||path.join(os.tmpdir(), `raw_bdscore_${Date.now()}_${(path.parse(request.url||request.socket?.path||crypto.randomBytes(16).toString("hex"))).name}`);
if (!await exists(folderToExtract)) await fs.promises.mkdir(folderToExtract, {recursive: true});
await pipeFetch({
...request,
waitFinish: true,
stream: tar.extract({
cwd: folderToExtract,
noChmod: false,
noMtime: false,
preserveOwner: true,
keep: true,
p: true
})
});
return folderToExtract
}
const githubAchive = /github.com\/[\S\w]+\/[\S\w]+\/archive\//;
export async function extractZip(request: requestOptions & {folderTarget: string}) {
const zip = new AdmZip(await saveFile(request));
const targetFolder = githubAchive.test(request.url)?await fs.promises.mkdtemp(path.join(os.tmpdir(), "githubRoot_"), "utf8"):request.folderTarget;
await new Promise<void>((done, reject) => {
zip.extractAllToAsync(targetFolder, true, true, (err) => {
if (!err) return done();
return reject(err);
})
});
if (githubAchive.test(request.url)) {
const files = await fs.promises.readdir(targetFolder);
if (files.length === 0) throw new Error("Invalid extract");
await fs.promises.cp(path.join(targetFolder, files[0]), request.folderTarget, {recursive: true, force: true, preserveTimestamps: true, verbatimSymlinks: true});
return await fs.promises.rm(targetFolder, {recursive: true, force: true});
}
return;
}

69
src/request/simples.ts Normal file
View File

@ -0,0 +1,69 @@
import type { Method } from "got";
import * as fs from "node:fs";
import * as stream from "node:stream";
export let got: (typeof import("got"))["default"];
async function gotCjs(): Promise<(typeof import("got"))["default"]> {
if (got) return got;
const dyImport = (await (eval('import("got")') as Promise<typeof import("got")>)).default.extend({
enableUnixSockets: true,
headers: {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
"Accept": "*/*"
}
});
got = dyImport;
return dyImport;
}
export type requestOptions = {
url?: string,
socket?: {
socketPath: string,
path?: string,
}
method?: Method,
headers?: {[headerName: string]: string[]|string},
body?: any,
};
export async function pipeFetch(options: requestOptions & {stream: fs.WriteStream|stream.Writable, waitFinish?: boolean}) {
if (!(options?.url||options?.socket)) throw new Error("Host blank")
const urlRequest = (typeof options.url === "string")?options.url:`http://unix:${options.socket.socketPath}:${options.socket.path||"/"}`;
const gotStream = (await gotCjs()).stream(urlRequest, {
isStream: true,
headers: options.headers||{},
method: options.method||"GET",
...((options.method||"GET").toLowerCase()!=="get"?(typeof options.body === "string"?{body: options.body}:{json: options.body}):{}),
});
await new Promise<void>((done, reject) => {
gotStream.pipe(options.stream);
options.stream.on("error", reject);
gotStream.on("error", reject);
gotStream.once("end", () => {
if (options.waitFinish) return options.stream.once("finish", done);
return done();
});
});
}
export async function bufferFetch(options: string|requestOptions) {
if (typeof options === "string") options = {url: options};
if (!(options.url||options.socket)) throw new Error("Host blank")
const urlRequest = (typeof options.url === "string")?options.url:`http://unix:${options.socket.socketPath}:${options.socket.path||"/"}`;
return (await gotCjs())(urlRequest, {
responseType: "buffer",
headers: options.headers||{},
method: options.method||"GET",
...((options.method||"GET").toLowerCase()!=="get"?(typeof options.body === "string"?{body: options.body}:{json: options.body}):{}),
}).then(res => ({headers: res.headers, data: Buffer.from(res.body), response: res}));
}
export async function getJSON<JSONReturn = any>(request: string|requestOptions): Promise<JSONReturn> {
return bufferFetch(request).then(({data}) => JSON.parse(data.toString("utf8")) as JSONReturn)
}
export async function urls(options: requestOptions|string): Promise<string[]> {
const data = (await bufferFetch(options)).data.toString("utf8");
return (data.match(/((http[s]):(([A-Za-z0-9$_.+!*(),;/?:@&~=-])|%[A-Fa-f0-9]{2}){2,}(#([a-zA-Z0-9][a-zA-Z0-9$_.+!*(),;/?:@&~=%-]*))?([A-Za-z0-9$_+!*();/?:~-]))/g)).map(res => typeof res === "string"?res:res[1]);
}

View File

@ -3,7 +3,9 @@ import fs from "node:fs/promises";
import fsOld from "node:fs"; import fsOld from "node:fs";
import os from "node:os"; import os from "node:os";
import * as globalPlatfroms from "./globalPlatfroms"; import * as globalPlatfroms from "./globalPlatfroms";
import { bufferFetch, getJSON, GithubRelease, saveFile } from "./lib/httpRequest"; import { bufferFetch, getJSON } from "@http/simples";
import { saveFile } from "@http/large";
import { GithubRelease } from "@http/github";
import { pathControl, bdsPlatformOptions } from "./platformPathManeger"; import { pathControl, bdsPlatformOptions } from "./platformPathManeger";
import Proprieties from "./lib/Proprieties" import Proprieties from "./lib/Proprieties"

View File

@ -1,9 +1,12 @@
import stream from "node:stream"; import stream from "node:stream";
import { getExternalIP, getJSON, saveFile, pipeFetch, githubTree, GithubRelease } from "../../src/lib/httpRequest"; import { getJSON, pipeFetch } from "../../src/lib/request/simples";
import { saveFile } from "../../src/lib/request/large";
import { GithubRelease, githubTree } from "../../src/lib/request/github";
import { getExternalIP } from "../../src/lib/request/client";
describe("HTTP Request", function(){ describe("HTTP Request", function(){
this.timeout(Infinity); this.timeout(Infinity);
const Stream = new stream.Writable({write(chunk, encoding, callback) {callback()}}); const Stream = new stream.Writable({write(_chunk, _encoding, callback) {callback()}});
it("External IP", async () => getExternalIP()); it("External IP", async () => getExternalIP());
it("Github Releases", async () => GithubRelease("The-Bds-Maneger", "Bds-Maneger-Core")); it("Github Releases", async () => GithubRelease("The-Bds-Maneger", "Bds-Maneger-Core"));
it("Github Tree", async () => githubTree("The-Bds-Maneger", "Bds-Maneger-Core")); it("Github Tree", async () => githubTree("The-Bds-Maneger", "Bds-Maneger-Core"));

View File

@ -1,29 +1,25 @@
{ {
"compilerOptions": { "compilerOptions": {
"esModuleInterop": true, "esModuleInterop": true,
"module": "commonjs", "module": "commonjs",
"moduleResolution": "node", "moduleResolution": "node",
"target": "ES2019", "target": "ES2019",
// "outDir": "./dist", "declaration": true,
"declaration": true, "strict": false,
"strict": false, "noUnusedLocals": true,
"noUnusedLocals": true, "noImplicitReturns": true,
"noImplicitReturns": true, "noFallthroughCasesInSwitch": true,
"noFallthroughCasesInSwitch": true, "skipLibCheck": true,
"skipLibCheck": true, "allowJs": true,
"allowJs": true, "lib": ["ES6"],
"paths": { "baseUrl": ".",
"@/*": [ "paths": {
"./node_modules/@*" "@http/*": [
] "src/request/*"
} ]
}, }
"include": [ },
"src/**/*" "include": [
, "load.js" ], "src/**/*"
"exclude": [ ]
"node_modules/",
"test/",
"load.js"
]
} }