Skip to content

Commit

Permalink
Add exec command, Variable replacement, improve logging, env vars
Browse files Browse the repository at this point in the history
  • Loading branch information
fredsted committed Sep 22, 2023
1 parent 40a82e8 commit 59133dc
Show file tree
Hide file tree
Showing 9 changed files with 334 additions and 64 deletions.
37 changes: 37 additions & 0 deletions commands/exec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import listen from "./lib/listen.js";
import replaceVariables from "./lib/replace-variables.js";
import log from "./lib/log.js";
import child_process from "child_process";

export default async (argv) => {
if (!argv.token && !process.env.WH_TOKEN) {
throw new Error('Please specify a token (--token)')
}
if (!argv['api-key'] && !process.env.WH_API_KEY) {
throw new Error('Please specify an API Key (--api-key)')
}
if (!argv.command && !process.env.WH_COMMAND) {
throw new Error('Please specify a command (--command)')
}

listen(
argv.token ?? process.env.WH_TOKEN,
argv['api-key'] ?? process.env.WH_API_KEY,
(data) => {
const command = replaceVariables(argv.command ?? process.env.WH_COMMAND, data.runtime_variables)

child_process.exec(
command,
(error, stdout, stderr) => {
log.info({
'msg': 'Command was executed',
command,
error,
stderr,
stdout,
});
}
)
}
)
}
104 changes: 47 additions & 57 deletions commands/forward.js
Original file line number Diff line number Diff line change
@@ -1,72 +1,62 @@
import Echo from "laravel-echo";
import client from "socket.io-client";
import fetch from "node-fetch";
import listen from "./lib/listen.js";
import replaceVariables from "./lib/replace-variables.js";
import log from "./lib/log.js";

export default async (argv) => {
if (!argv.token) {
if (!argv.token && !process.env.WH_TOKEN) {
throw new Error('Please specify a token (--token)')
}
if (!argv['api-key']) {
if (!argv['api-key'] && !process.env.WH_API_KEY) {
throw new Error('Please specify an API Key (--api-key)')
}
if (!argv.target) {
if (!argv.target && !process.env.WH_TARGET) {
throw new Error('Please specify a target (--target)')
}

const tokenId = argv.token
const apiKey = argv['api-key']
const target = argv.target

const echo = new Echo.default({
host: argv.url ?? 'wss://ws.webhook.site',
broadcaster: 'socket.io',
client,
auth: {headers: {'Api-Key': apiKey}}
})

let channel = echo.private(`token.${tokenId}`);

channel.socket.on('error', (error) => {
console.trace(`Error: ${error}`)
})

channel.listen('.request.created', (data) => {
const query = data.request.query !== null
? '?' + new URLSearchParams(data.request.query).toString()
: '';

// We only want the `/a/b/c` part:
// https://webhook.site/00000000-0000-0000-00000-000000000000/a/b/c
const pathMatch = data.request.url.match(/https?:\/\/[^\/]*\/[a-z0-9-]+(\/[^?#]+)/)
const path = pathMatch ? pathMatch[1] : '';

let options = {
method: data.request.method,
headers: data.request.headers,
body: null,
};

const removeHeaders = [
'host',
'content-length'
]
listen(
argv.token ?? process.env.WH_TOKEN,
argv['api-key'] ?? process.env.WH_API_KEY,
(data) => {
const target = replaceVariables(argv.target ?? process.env.WH_TARGET, data.variables)
const query = data.request.query !== null
? '?' + new URLSearchParams(data.request.query).toString()
: '';

// We only want the `/a/b/c` part:
// https://webhook.site/00000000-0000-0000-00000-000000000000/a/b/c
const pathMatch = data.request.url.match(/https?:\/\/[^\/]*\/[a-z0-9-]+(\/[^?#]+)/)
const path = pathMatch ? pathMatch[1] : '';

let options = {
method: data.request.method,
headers: data.request.headers,
body: null,
};

const removeHeaders = [
'host',
'content-length'
]

for (let headerName of removeHeaders) {
if (headerName in options.headers) {
delete options.headers[headerName]
}
}

for (let headerName of removeHeaders) {
if (headerName in options.headers) {
delete options.headers[headerName]
if (data.request.method !== 'GET' && data.request.method !== 'HEAD') {
options['body'] = data.request.content
}
}

if (data.request.method !== 'GET' && data.request.method !== 'HEAD') {
options['body'] = data.request.content
fetch(target + path + query, options)
.then((res) => {
log.info({
msg: 'Forwarded incoming request',
url: res.url,
status: res.status
});
})
}

fetch(target + path + query, options)
.then((res) => {
console.log('Forwarded incoming request', {
url: res.url,
status: res.status
});
})
})
)
}
44 changes: 39 additions & 5 deletions commands/help.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,44 @@
import "colors";

export default () => {
console.log(`whcli 0.1
Commands:
help Outputs this list of commands.
forward Forward traffic from a Webhook.site endpoint.
console.log(`${'⚓ whcli: Webhook.site CLI'.bold}
Usage: whcli [command] [--arg...]
Documentation: https://docs.webhook.site/cli.html
${'Commands and Arguments'.bold}
${'help'.underline} Outputs this list of commands.
${'forward'.underline} Forward traffic from a Webhook.site URL.
--token= Specifies the Webhook.site token ID where
traffic should be redirected from. (required)
--api-key= A valid Webhook.site API Key (required)
--target=https://example.com Specifies the forwarding target (required)`)
--target=https://example.com?action=$request.query.action$
Specifies the forwarding target. Variables
are replaced; see below. (required)
${'exec'.underline} Execute a shell command on incoming requests to a Webhook.site URL.
--token= Specifies which Webhook.site URL (token ID)
to listen for requests from. (required)
--api-key= A valid Webhook.site API Key (required)
--command='ping $request.host$' Specifies the command to run. Variables
are replaced; see below. (required)
${'Variable Replacement'.bold}
For some commands and arguments, runtime variables can be replaced with the standard
Webhook.site Custom Actions syntax (e.g. $variable$). Global Variables are not replaced.
The variables that are available are the default base variables and any variables
defined in a Custom Action that was run during the request.
More info here: https://docs.webhook.site/custom-actions/variables.html
${'Environment Variables'.bold}
Some command arguments can be specified via environment variables:
--token WH_TOKEN
--api-key WH_API_KEY
--target WH_TARGET
--command WH_COMMAND
General environment variables:
WH_LOG_LEVEL Sets log level (silent, trace, debug, info, warn, error, fatal)
Defaults to info.
`)
}
26 changes: 26 additions & 0 deletions commands/lib/listen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Echo from "laravel-echo";
import client from "socket.io-client";
import logger from "./log.js";

export default (tokenId, apiKey, onRequest) => {
const echo = new Echo.default({
host: process.env.WH_WS_HOST ?? 'wss://ws.webhook.site',
broadcaster: 'socket.io',
client,
auth: {headers: {'Api-Key': apiKey}}
})

let channel = echo.private(`token.${tokenId}`);

channel.socket.on('connect', (error) => {
logger.trace('WS: Connected', { error })
})

channel.socket.on('error', (error) => {
logger.trace('WS: Error', { error })
})

channel.listen('.request.created', (data) => {
onRequest(data)
})
}
7 changes: 7 additions & 0 deletions commands/lib/log.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import pino from "pino";

const logger = pino({
level: process.env.WH_LOG_LEVEL ?? 'info',
})

export default logger
6 changes: 6 additions & 0 deletions commands/lib/replace-variables.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default (subject, variables) => {
for (const varName in variables) {
subject = subject.replace('$' + varName + '$', variables[varName]);
}
return subject
}
7 changes: 6 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@ import minimist from 'minimist'
// Commands
import help from './commands/help.js'
import forward from './commands/forward.js'
import exec from "./commands/exec.js";

const argv = minimist(process.argv.slice(2));
const command = argv['_'][0]
const command = argv['_'][0];

switch (command) {
case 'forward':
forward(argv)
break;

case 'exec':
exec(argv)
break;

default:
help()
}
Loading

0 comments on commit 59133dc

Please sign in to comment.