Sync with master

This commit is contained in:
Bryan Ashby
2025-02-23 11:21:12 -07:00
19 changed files with 909 additions and 495 deletions

12
.gitignore vendored
View File

@@ -1,7 +1,7 @@
# Don't check in SSH keys!
*.pem
# Various directories
# Exclude User-Customized Directories
config/
db/
drop/
@@ -11,5 +11,13 @@ mail/
node_modules/
docs/_site/
docs/.sass-cache/
docs/.jekyll-cache/
# Ignore Web Assets not included with enigma-bbs
www/*
www/assets/*
!www/otp_register_template.html
!www/reset_password.template.html
# Runtime Environment
.venv

View File

@@ -8,7 +8,7 @@ ENiGMA½ is a modern BBS software with a nostalgic flair!
Below are just some of the features ENiGMA½ supports out of the box:
* **Multi platform** — Anywhere [Node.js](https://nodejs.org/) runs likely works (known to work under Linux, FreeBSD, OpenBSD, OS X and Windows)
* Unlimited multi node support (for all those BBS "callers"!)
* **Highly customizable** via [HJSON](http://hjson.org/) based configuration, menus, and themes in addition to JavaScript based [mods](./docs/_docs/modding/existing-mods.md)
* **Highly customizable** via [HJSON](https://hjson.github.io/) based configuration, menus, and themes in addition to JavaScript based [mods](./docs/_docs/modding/existing-mods.md)
* [MCI support](./docs/_docs/art/mci.md) for lightbars, toggles, input areas, and so on plus many other other bells and whistles
* Telnet, **SSH**, and both secure and non-secure [WebSocket](https://en.wikipedia.org/wiki/WebSocket) access built in! Additional servers are easy to implement
* [CP437](http://www.ascii-codes.com/) and UTF-8 output
@@ -87,31 +87,9 @@ ENiGMA has been tested with many terminals. However, the following are suggested
* Alpha for the [FTN-style configuration guide](https://medium.com/@alpha_11845/setting-up-ftn-style-message-networks-with-enigma%C2%BD-bbs-709b22a1ae0d)!
* Huge shout out to [cognitivegears ](https://github.com/cognitivegears) for the various fixes, improvements, and **removing the need for cursor position reports** providing a much better terminal experience!
...and so many others! This project would be nothing without the BBS and artscene communities!
...and so many others! This project would be nothing without the BBS and art scene communities!
[![Star History Chart](https://api.star-history.com/svg?repos=nuskooler/enigma-bbs&type=Date)](https://star-history.com/#nuskooler/enigma-bbs&Date)
## License
Released under the [BSD 2-clause](https://opensource.org/licenses/BSD-2-Clause) license:
Copyright (c) 2015-2024, Bryan D. Ashby
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Released under [BSD 2-clause](https://opensource.org/licenses/BSD-2-Clause). See [LICENSE.TXT](LICENSE.TXT)

93
autoexec.sh Executable file
View File

@@ -0,0 +1,93 @@
#!/usr/bin/env bash
# Environment
ENIGMA_INSTALL_DIR=${ENIGMA_INSTALL_DIR:=$HOME/enigma-bbs}
AUTOEXEC_LOGFILE="$ENIGMA_INSTALL_DIR/logs/autoexec.log"
TIME_FORMAT=`date "+%Y-%m-%d %H:%M:%S"`
# Mise en place
export PATH="$HOME/.local/bin:$PATH"
export PATH="$HOME/.local/share/mise/shims:$PATH"
export PATH="$HOME/.local/share/mise/installs/python/latest/bin:$PATH"
# Environment Versions
ENIGMA_NODE_VERSION=${ENIGMA_NODE_VERSION:=$(toml get --toml-path=$ENIGMA_INSTALL_DIR/mise.toml tools.node)}
ENIGMA_PYTHON_VERSION=${ENIGMA_PYTHON_VERSION:=$(toml get --toml-path=$ENIGMA_INSTALL_DIR/mise.toml tools.python)}
# Validate Environment
DEPENDENCIES_VALIDATED=1
# Shared Functions
log() {
echo "${TIME_FORMAT} " "$*" >> $AUTOEXEC_LOGFILE
}
# If this is a first run, the log path will not yet exist and must be created
if [ ! -d "$ENIGMA_INSTALL_DIR/logs" ]
then
mkdir -p $ENIGMA_INSTALL_DIR/logs
fi
log "START:"
log "- PATH: $PATH"
log "- CURRENT DIR: ${PWD##}"
if ! command -v "mise" 2>&1 >/dev/null
then
log "mise is not in your PATH"
log "ERROR END"
exit 1
fi
if ! command -v "node" 2>&1 >/dev/null
then
log "Node environment is not in your PATH"
log "ERROR END"
exit 1
else
NODE_VERSION=$(node --version | tee /dev/null)
log "- NODE VERSION: $NODE_VERSION"
if [[ $NODE_VERSION != "v$ENIGMA_NODE_VERSION."* ]]; then
log "Node version found in your PATH is $NODE_VERSION, was expecting v$ENIGMA_NODE_VERSION.*; you may encounter compatibility issues"
DEPENDENCIES_VALIDATED=0
fi
fi
if ! command -v "python" 2>&1 >/dev/null
then
log "Python environment is not in your PATH"
log "ERROR END"
exit 1
else
PYTHON_VERSION=$(python --version | tee /dev/null)
log "- PYTHON VERSION: $PYTHON_VERSION"
if [[ $PYTHON_VERSION != "Python $ENIGMA_PYTHON_VERSION"* ]]; then
log "Python version found in your PATH is $PYTHON_VERSION, was expecting Python $ENIGMA_PYTHON_VERSION.*; you may encounter compatibility issues"
DEPENDENCIES_VALIDATED=0
fi
fi
# Validate whether we are good to Start
if [ "$DEPENDENCIES_VALIDATED" == "0" ]; then
if [ -v ENIGMA_IGNORE_DEPENDENCIES ] && [ "${ENIGMA_IGNORE_DEPENDENCIES}" == "1" ]; then
log "ENIGMA_IGNORE_DEPENDENCIES=1 detected, starting up..."
else
log "NOTE: Please re-run with 'ENIGMA_IGNORE_DEPENDENCIES=1 /path/to/autoexec.sh' to force startup"
log "ERROR END"
exit 1
fi
fi
# Start BBS
log "Starting ENiGMA½"
~/enigma-bbs/main.js
result=$?
# Determine whether a Startup Crash Occurred
# if [ $result -eq 0 ]; then
# # TODO: Notify via SMS / Email of Startup Failure
# fi
log "ENiGMA½ exited with $result"
log "END"
exit $result

View File

@@ -85,23 +85,18 @@ function main() {
// then it's a fatal error
//
if (err) {
errorDisplayed = true;
console.error(`Configuration error: ${err.message}`); // eslint-disable-line no-console
if ('ENOENT' === err.code) {
if (configPathSupplied) {
console.error(
'Configuration file does not exist: ' + configFile
);
} else {
configPathSupplied = null; // make non-fatal; we'll go with defaults
}
} else {
errorDisplayed = true;
console.error(`Configuration error: ${err.message}`); // eslint-disable-line no-console
if (err.hint) {
console.error(`Hint: ${err.hint}`);
}
if (err.configPath) {
console.error(`Note: ${err.configPath}`);
}
console.error("\nConfiguration file does not exist: '{configFile}'\n\nIf this is a new installation please run './oputil.js config new' from the enigma-bbs directory");
}
if (err.hint) {
console.error(`Hint: ${err.hint}`);
}
if (err.configPath) {
console.error(`Note: ${err.configPath}`);
}
}
return callback(err);
@@ -134,7 +129,7 @@ function main() {
if (err && !errorDisplayed) {
console.error('Error initializing: ' + util.inspect(err));
return process.exit();
return process.exit(1);
}
}
);

View File

@@ -25,8 +25,8 @@ exports.getModule = class GoldmineModule extends MenuModule {
this.setConfigWithExtraArgs(options);
// http://goldminebbs.com/
this.config.host = this.config.host || '165.232.153.209';
this.config.rloginPort = this.config.rloginPort || 513;
this.config.host = this.config.host || 'goldminedoors.com';
this.config.rloginPort = this.config.rloginPort || 2513;
}
initSequence() {

View File

@@ -297,6 +297,14 @@ function catCurrentConfig() {
keepWsc: false === argv.comments ? false : true,
});
if (argv.meow) {
console.info(
` /\\_/\\
( o.o )
> ^ < ... mrow...`);
return;
}
console.log(hjson.stringify(config, hjsonOpts));
} catch (e) {
if ('ENOENT' == e.code) {

View File

@@ -20,6 +20,7 @@ Commands:
config Configuration management
fb File base management
mb Message base management
ssh SSH key management
`,
User: `usage: oputil.js user <action> [<arguments>]
@@ -101,7 +102,7 @@ Actions:
cat Write current configuration to stdout
cat arguments:
--no-color Disable color
--no-colors Disable color
--no-comments Strip any comments
`,
FileBase: `usage: oputil.js fb <action> [<arguments>]
@@ -219,6 +220,11 @@ qwk-export arguments:
TIMESTAMP.
--no-qwke Disable QWKE extensions.
--no-synchronet Disable Synchronet style extensions.
`,
SSH: `usage: oputil.js ssh <action>
Actions:
create Create new SSH Keys
`,
});

View File

@@ -10,6 +10,7 @@ const handleFileBaseCommand = require('./oputil_file_base.js').handleFileBaseCom
const handleMessageBaseCommand =
require('./oputil_message_base.js').handleMessageBaseCommand;
const handleConfigCommand = require('./oputil_config.js').handleConfigCommand;
const handleSSHKeyCommand = require('./oputil_ssh_key.js').handleSSHKeyCommand;
const getHelpFor = require('./oputil_help.js').getHelpFor;
module.exports = function () {
@@ -32,6 +33,8 @@ module.exports = function () {
return handleFileBaseCommand();
case 'mb':
return handleMessageBaseCommand();
case 'ssh':
return handleSSHKeyCommand();
default:
return printUsageAndSetExitCode(getHelpFor('General'), ExitCodes.BAD_COMMAND);
}

View File

@@ -0,0 +1,137 @@
/* jslint node: true */
/* eslint-disable no-console */
'use strict';
// ENiGMA½
const initConfigAndDatabases = require('./oputil_common.js').initConfigAndDatabases;
const {
printUsageAndSetExitCode,
argv,
ExitCodes,
getAnswers,
} = require('./oputil_common.js');
const getHelpFor = require('./oputil_help.js').getHelpFor;
// deps
const async = require('async');
const fs = require('fs-extra');
const exec = require('child_process').exec;
const inq = require('inquirer');
const _ = require('lodash');
exports.handleSSHKeyCommand = handleSSHKeyCommand;
const ConfigIncludeKeys = [
'loginServers.ssh',
'loginServers.ssh.privateKeyPem',
];
const MINIMUM_PASSWORD_LENGTH = 8;
const QUESTIONS = {
Create: [
{
name: 'createNew',
message: 'Generate New SSH Keys?',
type: 'confirm',
default: false,
},
{
name: 'password',
message: 'SSL Password:',
default: "",
when: answers => answers.createNew,
},
],
};
function execute(ui, command) {
exec(
command,
function (error, stdout, stderr) {
ui.log.write(error);
if (error) {
const reason = error ? error.message : 'OpenSSL Error';
logDebug(
{
reason: reason,
cmd: util.cmd,
args: args
},
`openssl command failed`
);
}
else {
ui.log.write("SSH Keys Generated")
}
}
);
}
function createNew(cb) {
const ui = new inq.ui.BottomBar();
async.waterfall(
[
function init(callback) {
return initConfigAndDatabases(callback);
},
function create(configuration, callback) {
getAnswers(QUESTIONS.Create, answers => {
if (!answers.createNew) {
return callback('exit');
}
// Get Answer Value
const sslPassword = answers.password.trim();
if (!sslPassword || sslPassword == "") {
ui.log.write('Password must be set.');
return callback('exit');
}
if (sslPassword.length < MINIMUM_PASSWORD_LENGTH) {
ui.log.write(`Password must be at least ${MINIMUM_PASSWORD_LENGTH} characters.`);
return callback('exit');
}
// Check if Keyfiles Exist
const sshKeyPath = "config/security/";
const sshKeyFilename = "ssh_private_key.pem";
const targetKeyFile = sshKeyPath + sshKeyFilename;
ui.log.write(`Creating SSH Key: ${targetKeyFile}`);
// Create Dir
ui.log.write(`Creating Directory: ${sshKeyPath}`);
fs.ensureDirSync(sshKeyPath);
// Create SSH Keys
const command = `openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 | openssl rsa -out ./${targetKeyFile} -aes128 -traditional -passout pass:`;
execute(ui, `${command}${sslPassword}`);
});
},
],
err => {
return cb(err, configPath, config);
}
);
}
function handleSSHKeyCommand() {
if (true === argv.help) {
return printUsageAndSetExitCode(getHelpFor('SSH'), ExitCodes.ERROR);
}
const action = argv._[1];
switch (action) {
case 'create':
return createNew();
default:
return printUsageAndSetExitCode(getHelpFor('SSH'), ExitCodes.ERROR);
}
}

View File

@@ -71,6 +71,25 @@ class WebSocketClient extends TelnetClient {
})(ws);
super(wsDuplex);
Log.trace({ headers: req.headers }, 'WebSocket connection headers');
//
// If the config allows it, look for 'x-forwarded-proto' as "https"
// to override |isSecure|
//
if (
true === _.get(Config(), 'loginServers.webSocket.proxied') &&
'https' === req.headers['x-forwarded-proto']
) {
Log.debug(
`Assuming secure connection due to X-Forwarded-Proto of "${req.headers['x-forwarded-proto']}"`
);
this.proxied = true;
} else {
this.proxied = false;
}
wsDuplex.setClient(this, req);
// fudge remoteAddress on socket, which is now TelnetSocket
@@ -91,24 +110,6 @@ class WebSocketClient extends TelnetClient {
ws.isConnectionAlive = true;
});
Log.trace({ headers: req.headers }, 'WebSocket connection headers');
//
// If the config allows it, look for 'x-forwarded-proto' as "https"
// to override |isSecure|
//
if (
true === _.get(Config(), 'loginServers.webSocket.proxied') &&
'https' === req.headers['x-forwarded-proto']
) {
Log.debug(
`Assuming secure connection due to X-Forwarded-Proto of "${req.headers['x-forwarded-proto']}"`
);
this.proxied = true;
} else {
this.proxied = false;
}
// start handshake process
this.banner();
}

View File

@@ -77,18 +77,18 @@ GEM
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0)
minitest (5.19.0)
nokogiri (1.15.4-aarch64-linux)
nokogiri (1.16.5-aarch64-linux)
racc (~> 1.4)
nokogiri (1.15.4-x86_64-linux)
nokogiri (1.16.5-x86_64-linux)
racc (~> 1.4)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (5.0.3)
racc (1.7.1)
racc (1.8.1)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.6)
rexml (3.3.9)
rouge (3.30.0)
safe_yaml (1.0.5)
sassc (2.4.0)

View File

@@ -78,22 +78,24 @@ webserver, and unpack it to a temporary directory.
````javascript
var vtxdata = {
sysName: "Your Awesome BBS",
wsConnect: "wss://your-hostname.here:8811",
term: "ansi-bbs",
codePage: "CP437",
fontName: "UVGA16",
fontSize: "24px",
crtCols: 80,
crtRows: 25,
crtHistory: 500,
xScale: 1,
initStr: "",
defPageAttr: 0x1010,
defCrsrAttr: ['thick', 'horizontal'],
defCellAttr: 0x0007,
telnet: 1,
autoConnect: 0
sysName: "Your Awesome BBS",
wsConnect: "wss://your-hostname.here:8811",
term: "ansi-bbs",
codePage: "CP437",
fontName: "UVGA16",
fontSize: "24px",
crtCols: 80,
crtRows: 25,
crtHistory: 500,
xScale: 1,
initStr: "",
defPageAttr: 0x1010,
defCrsrAttr: ['thick', 'horizontal'],
defCellAttr: 0x0007,
telnet: 1,
autoConnect: 0,
wsProtocol: 'telnet',
wsDataType: 'arraybuffer',
};
````

View File

@@ -20,7 +20,25 @@ Several things can cause this:
1. `ssh_private_key.pem` was installed to the wrong location. Make sure that it is in the `config/security` directory and has the name matching the error message. You can also change your `config.hjson` if you prefer to point to the location of the key file.
2. `ssh_private_key.pem` has the wrong file permissions. Verify that the file will be readable by the user that the BBS is running as. Because it is a cryptographic key however, we do recommend that access is restricted only to that user.
## Error With Netrunner
## Cannot parse privateKey: Unsupported key format
***Symptom:***
BBS not starting with an error similar to the following:
```shell
Error initializing: Error: Cannot parse privateKey: Unsupported key format
```
***Solution:***
Depending on the OpenSSL version, a specific key format is used to generate the private key. The error above is observed on Debian 12 (Bookworm), but might present itself on other Debian 12 derivatives.
Convert the unsupported key to a supported one:
```shell
openssl rsa -in unsupported.key -out converted.key -traditional
```
## Errors With Netrunner
***Symptom:***
Some ssh clients connect, but Netrunner (and other older clients) get a connection failed message and the following is in the log:

View File

@@ -157,7 +157,15 @@
//
// 3 - Finally, set 'enabled' to 'true'
//
// ! - If you receive the following error when starting ENiGMA:
// "Error initializing: Error: Cannot parse privateKey: Unsupported key format"
// You might need to convert your key. This can be done as follows:
//
// > openssl rsa -in unsupported.key -out converted.key -traditional
//
//
// Additional reading:
// - https://nuskooler.github.io/enigma-bbs/troubleshooting/ssh-troubleshooting.html
// - https://blog.sleeplessbeastie.eu/2017/12/28/how-to-generate-private-key/
// - https://gist.github.com/briansmith/2ee42439923d8e65a266994d0f70180b
//

View File

@@ -1,16 +1,52 @@
#!/usr/bin/env bash
{ # this ensures the entire script is downloaded before execution
ENIGMA_NODE_VERSION=${ENIGMA_NODE_VERSION:=14}
ENIGMA_BRANCH=${ENIGMA_BRANCH:=master}
ENIGMA_INSTALL_DIR=${ENIGMA_INSTALL_DIR:=$HOME/enigma-bbs}
ENIGMA_SOURCE=${ENIGMA_SOURCE:=https://github.com/NuSkooler/enigma-bbs.git}
TIME_FORMAT=`date "+%Y-%m-%d %H:%M:%S"`
WAIT_BEFORE_INSTALL=10
# ANSI Codes
RESET="\e[0m"
BOLD="\e[1m"
UNDERLINE="\e[4m"
INVERSE="\e7m"
FOREGROUND_BLACK="\e[30m"
FOREGROUND_RED="\e[31m"
FOREGROUND_GREEN="\e[32m"
FOREGROUND_YELLOW="\e[33m"
FOREGROUND_BLUE="\e[34m"
FOREGROUND_MAGENTA="\e[35m"
FOREGROUND_CYAN="\e[36m"
FOREGROUND_WHITE="\e[37m"
BACKGROUND_BLACK="\e[40m"
BACKGROUND_RED="\e[41m"
BACKGROUND_GREEN="\e[42m"
BACKGROUND_YELLOW="\e[43m"
BACKGROUND_BLUE="\e[44m"
BACKGROUND_MAGENTA="\e[45m"
BACKGROUND_CYAN="\e[46m"
BACKGROUND_WHITE="\e[47m"
FOREGROUND_STRONG_WHITE="\e[90m"
FOREGROUND_STRONG_RED="\e[91m"
FOREGROUND_STRONG_GREEN="\e[92m"
FOREGROUND_STRONG_YELLOW="\e[93m"
FOREGROUND_STRONG_BLUE="\e[94m"
FOREGROUND_STRONG_MAGENTA="\e[95m"
FOREGROUND_STRONG_CYAN="\e[96m"
FOREGROUND_STRONG_WHITE="\e[97m"
BACKGROUND_STRONG_BLACK="\e[100m"
BACKGROUND_STRONG_RED="\e[101m"
BACKGROUND_STRONG_GREEN="\e[102m"
BACKGROUND_STRONG_YELLOW="\e[103m"
BACKGROUND_STRONG_BLUE="\w[104m"
BACKGROUND_STRONG_MAGENTA="\e[105m"
BACKGROUND_STRONG_CYAN="\e[106m"
BACKGROUND_STRONG_WHITE="\e[107m"
enigma_header() {
clear
printf "$FOREGROUND_STRONG_WHITE"
cat << EndOfMessage
______
_____________________ _____ ____________________ __________\\_ /
@@ -22,27 +58,16 @@ _____________________ _____ ____________________ __________\\_ /
<*> ENiGMA½ // https://github.com/NuSkooler/enigma-bbs <*> /__/
Installing ENiGMA½:
ENiGMA½:
Source : ${ENIGMA_SOURCE} (${ENIGMA_BRANCH} branch)
Destination: ${ENIGMA_INSTALL_DIR}
Node.js : ${ENIGMA_NODE_VERSION}.x via NVM (If you have NVM it will be updated to the latest version)
>> If this isn't what you were expecting, hit CTRL-C now!
>> Installation will continue in ${WAIT_BEFORE_INSTALL} seconds...
EndOfMessage
SECS=10
while [ $SECS -gt 0 ]; do
echo -ne "${SECS}... "
sleep 1
((SECS --))
done
echo ""
printf "$RESET"
}
fatal_error() {
printf "${TIME_FORMAT} \e[41mERROR:\033[0m %b\n" "$*" >&2;
log "${TIME_FORMAT} ERROR: %b\n $*" >&2;
exit 1
}
@@ -51,52 +76,68 @@ check_exists() {
}
enigma_install_needs_ex() {
echo -ne "Checking for '$1'..."
log "Checking for '$1'...${RESET}"
if check_exists $1 ; then
echo " Found!"
log " Found!"
else
echo ""
fatal_error "ENiGMA½ requires '$1' but it was not found. Please install it and/or make sure it is in your path then restart the installer.\n\n$2"
fi
}
enigma_install_needs_python() {
echo -ne "Checking for a suitable Python installation..."
if check_exists "python" || check_exists "python7" || check_exists "python3" ; then
echo " Found!"
else
echo ""
fatal_error "ENiGMA½ requires Python for node-gyp to build binaries. Please see https://www.npmjs.com/package/node-gyp for details."
fi
}
enigma_install_needs() {
enigma_install_needs_ex $1 "Examples:\n sudo apt install $1 # Debian/Ubuntu\n sudo yum install $1 # CentOS"
}
log() {
printf "${TIME_FORMAT} %b\n" "$*";
enigma_has_mise() {
log "Checking for an installation of mise-en-place (https://mise.jdx.dev/)"
if check_exists "mise"; then
log "Found!"
else
log ""
fatal_error "ENiGMA½ requires mise-enplace to install dependencies."
fi
}
log() {
local LOG_CONTENT=$1
local COLOUR=""
case $LOG_CONTENT in
"ERROR")
COLOUR="${FOREGROUND_STRONG_RED}"
;;
*)
COLOUR="${FOREGROUND_GREEN}"
;;
esac
printf "${TIME_FORMAT} %b\n" "${COLOUR}${LOG_CONTENT}${RESET}";
}
enigma_install_init() {
enigma_install_needs git
enigma_install_needs curl
enigma_install_needs_python
enigma_install_needs_ex make "Examples:\n sudo apt install build-essential # Debian/Ubuntu\n sudo yum groupinstall 'Development Tools' # CentOS"
enigma_install_needs make
enigma_install_needs gcc
}
install_nvm() {
log "Installing nvm"
curl -o- https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
install_mise_en_place() {
curl https://mise.run | sh
# ~/.local/bin/mise activate bash >> bash
eval "$(~/.local/bin/mise activate bash)"
cd $ENIGMA_INSTALL_DIR
mise install
export PATH="$HOME/.local/share/mise/shims:$PATH"
}
configure_nvm() {
log "Installing Node ${ENIGMA_NODE_VERSION} via nvm"
. ~/.nvm/nvm.sh
nvm install ${ENIGMA_NODE_VERSION}
nvm use ${ENIGMA_NODE_VERSION}
install_tools() {
# Used to read toml files from bash scripts
python -m pip install toml-cli
}
download_enigma_source() {
@@ -104,7 +145,7 @@ download_enigma_source() {
INSTALL_DIR=${ENIGMA_INSTALL_DIR}
if [ -d "$INSTALL_DIR/.git" ]; then
log "ENiGMA½ is already installed in $INSTALL_DIR, trying to update using git"
log "ENiGMA½ is already installed in $INSTALL_DIR, trying to update using git..."
command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" fetch 2> /dev/null ||
fatal_error "Failed to update ENiGMA½, run 'git fetch' in $INSTALL_DIR yourself."
else
@@ -126,19 +167,21 @@ is_arch_arm() {
extra_npm_install_args() {
if is_arch_arm ; then
echo "--build-from-source"
printf "--build-from-source"
else
echo ""
printf ""
fi
}
install_node_packages() {
log "Installing required Node packages..."
log "Note that on some systems such as RPi, this can take a VERY long time. Be patient!"
printf "Note that on some systems such as RPi, this can take a VERY long time. Be patient!"
cd ${ENIGMA_INSTALL_DIR}
local EXTRA_NPM_ARGS=$(extra_npm_install_args)
git checkout ${ENIGMA_BRANCH} && npm install ${EXTRA_NPM_ARGS}
git checkout ${ENIGMA_BRANCH}
npm install ${EXTRA_NPM_ARGS}
if [ $? -eq 0 ]; then
log "npm package installation complete"
else
@@ -147,21 +190,22 @@ install_node_packages() {
}
copy_template_files() {
if [[ ! -f "./gopher/gophermap" ]]; then
cp "./misc/gophermap" "./gopher/gophermap"
log "Copying Template Files to ${ENIGMA_INSTALL_DIR}/misc/gophermap"
if [[ ! -f "$ENIGMA_INSTALL_DIR/gopher/gophermap" ]]; then
cp "$ENIGMA_INSTALL_DIR/misc/gophermap" "$ENIGMA_INSTALL_DIR/gopher/gophermap"
fi
}
enigma_footer() {
log "ENiGMA½ installation complete!"
echo -e "\e[1;33m"
printf "${FOREGROUND_YELLOW}"
cat << EndOfMessage
ADDITIONAL ACTIONS ARE REQUIRED!
--------------------------------
1 - If you did not have Node.js and/or NVM installed previous to this please open a new shell/terminal now!
(!) Not doing so will prevent 'nvm' or 'node' commands from functioning!
1 - If you did not have Node.js and/or mise installed previous to this please open a new shell/terminal now!
(!) Not doing so will prevent 'nvm', 'node', or 'python' commands from functioning!
2 - If this is the first time you've installed ENiGMA½, you now need to generate a minimal configuration:
@@ -185,17 +229,94 @@ ADDITIONAL ACTIONS ARE REQUIRED!
See docs for more information including other useful binaries!
4 - Start ENiGMA½ BBS!
./autoexec.sh
5 - Enable Automated Startup on Boot (optional)
Create a file in /etc/systemd/system/bbs.service with the following contents:
[Unit]
Description=Enigma½ BBS
[Install]
WantedBy=multi-user.target
[Service]
ExecStart=/home/<YOUR_USERNAME>/enigma-bbs/autoexec.sh
Type=simple
User=<YOUR_USERNAME>
Group=<YOUR_USERNAME>
WorkingDirectory=/home/<YOUR_USERNAME>/enigma-bbs/
Restart=on-failure
Run 'sudo systemctl enable bbs.service'
EndOfMessage
echo -e "\e[39m"
printf "${RESET}"
}
post_install() {
MISE_SHIM_PATH_COMMAND='export PATH="$HOME/.local/share/mise/shims:$PATH"'
if grep -Fxq "$MISE_SHIM_PATH_COMMAND" ~/.bashrc
then
log "Mise Shims found in your ~/.bashrc"
else
echo $MISE_SHIM_PATH_COMMAND >> ~/.bashrc
log "Installed Mise Shims into your ~/.bashrc"
fi
}
install_dependencies() {
log "Installing Dependencies..."
enigma_install_init
install_mise_en_place
install_tools
install_node_packages
post_install
}
install_bbs() {
log "Installing ENiGMA½..."
download_enigma_source
copy_template_files
}
install_everything() {
log "Installing Everything..."
download_enigma_source
install_dependencies
copy_template_files
}
menu() {
title="Installation Options"
prompt="Select>"
options=(
"Install Dependencies"
"Install ENiGMA½"
"Install Everything"
)
echo "$title"
PS3="$prompt "
select opt in "${options[@]}" "Quit"; do
case "$REPLY" in
1) enigma_install_init; install_dependencies; break;;
2) install_bbs; break;;
3) enigma_install_init; install_everything; break;;
$((${#options[@]}+1))) printf "Goodbye!"; exit 0;;
*) printf "${FOREGROUND_STRONG_RED}Invalid option.${RESET}\n";continue;;
esac
done < /dev/tty
unset PS3
}
enigma_header
enigma_install_init
install_nvm
configure_nvm
download_enigma_source
install_node_packages
copy_template_files
menu
enigma_footer
} # this ensures the entire script is downloaded before execution

7
mise.toml Normal file
View File

@@ -0,0 +1,7 @@
[tools]
node = '18'
python = '3.10'
[env]
NODE_ENV = 'production'
_.python.venv = { path = ".venv", create = true }

View File

@@ -65,7 +65,7 @@
"temptmp": "^1.1.0",
"uuid": "8.3.2",
"uuid-parse": "1.1.0",
"ws": "7.5.10",
"ws": "8.18.0",
"yazl": "^2.5.1"
},
"devDependencies": {

8
watch.sh Normal file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
LOGFILE_PATH=~/enigma-bbs/logs/enigma-bbs.log
BUNYAN_BINARY_PATH=~/enigma-bbs/node_modules/bunyan/bin/bunyan
PATH="$HOME/.local/share/mise/shims:$PATH"
tail -F $LOGFILE_PATH | $BUNYAN_BINARY_PATH

717
yarn.lock

File diff suppressed because it is too large Load Diff