Skip to content

Instantly share code, notes, and snippets.

@NWYLZW
Created October 21, 2022 07:01
Show Gist options
  • Save NWYLZW/95aaae0f65fae3ee5e89aa86b06a99c9 to your computer and use it in GitHub Desktop.
Save NWYLZW/95aaae0f65fae3ee5e89aa86b06a99c9 to your computer and use it in GitHub Desktop.
jms
import { JMS } from './jms'
const JMS_HOST = 'xxxxx'
const JMS_PORT = xxxx
const JMS_KEY_ID = 'xxxxxx'
const JMS_SECRET = 'xxxxxxxx'
;(async () => {
try {
const api = JMS.createApi({
HOST: JMS_HOST,
PORT: JMS_PORT,
KEY_ID: JMS_KEY_ID,
SECRET: JMS_SECRET
})
const { assets, user } = await api.getAssetsAndSysUser('xxxxxxxxxxx')
const { data } = await api.doCommand(assets, user, 'ls -l')
console.log(data)
} catch (e) {
console.error(e)
}
})()
import { RequestOptions } from 'http'
import http from 'node:http'
import httpSignature from 'http-signature'
export namespace JMS {
export interface Assets {
id: string
ip: string
hostname: string
}
export interface SysUser {
id: string
name: string
username: string
}
export interface Pagination<T> {
results?: T[]
}
export let questerDebug = {
v: false,
toogle: () => questerDebug.v = !questerDebug.v
}
const debug = (...args: any[]) => questerDebug.v && console.debug(...args)
export interface JMSOptions {
HOST: string
PORT: number
KEY_ID: string
SECRET: string
}
export const createApi = (options: JMSOptions) => {
const {
HOST,
PORT,
KEY_ID,
SECRET,
} = options
const quester = {
basePath: '/api/v1',
options: {
host: HOST,
port: PORT,
headers: {
'Accept': 'application/json',
'X-JMS-ORG': '00000000-0000-0000-0000-000000000002'
}
},
inner(options: RequestOptions, body?: string) {
return new Promise<string>((resolve, reject) => {
const req = http.request({
...quester.options,
...options,
path: quester.basePath + options.path,
headers: {
...quester.options.headers,
...options.headers,
...(body ? { 'Content-Length': Buffer.byteLength(body) } : {})
}
}, res => {
// @ts-ignore
debug(req._header, res.headers)
const chunks: Buffer[] = []
res.on('data', chunk => chunks.push(chunk))
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(Buffer.concat(chunks).toString())
} else {
reject(`[${res.statusCode}] "${Buffer.concat(chunks).toString()}"`)
}
})
})
req.on('error', error => {
reject(error)
})
httpSignature.sign(req, {
keyId: KEY_ID,
key: SECRET,
headers: ['(request-target)', 'accept', 'date'],
algorithm: 'hmac-sha256',
})
if (body) {
req.write(body)
}
req.end()
})
},
get(path: string, query?: Record<string, string>, options: Omit<RequestOptions, 'method'> = {}) {
return this.inner({
...options,
path: `${path}${query ? '?' + new URLSearchParams(query).toString() : ''}`,
method: 'GET',
headers: {
...options.headers,
...quester.options.headers,
}
})
},
post(path: string, body?: Record<string, any>, options: Omit<RequestOptions, 'method'> = {}) {
return this.inner({
...options,
path,
method: 'POST',
headers: {
...options.headers,
...quester.options.headers,
'Content-Type': 'application/json',
},
}, JSON.stringify(body))
}
}
const assets = {
basePath: '/perms/users/assets/',
get(keywords?: string) {
return quester.get(this.basePath, {
offset: '0',
limit: '15',
display: '1',
search: keywords,
}).then<Pagination<Assets>>(JSON.parse)
},
systemUsers(id: string) {
return quester.get(`${this.basePath}${id}/system-users/`).then<SysUser[]>(JSON.parse)
},
}
const ops = {
basePath: '/ops/',
execute(assetsIds: string[], sysUId: string, command: string) {
return quester.post(`${this.basePath}command-executions/`, {
hosts: assetsIds,
run_as: sysUId,
command,
}).then<{ log_url: string }>(JSON.parse)
}
}
async function getAssetsAndSysUser(keywords?: string) {
const { results: [ asset ] = [] } = await assets.get(keywords)
if (!asset)
throw new Error(`Not found assets: ${keywords}`)
const [ user ] = await assets.systemUsers(asset.id)
if (!user)
throw new Error(`Not found system user: ${asset.id}`)
return { assets: asset, user }
}
async function doCommand(assets: Assets, u: SysUser, command: string) {
const { log_url } = await ops.execute([assets.id], u.id, command)
debug(`http://${HOST}:${PORT}${log_url}`)
return quester.get(log_url.replace('/api/v1', '')).then<{
data: string
}>(JSON.parse)
}
return {
assets, ops,
getAssetsAndSysUser, doCommand
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment