service-status-cli
Advanced tools
Comparing version 0.0.5 to 0.0.7
@@ -18,2 +18,3 @@ #! /usr/bin/env node | ||
.option("--list", "list the services available") | ||
.option("--all", "check all services") | ||
.addOption( | ||
@@ -20,0 +21,0 @@ new Option("-q, --quiet", "quiet mode") |
import process from "process"; | ||
import ora from "ora"; | ||
import { listServices, findService } from "./services.js"; | ||
import { | ||
listServices, | ||
findService, | ||
listServicesToConsole, | ||
} from "./services.js"; | ||
import { | ||
loggingLevels, | ||
@@ -10,9 +14,9 @@ exitCodes, | ||
} from "./constants.js"; | ||
import { request } from "http"; | ||
export async function main(requested_service, options) { | ||
let throbber = null; | ||
options.log.info(`User requested service: ${requested_service}`); | ||
if (options.list) { | ||
listServices(options); | ||
listServicesToConsole(options); | ||
process.exitCode = exitCodes.ok; | ||
@@ -22,3 +26,14 @@ return; | ||
if (!requested_service) { | ||
let requested_services = []; | ||
if (options.all) { | ||
options.log.info(`User requested all services.`); | ||
requested_services = listServices(options); | ||
} else { | ||
if (requested_service) { | ||
options.log.info(`User requested service: ${requested_service}`); | ||
requested_services = [requested_service]; | ||
} | ||
} | ||
if (!requested_services) { | ||
options.log.error( | ||
@@ -32,50 +47,56 @@ "No service specified, use --list to see available services" | ||
let service = null; | ||
service = findService(requested_service, options); | ||
for (let service_name of requested_services) { | ||
service = findService(service_name, options); | ||
if (options.log.level() === loggingLevels["warn"] && !options.web) { | ||
let msg = `${service.data.name}`; | ||
throbber = ora(msg, { spinner: "noise" }).start(); | ||
} | ||
if (options.log.level() === loggingLevels["warn"] && !options.web) { | ||
let msg = `${service.data.name}`; | ||
throbber = ora(msg, { spinner: "noise" }).start(); | ||
} | ||
if (options.web) { | ||
service.openWeb(); | ||
return; | ||
} | ||
if (options.web) { | ||
service.openWeb(); | ||
return; | ||
} | ||
try { | ||
await service.getStatus(); | ||
} catch (e) { | ||
try { | ||
await service.getStatus(); | ||
} catch (e) { | ||
if (throbber) { | ||
throbber.fail(); | ||
} | ||
options.log.error(e.message); | ||
process.exitCode = exitCodes.error; | ||
return; | ||
} | ||
options.log.info(`Got status: ${service.status}`); | ||
if (service.description) { | ||
options.log.info(`Got description: ${service.description}`); | ||
} | ||
if (throbber) { | ||
throbber.fail(); | ||
if (service.status === statusLevels.ok) { | ||
throbber.text = `${ | ||
service.data.name | ||
} 👉 ${service.status.toLowerCase()}`; | ||
throbber.succeed(); | ||
} else if ( | ||
[statusLevels.partial, statusLevels.maintenance].includes( | ||
service.status | ||
) | ||
) { | ||
// If we can find a meaningful description, show it. | ||
let desc = service.description ? `\n "${service.description}"` : ""; | ||
throbber.text = `${ | ||
service.data.name | ||
} 👉 ${service.status.toLowerCase()}${desc} see: ${service.data.web}`; | ||
throbber.warn(); | ||
} else { | ||
throbber.text = `${ | ||
service.data.name | ||
} 👉 ${service.status.toLowerCase()} see: ${service.data.web}`; | ||
throbber.fail(); | ||
} | ||
} | ||
options.log.error(e.message); | ||
process.exitCode = exitCodes.error; | ||
return; | ||
} | ||
options.log.info(`Got status: ${service.status}`); | ||
if (service.description) { | ||
options.log.info(`Got description: ${service.description}`); | ||
} | ||
if (throbber) { | ||
if (service.status === statusLevels.ok) { | ||
throbber.text = `${service.data.name} 👉 ${service.status.toLowerCase()}`; | ||
throbber.succeed(); | ||
} else if ( | ||
[statusLevels.partial, statusLevels.maintenance].includes(service.status) | ||
) { | ||
// If we can find a meaningful description, show it. | ||
let desc = service.description ? `\n "${service.description}"` : ""; | ||
throbber.text = `${ | ||
service.data.name | ||
} 👉 ${service.status.toLowerCase()}${desc} see: ${service.data.web}`; | ||
throbber.warn(); | ||
} else { | ||
throbber.text = `${ | ||
service.data.name | ||
} 👉 ${service.status.toLowerCase()} see: ${service.data.web}`; | ||
throbber.fail(); | ||
} | ||
} | ||
options.log.info(`Exit code: ${statusToExitCode[service.status]}`); | ||
process.exitCode = statusToExitCode[service.status]; | ||
} |
@@ -37,2 +37,19 @@ import open from "open"; | ||
async parseHTML(url) { | ||
url = url || this.getStatusURL(); | ||
this.options.log.info( | ||
`Getting HTML for service: ${this.data.name} from: ${url}` | ||
); | ||
return await fetch(url) | ||
.then((res) => { | ||
if (!res.ok) { | ||
throw new Error(`Error got status code: ${res.status}.`); | ||
} | ||
return res.text(); | ||
}) | ||
.catch((error) => { | ||
throw new Error(`Failed to fetch URL: ${url}, got ${error}`); | ||
}); | ||
} | ||
async getRSS(url) { | ||
@@ -102,5 +119,5 @@ url = url || this.getStatusURL(); | ||
async getStatus() { | ||
return await this.getJSON().then((data) => { | ||
this.status = statusLevels.partial; | ||
if (data.result["status_overall"].status == "Operational") { | ||
this.status = statusLevels.partial; | ||
return await this.parseHTML().then((data) => { | ||
if (data.includes("All Systems Operational")) { | ||
this.status = statusLevels.ok; | ||
@@ -130,2 +147,19 @@ } | ||
class IncidentIO extends Service { | ||
getStatusURL() { | ||
let host = new URL(this.data.web).hostname; | ||
return `${this.data.web}proxy/${host}/incidents`; | ||
} | ||
async getStatus() { | ||
this.status = statusLevels.partial; | ||
return await this.getJSON().then((data) => { | ||
if (data.incidents.every((x) => x.status == "resolved")) { | ||
this.status = statusLevels.ok; | ||
} | ||
}); | ||
} | ||
} | ||
const service_map = { | ||
@@ -137,2 +171,3 @@ atlassian: Atlassian, | ||
slack: Slack, | ||
"incident.io": IncidentIO | ||
}; | ||
@@ -161,7 +196,27 @@ | ||
const services = list(); | ||
const service_names = []; | ||
services.forEach((service) => { | ||
let data = get(service); | ||
if (service_map[data.host]) { | ||
service_names.push(service.replace(".json", "")); | ||
} else { | ||
options.log.info(`Ignoring service: ${service} which is not supported.`) | ||
} | ||
}); | ||
return service_names; | ||
} | ||
export function listServicesToConsole(options) { | ||
const services = list(); | ||
// Note user has asked for a list of services, so we'll set to warn so it outputs to stdout. | ||
options.log.warn("Available services:"); | ||
services.forEach((service) => { | ||
options.log.warn(`- ${service.replace(".json", "")}`); | ||
let data = get(service); | ||
if (service_map[data.host]) { | ||
options.log.warn(`- ${service.replace(".json", "")}`); | ||
} else { | ||
options.log.info(`Ignoring service: ${service} which is not supported.`) | ||
} | ||
}); | ||
return services; | ||
} |
@@ -56,1 +56,15 @@ import { findService } from "./services.js"; | ||
}); | ||
test("docker good #docker-good.html", () => { | ||
let service = findService("docker", { log: console }); | ||
service.getStatus().then(() => { | ||
expect(service.status).toBe(statusLevels.ok); | ||
}); | ||
}); | ||
test("planetscale good #planetscale-good.json", () => { | ||
let service = findService("planetscale", { log: console }); | ||
service.getStatus().then(() => { | ||
expect(service.status).toBe(statusLevels.ok); | ||
}); | ||
}); |
@@ -7,3 +7,3 @@ import { afterAll, beforeEach, afterEach, beforeAll } from "vitest"; | ||
function getExample(file) { | ||
return JSON.parse(fs.readFileSync(`./examples/${file}`)); | ||
return fs.readFileSync(`./examples/${file}`); | ||
} | ||
@@ -15,2 +15,4 @@ | ||
trello: "https://trello.status.atlassian.com/api/v2/status.json", | ||
planetscale: "https://www.planetscalestatus.com/proxy/www.planetscalestatus.com/incidents", | ||
docker: "https://www.dockerstatus.com/", | ||
}; | ||
@@ -35,6 +37,9 @@ | ||
rest.get(hosts[host], (req, res, ctx) => { | ||
console.log( | ||
`Call was intercepted to ${hosts[host]} and replaced with ${file}` | ||
); | ||
return res(ctx.status(200), ctx.json(getExample(file))); | ||
if (file.endsWith(".json")) { | ||
console.log(`Intercepted: ${hosts[host]} replaced with JSON: ${file}`); | ||
return res(ctx.status(200), ctx.json(JSON.parse(getExample(file)))); | ||
} else { | ||
console.log(`Intercepted: ${hosts[host]} replaced with Text: ${file}`); | ||
return res(ctx.status(200), ctx.text(getExample(file))); | ||
} | ||
}), | ||
@@ -41,0 +46,0 @@ ]; |
{ | ||
"name": "service-status-cli", | ||
"version": "0.0.5", | ||
"version": "0.0.7", | ||
"description": "", | ||
@@ -25,3 +25,3 @@ "exports": "./index.js", | ||
"rss-parser": "^3.12.0", | ||
"service-status-data": "^0.0.2" | ||
"service-status-data": "^0.0.3" | ||
}, | ||
@@ -28,0 +28,0 @@ "type": "module", |
A command line interface to query the status page of internet service providers and provide a uniform response. | ||
This is currently very much a work in progress and is not in a stable state. | ||
## Service data | ||
@@ -40,2 +38,3 @@ | ||
- `--web` opens the web page for the service in your browser. | ||
- `--all` returns the status for all services. | ||
- `-v` or `--verbose` get verbose logging, including URL to the endpoint used. | ||
@@ -42,0 +41,0 @@ - `-q` suppress all output, except errors. Exit codes are returned see below 👇 |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
202203
20
1447
78
4
+ Addedservice-status-data@0.0.3(transitive)
- Removedservice-status-data@0.0.2(transitive)
Updatedservice-status-data@^0.0.3