Verified Commit f79daa69 authored by Aral Balkan's avatar Aral Balkan
Browse files

Simplified commandline vocabulary

parents b1c0692c f60067fe
......@@ -6,13 +6,10 @@ Indie Web Server is a secure and seamless [Small Tech](https://ar.al/2019/03/04/
- Zero-configuration – It Just Works 🤞™.
- Develop and test with automatically-provisioned locally-trusted TLS thanks to [mkcert](https://github.com/FiloSottile/mkcert) via [Nodecert](https://source.ind.ie/hypha/tools/nodecert).
- Develop with automatically-provisioned locally-trusted TLS thanks to [mkcert](https://github.com/FiloSottile/mkcert) via [Nodecert](https://source.ind.ie/hypha/tools/nodecert).
- Stage and deploy with automatically-provisioned globally-trusted TLS thanks to [Let’s Encrypt](https://letsencrypt.org/) via [ACME TLS](https://source.ind.ie/hypha/tools/acme-tls) (with an A score on [SSL Labs SSL Server Test](https://www.ssllabs.com/ssltest)) and the seamlessly-integrated [pm2](https://pm2.io/runtime/) process manager.
- Test and deploy with automatically-provisioned globally-trusted TLS thanks to [Let’s Encrypt](https://letsencrypt.org/) via [ACME TLS](https://source.ind.ie/hypha/tools/acme-tls) (with an A score on [SSL Labs SSL Server Test](https://www.ssllabs.com/ssltest)) and the seamlessly-integrated [pm2](https://pm2.io/runtime/) process manager.
[Watch a short 4-minute video](https://ar.al/2019/03/14/introducing-indie-web-server-video/) demonstrating how easy Indie Web Server is to install and use.
(Note: the video is about two weeks old and so it does not demonstrate the new `--live` feature for seamless deployments or the native binary distributions. I will record a new one as soon as I find some time.)
## Install
......@@ -46,12 +43,12 @@ Start serving the current directory at https://localhost using locally-trusted c
$ web-server
```
### Staging
### Testing
Start serving the _site_ directory at your _hostname_ as a regular process using globally-trusted Let’s Encrypt certificates:
```shell
$ web-server --staging site
$ web-server test site
```
For example, use [ngrok](https://ngrok.com/) (Pro+) with a custom domain name that you set in your `hostname` file (e.g., in `/etc/hostname` or via `hostnamectl set-hostname <hostname>` or the equivalent for your platform). The first time you hit your staging server via your hosname it will take a little longer to load as your Let’s Encrypt certificates are being automatically provisioned by ACME TLS.
......@@ -61,21 +58,21 @@ For example, use [ngrok](https://ngrok.com/) (Pro+) with a custom domain name th
Start serving the _site_ directory at your _hostname_ as a daemon that is automatically run at system startup and restarted if it crashes:
```shell
$ web-server --live site
$ web-server on site
```
The `--live` option sets up your server to (re)start automatically when your server (re)starts and/or crashes, etc. Requires superuser privileges on first run to set up the launch item.
The `on` command sets up your server to (re)start automatically when your server (re)starts and/or crashes, etc. Requires superuser privileges on first run to set up the launch item.
For example, if you run the command on a connected server that has the ar.al domain pointing to it and `ar.al` set in _/etc/hostname_ (on Unix/Linux/macOS), you will be able to access the site at https://ar.al. The first time you hit it, it will take a little longer to load as your Let’s Encrypt certificates are being automatically provisioned by ACME TLS.
With a running live server, you can also use the following commands:
When the server is on, you can also use:
- `--monitor`: Monitor the server.
- `--logs`: Display and tail the server logs.
- `--info`: Display detailed information about the server.
- `--offline`: Take the server offline and remove it from startup items.
- `off`: Turn server off and remove it from startup items.
- `monitor`: Monitor server state.
- `logs`: Display and tail server logs.
- `info`: Display detailed server information.
The live server uses the [pm2](https://pm2.io/runtime/) process manager internally. Beyond the commands listed above that Indie Web Server supports natively (and proxies to pm2), you can make use of all pm2 functionality via the pm2 command directly should you need to.
Indie Web Server uses the [pm2](https://pm2.io/runtime/) process manager internally to start and manage the daemon. Beyond the commands listed above that Indie Web Server supports natively (and proxies to pm2), you can make use of all pm2 functionality via the pm2 command directly should you need to.
## Build and test from source
......@@ -111,40 +108,49 @@ npm run build
# Serve the test site (visit https://localhost to view).
# e.g., To run the version 7.2.0 Linux binary:
dist/linux/7.2.0/web-server test/site
dist-iws/linux/7.2.0/web-server test/site
```
## Syntax
```shell
web-server [folder-to-serve] [options]
web-server [command] [folder] [options]
```
* __folder-to-serve:__ Path to the folder to serve (defaults to current folder).
* `command`: version | help | dev | test | on | off | monitor | logs | info
* `folder`: Path of folder to serve (defaults to current folder).
* `options`: Settings that alter server characteristics.
### Options:
### Commands:
* `version`: Display version and exit.
* `help`: Display help screen and exit.
* __--port=N:__ The port to start the server on (defaults to 443).
* __--version:__ Display the version and exit.
* `dev`: Launch server as regular process with locally-trusted certificates.
* `test`: Launch server as regular process with globally-trusted certificates.
* `on`: Launch server as startup daemon with globally-trusted certificates.
* __--staging:__ Launch server as regular process with globally-trusted certificates.
When the server is on, you can also use:
* __--live:__ Launch server as startup daemon with globally-trusted certificates.
* `off`: Take server offline and remove it from startup items.
### With a running live server, you can also:
* `monitor`: Monitor server state.
* __--monitor:__ Monitor the server.
* `logs`: Display and tail server logs.
* __--logs:__ Display and tail the server logs.
* `info`: Display detailed server information.
* __--info:__ Display detailed information about the server.
If `command` is omitted, behaviour defaults to `dev`.
### Options:
* __--offline:__ Take the server offline and remove it from startup items.
* `--port=N`: Port to start the server on (defaults to 443).
All command-line arguments are optional. By default, Indie Web Server will serve your current working folder over port 443 with locally-trusted certificates.
All command-line arguments are optional. By default, Indie Web Server will serve your current working folder over port 433 with locally-trusted certificates.
If you want to serve a directory that has the same name as a command, you can speficy the command in _options_ format. e.g., `web-server --on logs` will start Indie Web Server as a startup daemon to serve the _logs_ folder.
If you launch with the `--staging` or `--global` options, globally-trusted Let’s Encrypt TLS certificates are automatically provisioned for you using ACME TLS the first time you hit your hostname. The hostname for the certificates is automatically set from the hostname of your system (and the _www._ subdomain is also automatically provisioned).
If you use the `test` or `on` commands, globally-trusted Let’s Encrypt TLS certificates are automatically provisioned for you using ACME TLS the first time you hit your hostname. The hostname for the certificates is automatically set from the hostname of your system (and the _www._ subdomain is also automatically provisioned).
## Native 404 → 302 support for an evergreen web
......
......@@ -56,43 +56,74 @@ if (!fs.existsSync(externalDirectory)) {
}
}
//
// Display usage/help.
//
if (arguments._.length > 2 || arguments.help === true) {
const usageFolderToServe = clr('folder-to-serve', 'green')
const usagePortOption = `${clr('--port', 'yellow')}=${clr('N', 'cyan')}`
const usageStagingOption = `${clr('--staging', 'yellow')}`
const usageLiveOption = `${clr('--live', 'yellow')}`
const usageMonitorOption = `${clr('--monitor', 'yellow')}`
const usageLogsOption = `${clr('--logs', 'yellow')}`
const usageInfoOption = `${clr('--info', 'yellow')}`
const usageOfflineOption = `${clr('--offline', 'yellow')}`
const usageVersionOption = `${clr('--version', 'yellow')}`
// Get the command
const positionalArguments = arguments._
const firstPositionalArgument = positionalArguments[0]
const secondPositionalArgument = positionalArguments[1]
const command = {
isHelp: (arguments.h || arguments.help || positionalArguments.length > 2 || firstPositionalArgument === 'help'),
isVersion: (arguments.version || arguments.v || firstPositionalArgument === 'version'),
isTest: (arguments.test || firstPositionalArgument === 'test'),
isOn: (arguments.on || firstPositionalArgument === 'on'),
isOff: (arguments.off || firstPositionalArgument === 'off'),
isMonitor: (arguments.monitor || firstPositionalArgument === 'monitor'),
isLogs: (arguments.logs || firstPositionalArgument === 'logs'),
isInfo: (arguments.info || firstPositionalArgument === 'info')
}
// If we didn’t match a command, we default to dev.
const didMatchCommand = Object.values(command).reduce((p,n) => p || n)
command.isDev = (arguments.dev || firstPositionalArgument === 'dev' || !didMatchCommand)
const firstPositionalArgumentDidMatchCommand = ['version', 'help', 'test', 'on', 'off', 'monitor', 'logs', 'info'].reduce((p, n) => p || (firstPositionalArgument === n), false)
// Help / usage instructions.
if (command.isHelp) {
const usageCommand = `${clr('command', 'green')}`
const usageFolderToServe = clr('folder', 'cyan')
const usageOptions = clr('options', 'yellow')
const usageVersion = `${clr('version', 'green')}`
const usageHelp = `${clr('help', 'green')}`
const usageDev = `${clr('dev', 'green')}`
const usageTest = `${clr('test', 'green')}`
const usageOn = `${clr('on', 'green')}`
const usageOff = `${clr('off', 'green')}`
const usageMonitor = `${clr('monitor', 'green')}`
const usageLogs = `${clr('logs', 'green')}`
const usageInfo = `${clr('info', 'green')}`
const usagePort = `${clr('--port', 'yellow')}=${clr('N', 'cyan')}`
const usage = `
${webServer.version()}
${clr('Usage:', 'underline')}
${clr('web-server', 'bold')} [${usageFolderToServe}] [${clr('options', 'yellow')}]
${clr('web-server', 'bold')} [${usageCommand}] [${usageFolderToServe}] [${usageOptions}]
${usageFolderToServe}\tPath to the folder to serve (defaults to current folder).
${usageCommand}\t${usageVersion} | ${usageHelp} | ${usageDev} | ${usageTest} | ${usageOn} | ${usageOff} | ${usageMonitor} | ${usageLogs} | ${usageInfo}
${usageFolderToServe}\tPath of folder to serve (defaults to current folder).
${usageOptions}\tSettings that alter server characteristics.
${clr('Options:', 'underline')}
${clr('Commands:', 'underline')}
${usagePortOption}\t\tThe port to start the server on (defaults to 443).
${usageVersionOption}\t\tDisplay the version and exit.
${usageVersion}\tDisplay version and exit.
${usageHelp}\t\tDisplay this help screen and exit.
${usageStagingOption}\t\tLaunch server as regular process with globally-trusted certificates.
${usageLiveOption}\t\tLaunch server as startup daemon with globally-trusted certificates.
${usageDev}\t\tLaunch server as regular process with locally-trusted certificates.
${usageTest}\t\tLaunch server as regular process with globally-trusted certificates.
${usageOn}\t\tLaunch server as startup daemon with globally-trusted certificates.
${clr('With a running live server, you can also:', 'underline')}
When server is on, you can also use:
${usageMonitorOption}\t\tMonitor the server.
${usageLogsOption}\t\tDisplay and tail the server logs.
${usageInfoOption}\t\tDisplay detailed information about the server.
${usageOfflineOption}\t\tTake the server offline and remove it from startup items.
${usageOff}\t\tTurn server off and remove it from startup items.
${usageMonitor}\tMonitor server state.
${usageLogs}\t\tDisplay and tail server logs.
${usageInfo}\t\tDisplay detailed server information.
If ${usageCommand} is omitted, behaviour defaults to ${usageDev}.
${clr('Options:', 'underline')}
${usagePort}\tPort to start server on (defaults to 443).
`.replace(/\n$/, '').replace(/^\n/, '')
console.log(usage)
......@@ -100,13 +131,13 @@ if (arguments._.length > 2 || arguments.help === true) {
}
// Version.
if (arguments.version !== undefined) {
if (command.isVersion) {
console.log(webServer.version())
process.exit()
}
// Monitor (pm2 proxy).
if (arguments.monitor !== undefined) {
if (command.isMonitor) {
// Launch pm2 monit.
const options = {
env: process.env,
......@@ -123,7 +154,7 @@ if (arguments.monitor !== undefined) {
}
// Logs (pm2 proxy).
if (arguments.logs !== undefined) {
if (command.isLogs) {
// Launch pm2 logs.
const options = {
env: process.env,
......@@ -140,7 +171,7 @@ if (arguments.logs !== undefined) {
}
// Info (pm2 proxy).
if (arguments.info !== undefined) {
if (command.isInfo) {
// Launch pm2 logs.
const options = {
env: process.env,
......@@ -157,7 +188,7 @@ if (arguments.info !== undefined) {
}
// Offline (pm2 proxy for unstartup + delete)
if (arguments.offline !== undefined) {
if (command.isOff) {
const options = {
env: process.env,
stdio: 'pipe' // Suppress output.
......@@ -214,11 +245,19 @@ if (arguments.offline !== undefined) {
success()
}
// If no path is passed, serve the current folder.
// If no path is passed, serve the current folder (i.e., called with just web-server)
// If there is a path, serve that.
let pathToServe = '.'
if (arguments._.length > 0) {
pathToServe = arguments._[0]
if ((command.isDev || command.isTest || command.isOn) && positionalArguments.length === 2) {
// e.g., web-server on path-to-serve
pathToServe = secondPositionalArgument
} else if (!firstPositionalArgumentDidMatchCommand && (command.isDev || command.isTest || command.isOn) && positionalArguments.length === 1) {
// e.g., web-server --on path-to-serve
pathToServe = firstPositionalArgument
} else if (command.isDev && positionalArguments.lenght === 1) {
// i.e., web-server path-to-serve
pathToServe = firstPositionalArgument
}
// If a port is specified, use it. Otherwise use the default port (443).
......@@ -227,9 +266,9 @@ if (arguments.port !== undefined) {
port = parseInt(arguments.port)
}
// If staging is specified, use it.
// If a test server is specified, use it.
let global = false
if (arguments.staging !== undefined) {
if (command.isTest) {
global = true
}
......@@ -238,9 +277,9 @@ if (!fs.existsSync(pathToServe)) {
process.exit(1)
}
// If live mode is specified, run as a daemon using the pm2 process manager.
// Otherwise, start it as a regular process.
if (arguments.live !== undefined) {
// If on is specified, run as a daemon using the pm2 process manager.
// Otherwise, start the server as a regular process.
if (command.isOn) {
pm2.connect((error) => {
if (error) {
......@@ -258,7 +297,7 @@ if (arguments.live !== undefined) {
throw error
}
console.log(`${webServer.version()}\n 😈 Launched as daemon on https://${os.hostname()}\n`)
console.log(`${webServer.version()}\n 😈 Launched as daemon on https://${os.hostname()} serving ${pathToServe}\n`)
//
// Run the script that tells the process manager to add the server to launch at startup
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment