diff --git a/.gitignore b/.gitignore index c9464d0..9b1c376 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,11 @@ -/node_modules -/dist -/docs -/test -/types - -*.cmd -*.sh -*.test.* - -package-lock.json +/node_modules +/dist +/docs +/test +/types + +*.cmd +*.sh +*.test.* + +package-lock.json diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 24e25f1..0000000 --- a/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "arrowParens": "avoid", - "bracketSpacing": true, - "singleQuote": true, - "tabWidth": 4 -} \ No newline at end of file diff --git a/README.md b/README.md index 71a148c..0d50bc1 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,68 @@ -

PteroJSLite

-

A lightweight alternative to PteroJS

-

- -## About - -PteroJSLite is a lightweight alternative to [PteroJS](https://github.com/PteroPackages/PteroJS) using only objects and functions - absolutely no classes! This package is ideal if you want a high-level package without the abstractions and helper methods of larger libraries, while still getting same amount of functionality. - -## Installing - -You must have NodeJS v14 or above to use this package. - -``` -npm install @devnote-dev/pterojslite -yarn add @devnote-dev/pterojslite -``` - -## Compatibility - -PteroJSLite supports version 1.7+ of the panel, and version 1.6+ of Wings. - -## Getting Started - -Application and Client API instances can be created using the `createApp` and `createClient` functions respectively. Both application keys (`ptla`) and client keys (`ptlc`) are currently supported. - -```js -const { createApp } = require('@devnote-dev/pterojslite'); - -const app = createApp('https://your.panel.domain', 'ptla_your_api_key'); - -app.getServers().then(console.log); - -(async () => { - const users = await app.getUsers(); // returns an array of user objects - console.log(users.filter(u => u.rootAdmin)); // filters out non-admin users -})(); -``` - -```js -const { createClient } = require('@devnote-dev/pterojslite'); - -const client = createClient('https://your.panel.domain', 'ptlc_your_api_key'); - -client.getAccount().then(console.log); - -(async () => { - const activities = await app.getActivities(); // returns an array of activity logs - console.log(activities.filter(a => a.isAPI)); // filters out non-API activities -})(); -``` - -## Contributing - -Please [create an issue](https://github.com/PteroPackages/PteroJSLite/issues) for issues or feature requests for the package. - -1. [Fork this repo](https://github.com/PteroPackages/PteroJSLite/fork)! -2. Make a branch from `main` (`git branch -b `) -3. Commit your changes (`git commit -am "..."`) -4. Open a PR here (`git push origin `) - -## Contributors - -- [Devonte](https://github.com/devnote-dev) - Owner, maintainer -- [Tlkh40](https://github.com/tlkh40) - Code contributor - -This repository is managed under the MIT license. - -© 2021-2022 PteroPackages +

PteroJSLite

+

A lightweight alternative to PteroJS

+

+ +## About + +PteroJSLite is a lightweight alternative to [PteroJS](https://github.com/PteroPackages/PteroJS) using only objects and functions - absolutely no classes! This package is ideal if you want a high-level package without the abstractions and helper methods of larger libraries, while still getting same amount of functionality. + +## Installing + +You must have NodeJS v14 or above to use this package. + +``` +npm install @devnote-dev/pterojslite +yarn add @devnote-dev/pterojslite +``` + +## Compatibility + +PteroJSLite supports version 1.7+ of the panel, and version 1.6+ of Wings. + +## Getting Started + +Application and Client API instances can be created using the `createApp` and `createClient` functions respectively. Both application keys (`ptla`) and client keys (`ptlc`) are currently supported. + +```js +const { createApp } = require('@devnote-dev/pterojslite'); + +const app = createApp('https://your.panel.domain', 'ptla_your_api_key'); + +app.getServers().then(console.log); + +(async () => { + const users = await app.getUsers(); // returns an array of user objects + console.log(users.filter(u => u.rootAdmin)); // filters out non-admin users +})(); +``` + +```js +const { createClient } = require('@devnote-dev/pterojslite'); + +const client = createClient('https://your.panel.domain', 'ptlc_your_api_key'); + +client.getAccount().then(console.log); + +(async () => { + const activities = await app.getActivities(); // returns an array of activity logs + console.log(activities.filter(a => a.isAPI)); // filters out non-API activities +})(); +``` + +## Contributing + +Please [create an issue](https://github.com/PteroPackages/PteroJSLite/issues) for issues or feature requests for the package. + +1. [Fork this repo](https://github.com/PteroPackages/PteroJSLite/fork)! +2. Make a branch from `main` (`git branch -b `) +3. Commit your changes (`git commit -am "..."`) +4. Open a PR here (`git push origin `) + +## Contributors + +- [Devonte](https://github.com/devnote-dev) - Owner, maintainer +- [Tlkh40](https://github.com/tlkh40) - Code contributor + +This repository is managed under the MIT license. + +© 2021-2022 PteroPackages diff --git a/package.json b/package.json index 448b954..ba92a91 100644 --- a/package.json +++ b/package.json @@ -1,73 +1,79 @@ -{ - "name": "@devnote-dev/pterojslite", - "version": "1.0.0", - "description": "A lightweight version of the PteroJS package", - "author": "Devonte W ", - "license": "MIT", - "bugs": { - "url": "https://github.com/PteroPackages/PteroJSLite/issues" - }, - "homepage": "https://github.com/PteroPackages/PteroJSLite#readme", - "repository": { - "type": "git", - "url": "git+https://github.com/PteroPackages/PteroJSLite.git" - }, - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "exports": { - "require": "./dist/index.js", - "import": "./dist/index.mjs", - "default": "./dist/index.mjs" - }, - "files": [ - "dist", - "CHANGELOG.md", - "LICENSE", - "README.md" - ], - "engines": { - "node": ">=14.16" - }, - "scripts": { - "prepublish": "tsup", - "build": "tsup", - "docs": "typedoc --out docs src/index.ts", - "format": "prettier --write src/**/**.ts" - }, - "tsup": { - "bundle": true, - "clean": true, - "dts": true, - "entryPoints": [ - "src/index.ts" - ], - "format": [ - "cjs", - "esm" - ], - "target": "esnext" - }, - "keywords": [ - "api", - "nodejs", - "pterojs", - "wrapper", - "javascript", - "typescript", - "pterojslite", - "pterodactyl", - "pterodactyl-api" - ], - "dependencies": { - "axios": "^0.27.2", - "ws": "^8.11.0" - }, - "devDependencies": { - "@types/node": "^18.6.4", - "@types/ws": "^8.5.3", - "prettier": "^2.7.1", - "tsup": "^6.3.0", - "typedoc": "^0.23.19", - "typescript": "^4.8.4" - } -} +{ + "name": "@devnote-dev/pterojslite", + "version": "1.0.0", + "description": "A lightweight version of the PteroJS package", + "author": "Devonte W ", + "license": "MIT", + "bugs": { + "url": "https://github.com/PteroPackages/PteroJSLite/issues" + }, + "homepage": "https://github.com/PteroPackages/PteroJSLite#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/PteroPackages/PteroJSLite.git" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + "require": "./dist/index.js", + "import": "./dist/index.mjs", + "default": "./dist/index.mjs" + }, + "files": [ + "dist", + "CHANGELOG.md", + "LICENSE", + "README.md" + ], + "engines": { + "node": ">=14.16" + }, + "scripts": { + "prepublish": "tsup", + "build": "tsup", + "docs": "typedoc --out docs src/index.ts", + "format": "prettier --write src/**/**.ts" + }, + "prettier": { + "arrowParens": "avoid", + "bracketSpacing": true, + "singleQuote": true, + "tabWidth": 4 + }, + "tsup": { + "bundle": true, + "clean": true, + "dts": true, + "entryPoints": [ + "src/index.ts" + ], + "format": [ + "cjs", + "esm" + ], + "target": "esnext" + }, + "keywords": [ + "api", + "nodejs", + "pterojs", + "wrapper", + "javascript", + "typescript", + "pterojslite", + "pterodactyl", + "pterodactyl-api" + ], + "dependencies": { + "axios": "^0.27.2", + "ws": "^8.11.0" + }, + "devDependencies": { + "@types/node": "^18.6.4", + "@types/ws": "^8.5.3", + "prettier": "^2.7.1", + "tsup": "^6.3.0", + "typedoc": "^0.23.19", + "typescript": "^4.8.4" + } +} diff --git a/src/ws.ts b/src/ws.ts index ea04933..c31e47b 100644 --- a/src/ws.ts +++ b/src/ws.ts @@ -1,108 +1,166 @@ -import { MessageEvent, WebSocket } from 'ws'; -import { PowerSignal, Resources, WebSocketAuth, WebSocketPayload } from './client/types'; -import { Auth } from './common'; -import conv from './conversions'; -import http from './http'; - -export interface IShard { - connect(): Promise; - _heartbeat(): Promise; - destroy(): void; - onDebug?: (data: any) => void; - onRaw?: (payload: WebSocketPayload) => void; - onError?: (error: Error) => void; - onReady?: () => void; - onBackupComplete?: (backup: any) => void; - onConsoleOutput?: (out: string[]) => void; - onDaemonOutput?: (out: string[]) => void; - onDaemonError?: (err: Error) => void; - onInstallStart?: () => void; - onInstallOutput?: (out: string[]) => void; - onInstallComplete?: () => void; - onJWTError?: (err: Error) => void; - onStatsUpdate?: (stats: Resources) => void; - onStatusUpdate?: (status: string) => void; - onTransferOutput?: (out: string[]) => void; - onTransferStatus?: (status: string) => void; - requestLogs(): void; - requestStats(): void; - sendCommand(command: string): void; - setPowerState(signal: PowerSignal): void; -} - -export type Shard = IShard & ThisType<{ auth: Auth, id: string, ws: WebSocket | null }>; - -export function createShard(id: string, auth: Auth): Shard { - const impl = { - async connect() { - const auth = await http.get<{ data: WebSocketAuth }>(`/api/client/servers/${this.id}/websocket`, this.auth); - this.ws = new WebSocket(auth.data.socket, { origin: this.auth.url }); - - this.ws.on('open', () => { - this.onDebug?.(`authenticating shard ${this.id}`); - this.ws.send(JSON.stringify({ event: 'auth', args:[auth.data.token] })); - }); - - this.ws.on('message', (m: MessageEvent) => { - const packet = JSON.parse(m.toString()) as WebSocketPayload; - this.onRaw?.(packet); - - switch (packet.event) { - case 'auth success': this.onReady?.(); break; - case 'token expiring': this._heartbeat(); break; - case 'token expired': this.disconnect(); break; - case 'backup completed': this.onBackupComplete?.(JSON.parse(packet.args.join())); break; - case 'console output': this.onConsoleOutput?.(packet.args); break; - case 'daemon message': this.onDaemonOutput?.(packet.args); break; - case 'daemon error': this.onDaemonError?.(new Error(packet.args.join())); break; - case 'install started': this.onInstallStart?.(); break; - case 'install output': this.onInstallOutput?.(packet.args); break; - case 'install completed': this.onInstallComplete?.(); break; - case 'jwt error': this.onJWTError?.(new Error(packet.args.join())); break; - case 'stats': this.onStatsUpdate?.(conv.toCamelCase(JSON.parse(packet.args.join()))); break; - case 'status': this.onStatusUpdate?.(packet.args.join()); break; - case 'transfer logs': this.onTransferOutput?.(packet.args); break; - case 'transfer status': this.onTransferStatus?.(packet.args[0]); break; - default: this.onError?.(`received unknown event '${packet.event}'`); break; - } - }); - - this.ws.on('error', (e: Error) => this.onError?.(e)); - this.ws.on('close', () => this.ws = null); - }, - - async _heartbeat() { - this.onDebug?.(`reauthenticating shard ${this.id}`); - const auth = await http.get<{ data: WebSocketAuth }>(`/api/client/servers/${this.id}/websocket`, this.auth); - this.ws?.send(JSON.stringify({ event: 'auth', args:[auth.data.token] })); - }, - - destroy() { - this.ws?.close(1000); - this.ws = null; - }, - - requestLogs() { - this.ws?.send(JSON.stringify({ event: 'send logs', args:[] })); - }, - - requestStats() { - this.ws?.send(JSON.stringify({ event: 'send stats', args:[] })); - }, - - sendCommand(command) { - this.ws?.send(JSON.stringify({ event: 'send command', args:[command] })); - }, - - setPowerState(signal) { - this.ws?.send(JSON.stringify({ event: 'set state', args:[signal] })); - }, - }; - - return { - auth, - id, - ws: null, - ...impl - }; -} \ No newline at end of file +import { MessageEvent, WebSocket } from 'ws'; +import { + PowerSignal, + Resources, + WebSocketAuth, + WebSocketPayload, +} from './client/types'; +import { Auth } from './common'; +import conv from './conversions'; +import http from './http'; + +export interface IShard { + connect(): Promise; + _heartbeat(): Promise; + destroy(): void; + onDebug?: (data: any) => void; + onRaw?: (payload: WebSocketPayload) => void; + onError?: (error: Error) => void; + onReady?: () => void; + onBackupComplete?: (backup: any) => void; + onConsoleOutput?: (out: string[]) => void; + onDaemonOutput?: (out: string[]) => void; + onDaemonError?: (err: Error) => void; + onInstallStart?: () => void; + onInstallOutput?: (out: string[]) => void; + onInstallComplete?: () => void; + onJWTError?: (err: Error) => void; + onStatsUpdate?: (stats: Resources) => void; + onStatusUpdate?: (status: string) => void; + onTransferOutput?: (out: string[]) => void; + onTransferStatus?: (status: string) => void; + requestLogs(): void; + requestStats(): void; + sendCommand(command: string): void; + setPowerState(signal: PowerSignal): void; +} + +export type Shard = IShard & + ThisType<{ auth: Auth; id: string; ws: WebSocket | null }>; + +export function createShard(id: string, auth: Auth): Shard { + const impl = { + async connect() { + const auth = await http.get<{ data: WebSocketAuth }>( + `/api/client/servers/${this.id}/websocket`, + this.auth + ); + this.ws = new WebSocket(auth.data.socket, { + origin: this.auth.url, + }); + + this.ws.on('open', () => { + this.onDebug?.(`authenticating shard ${this.id}`); + this.ws.send( + JSON.stringify({ event: 'auth', args: [auth.data.token] }) + ); + }); + + this.ws.on('message', (m: MessageEvent) => { + const packet = JSON.parse(m.toString()) as WebSocketPayload; + this.onRaw?.(packet); + + switch (packet.event) { + case 'auth success': + this.onReady?.(); + break; + case 'token expiring': + this._heartbeat(); + break; + case 'token expired': + this.disconnect(); + break; + case 'backup completed': + this.onBackupComplete?.(JSON.parse(packet.args.join())); + break; + case 'console output': + this.onConsoleOutput?.(packet.args); + break; + case 'daemon message': + this.onDaemonOutput?.(packet.args); + break; + case 'daemon error': + this.onDaemonError?.(new Error(packet.args.join())); + break; + case 'install started': + this.onInstallStart?.(); + break; + case 'install output': + this.onInstallOutput?.(packet.args); + break; + case 'install completed': + this.onInstallComplete?.(); + break; + case 'jwt error': + this.onJWTError?.(new Error(packet.args.join())); + break; + case 'stats': + this.onStatsUpdate?.( + conv.toCamelCase(JSON.parse(packet.args.join())) + ); + break; + case 'status': + this.onStatusUpdate?.(packet.args.join()); + break; + case 'transfer logs': + this.onTransferOutput?.(packet.args); + break; + case 'transfer status': + this.onTransferStatus?.(packet.args[0]); + break; + default: + this.onError?.( + `received unknown event '${packet.event}'` + ); + break; + } + }); + + this.ws.on('error', (e: Error) => this.onError?.(e)); + this.ws.on('close', () => (this.ws = null)); + }, + + async _heartbeat() { + this.onDebug?.(`reauthenticating shard ${this.id}`); + const auth = await http.get<{ data: WebSocketAuth }>( + `/api/client/servers/${this.id}/websocket`, + this.auth + ); + this.ws?.send( + JSON.stringify({ event: 'auth', args: [auth.data.token] }) + ); + }, + + destroy() { + this.ws?.close(1000); + this.ws = null; + }, + + requestLogs() { + this.ws?.send(JSON.stringify({ event: 'send logs', args: [] })); + }, + + requestStats() { + this.ws?.send(JSON.stringify({ event: 'send stats', args: [] })); + }, + + sendCommand(command) { + this.ws?.send( + JSON.stringify({ event: 'send command', args: [command] }) + ); + }, + + setPowerState(signal) { + this.ws?.send( + JSON.stringify({ event: 'set state', args: [signal] }) + ); + }, + }; + + return { + auth, + id, + ws: null, + ...impl, + }; +} diff --git a/tsconfig.json b/tsconfig.json index c3e5608..f9d9e9b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,16 @@ -{ - "compilerOptions": { - "lib": ["ES2021"], - "outDir": "./dist", - "target": "ES2021", - "declaration": true, - "module": "CommonJS", - "noImplicitAny": true, - "removeComments": false, - "strictNullChecks": true, - "resolveJsonModule": true, - "moduleResolution": "Node", - "declarationDir": "./dist/src/types" - }, - "include": ["./src/**.ts"] +{ + "compilerOptions": { + "lib": ["ES2021"], + "outDir": "./dist", + "target": "ES2021", + "declaration": true, + "module": "CommonJS", + "noImplicitAny": true, + "removeComments": false, + "strictNullChecks": true, + "resolveJsonModule": true, + "moduleResolution": "Node", + "declarationDir": "./dist/src/types" + }, + "include": ["./src/**.ts"] } \ No newline at end of file