Browse Source

restructor repo to reduce top level files

pull/270/head
butlerx 4 years ago
parent
commit
8bb1d97774
No known key found for this signature in database GPG Key ID: B37CA765BAA89170
  1. 5
      .babelrc
  2. 45
      .eslintrc.js
  3. 68
      .eslintrc.json
  4. 124
      .gitignore
  5. 4
      .npmignore
  6. 13
      .prettierrc.js
  7. 13
      .prettierrc.json
  8. 68
      README.md
  9. 0
      conf/nginx.template
  10. 0
      conf/wetty.conf
  11. 0
      conf/wetty.service
  12. 0
      containers/ssh/Dockerfile
  13. 0
      containers/wetty/Dockerfile
  14. 11
      docker-compose.yml
  15. 124
      index.js
  16. 54
      package.json
  17. 0
      src/buffer.ts
  18. 20
      src/client.ts
  19. 6
      src/client/disconnect.ts
  20. 195
      src/client/download.spec.ts
  21. 2
      src/client/mobile.ts
  22. 2
      src/client/options.ts
  23. 2
      src/client/socket.ts
  24. 195
      src/client/specs/download.spec.ts
  25. 4
      src/client/verify.ts
  26. 205
      src/server.ts
  27. 18
      src/server/cli/index.ts
  28. 22
      src/server/cli/options.ts
  29. 32
      src/server/cli/parseArgs.ts
  30. 22
      src/server/command.ts
  31. 6
      src/server/command/address.ts
  32. 5
      src/server/command/login.ts
  33. 11
      src/server/command/ssh.ts
  34. 2
      src/server/command/ssh/parse.ts
  35. 22
      src/server/default.ts
  36. 4
      src/server/index.ts
  37. 2
      src/server/login.ts
  38. 0
      src/server/shared/xterm.ts
  39. 14
      src/server/socketServer.ts
  40. 13
      src/server/spawn.ts
  41. 2
      src/server/ssl.ts
  42. 5
      src/server/utils/index.ts
  43. 72
      src/server/wetty/index.ts
  44. 5
      src/server/wetty/term/index.ts
  45. 0
      src/shared/elements.ts
  46. 4
      src/shared/interfaces.ts
  47. 4
      src/shared/logger.ts
  48. 4
      src/shared/verify.ts
  49. 13
      tsconfig.json
  50. 7
      tsconfig/cjs.json
  51. 14
      tsconfig/esm.json
  52. 137
      webpack.config.babel.js
  53. 4036
      yarn.lock

5
.babelrc

@ -1,5 +0,0 @@
{
"presets": ["@babel/preset-typescript", ["@babel/env"]],
"compact": true,
"plugins": ["lodash", "@babel/plugin-proposal-class-properties"]
}

45
.eslintrc.js

@ -1,45 +0,0 @@
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'prettier'],
env: {
es6: true,
node: true,
browser: true,
},
root: true,
extends: [
'airbnb-base',
'plugin:@typescript-eslint/recommended',
'prettier',
'prettier/@typescript-eslint',
],
rules: {
'linebreak-style': ['error', 'unix'],
'arrow-parens': ['error', 'as-needed'],
'no-param-reassign': ['error', { props: false }],
'func-style': ['error', 'declaration', { allowArrowFunctions: true }],
'no-use-before-define': ['error', { functions: false }],
'@typescript-eslint/no-use-before-define': ['error', { functions: false }],
'import/prefer-default-export': 'off',
'lines-between-class-members': [
'error',
'always',
{ exceptAfterSingleLine: true },
],
'import/extensions': [
'error',
'always',
{
js: 'ignorePackages',
ts: 'never',
},
],
},
settings: {
'import/resolver': {
node: {
extensions: ['.ts', '.js'],
},
},
},
};

68
.eslintrc.json

@ -0,0 +1,68 @@
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "prettier"],
"env": {
"es6": true,
"node": true,
"browser": true
},
"root": true,
"extends": [
"airbnb-base",
"plugin:@typescript-eslint/recommended",
"prettier",
"prettier/@typescript-eslint"
],
"rules": {
"linebreak-style": ["error", "unix"],
"arrow-parens": ["error", "as-needed"],
"no-param-reassign": [
"error",
{
"props": false
}
],
"func-style": [
"error",
"declaration",
{
"allowArrowFunctions": true
}
],
"no-use-before-define": [
"error",
{
"functions": false
}
],
"@typescript-eslint/no-use-before-define": [
"error",
{
"functions": false
}
],
"import/prefer-default-export": "off",
"lines-between-class-members": [
"error",
"always",
{
"exceptAfterSingleLine": true
}
],
"import/extensions": [
"error",
"always",
{
"js": "ignorePackages",
"ts": "never"
}
]
},
"settings": {
"import/resolver": {
"node": {
"extensions": [".ts", ".js"]
}
}
}
}

124
.gitignore

@ -1,19 +1,115 @@
lib-cov
*.seed
./lib
# Created by https://www.toptal.com/developers/gitignore/api/node
# Edit at https://www.toptal.com/developers/gitignore?templates=node
### Node ###
# Logs
logs
*.log
*.csv
*.dat
*.out
*.pid
*.gz
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
tmp
# Runtime data
pids
logs
results
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
npm-debug.log
node_modules
.esm-cache
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
.idea
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# End of https://www.toptal.com/developers/gitignore/api/node

4
.npmignore

@ -19,10 +19,8 @@ src
*.yml
Dockerfile
*.png
.babelrc
.dockerignore
.eslint*
.prettierrc.js
tsconfig.json
webpack.config.babel.js
tsconfig
docs

13
.prettierrc.js

@ -1,13 +0,0 @@
module.exports = {
singleQuote: true,
trailingComma: 'es5',
proseWrap: 'always',
overrides: [
{
files: ['*.js', '*.ts'],
options: {
printWidth: 80,
},
},
],
};

13
.prettierrc.json

@ -0,0 +1,13 @@
{
"singleQuote": true,
"trailingComma": "es5",
"proseWrap": "always",
"overrides": [
{
"files": ["*.js", "*.ts"],
"options": {
"printWidth": 80
}
}
]
}

68
README.md

@ -2,7 +2,7 @@
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
![All Contributors](https://img.shields.io/badge/all_contributors-33-orange.svg?style=flat-square)
[![All Contributors](https://img.shields.io/badge/all_contributors-33-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
@ -94,47 +94,47 @@ Thanks goes to these wonderful people
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="http://cianbutler.ie"><img src="https://avatars1.githubusercontent.com/u/867930?v=4" width="100px;" alt="Cian Butler"/><br /><sub><b>Cian Butler</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=butlerx" title="Code">💻</a> <a href="https://github.com/butlerx/WeTTy/commits?author=butlerx" title="Documentation">📖</a></td>
<td align="center"><a href="http://about.me/krishnasrinivas"><img src="https://avatars0.githubusercontent.com/u/634494?v=4" width="100px;" alt="Krishna Srinivas"/><br /><sub><b>Krishna Srinivas</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=krishnasrinivas" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/acalatrava"><img src="https://avatars1.githubusercontent.com/u/8502129?v=4" width="100px;" alt="acalatrava"/><br /><sub><b>acalatrava</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=acalatrava" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Strubbl"><img src="https://avatars3.githubusercontent.com/u/97055?v=4" width="100px;" alt="Strubbl"/><br /><sub><b>Strubbl</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=Strubbl" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/2sheds"><img src="https://avatars3.githubusercontent.com/u/16163?v=4" width="100px;" alt="Oleg Kurapov"/><br /><sub><b>Oleg Kurapov</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=2sheds" title="Code">💻</a></td>
<td align="center"><a href="http://www.rabchev.com"><img src="https://avatars0.githubusercontent.com/u/1876061?v=4" width="100px;" alt="Boyan Rabchev"/><br /><sub><b>Boyan Rabchev</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=rabchev" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nosemeocurrenada"><img src="https://avatars1.githubusercontent.com/u/3845708?v=4" width="100px;" alt="Jimmy"/><br /><sub><b>Jimmy</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=nosemeocurrenada" title="Code">💻</a></td>
<td align="center"><a href="http://cianbutler.ie"><img src="https://avatars1.githubusercontent.com/u/867930?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Cian Butler</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=butlerx" title="Code">💻</a> <a href="https://github.com/butlerx/WeTTy/commits?author=butlerx" title="Documentation">📖</a></td>
<td align="center"><a href="http://about.me/krishnasrinivas"><img src="https://avatars0.githubusercontent.com/u/634494?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Krishna Srinivas</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=krishnasrinivas" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/acalatrava"><img src="https://avatars1.githubusercontent.com/u/8502129?v=4?s=100" width="100px;" alt=""/><br /><sub><b>acalatrava</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=acalatrava" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Strubbl"><img src="https://avatars3.githubusercontent.com/u/97055?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Strubbl</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=Strubbl" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/2sheds"><img src="https://avatars3.githubusercontent.com/u/16163?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Oleg Kurapov</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=2sheds" title="Code">💻</a></td>
<td align="center"><a href="http://www.rabchev.com"><img src="https://avatars0.githubusercontent.com/u/1876061?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Boyan Rabchev</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=rabchev" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nosemeocurrenada"><img src="https://avatars1.githubusercontent.com/u/3845708?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jimmy</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=nosemeocurrenada" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://www.gerritforge.com"><img src="https://avatars3.githubusercontent.com/u/182893?v=4" width="100px;" alt="Luca Milanesio"/><br /><sub><b>Luca Milanesio</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=lucamilanesio" title="Code">💻</a></td>
<td align="center"><a href="http://anthonyjund.com"><img src="https://avatars3.githubusercontent.com/u/39376331?v=4" width="100px;" alt="Anthony Jund"/><br /><sub><b>Anthony Jund</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=antonyjim" title="Code">💻</a></td>
<td align="center"><a href="https://www.mirtouf.fr"><img src="https://avatars3.githubusercontent.com/u/5165058?v=4" width="100px;" alt="mirtouf"/><br /><sub><b>mirtouf</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=mirtouf" title="Code">💻</a></td>
<td align="center"><a href="https://cor-net.org"><img src="https://avatars1.githubusercontent.com/u/556693?v=4" width="100px;" alt="Bertrand Roussel"/><br /><sub><b>Bertrand Roussel</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=CoRfr" title="Code">💻</a></td>
<td align="center"><a href="https://www.benl.com.au/"><img src="https://avatars0.githubusercontent.com/u/6703966?v=4" width="100px;" alt="Ben Letchford"/><br /><sub><b>Ben Letchford</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=benletchford" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/SouraDutta"><img src="https://avatars0.githubusercontent.com/u/33066261?v=4" width="100px;" alt="SouraDutta"/><br /><sub><b>SouraDutta</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=SouraDutta" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/koushikmln"><img src="https://avatars3.githubusercontent.com/u/8670988?v=4" width="100px;" alt="Koushik M.L.N"/><br /><sub><b>Koushik M.L.N</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=koushikmln" title="Code">💻</a></td>
<td align="center"><a href="http://www.gerritforge.com"><img src="https://avatars3.githubusercontent.com/u/182893?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Luca Milanesio</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=lucamilanesio" title="Code">💻</a></td>
<td align="center"><a href="http://anthonyjund.com"><img src="https://avatars3.githubusercontent.com/u/39376331?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anthony Jund</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=antonyjim" title="Code">💻</a></td>
<td align="center"><a href="https://www.mirtouf.fr"><img src="https://avatars3.githubusercontent.com/u/5165058?v=4?s=100" width="100px;" alt=""/><br /><sub><b>mirtouf</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=mirtouf" title="Code">💻</a></td>
<td align="center"><a href="https://cor-net.org"><img src="https://avatars1.githubusercontent.com/u/556693?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bertrand Roussel</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=CoRfr" title="Code">💻</a></td>
<td align="center"><a href="https://www.benl.com.au/"><img src="https://avatars0.githubusercontent.com/u/6703966?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ben Letchford</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=benletchford" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/SouraDutta"><img src="https://avatars0.githubusercontent.com/u/33066261?v=4?s=100" width="100px;" alt=""/><br /><sub><b>SouraDutta</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=SouraDutta" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/koushikmln"><img src="https://avatars3.githubusercontent.com/u/8670988?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Koushik M.L.N</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=koushikmln" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://imu.li/"><img src="https://avatars3.githubusercontent.com/u/4085046?v=4" width="100px;" alt="Imuli"/><br /><sub><b>Imuli</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=imuli" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/perpen"><img src="https://avatars2.githubusercontent.com/u/9963805?v=4" width="100px;" alt="perpen"/><br /><sub><b>perpen</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=perpen" title="Code">💻</a></td>
<td align="center"><a href="https://nathanleclaire.com"><img src="https://avatars3.githubusercontent.com/u/1476820?v=4" width="100px;" alt="Nathan LeClaire"/><br /><sub><b>Nathan LeClaire</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=nathanleclaire" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/MiKr13"><img src="https://avatars2.githubusercontent.com/u/34394719?v=4" width="100px;" alt="Mihir Kumar"/><br /><sub><b>Mihir Kumar</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=MiKr13" title="Code">💻</a></td>
<td align="center"><a href="http://redhat.com"><img src="https://avatars0.githubusercontent.com/u/540893?v=4" width="100px;" alt="Chris Suszynski"/><br /><sub><b>Chris Suszynski</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=cardil" title="Code">💻</a></td>
<td align="center"><a href="http://9wd.de"><img src="https://avatars1.githubusercontent.com/u/1257835?v=4" width="100px;" alt="Felix Bartels"/><br /><sub><b>Felix Bartels</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=fbartels" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jarrettgilliam"><img src="https://avatars3.githubusercontent.com/u/5099690?v=4" width="100px;" alt="Jarrett Gilliam"/><br /><sub><b>Jarrett Gilliam</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=jarrettgilliam" title="Code">💻</a></td>
<td align="center"><a href="https://imu.li/"><img src="https://avatars3.githubusercontent.com/u/4085046?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Imuli</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=imuli" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/perpen"><img src="https://avatars2.githubusercontent.com/u/9963805?v=4?s=100" width="100px;" alt=""/><br /><sub><b>perpen</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=perpen" title="Code">💻</a></td>
<td align="center"><a href="https://nathanleclaire.com"><img src="https://avatars3.githubusercontent.com/u/1476820?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nathan LeClaire</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=nathanleclaire" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/MiKr13"><img src="https://avatars2.githubusercontent.com/u/34394719?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mihir Kumar</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=MiKr13" title="Code">💻</a></td>
<td align="center"><a href="http://redhat.com"><img src="https://avatars0.githubusercontent.com/u/540893?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chris Suszynski</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=cardil" title="Code">💻</a></td>
<td align="center"><a href="http://9wd.de"><img src="https://avatars1.githubusercontent.com/u/1257835?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Felix Bartels</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=fbartels" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jarrettgilliam"><img src="https://avatars3.githubusercontent.com/u/5099690?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jarrett Gilliam</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=jarrettgilliam" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://harrylee.me"><img src="https://avatars0.githubusercontent.com/u/7056279?v=4" width="100px;" alt="Harry Lee"/><br /><sub><b>Harry Lee</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=harryleesan" title="Code">💻</a></td>
<td align="center"><a href="http://andreask.cs.illinois.edu"><img src="https://avatars3.githubusercontent.com/u/352067?v=4" width="100px;" alt="Andreas Klöckner"/><br /><sub><b>Andreas Klöckner</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=inducer" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/DenisKramer"><img src="https://avatars1.githubusercontent.com/u/23534092?v=4" width="100px;" alt="DenisKramer"/><br /><sub><b>DenisKramer</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=DenisKramer" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/vamship"><img src="https://avatars0.githubusercontent.com/u/7143376?v=4" width="100px;" alt="Vamshi K Ponnapalli"/><br /><sub><b>Vamshi K Ponnapalli</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=vamship" title="Code">💻</a></td>
<td align="center"><a href="https://tridnguyen.com"><img src="https://avatars1.githubusercontent.com/u/1652595?v=4" width="100px;" alt="Tri Nguyen"/><br /><sub><b>Tri Nguyen</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=tnguyen14" title="Documentation">📖</a></td>
<td align="center"><a href="https://felix.pojtinger.com/"><img src="https://avatars1.githubusercontent.com/u/28832235?v=4" width="100px;" alt="Felix Pojtinger"/><br /><sub><b>Felix Pojtinger</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=pojntfx" title="Documentation">📖</a></td>
<td align="center"><a href="https://nealey.github.io/"><img src="https://avatars3.githubusercontent.com/u/423780?v=4" width="100px;" alt="Neale Pickett"/><br /><sub><b>Neale Pickett</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=nealey" title="Code">💻</a></td>
<td align="center"><a href="https://harrylee.me"><img src="https://avatars0.githubusercontent.com/u/7056279?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Harry Lee</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=harryleesan" title="Code">💻</a></td>
<td align="center"><a href="http://andreask.cs.illinois.edu"><img src="https://avatars3.githubusercontent.com/u/352067?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andreas Klöckner</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=inducer" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/DenisKramer"><img src="https://avatars1.githubusercontent.com/u/23534092?v=4?s=100" width="100px;" alt=""/><br /><sub><b>DenisKramer</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=DenisKramer" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/vamship"><img src="https://avatars0.githubusercontent.com/u/7143376?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vamshi K Ponnapalli</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=vamship" title="Code">💻</a></td>
<td align="center"><a href="https://tridnguyen.com"><img src="https://avatars1.githubusercontent.com/u/1652595?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tri Nguyen</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=tnguyen14" title="Documentation">📖</a></td>
<td align="center"><a href="https://felix.pojtinger.com/"><img src="https://avatars1.githubusercontent.com/u/28832235?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Felix Pojtinger</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=pojntfx" title="Documentation">📖</a></td>
<td align="center"><a href="https://nealey.github.io/"><img src="https://avatars3.githubusercontent.com/u/423780?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Neale Pickett</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=nealey" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://www.matthewpiercey.ml"><img src="https://avatars3.githubusercontent.com/u/22581026?v=4" width="100px;" alt="Matthew Piercey"/><br /><sub><b>Matthew Piercey</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=mtpiercey" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/kholbekj"><img src="https://avatars3.githubusercontent.com/u/2786571?v=4" width="100px;" alt="Kasper Holbek Jensen"/><br /><sub><b>Kasper Holbek Jensen</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=kholbekj" title="Documentation">📖</a></td>
<td align="center"><a href="https://mastodon.technology/@farhan"><img src="https://avatars1.githubusercontent.com/u/10103765?v=4" width="100px;" alt="Farhan Khan"/><br /><sub><b>Farhan Khan</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=khanzf" title="Code">💻</a></td>
<td align="center"><a href="https://www.jurrevriesen.nl"><img src="https://avatars1.githubusercontent.com/u/7419259?v=4" width="100px;" alt="Jurre Vriesen"/><br /><sub><b>Jurre Vriesen</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=jurruh" title="Code">💻</a></td>
<td align="center"><a href="https://www.kartar.net/"><img src="https://avatars3.githubusercontent.com/u/4365?v=4" width="100px;" alt="James Turnbull"/><br /><sub><b>James Turnbull</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=jamtur01" title="Code">💻</a></td>
<td align="center"><a href="https://www.matthewpiercey.ml"><img src="https://avatars3.githubusercontent.com/u/22581026?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matthew Piercey</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=mtpiercey" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/kholbekj"><img src="https://avatars3.githubusercontent.com/u/2786571?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kasper Holbek Jensen</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=kholbekj" title="Documentation">📖</a></td>
<td align="center"><a href="https://mastodon.technology/@farhan"><img src="https://avatars1.githubusercontent.com/u/10103765?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Farhan Khan</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=khanzf" title="Code">💻</a></td>
<td align="center"><a href="https://www.jurrevriesen.nl"><img src="https://avatars1.githubusercontent.com/u/7419259?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jurre Vriesen</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=jurruh" title="Code">💻</a></td>
<td align="center"><a href="https://www.kartar.net/"><img src="https://avatars3.githubusercontent.com/u/4365?v=4?s=100" width="100px;" alt=""/><br /><sub><b>James Turnbull</b></sub></a><br /><a href="https://github.com/butlerx/WeTTy/commits?author=jamtur01" title="Code">💻</a></td>
</tr>
</table>

0
bin/nginx.template → conf/nginx.template

0
bin/wetty.conf → conf/wetty.conf

0
bin/wetty.service → conf/wetty.service

0
ssh.Dockerfile → containers/ssh/Dockerfile

0
Dockerfile → containers/wetty/Dockerfile

11
docker-compose.yml

@ -2,9 +2,10 @@
version: '3.5'
services:
wetty:
build: .
image: wettyoss/wetty
container_name: wetty
build:
context: .
dockerfile: containers/wetty/Dockerfile
tty: true
working_dir: /usr/src/app
ports:
@ -16,9 +17,8 @@ services:
web:
image: nginx
container_name: wetty-nginx
volumes:
- ./bin/nginx.template:/etc/nginx/conf.d/wetty.template
- ./conf/nginx.template:/etc/nginx/conf.d/wetty.template
ports:
- '80:80'
environment:
@ -35,9 +35,8 @@ services:
wetty-ssh:
build:
context: .
dockerfile: ssh.Dockerfile
dockerfile: containers/ssh/Dockerfile
image: wettyoss/wetty:ssh
container_name: 'wetty-ssh'
networks:
default:

124
index.js

@ -1,124 +0,0 @@
#! /usr/bin/env node
/* eslint-disable @typescript-eslint/no-var-requires, import/no-unresolved */
const yargs = require('yargs');
const wetty = require('./dist').default;
module.exports = wetty.wetty;
/**
* Check if being run by cli or require
*/
if (require.main === module) {
wetty.init(
yargs
.options({
sslkey: {
demand: false,
type: 'string',
description: 'path to SSL key',
},
sslcert: {
demand: false,
type: 'string',
description: 'path to SSL certificate',
},
sshhost: {
demand: false,
description: 'ssh server host',
type: 'string',
default: process.env.SSHHOST || 'localhost',
},
sshport: {
demand: false,
description: 'ssh server port',
type: 'number',
default: parseInt(process.env.SSHPORT, 10) || 22,
},
sshuser: {
demand: false,
description: 'ssh user',
type: 'string',
default: process.env.SSHUSER || '',
},
title: {
demand: false,
description: 'window title',
type: 'string',
default: process.env.TITLE || 'WeTTy - The Web Terminal Emulator',
},
sshauth: {
demand: false,
description:
'defaults to "password", you can use "publickey,password" instead',
type: 'string',
default: process.env.SSHAUTH || 'password',
},
sshpass: {
demand: false,
description: 'ssh password',
type: 'string',
default: process.env.SSHPASS || undefined,
},
sshkey: {
demand: false,
description:
'path to an optional client private key (connection will be password-less and insecure!)',
type: 'string',
default: process.env.SSHKEY || undefined,
},
forcessh: {
demand: false,
description: 'Connecting through ssh even if running as root',
type: 'boolean',
default: process.env.FORCESSH || false
},
knownhosts: {
demand: false,
description: 'path to known hosts file',
type: 'string',
default: process.env.KNOWNHOSTS || '/dev/null',
},
base: {
demand: false,
alias: 'b',
description: 'base path to wetty',
type: 'string',
default: process.env.BASE || '/wetty/',
},
port: {
demand: false,
alias: 'p',
description: 'wetty listen port',
type: 'number',
default: parseInt(process.env.PORT, 10) || 3000,
},
host: {
demand: false,
description: 'wetty listen host',
default: '0.0.0.0',
type: 'string',
},
command: {
demand: false,
alias: 'c',
description: 'command to run in shell',
type: 'string',
default: process.env.COMMAND || 'login',
},
bypasshelmet: {
demand: false,
description: 'disable helmet from placing security restrictions',
type: 'boolean',
default: false,
},
help: {
demand: false,
alias: 'h',
type: 'boolean',
description: 'Print help message',
},
})
.boolean('allow_discovery').argv
);
}

54
package.json

@ -1,8 +1,9 @@
{
"name": "wetty",
"version": "1.4.1",
"version": "2.0.0",
"description": "WeTTY = Web + TTY. Terminal access in browser over http/https",
"homepage": "https://github.com/butlerx/wetty",
"type": "module",
"repository": {
"type": "git",
"url": "git://github.com/butlerx/wetty.git"
@ -16,9 +17,13 @@
"bugs": {
"url": "https://github.com/butlerx/wetty/issues"
},
"main": "index.js",
"main": "./lib/cjs/server.js",
"module": "./lib/esm/server.js",
"files": [
"lib/"
],
"scripts": {
"build": "babel-node node_modules/.bin/webpack",
"build": "tsc -p tsconfig/esm.json && tsc -p tsconfig/cjs.json",
"contributor": "all-contributors",
"dev": "NODE_ENV=development concurrently --kill-others --success first \"babel-node node_modules/.bin/webpack --watch\" \"nodemon .\"",
"lint": "eslint --ext .ts,.js .",
@ -41,9 +46,6 @@
"git add"
]
},
"bin": {
"wetty": "./index.js"
},
"engines": {
"node": ">=10.22"
},
@ -86,7 +88,7 @@
"@types/compression": "^1.0.1",
"@types/express": "^4.17.1",
"@types/fs-extra": "^8.0.0",
"@types/helmet": "^0.0.44",
"@types/helmet": "^0.0.47",
"@types/jsdom": "^12.2.4",
"@types/lodash": "^4.14.138",
"@types/mocha": "^5.2.7",
@ -97,38 +99,28 @@
"@types/socket.io": "^2.1.2",
"@types/socket.io-client": "^1.4.32",
"@types/webpack-env": "^1.14.0",
"@types/yargs": "^13.0.2",
"@types/winston": "^2.4.4",
"@types/yargs": "^15.0.5",
"@typescript-eslint/eslint-plugin": "^2.5.0",
"@typescript-eslint/parser": "^2.5.0",
"all-contributors-cli": "^6.9.3",
"babel-loader": "^8.0.6",
"babel-plugin-lodash": "^3.3.4",
"babel-register-ts": "^7.0.0",
"all-contributors-cli": "^6.17.0",
"chai": "^4.2.0",
"concurrently": "^4.1.2",
"css-loader": "^3.2.0",
"eslint": "6.1.0",
"eslint-config-airbnb-base": "14.0.0",
"eslint-config-prettier": "^6.4.0",
"concurrently": "^5.2.0",
"eslint": "^7.6.0",
"eslint-config-airbnb-base": "^14.2.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-prettier": "^3.1.0",
"eslint-plugin-prettier": "^3.1.4",
"file-loader": "^4.2.0",
"git-authors-cli": "^1.0.18",
"husky": "^3.0.9",
"git-authors-cli": "^1.0.27",
"husky": "^4.2.5",
"jsdom": "^15.2.1",
"lint-staged": "~9.2.5",
"mini-css-extract-plugin": "^0.8.0",
"lint-staged": "^10.2.11",
"mocha": "^6.2.2",
"node-sass": "^4.12.0",
"nodemon": "^1.19.2",
"prettier": "^1.18.2",
"sass-loader": "^8.0.0",
"nodemon": "^2.0.4",
"prettier": "^2.0.5",
"sinon": "^7.5.0",
"style-loader": "^1.0.0",
"typescript": "~3.6.2",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.7",
"webpack-node-externals": "^1.7.2"
"typescript": "^3.9.7"
},
"contributors": [
"Krishna Srinivas <krishna.srinivas@gmail.com>",

0
src/server/buffer.ts → src/buffer.ts

20
src/client/index.ts → src/client.ts

@ -6,16 +6,16 @@ import { faCogs } from '@fortawesome/free-solid-svg-icons/faCogs';
import Toastify from 'toastify-js';
import * as fileType from 'file-type';
import { socket } from './socket';
import { overlay, terminal } from './elements';
import { FileDownloader } from './download';
import verifyPrompt from './verify';
import disconnect from './disconnect';
import mobileKeyboard from './mobile';
import loadOptions from './options';
import { copySelected, copyShortcut } from './copyToClipboard';
import './wetty.scss';
import './favicon.ico';
import { FileDownloader } from './client/download';
import { copySelected, copyShortcut } from './client/copyToClipboard';
import { disconnect } from './client/disconnect';
import { loadOptions } from './client/options';
import { mobileKeyboard } from './client/mobile';
import { overlay, terminal } from './shared/elements';
import { socket } from './client/socket';
import { verifyPrompt } from './shared/verify';
import './client/wetty.scss';
import './client/favicon.ico';
// Setup for fontawesome
library.add(faCogs);

6
src/client/disconnect.ts

@ -1,8 +1,8 @@
import { isNull, isUndefined } from 'lodash';
import verifyPrompt from './verify';
import { overlay } from './elements';
import { verifyPrompt } from '../shared/verify';
import { overlay } from '../shared/elements';
export default function disconnect(reason: string): void {
export function disconnect(reason: string): void {
if (isNull(overlay)) return;
overlay.style.display = 'block';
const msg = document.getElementById('msg');

195
src/client/download.spec.ts

@ -0,0 +1,195 @@
/* eslint-disable */
import { expect } from 'chai';
import 'mocha';
import * as sinon from 'sinon';
import { JSDOM } from 'jsdom';
import { FileDownloader } from './download';
const { window } = new JSDOM(`...`);
describe('FileDownloader', () => {
const FILE_BEGIN = 'BEGIN';
const FILE_END = 'END';
let fileDownloader: any;
beforeEach(() => {
fileDownloader = new FileDownloader(() => {}, FILE_BEGIN, FILE_END);
});
afterEach(() => {
sinon.restore();
});
it('should return data before file markers', () => {
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(
fileDownloader.buffer(`DATA AT THE LEFT${FILE_BEGIN}FILE${FILE_END}`)
).to.equal('DATA AT THE LEFT');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should return data after file markers', () => {
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(
fileDownloader.buffer(`${FILE_BEGIN}FILE${FILE_END}DATA AT THE RIGHT`)
).to.equal('DATA AT THE RIGHT');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should return data before and after file markers', () => {
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(
fileDownloader.buffer(
`DATA AT THE LEFT${FILE_BEGIN}FILE${FILE_END}DATA AT THE RIGHT`
)
).to.equal('DATA AT THE LEFTDATA AT THE RIGHT');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should return data before a beginning marker found', () => {
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer(`DATA AT THE LEFT${FILE_BEGIN}FILE`)).to.equal(
'DATA AT THE LEFT'
);
});
it('should return data after an ending marker found', () => {
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer(`${FILE_BEGIN}FI`)).to.equal('');
expect(fileDownloader.buffer(`LE${FILE_END}DATA AT THE RIGHT`)).to.equal(
'DATA AT THE RIGHT'
);
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should buffer across incomplete file begin marker sequence on two calls', () => {
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END');
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer('BEG')).to.equal('');
expect(fileDownloader.buffer('INFILEEND')).to.equal('');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should buffer across incomplete file begin marker sequence on n calls', () => {
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END');
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer('B')).to.equal('');
expect(fileDownloader.buffer('E')).to.equal('');
expect(fileDownloader.buffer('G')).to.equal('');
expect(fileDownloader.buffer('I')).to.equal('');
expect(fileDownloader.buffer('NFILE' + 'END')).to.equal('');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should buffer across incomplete file begin marker sequence with data on the left and right on multiple calls', () => {
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END');
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer('DATA AT THE LEFT' + 'B')).to.equal(
'DATA AT THE LEFT'
);
expect(fileDownloader.buffer('E')).to.equal('');
expect(fileDownloader.buffer('G')).to.equal('');
expect(fileDownloader.buffer('I')).to.equal('');
expect(fileDownloader.buffer('NFILE' + 'ENDDATA AT THE RIGHT')).to.equal(
'DATA AT THE RIGHT'
);
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should buffer across incomplete file begin marker sequence then handle false positive', () => {
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END');
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer('DATA AT THE LEFT' + 'B')).to.equal(
'DATA AT THE LEFT'
);
expect(fileDownloader.buffer('E')).to.equal('');
expect(fileDownloader.buffer('G')).to.equal('');
// This isn't part of the file_begin marker and should trigger the partial
// file begin marker to be returned with the normal data
expect(fileDownloader.buffer('ZDATA AT THE RIGHT')).to.equal(
'BEGZDATA AT THE RIGHT'
);
expect(onCompleteFileStub.called).to.be.false;
});
it('should buffer across incomplete file end marker sequence on two calls', () => {
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END');
const mockFilePart1 = 'DATA AT THE LEFTBEGINFILEE';
const mockFilePart2 = 'NDDATA AT THE RIGHT';
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer(mockFilePart1)).to.equal('DATA AT THE LEFT');
expect(fileDownloader.buffer(mockFilePart2)).to.equal('DATA AT THE RIGHT');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should buffer across incomplete file end and file begin marker sequence with data on the left and right on multiple calls', () => {
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END');
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer('DATA AT THE LEFT' + 'BE')).to.equal(
'DATA AT THE LEFT'
);
expect(fileDownloader.buffer('G')).to.equal('');
expect(fileDownloader.buffer('I')).to.equal('');
expect(fileDownloader.buffer('NFILEE')).to.equal('');
expect(fileDownloader.buffer('N')).to.equal('');
expect(fileDownloader.buffer('DDATA AT THE RIGHT')).to.equal(
'DATA AT THE RIGHT'
);
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should be able to handle multiple files', () => {
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END');
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(
fileDownloader.buffer(
'DATA AT THE LEFT' + 'BEGIN' + 'FILE1' + 'END' + 'SECOND DATA' + 'BEGIN'
)
).to.equal('DATA AT THE LEFT' + 'SECOND DATA');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE1');
expect(fileDownloader.buffer('FILE2')).to.equal('');
expect(fileDownloader.buffer('E')).to.equal('');
expect(fileDownloader.buffer('NDRIGHT')).to.equal('RIGHT');
expect(onCompleteFileStub.calledTwice).to.be.true;
expect(onCompleteFileStub.getCall(1).args[0]).to.equal('FILE2');
});
it('should be able to handle multiple files with an ending marker', () => {
fileDownloader = new FileDownloader(() => {}, 'BEGIN', 'END');
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(
fileDownloader.buffer('DATA AT THE LEFT' + 'BEGIN' + 'FILE1' + 'EN')
).to.equal('DATA AT THE LEFT');
expect(onCompleteFileStub.calledOnce).to.be.false;
expect(
fileDownloader.buffer('D' + 'SECOND DATA' + 'BEGIN' + 'FILE2' + 'EN')
).to.equal('SECOND DATA');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE1');
expect(fileDownloader.buffer('D')).to.equal('');
expect(onCompleteFileStub.calledTwice).to.be.true;
expect(onCompleteFileStub.getCall(1).args[0]).to.equal('FILE2');
});
});

2
src/client/mobile.ts

@ -1,6 +1,6 @@
import { isNull } from 'lodash';
export default function mobileKeyboard(): void {
export function mobileKeyboard(): void {
const [screen] = document.getElementsByClassName('xterm-screen');
if (isNull(screen)) return;
screen.setAttribute('contenteditable', 'true');

2
src/client/options.ts

@ -1,6 +1,6 @@
import { isUndefined } from 'lodash';
export default function loadOptions(): object {
export function loadOptions(): object {
const defaultOptions = { fontSize: 14 };
try {
return isUndefined(localStorage.options)

2
src/client/socket.ts

@ -1,4 +1,4 @@
import * as io from 'socket.io-client';
import io from 'socket.io-client';
const userRegex = new RegExp('ssh/[^/]+$');
export const trim = (str: string): string => str.replace(/\/*$/, '');

195
src/client/specs/download.spec.ts

@ -1,195 +0,0 @@
/* eslint-disable */
import { expect } from 'chai';
import 'mocha';
import * as sinon from 'sinon';
import { JSDOM } from 'jsdom';
import { FileDownloader } from '../download';
const { window } = new JSDOM(`...`);
describe('FileDownloader', () => {
const FILE_BEGIN = 'BEGIN';
const FILE_END = 'END';
let fileDownloader: any;
beforeEach(() => {
fileDownloader = new FileDownloader(() => { }, FILE_BEGIN, FILE_END);
});
afterEach(() => {
sinon.restore();
});
it('should return data before file markers', () => {
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(
fileDownloader.buffer(`DATA AT THE LEFT${FILE_BEGIN}FILE${FILE_END}`)
).to.equal('DATA AT THE LEFT');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should return data after file markers', () => {
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(
fileDownloader.buffer(`${FILE_BEGIN}FILE${FILE_END}DATA AT THE RIGHT`)
).to.equal('DATA AT THE RIGHT');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should return data before and after file markers', () => {
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(
fileDownloader.buffer(
`DATA AT THE LEFT${FILE_BEGIN}FILE${FILE_END}DATA AT THE RIGHT`
)
).to.equal('DATA AT THE LEFTDATA AT THE RIGHT');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should return data before a beginning marker found', () => {
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer(`DATA AT THE LEFT${FILE_BEGIN}FILE`)).to.equal(
'DATA AT THE LEFT'
);
});
it('should return data after an ending marker found', () => {
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer(`${FILE_BEGIN}FI`)).to.equal('');
expect(fileDownloader.buffer(`LE${FILE_END}DATA AT THE RIGHT`)).to.equal(
'DATA AT THE RIGHT'
);
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should buffer across incomplete file begin marker sequence on two calls', () => {
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END');
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer('BEG')).to.equal('');
expect(fileDownloader.buffer('INFILEEND')).to.equal('');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should buffer across incomplete file begin marker sequence on n calls', () => {
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END');
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer('B')).to.equal('');
expect(fileDownloader.buffer('E')).to.equal('');
expect(fileDownloader.buffer('G')).to.equal('');
expect(fileDownloader.buffer('I')).to.equal('');
expect(fileDownloader.buffer('NFILE' + 'END')).to.equal('');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should buffer across incomplete file begin marker sequence with data on the left and right on multiple calls', () => {
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END');
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer('DATA AT THE LEFT' + 'B')).to.equal(
'DATA AT THE LEFT'
);
expect(fileDownloader.buffer('E')).to.equal('');
expect(fileDownloader.buffer('G')).to.equal('');
expect(fileDownloader.buffer('I')).to.equal('');
expect(fileDownloader.buffer('NFILE' + 'ENDDATA AT THE RIGHT')).to.equal(
'DATA AT THE RIGHT'
);
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should buffer across incomplete file begin marker sequence then handle false positive', () => {
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END');
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer('DATA AT THE LEFT' + 'B')).to.equal(
'DATA AT THE LEFT'
);
expect(fileDownloader.buffer('E')).to.equal('');
expect(fileDownloader.buffer('G')).to.equal('');
// This isn't part of the file_begin marker and should trigger the partial
// file begin marker to be returned with the normal data
expect(fileDownloader.buffer('ZDATA AT THE RIGHT')).to.equal(
'BEGZDATA AT THE RIGHT'
);
expect(onCompleteFileStub.called).to.be.false;
});
it('should buffer across incomplete file end marker sequence on two calls', () => {
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END');
const mockFilePart1 = 'DATA AT THE LEFTBEGINFILEE';
const mockFilePart2 = 'NDDATA AT THE RIGHT';
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer(mockFilePart1)).to.equal('DATA AT THE LEFT');
expect(fileDownloader.buffer(mockFilePart2)).to.equal('DATA AT THE RIGHT');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should buffer across incomplete file end and file begin marker sequence with data on the left and right on multiple calls', () => {
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END');
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(fileDownloader.buffer('DATA AT THE LEFT' + 'BE')).to.equal(
'DATA AT THE LEFT'
);
expect(fileDownloader.buffer('G')).to.equal('');
expect(fileDownloader.buffer('I')).to.equal('');
expect(fileDownloader.buffer('NFILEE')).to.equal('');
expect(fileDownloader.buffer('N')).to.equal('');
expect(fileDownloader.buffer('DDATA AT THE RIGHT')).to.equal(
'DATA AT THE RIGHT'
);
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE');
});
it('should be able to handle multiple files', () => {
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END');
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(
fileDownloader.buffer(
'DATA AT THE LEFT' + 'BEGIN' + 'FILE1' + 'END' + 'SECOND DATA' + 'BEGIN'
)
).to.equal('DATA AT THE LEFT' + 'SECOND DATA');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE1');
expect(fileDownloader.buffer('FILE2')).to.equal('');
expect(fileDownloader.buffer('E')).to.equal('');
expect(fileDownloader.buffer('NDRIGHT')).to.equal('RIGHT');
expect(onCompleteFileStub.calledTwice).to.be.true;
expect(onCompleteFileStub.getCall(1).args[0]).to.equal('FILE2');
});
it('should be able to handle multiple files with an ending marker', () => {
fileDownloader = new FileDownloader(() => { }, 'BEGIN', 'END');
const onCompleteFileStub = sinon.stub(fileDownloader, 'onCompleteFile');
expect(
fileDownloader.buffer('DATA AT THE LEFT' + 'BEGIN' + 'FILE1' + 'EN')
).to.equal('DATA AT THE LEFT');
expect(onCompleteFileStub.calledOnce).to.be.false;
expect(
fileDownloader.buffer('D' + 'SECOND DATA' + 'BEGIN' + 'FILE2' + 'EN')
).to.equal('SECOND DATA');
expect(onCompleteFileStub.calledOnce).to.be.true;
expect(onCompleteFileStub.getCall(0).args[0]).to.equal('FILE1');
expect(fileDownloader.buffer('D')).to.equal('');
expect(onCompleteFileStub.calledTwice).to.be.true;
expect(onCompleteFileStub.getCall(1).args[0]).to.equal('FILE2');
});
});

4
src/client/verify.ts

@ -1,4 +0,0 @@
export default function verifyPrompt(e: { returnValue: string }): string {
e.returnValue = 'Are you sure?';
return e.returnValue;
}

205
src/server.ts

@ -0,0 +1,205 @@
/**
* Create WeTTY server
* @module WeTTy
*/
import * as yargs from 'yargs';
import { isUndefined } from 'lodash';
import { SSH, SSL, SSLBuffer, Server } from './shared/interfaces';
import { getCommand } from './server/command';
import { loadSSL } from './server/ssl';
import { logger } from './shared/logger';
import { login } from './server/login';
import { server } from './server/socketServer';
import { spawn } from './server/spawn';
import { sshDefault, serverDefault, forceSSHDefault, defaultCommand} from './server/default';
/**
* Check if being run by cli or require
*/
if (require.main === module) {
const opts = yargs
.option('ssl-key', {
type: 'string',
description: 'path to SSL key',
})
.option('ssl-cert', {
type: 'string',
description: 'path to SSL certificate',
})
.option('ssh-host', {
description: 'ssh server host',
type: 'string',
default: sshDefault.host,
})
.option('ssh-port', {
description: 'ssh server port',
type: 'number',
default: sshDefault.port,
})
.option('ssh-user', {
description: 'ssh user',
type: 'string',
default: sshDefault.user,
})
.option('title', {
description: 'window title',
type: 'string',
default: serverDefault.title,
})
.option('ssh-auth', {
description:
'defaults to "password", you can use "publickey,password" instead',
type: 'string',
default: sshDefault.auth,
})
.option('ssh-pass', {
description: 'ssh password',
type: 'string',
default: sshDefault.pass,
})
.option('ssh-key', {
demand: false,
description:
'path to an optional client private key (connection will be password-less and insecure!)',
type: 'string',
default: sshDefault.key,
})
.option('force-ssh', {
description: 'Connecting through ssh even if running as root',
type: 'boolean',
default: forceSSHDefault
})
.option('known-hosts', {
description: 'path to known hosts file',
type: 'string',
default: sshDefault.knownHosts,
})
.option('base', {
alias: 'b',
description: 'base path to wetty',
type: 'string',
default: serverDefault.base,
})
.option('port', {
alias: 'p',
description: 'wetty listen port',
type: 'number',
default: serverDefault.port,
})
.option('host', {
description: 'wetty listen host',
default: serverDefault.host,
type: 'string',
})
.option('command', {
alias: 'c',
description: 'command to run in shell',
type: 'string',
default: defaultCommand,
})
.option('bypass-helmet', {
description: 'disable helmet from placing security restrictions',
type: 'boolean',
default: serverDefault.bypassHelmet,
})
.option('help', {
alias: 'h',
type: 'boolean',
description: 'Print help message',
})
.boolean('allow_discovery').argv;
if (!opts.help) {
startServer(
{
user: opts['ssh-user'],
host: opts['ssh-host'],
auth: opts['ssh-auth'],
port: opts['ssh-port'],
pass: opts['ssh-pass'],
key: opts['ssh-key'],
knownHosts: opts['known-hosts'],
},
{
base: opts.base,
host: opts.host,
port: opts.port,
title: opts.title,
bypassHelmet: opts['bypass-helmet'],
},
opts.command,
opts['force-ssh'],
isUndefined(opts['ssl-key']) || isUndefined(opts['ssl-cert'])
? undefined
: { key: opts['ssl-key'], cert: opts['ssl-cert'] }
).catch(err => {
logger.error(err);
process.exitCode = 1;
});
} else {
yargs.showHelp();
process.exitCode = 0;
}
}
/**
* Starts WeTTy Server
* @name startServer
*/
export async function startServer(
ssh: SSH = sshDefault,
serverConf: Server = serverDefault,
command:string = defaultCommand,
forcessh:boolean = forceSSHDefault,
ssl?: SSL
): Promise<SocketIO.Server> {
if (ssh.key) {
logger.warn(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Password-less auth enabled using private key from ${ssh.key}.
! This is dangerous, anything that reaches the wetty server
! will be able to run remote operations without authentication.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`);
}
const sslBuffer: SSLBuffer = await loadSSL(ssl);
const io = server(serverConf, sslBuffer);
/**
* Wetty server connected too
* @fires WeTTy#connnection
*/
io.on('connection', async (socket: SocketIO.Socket) => {
/**
* @event wetty#connection
* @name connection
*/
logger.info('Connection accepted.');
const { args, user: sshUser } = getCommand(
socket,
ssh,
command,
forcessh
);
logger.debug('Command Generated', {
user: sshUser,
cmd: args.join(' '),
});
if (sshUser) {
spawn(socket, args);
} else {
try {
const username = await login(socket)
args[1] = `${username.trim()}@${args[1]}`;
logger.debug('Spawning term', {
username: username.trim(),
cmd: args.join(' ').trim(),
});
spawn(socket, args);
}catch (error) {
logger.info('Disconnect signal sent');
}
}
});
return io
}

18
src/server/cli/index.ts

@ -1,18 +0,0 @@
import * as yargs from 'yargs';
import { logger } from '../utils';
import WeTTy from '../wetty';
import { CLI } from './options';
import { unWrapArgs } from './parseArgs';
export default function init(opts: CLI): void {
if (!opts.help) {
const { ssh, server, command, forcessh, ssl } = unWrapArgs(opts);
WeTTy(ssh, server, command, forcessh, ssl).catch(err => {
logger.error(err);
process.exitCode = 1;
});
} else {
yargs.showHelp();
process.exitCode = 0;
}
}

22
src/server/cli/options.ts

@ -1,22 +0,0 @@
export interface Options {
sshhost: string;
sshport: number;
sshuser: string;
sshauth: string;
sshkey?: string;
sshpass?: string;
knownhosts: string;
sslkey?: string;
sslcert?: string;
base: string;
host: string;
port: number;
title: string;
command?: string;
forcessh?: boolean;
bypasshelmet?: boolean;
}
export interface CLI extends Options {
help: boolean;
}

32
src/server/cli/parseArgs.ts

@ -1,32 +0,0 @@
import { isUndefined } from 'lodash';
import { SSH, SSL, Server } from '../interfaces';
import { Options } from './options';
export function unWrapArgs(
args: Options
): { ssh: SSH; server: Server; command?: string; forcessh?: boolean; ssl?: SSL } {
return {
ssh: {
user: args.sshuser,
host: args.sshhost,
auth: args.sshauth,
port: args.sshport,
pass: args.sshpass,
key: args.sshkey,
knownhosts: args.knownhosts,
},
server: {
base: args.base,
host: args.host,
port: args.port,
title: args.title,
bypasshelmet: args.bypasshelmet || false,
},
command: args.command,
forcessh: args.forcessh,
ssl:
isUndefined(args.sslkey) || isUndefined(args.sslcert)
? undefined
: { key: args.sslkey, cert: args.sslcert },
};
}

22
src/server/command/index.ts → src/server/command.ts

@ -1,9 +1,9 @@
import * as url from 'url';
import { Socket } from 'socket.io';
import { SSH } from '../interfaces';
import address from './address';
import loginOptions from './login';
import sshOptions from './ssh';
import { SSH } from '../shared/interfaces';
import { address } from './command/address';
import { loginOptions } from './command/login';
import { sshOptions } from './command/ssh';
const localhost = (host: string): boolean =>
process.getuid() === 0 &&
@ -15,7 +15,7 @@ const urlArgs = (
): { [s: string]: string } =>
Object.assign(def, url.parse(referer, true).query);
export default (
export const getCommand = (
{
request: {
headers: { referer },
@ -24,21 +24,23 @@ export default (
conn: { remoteAddress },
},
}: Socket,
{ user, host, port, auth, pass, key, knownhosts }: SSH,
{ user, host, port, auth, pass, key, knownHosts }: SSH,
command: string,
forcessh: boolean
): { args: string[]; user: boolean } => ({
args: !forcessh && localhost(host)
args:
!forcessh && localhost(host)
? loginOptions(command, remoteAddress)
: sshOptions(
{ ...urlArgs(referer, {
{
...urlArgs(referer, {
port: `${port}`,
pass: pass || '',
command,
auth,
knownhosts,
knownHosts,
}),
host: address(referer, user, host)
host: address(referer, user, host),
},
key
),

6
src/server/command/address.ts

@ -1,8 +1,4 @@
export default function address(
referer: string,
user: string,
host: string
): string {
export function address(referer: string, user: string, host: string): string {
const match = referer.match('.+/ssh/([^/]+)$');
const fallback = user ? `${user}@${host}` : host;
return match ? `${match[1].split('?')[0]}@${host}` : fallback;

5
src/server/command/login.ts

@ -5,10 +5,7 @@ const getRemoteAddress = (remoteAddress: string): string =>
? 'localhost'
: remoteAddress.split(':')[3];
export default function loginOptions(
command: string,
remoteAddress: string
): string[] {
export function loginOptions(command: string, remoteAddress: string): string[] {
return command === 'login'
? [command, '-h', getRemoteAddress(remoteAddress)]
: [command];

11
src/server/command/ssh.ts

@ -1,8 +1,8 @@
import { isUndefined } from 'lodash';
import parseCommand from './parse';
import logger from '../utils/logger';
import { parseCommand } from './ssh/parse';
import { logger } from '../../shared/logger';
export default function sshOptions(
export function sshOptions(
{
pass,
path,
@ -40,8 +40,5 @@ export default function sshOptions(
sshRemoteOptsBase.splice(sshRemoteOptsBase.indexOf('-o'), 2);
}
if (cmd === '') {
return sshRemoteOptsBase;
}
return sshRemoteOptsBase.concat([cmd]);
return cmd === '' ? sshRemoteOptsBase : sshRemoteOptsBase.concat([cmd]);
}

2
src/server/command/parse.ts → src/server/command/ssh/parse.ts

@ -1,6 +1,6 @@
import { isUndefined } from 'lodash';
export default function parseCommand(command: string, path?: string): string {
export function parseCommand(command: string, path?: string): string {
if (command === 'login' && isUndefined(path)) return '';
return !isUndefined(path)
? `$SHELL -c "cd ${path};${command === 'login' ? '$SHELL' : command}"`

22
src/server/default.ts

@ -0,0 +1,22 @@
import { SSH, Server } from '../shared/interfaces';
export const sshDefault:SSH = {
user: process.env.SSHUSER || '',
host: process.env.SSHHOST || 'localhost',
auth: process.env.SSHAUTH || 'password',
pass: process.env.SSHPASS || undefined,
key: process.env.SSHKEY || undefined,
port: parseInt(process.env.SSHPORT || '22', 10),
knownHosts: process.env.KNOWNHOSTS || '/dev/null',
}
export const serverDefault: Server = {
base: process.env.BASE || '/wetty/',
port: parseInt(process.env.PORT || '3000', 10),
host: '0.0.0.0',
title: process.env.TITLE || 'WeTTy - The Web Terminal Emulator',
bypassHelmet: false,
};
export const forceSSHDefault = process.env.FORCESSH === 'true' || false;
export const defaultCommand = process.env.COMMAND || 'login'

4
src/server/index.ts

@ -1,4 +0,0 @@
import WeTTy from './wetty';
import init from './cli';
export default { start: WeTTy, init };

2
src/server/wetty/term/login.ts → src/server/login.ts

@ -1,5 +1,5 @@
import { spawn } from 'node-pty';
import { xterm } from './xterm';
import { xterm } from './shared/xterm';
export function login(socket: SocketIO.Socket): Promise<string> {
// Check request-header for username

0
src/server/wetty/term/xterm.ts → src/server/shared/xterm.ts

14
src/server/socketServer/index.ts → src/server/socketServer.ts

@ -8,16 +8,16 @@ import * as https from 'https';
import * as path from 'path';
import * as socket from 'socket.io';
import * as expressWinston from 'express-winston';
import { SSLBuffer, Server } from '../interfaces';
import html from './html';
import logger from '../utils/logger';
import { SSLBuffer, Server } from '../shared/interfaces';
import html from './socketServer/html';
import { logger } from '../shared/logger';
const distDir = path.join(__dirname, 'client');
const trim = (str: string): string => str.replace(/\/*$/, '');
export default function createServer(
{ base, port, host, title, bypasshelmet }: Server,
export function server(
{ base, port, host, title, bypassHelmet }: Server,
{ key, cert }: SSLBuffer
): SocketIO.Server {
const basePath = trim(base);
@ -48,7 +48,7 @@ export default function createServer(
// Allow helmet to be bypassed.
// Unfortunately, order matters with middleware
// which is why this is thrown in the middle
if (!bypasshelmet) {
if (!bypassHelmet) {
app.use(helmet());
}
@ -72,7 +72,7 @@ export default function createServer(
{
path: `${basePath}/socket.io`,
pingInterval: 3000,
pingTimeout: 7000
pingTimeout: 7000,
}
);
}

13
src/server/wetty/term/spawn.ts → src/server/spawn.ts

@ -1,13 +1,10 @@
import { isUndefined } from 'lodash';
import { spawn } from 'node-pty';
import { logger } from '../../utils';
import { xterm } from './xterm';
import { spawn as spawnTerm } from 'node-pty';
import { logger } from '../shared/logger';
import { xterm } from './shared/xterm';
export default function spawnTerm(
socket: SocketIO.Socket,
args: string[]
): void {
const term = spawn('/usr/bin/env', args, xterm);
export function spawn(socket: SocketIO.Socket, args: string[]): void {
const term = spawnTerm('/usr/bin/env', args, xterm);
const { pid } = term;
const address = args[0] === 'ssh' ? args[1] : 'localhost';
logger.info('Process Started on behalf of user', {

2
src/server/utils/ssl.ts → src/server/ssl.ts

@ -1,7 +1,7 @@
import { readFile } from 'fs-extra';
import { resolve } from 'path';
import { isUndefined } from 'lodash';
import { SSL, SSLBuffer } from '../interfaces';
import { SSL, SSLBuffer } from '../shared/interfaces';
export async function loadSSL(ssl?: SSL): Promise<SSLBuffer> {
if (isUndefined(ssl) || isUndefined(ssl.key) || isUndefined(ssl.cert))

5
src/server/utils/index.ts

@ -1,5 +0,0 @@
import logger from './logger';
export { logger };
export * from './ssl';

72
src/server/wetty/index.ts

@ -1,72 +0,0 @@
/**
* Create WeTTY server
* @module WeTTy
*/
import server from '../socketServer';
import getCommand from '../command';
import { login, spawn } from './term';
import { loadSSL, logger } from '../utils';
import { SSH, SSL, SSLBuffer, Server } from '../interfaces';
/**
* Starts WeTTy Server
* @name startWeTTy
*/
export default function startWeTTy(
ssh: SSH = { user: '', host: 'localhost', auth: 'password', port: 22 },
serverConf: Server = {
base: '/wetty/',
port: 3000,
host: '0.0.0.0',
title: 'WeTTy',
bypasshelmet: false,
},
command = '',
forcessh = false,
ssl?: SSL
): Promise<void> {
return loadSSL(ssl).then((sslBuffer: SSLBuffer) => {
if (ssh.key) {
logger.warn(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Password-less auth enabled using private key from ${ssh.key}.
! This is dangerous, anything that reaches the wetty server
! will be able to run remote operations without authentication.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`);
}
const io = server(serverConf, sslBuffer);
/**
* Wetty server connected too
* @fires WeTTy#connnection
*/
io.on('connection', (socket: SocketIO.Socket) => {
/**
* @event wetty#connection
* @name connection
*/
logger.info('Connection accepted.');
const { args, user: sshUser } = getCommand(socket, ssh, command, forcessh);
logger.debug('Command Generated', {
user: sshUser,
cmd: args.join(' '),
});
if (sshUser) {
spawn(socket, args);
} else {
login(socket)
.then((username: string) => {
args[1] = `${username.trim()}@${args[1]}`;
logger.debug('Spawning term', {
username: username.trim(),
cmd: args.join(' ').trim(),
});
return spawn(socket, args);
})
.catch(() => {
logger.info('Disconnect signal sent');
});
}
});
});
}

5
src/server/wetty/term/index.ts

@ -1,5 +0,0 @@
import spawn from './spawn';
export { spawn };
export * from './login';

0
src/client/elements.ts → src/shared/elements.ts

4
src/server/interfaces.ts → src/shared/interfaces.ts

@ -3,7 +3,7 @@ export interface SSH {
host: string;
auth: string;
port: number;
knownhosts: string;
knownHosts: string;
pass?: string;
key?: string;
}
@ -23,5 +23,5 @@ export interface Server {
host: string;
title: string;
base: string;
bypasshelmet: boolean;
bypassHelmet: boolean;
}

4
src/server/utils/logger.ts → src/shared/logger.ts

@ -11,7 +11,7 @@ const dev = combine(
const prod = combine(label({ label: 'Wetty' }), timestamp(), json());
const logger = createLogger({
export const logger = createLogger({
format: process.env.NODE_ENV === 'development' ? dev : prod,
transports: [
new transports.Console({
@ -20,5 +20,3 @@ const logger = createLogger({
}),
],
});
export default logger;

4
src/shared/verify.ts

@ -0,0 +1,4 @@
export function verifyPrompt(e: { returnValue: string }): string {
e.returnValue = 'Are you sure?';
return e.returnValue;
}

13
tsconfig.json

@ -1,13 +0,0 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"lib": ["es2015"],
"sourceMap": true,
"strict": true,
"moduleResolution": "node",
"typeRoots": ["node_module/@types"],
"outDir": "./dist"
},
"include": ["./src/**/*.ts"]
}

7
tsconfig/cjs.json

@ -0,0 +1,7 @@
{
"extends": "./esm.json",
"compilerOptions": {
"module": "CommonJS",
"outDir": "../lib/cjs"
}
}

14
tsconfig/esm.json

@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2015",
"module": "ES2020",
"strict": true,
"sourceMap": true,
"esModuleInterop": true,
"declaration": true,
"forceConsistentCasingInFileNames": true,
"outDir": "../lib/esm",
"typeRoots": ["node_module/@types"]
},
"include": ["../src/**/*.ts"]
}

137
webpack.config.babel.js

@ -1,137 +0,0 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import nodeExternals from 'webpack-node-externals';
import path from 'path';
import webpack from 'webpack';
const template = override => ({
mode: process.env.NODE_ENV || 'development',
resolve: {
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
extensions: ['.ts', '.json', '.js', '.node'],
},
stats: {
colors: true,
},
...override,
});
const entry = (folder, file) =>
path.join(__dirname, 'src', folder, `${file}.ts`);
const entries = (folder, files) =>
Object.assign(...files.map(file => ({ [file]: entry(folder, file) })));
export default [
template({
entry: entries('server', ['index', 'buffer']),
target: 'node',
devtool: 'source-map',
output: {
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'commonjs2',
filename: '[name].js',
},
node: {
__filename: false,
__dirname: false,
},
externals: [nodeExternals()],
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-typescript',
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
],
plugins: ['lodash'],
},
},
},
{
test: /\.js$/,
use: ['source-map-loader'],
enforce: 'pre',
},
],
},
plugins: [new webpack.IgnorePlugin(/uws/)],
}),
template({
entry: entries('client', ['index']),
output: {
path: path.resolve(__dirname, 'dist', 'client'),
filename: '[name].js',
},
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-typescript',
[
'@babel/preset-env',
{
targets: {
browsers: ['last 2 versions', 'safari >= 7'],
},
},
],
],
plugins: ['lodash', '@babel/plugin-proposal-class-properties'],
},
},
},
{
test: /\.js$/,
use: ['source-map-loader'],
enforce: 'pre',
},
{
test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
},
{
loader: 'sass-loader',
},
],
},
{
test: /\.(jpg|jpeg|png|gif|mp3|svg|ico)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
}),
],
devtool: 'source-map',
}),
];

4036
yarn.lock

File diff suppressed because it is too large
Loading…
Cancel
Save