Playit.gg tunnel #491
@ -4,6 +4,35 @@ import { format } from "node:util";
|
|||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
import dgram from "node:dgram";
|
import dgram from "node:dgram";
|
||||||
import net from "node:net";
|
import net from "node:net";
|
||||||
|
import { writeFileSync } from "node:fs";
|
||||||
|
|
||||||
|
export type agentSecret = {type: "agent-secret", secret_key: string};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a key to asynchronously authenticate playit.gg clients
|
||||||
|
*/
|
||||||
|
export async function playitClaimUrl(clainUrlCallback?: (url: string) => void) {
|
||||||
|
const claimCode = crypto.pseudoRandomBytes(5).toString("hex");
|
||||||
|
const url = format("https://playit.gg/claim/%s?type=%s&name=%s", claimCode, "self-managed", `bdscore_agent`);
|
||||||
|
if (clainUrlCallback) clainUrlCallback(url); else console.log("Playit claim url: %s", url);
|
||||||
|
|
||||||
|
// Register to API
|
||||||
|
let waitAuth = 0;
|
||||||
|
let authAttempts = 0;
|
||||||
|
async function getSecret(): Promise<agentSecret> {
|
||||||
|
return httpRequest.getJSON({url: "https://api.playit.cloud/agent", method: "POST", headers: {}, body: {type: "exchange-claim-for-secret", claim_key: claimCode}}).catch(async (err: HTTPError) => {
|
||||||
|
if (err?.response?.statusCode === 404||err?.response?.statusCode === 401) {
|
||||||
|
if (err.response.statusCode === 404) if (authAttempts++ > 225) throw new Error("client not open auth url");
|
||||||
|
if (err.response.statusCode === 401) if (waitAuth++ > 16) throw new Error("Claim code not authorized per client");
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
return getSecret();
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await getSecret()).secret_key;
|
||||||
|
}
|
||||||
|
|
||||||
export type playitAgentAccountStatus = {type: "agent-account-status", status: "verified-account", account_id: number};
|
export type playitAgentAccountStatus = {type: "agent-account-status", status: "verified-account", account_id: number};
|
||||||
export type agentConfig = {
|
export type agentConfig = {
|
||||||
@ -35,47 +64,84 @@ export type playitTunnelAuth = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type playitTunnelOptions = {secretKey: string, apiUrl?: string, controlAddress?: string, clientVersion?: string};
|
export type playitTunnelOptions = {secretKey: string, apiUrl?: string, controlAddress?: string, clientVersion?: string};
|
||||||
|
export type tunnelList = {
|
||||||
|
type: "account-tunnels",
|
||||||
|
agent_id: string,
|
||||||
|
tunnels: {
|
||||||
|
id: string,
|
||||||
|
enabled: boolean,
|
||||||
|
name?: string,
|
||||||
|
ip_address: string,
|
||||||
|
ip_hostname: string,
|
||||||
|
custom_domain?: string,
|
||||||
|
assigned_domain: string,
|
||||||
|
display_address: string,
|
||||||
|
is_dedicated_ip: boolean,
|
||||||
|
from_port: number,
|
||||||
|
to_port: number,
|
||||||
|
tunnel_type: "minecraft-bedrock"|"minecraft-java",
|
||||||
|
port_type: "udp"|"tcp"|"both",
|
||||||
|
firewall_id?: string,
|
||||||
|
protocol: {
|
||||||
|
protocol: "to-agent",
|
||||||
|
local_ip: string,
|
||||||
|
local_port: number,
|
||||||
|
agent_id: number
|
||||||
|
}
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crie um tunnel para o minecraft bedrock ou minecraft java para que usuarios fora da rede possa se conectar no servidor
|
||||||
|
*/
|
||||||
export async function playitTunnel(options: playitTunnelOptions) {
|
export async function playitTunnel(options: playitTunnelOptions) {
|
||||||
options = {apiUrl: "api.playit.cloud", controlAddress: "control.playit.gg", clientVersion: "0.2.3", ...options};
|
options = {apiUrl: "api.playit.cloud", controlAddress: "control.playit.gg", clientVersion: "0.2.3", ...options};
|
||||||
if (!options.secretKey) throw new Error("Required secret key to auth in playit.gg");
|
if (!options.secretKey) throw new Error("Required secret key to auth in playit.gg");
|
||||||
const Authorization = format("agent-key %s", options.secretKey);
|
const Authorization = format("agent-key %s", options.secretKey);
|
||||||
const accountInfo = await httpRequest.getJSON<playitAgentAccountStatus>({url: format("https://%s/agent", options.apiUrl), method: "POST", headers: {Authorization}, body: {type: "get-agent-account-status", client_version: options.clientVersion}}).catch((err: HTTPError) => {
|
const playitApiUrls = {
|
||||||
|
agent: format("https://%s/agent", options.apiUrl),
|
||||||
|
account: format("https://%s/account", options.apiUrl)
|
||||||
|
};
|
||||||
|
const accountInfo = await httpRequest.getJSON<playitAgentAccountStatus>({url: playitApiUrls.agent, method: "POST", headers: {Authorization}, body: {type: "get-agent-account-status", client_version: options.clientVersion}}).catch((err: HTTPError) => {
|
||||||
if (err.response.statusCode === 400) throw new Error("Secret key is invalid or not registred");
|
if (err.response.statusCode === 400) throw new Error("Secret key is invalid or not registred");
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
if (accountInfo.status !== "verified-account") throw new Error("Verify account fist");
|
if (accountInfo.status !== "verified-account") throw new Error("Verify account fist");
|
||||||
const agentInfo = await httpRequest.getJSON<agentConfig>({url: format("https://%s/agent", options.apiUrl), method: "POST", headers: {Authorization}, body: {type: "get-agent-config", client_version: options.clientVersion}});
|
const agentInfo = await httpRequest.getJSON<agentConfig>({url: playitApiUrls.agent, method: "POST", headers: {Authorization}, body: {type: "get-agent-config", client_version: options.clientVersion}});
|
||||||
const signTunnel = await httpRequest.getJSON<playitTunnelAuth>({url: format("https://%s/agent", options.apiUrl), method: "POST", headers: {Authorization}, body: { type: "sign-tunnel-request", RegisterAgent: null }}).catch(err => err.response?.body?.toString()||err);
|
const signTunnel = await httpRequest.getJSON<playitTunnelAuth>({url: playitApiUrls.agent, method: "POST", headers: {Authorization}, body: { type: "sign-tunnel-request", RegisterAgent: null }}).catch(err => err.response?.body?.toString()||err);
|
||||||
|
|
||||||
return {agentInfo, signTunnel};
|
async function listTunnels() {
|
||||||
}
|
const data = await httpRequest.getJSON<tunnelList>({
|
||||||
|
url: playitApiUrls.account,
|
||||||
export type agentSecret = {type: "agent-secret", secret_key: string};
|
method: "POST",
|
||||||
|
headers: {Authorization},
|
||||||
/**
|
body: {
|
||||||
* Create a key to asynchronously authenticate playit.gg clients
|
type: "list-account-tunnels"
|
||||||
*/
|
|
||||||
export async function playitClaimUrl(clainUrlCallback?: (url: string) => void) {
|
|
||||||
const claimCode = crypto.pseudoRandomBytes(5).toString("hex");
|
|
||||||
const url = format("https://playit.gg/claim/%s?type=%s&name=%s", claimCode, "bdscore_agent", `bdscore_agent`);
|
|
||||||
if (clainUrlCallback) clainUrlCallback(url); else console.log("Playit claim url: %s", url);
|
|
||||||
|
|
||||||
// Register to API
|
|
||||||
let waitAuth = 0;
|
|
||||||
let authAttempts = 0;
|
|
||||||
async function getSecret(): Promise<agentSecret> {
|
|
||||||
return httpRequest.getJSON({url: "https://api.playit.cloud/agent", method: "POST", headers: {}, body: {type: "exchange-claim-for-secret", claim_key: claimCode}}).catch(async (err: HTTPError) => {
|
|
||||||
if (err?.response?.statusCode === 404||err?.response?.statusCode === 401) {
|
|
||||||
if (err.response.statusCode === 404) if (authAttempts++ > 225) throw new Error("client not open auth url");
|
|
||||||
if (err.response.statusCode === 401) if (waitAuth++ > 16) throw new Error("Claim code not authorized per client");
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 500));
|
|
||||||
return getSecret();
|
|
||||||
}
|
}
|
||||||
throw err;
|
}).catch(err => Promise.reject(JSON.parse(err.response?.body?.toString())));
|
||||||
});
|
|
||||||
|
return {...data};
|
||||||
}
|
}
|
||||||
|
|
||||||
return (await getSecret()).secret_key;
|
async function createTunnel(options: {tunnel_for: "bedrock"|"java", name: string, local_ip: string, local_port: number}) {
|
||||||
|
const agent_id = (await listTunnels()).agent_id;
|
||||||
|
const tunnelCreated = await httpRequest.getJSON<{ type: "created", id: string }>({
|
||||||
|
url: playitApiUrls.account,
|
||||||
|
method: "POST",
|
||||||
|
headers: {Authorization},
|
||||||
|
body: {
|
||||||
|
type: "create-tunnel", agent_id,
|
||||||
|
name: options.name,
|
||||||
|
tunnel_type: options.tunnel_for === "bedrock"?"minecraft-bedrock":"minecraft-java",
|
||||||
|
port_type: options.tunnel_for === "bedrock"?"udp":"tcp",
|
||||||
|
port_count: 1,
|
||||||
|
local_ip: options.local_ip, local_port: options.local_port,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return (await listTunnels()).tunnels.find(tunnel => tunnel.id === tunnelCreated.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {agentInfo, signTunnel, listTunnels, createTunnel};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type proxyUdpToTcpOptions = {
|
export type proxyUdpToTcpOptions = {
|
||||||
|
Reference in New Issue
Block a user