mirror of
https://github.com/ION606/web-to-fish.git
synced 2026-05-14 18:36:53 +00:00
I gave up
This commit is contained in:
-150
@@ -1,150 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>TTY Login</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background: #000;
|
|
||||||
/* Black background for terminal feel */
|
|
||||||
color: #00ff00;
|
|
||||||
/* Green text for TTY style */
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tty-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 600px;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
background: transparent;
|
|
||||||
color: #00ff00;
|
|
||||||
border: none;
|
|
||||||
border-bottom: 1px solid #00ff00;
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 1rem;
|
|
||||||
padding: 0.25rem;
|
|
||||||
outline: none;
|
|
||||||
caret-color: #00ff00;
|
|
||||||
}
|
|
||||||
|
|
||||||
input::placeholder {
|
|
||||||
color: #008000;
|
|
||||||
/* Dim green for placeholder text */
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus {
|
|
||||||
border-bottom-color: #00ff00;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
display: none;
|
|
||||||
/* TTY doesn't have a visible button */
|
|
||||||
}
|
|
||||||
|
|
||||||
.tty-header {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tty-header h1 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
color: #00ff00;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tty-message {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tty-footer {
|
|
||||||
margin-top: 1rem;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
color: #008000;
|
|
||||||
/* Dim green for footer */
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-message {
|
|
||||||
color: red;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="tty-container">
|
|
||||||
<div class="tty-header">
|
|
||||||
<h1>TTY Login</h1>
|
|
||||||
</div>
|
|
||||||
<form id="loginForm">
|
|
||||||
<div class="tty-message">login:</div>
|
|
||||||
<input name="username" type="text" placeholder="Enter username" required autocomplete="off" />
|
|
||||||
<div class="tty-message">password:</div>
|
|
||||||
<input name="password" type="password" placeholder="Enter password" required />
|
|
||||||
<button type="submit">Login</button>
|
|
||||||
<div class="error-message" id="errorMessage"></div>
|
|
||||||
</form>
|
|
||||||
<div class="tty-footer">
|
|
||||||
Type your credentials and press <kbd>Enter</kbd>.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const form = document.querySelector('#loginForm');
|
|
||||||
const errorMessage = document.querySelector('#errorMessage');
|
|
||||||
|
|
||||||
form.addEventListener('submit', async (event) => {
|
|
||||||
event.preventDefault(); // Prevent form from submitting the traditional way
|
|
||||||
errorMessage.textContent = ''; // Clear previous error message
|
|
||||||
|
|
||||||
const formData = new FormData(form);
|
|
||||||
const payload = {
|
|
||||||
username: formData.get('username'),
|
|
||||||
password: formData.get('password')
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch('login', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify(payload)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.status === 404) {
|
|
||||||
errorMessage.textContent = 'User not found.';
|
|
||||||
} else if (response.status === 401) {
|
|
||||||
errorMessage.textContent = 'Incorrect password.';
|
|
||||||
} else if (response.status === 500) {
|
|
||||||
errorMessage.textContent = 'Error logging in. Please try again later.';
|
|
||||||
} else if (response.ok) {
|
|
||||||
window.location.href = '/shell';
|
|
||||||
} else {
|
|
||||||
errorMessage.textContent = 'Unexpected error occurred.';
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
errorMessage.textContent = 'Unable to connect to the server. Please try again later.';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import session from 'express-session';
|
|
||||||
import expressWs from 'express-ws';
|
import expressWs from 'express-ws';
|
||||||
import { spawn } from 'node-pty';
|
import { spawn } from 'node-pty';
|
||||||
import json from './secrets/config.json' with { type: 'json' };
|
import json from './secrets/config.json' with { type: 'json' };
|
||||||
@@ -13,76 +12,18 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)),
|
|||||||
expressWs(app); // enable websockets for the app
|
expressWs(app); // enable websockets for the app
|
||||||
|
|
||||||
|
|
||||||
// configure session
|
|
||||||
app.use(session({
|
|
||||||
secret: json.secretkey,
|
|
||||||
resave: false,
|
|
||||||
saveUninitialized: false
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
// parse form data
|
// parse form data
|
||||||
app.use(express.urlencoded({ extended: true }));
|
app.use(express.urlencoded({ extended: true }));
|
||||||
app.use(express.json({ extended: true }));
|
app.use(express.json({ extended: true }));
|
||||||
app.use('/node_modules', express.static('node_modules'));
|
app.use('/node_modules', express.static('node_modules'));
|
||||||
|
|
||||||
|
|
||||||
// authentication middleware
|
|
||||||
function requireAuth(req, res, next) {
|
|
||||||
if (req.session && req.session.authenticated) {
|
|
||||||
return next();
|
|
||||||
} else {
|
|
||||||
res.redirect('/login');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// serve login page
|
|
||||||
app.get('/login', (req, res) => {
|
|
||||||
res.sendFile('login.html', { root: path.join(__dirname, 'HTML') });
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
app.post('/login', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const username = req.body.username;
|
|
||||||
const password = req.body.password;
|
|
||||||
|
|
||||||
// validate input
|
|
||||||
if (!username) return res.status(400).send('Username is required');
|
|
||||||
if (!password) return res.status(400).send('Password is required');
|
|
||||||
|
|
||||||
const { uname, upass } = json;
|
|
||||||
|
|
||||||
if (username !== uname) return res.sendStatus(404);
|
|
||||||
else if (password !== upass) return res.sendStatus(401);
|
|
||||||
|
|
||||||
req.session.authenticated = true;
|
|
||||||
req.session.username = username;
|
|
||||||
|
|
||||||
return res.redirect('/shell');
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.status(500).send('Internal server error');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// shell interface
|
// shell interface
|
||||||
app.get('/shell', requireAuth, (req, res) => {
|
app.get('/shell', (req, res) => {
|
||||||
res.sendFile('shell.html', { root: path.join(__dirname, 'HTML') });
|
res.sendFile('shell.html', { root: path.join(__dirname, 'HTML') });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// logout route
|
|
||||||
app.get('/logout', (req, res) => {
|
|
||||||
req.session.destroy(() => {
|
|
||||||
res.redirect('/login');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// when a websocket is opened at /shell-ws, spawn a fish shell in a pty
|
// when a websocket is opened at /shell-ws, spawn a fish shell in a pty
|
||||||
app.ws('/shell-ws', (ws, req) => {
|
app.ws('/shell-ws', (ws, req) => {
|
||||||
// spawn a fish shell using node-pty with a pseudo-terminal
|
// spawn a fish shell using node-pty with a pseudo-terminal
|
||||||
|
|||||||
Generated
+1
-120
@@ -11,13 +11,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xterm/addon-fit": "^0.10.0",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
"@xterm/xterm": "^5.5.0",
|
"@xterm/xterm": "^5.5.0",
|
||||||
"authenticate-pam": "^1.0.5",
|
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"express-session": "^1.18.1",
|
|
||||||
"express-ws": "^5.0.2",
|
"express-ws": "^5.0.2",
|
||||||
"node-pty": "^1.0.0",
|
"node-pty": "^1.0.0"
|
||||||
"passport": "^0.7.0",
|
|
||||||
"passport-local": "^1.0.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@xterm/addon-fit": {
|
"node_modules/@xterm/addon-fit": {
|
||||||
@@ -54,15 +50,6 @@
|
|||||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/authenticate-pam": {
|
|
||||||
"version": "1.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/authenticate-pam/-/authenticate-pam-1.0.5.tgz",
|
|
||||||
"integrity": "sha512-zaPml3/19Sa3XLewuOoUNsxwnNz13mTNoO4Q09vr93cjTrH0dwXOU49Bcetk/XWl22bw9zO9WovSKkddGvBEsQ==",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"dependencies": {
|
|
||||||
"nan": "^2.3.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "1.20.3",
|
"version": "1.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||||
@@ -344,40 +331,6 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/express-session": {
|
|
||||||
"version": "1.18.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz",
|
|
||||||
"integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"cookie": "0.7.2",
|
|
||||||
"cookie-signature": "1.0.7",
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"depd": "~2.0.0",
|
|
||||||
"on-headers": "~1.0.2",
|
|
||||||
"parseurl": "~1.3.3",
|
|
||||||
"safe-buffer": "5.2.1",
|
|
||||||
"uid-safe": "~2.1.5"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/express-session/node_modules/cookie": {
|
|
||||||
"version": "0.7.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
|
||||||
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/express-session/node_modules/cookie-signature": {
|
|
||||||
"version": "1.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
|
|
||||||
"integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/express-ws": {
|
"node_modules/express-ws": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/express-ws/-/express-ws-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/express-ws/-/express-ws-5.0.2.tgz",
|
||||||
@@ -677,15 +630,6 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/on-headers": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/parseurl": {
|
"node_modules/parseurl": {
|
||||||
"version": "1.3.3",
|
"version": "1.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||||
@@ -695,54 +639,12 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/passport": {
|
|
||||||
"version": "0.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz",
|
|
||||||
"integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"passport-strategy": "1.x.x",
|
|
||||||
"pause": "0.0.1",
|
|
||||||
"utils-merge": "^1.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/jaredhanson"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/passport-local": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==",
|
|
||||||
"dependencies": {
|
|
||||||
"passport-strategy": "1.x.x"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/passport-strategy": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/path-to-regexp": {
|
"node_modules/path-to-regexp": {
|
||||||
"version": "0.1.12",
|
"version": "0.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/pause": {
|
|
||||||
"version": "0.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
|
|
||||||
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
|
|
||||||
},
|
|
||||||
"node_modules/proxy-addr": {
|
"node_modules/proxy-addr": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
@@ -771,15 +673,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/random-bytes": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/range-parser": {
|
"node_modules/range-parser": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||||
@@ -1010,18 +903,6 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/uid-safe": {
|
|
||||||
"version": "2.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
|
||||||
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"random-bytes": "~1.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
|
|||||||
+1
-5
@@ -11,13 +11,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xterm/addon-fit": "^0.10.0",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
"@xterm/xterm": "^5.5.0",
|
"@xterm/xterm": "^5.5.0",
|
||||||
"authenticate-pam": "^1.0.5",
|
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"express-session": "^1.18.1",
|
|
||||||
"express-ws": "^5.0.2",
|
"express-ws": "^5.0.2",
|
||||||
"node-pty": "^1.0.0",
|
"node-pty": "^1.0.0"
|
||||||
"passport": "^0.7.0",
|
|
||||||
"passport-local": "^1.0.0"
|
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user