chore: implement constants

This commit is contained in:
Bill Church 2024-08-21 17:42:22 +00:00
parent 13fd49e008
commit 5a65b6e91d
No known key found for this signature in database
9 changed files with 122 additions and 38 deletions

View file

@ -2,7 +2,6 @@
// app/app.js
const express = require("express")
const path = require("path")
const config = require("./config")
const socketHandler = require("./socket")
const sshRoutes = require("./routes")
@ -11,6 +10,7 @@ const { createServer, startServer } = require("./server")
const { configureSocketIO } = require("./io")
const { handleError, ConfigError } = require("./errors")
const { createNamespacedDebug } = require("./logger")
const { DEFAULTS, MESSAGES } = require("./constants")
const debug = createNamespacedDebug("app")
@ -23,14 +23,7 @@ function createApp() {
try {
// Resolve the correct path to the webssh2_client module
const clientPath = path.resolve(
__dirname,
"..",
"node_modules",
"webssh2_client",
"client",
"public"
)
const clientPath = DEFAULTS.WEBSSH2_CLIENT_PATH
// Apply middleware
const { sessionMiddleware } = applyMiddleware(app, config)
@ -43,7 +36,9 @@ function createApp() {
return { app: app, sessionMiddleware: sessionMiddleware }
} catch (err) {
throw new ConfigError(`Failed to configure Express app: ${err.message}`)
throw new ConfigError(
`${MESSAGES.EXPRESS_APP_CONFIG_ERROR}: ${err.message}`
)
}
}

View file

@ -5,13 +5,14 @@ const Ajv = require("ajv")
const { deepMerge, generateSecureSecret } = require("./utils")
const { createNamespacedDebug } = require("./logger")
const { ConfigError, handleError } = require("./errors")
const { DEFAULTS } = require("./constants")
const debug = createNamespacedDebug("config")
const defaultConfig = {
listen: {
ip: "0.0.0.0",
port: 2222
port: DEFAULTS.LISTEN_PORT
},
http: {
origins: ["*:*"]
@ -22,8 +23,8 @@ const defaultConfig = {
},
ssh: {
host: null,
port: 22,
term: "xterm-color",
port: DEFAULTS.SSH_PORT,
term: DEFAULTS.SSH_TERM,
readyTimeout: 20000,
keepaliveInterval: 120000,
keepaliveCountMax: 10
@ -61,7 +62,7 @@ const defaultConfig = {
compress: ["none", "zlib@openssh.com", "zlib"]
},
session: {
secret: generateSecureSecret(),
secret: process.env.WEBSSH_SESSION_SECRET || generateSecureSecret(),
name: "webssh2.sid"
}
}

View file

@ -4,6 +4,7 @@
const fs = require("fs")
const path = require("path")
const { createNamespacedDebug } = require("./logger")
const { HTTP, MESSAGES, DEFAULTS } = require("./constants")
const debug = createNamespacedDebug("connectionHandler")
/**
@ -34,7 +35,9 @@ function handleFileRead(filePath, config, res) {
// eslint-disable-next-line consistent-return
fs.readFile(filePath, "utf8", function(err, data) {
if (err) {
return res.status(500).send("Error loading client file")
return res
.status(HTTP.INTERNAL_SERVER_ERROR)
.send(MESSAGES.CLIENT_FILE_ERROR)
}
const modifiedHtml = modifyHtml(data, config)
@ -67,7 +70,7 @@ function handleConnection(req, res) {
autoConnect: req.path.startsWith("/host/") // Automatically connect if path starts with /host/
}
const filePath = path.join(clientPath, "client.htm")
const filePath = path.join(clientPath, DEFAULTS.CLIENT_FILE)
handleFileRead(filePath, tempConfig, res)
}

77
app/constants.js Normal file
View file

@ -0,0 +1,77 @@
// server
// app/constants.js
const crypto = require("crypto")
const path = require("path")
/**
* Error messages
*/
const MESSAGES = {
INVALID_CREDENTIALS: "Invalid credentials format",
SSH_CONNECTION_ERROR: "SSH CONNECTION ERROR",
SHELL_ERROR: "SHELL ERROR",
CONFIG_ERROR: "CONFIG_ERROR",
UNEXPECTED_ERROR: "An unexpected error occurred",
EXPRESS_APP_CONFIG_ERROR: "Failed to configure Express app",
CLIENT_FILE_ERROR: "Error loading client file"
}
/**
* Default values
*/
const DEFAULTS = {
SSH_PORT: 22,
LISTEN_PORT: 2222,
SSH_TERM: "xterm-color",
IO_PING_TIMEOUT: 60000, // 1 minute
IO_PING_INTERVAL: 25000, // 25 seconds
IO_PATH: "/ssh/socket.io",
WEBSSH2_CLIENT_PATH: path.resolve(
__dirname,
"..",
"node_modules",
"webssh2_client",
"client",
"public"
),
CLIENT_FILE: "client.htm"
}
/**
* Socket events
*/
const SOCKET_EVENTS = {
CONNECTION: "connection",
DISCONNECT: "disconnect",
AUTHENTICATE: "authenticate",
AUTHENTICATION: "authentication",
TERMINAL: "terminal",
DATA: "data",
RESIZE: "resize",
CONTROL: "control"
}
/**
* HTTP Related
*/
const HTTP = {
OK: 200,
UNAUTHORIZED: 401,
INTERNAL_SERVER_ERROR: 500,
AUTHENTICATE: "WWW-Authenticate",
REALM: 'Basic realm="WebSSH2"',
AUTH_REQUIRED: "Authentication required.",
COOKIE: "basicauth",
PATH: "/ssh/host/",
SAMESITE: "Strict",
SESSION_SID: "webssh2_sid",
CREDS_CLEARED: "Credentials cleared."
}
module.exports = {
MESSAGES,
DEFAULTS,
SOCKET_EVENTS,
HTTP
}

View file

@ -3,6 +3,7 @@
const util = require("util")
const { logError, createNamespacedDebug } = require("./logger")
const { HTTP, MESSAGES } = require("./constants")
const debug = createNamespacedDebug("errors")
@ -25,7 +26,7 @@ util.inherits(WebSSH2Error, Error)
* @param {string} message - The error message
*/
function ConfigError(message) {
WebSSH2Error.call(this, message, "CONFIG_ERROR")
WebSSH2Error.call(this, message, MESSAGES.CONFIG_ERROR)
}
util.inherits(ConfigError, WebSSH2Error)
@ -35,7 +36,7 @@ util.inherits(ConfigError, WebSSH2Error)
* @param {string} message - The error message
*/
function SSHConnectionError(message) {
WebSSH2Error.call(this, message, "SSH_CONNECTION_ERROR")
WebSSH2Error.call(this, message, MESSAGES.SSH_CONNECTION_ERROR)
}
util.inherits(SSHConnectionError, WebSSH2Error)
@ -50,13 +51,17 @@ function handleError(err, res) {
logError(err.message, err)
debug(err.message)
if (res) {
res.status(500).json({ error: err.message, code: err.code })
res
.status(HTTP.INTERNAL_SERVER_ERROR)
.json({ error: err.message, code: err.code })
}
} else {
logError("An unexpected error occurred", err)
debug("Unexpected error: %O", err)
logError(MESSAGES.UNEXPECTED_ERROR, err)
debug(`handleError: ${MESSAGES.UNEXPECTED_ERROR}: %O`, err)
if (res) {
res.status(500).json({ error: "An unexpected error occurred" })
res
.status(HTTP.INTERNAL_SERVER_ERROR)
.json({ error: MESSAGES.UNEXPECTED_ERROR })
}
}
}

View file

@ -1,6 +1,7 @@
const socketIo = require("socket.io")
const sharedsession = require("express-socket.io-session")
const { createNamespacedDebug } = require("./logger")
const { DEFAULTS } = require("./constants")
const debug = createNamespacedDebug("app")
@ -14,9 +15,9 @@ const debug = createNamespacedDebug("app")
function configureSocketIO(server, sessionMiddleware, config) {
const io = socketIo(server, {
serveClient: false,
path: "/ssh/socket.io",
pingTimeout: 60000, // 1 minute
pingInterval: 25000, // 25 seconds
path: DEFAULTS.IO_PATH,
pingTimeout: DEFAULTS.IO_PING_TIMEOUT,
pingInterval: DEFAULTS.IO_PING_INTERVAL,
cors: config.getCorsConfig()
})
@ -27,7 +28,7 @@ function configureSocketIO(server, sessionMiddleware, config) {
})
)
debug("Socket.IO configured")
debug("IO configured")
return io
}

View file

@ -20,7 +20,7 @@ function createNamespacedDebug(namespace) {
function logError(message, error) {
console.error(message)
if (error) {
console.error(`ERROR:\n\n ${error}`)
console.error(`ERROR: ${error}`)
}
}

View file

@ -6,6 +6,7 @@ const session = require("express-session")
const bodyParser = require("body-parser")
const debug = createDebug("webssh2:middleware")
const { HTTP } = require("./constants")
/**
* Creates and configures session middleware
@ -14,10 +15,10 @@ const debug = createDebug("webssh2:middleware")
*/
function createSessionMiddleware(config) {
return session({
secret: config.session.secret || "webssh2_secret",
secret: config.session.secret,
resave: false,
saveUninitialized: true,
name: config.session.name || "webssh2.sid"
name: config.session.name
})
}
@ -40,10 +41,10 @@ function createCookieMiddleware() {
host: req.session.sshCredentials.host,
port: req.session.sshCredentials.port
}
res.cookie("basicauth", JSON.stringify(cookieData), {
res.cookie(HTTP.COOKIE, JSON.stringify(cookieData), {
httpOnly: false,
path: "/ssh/host/",
sameSite: "Strict"
path: HTTP.PATH,
sameSite: HTTP.SAMESITE
})
}
next()
@ -63,7 +64,7 @@ function applyMiddleware(app, config) {
app.use(createBodyParserMiddleware())
app.use(createCookieMiddleware())
debug("Middleware applied")
debug("applyMiddleware")
return { sessionMiddleware }
}

View file

@ -1,5 +1,5 @@
// server
// /app/routes.js
// app/routes.js
const express = require("express")
const basicAuth = require("basic-auth")
@ -13,6 +13,7 @@ const {
const handleConnection = require("./connectionHandler")
const { createNamespacedDebug } = require("./logger")
const { ConfigError, handleError } = require("./errors")
const { HTTP } = require("./constants")
const debug = createNamespacedDebug("routes")
const router = express.Router()
@ -22,8 +23,8 @@ function auth(req, res, next) {
debug("auth: Basic Auth")
const credentials = basicAuth(req)
if (!credentials) {
res.setHeader("WWW-Authenticate", 'Basic realm="WebSSH2"')
return res.status(401).send("Authentication required.")
res.setHeader(HTTP.AUTHENTICATE, HTTP.REALM)
return res.status(HTTP.UNAUTHORIZED).send(HTTP.AUTH_REQUIRED)
}
// Validate and sanitize credentials
req.session.sshCredentials = {
@ -75,12 +76,12 @@ router.get("/host/:host", auth, function(req, res) {
// Clear credentials route
router.get("/clear-credentials", function(req, res) {
req.session.sshCredentials = null
res.status(200).send("Credentials cleared.")
res.status(HTTP.OK).send(HTTP.CREDENTIALS_CLEARED)
})
router.get("/force-reconnect", function(req, res) {
req.session.sshCredentials = null
res.status(401).send("Authentication required.")
res.status(HTTP.UNAUTHORIZED).send(HTTP.AUTH_REQUIRED)
})
module.exports = router