From 8a779ff9302669a901a4e3199b6eaddb93f6d8c8 Mon Sep 17 00:00:00 2001 From: thelamer Date: Thu, 9 Feb 2023 13:58:42 -0800 Subject: [PATCH] adding initial base logic --- README.md | 3 + index.js | 136 +++++++++++++++++++ package.json | 31 +++++ public/css/filebrowser.css | 72 ++++++++++ public/css/files.svg | 2 + public/css/kclient.css | 74 +++++++++++ public/favicon.ico | Bin 0 -> 1150 bytes public/filebrowser.html | 20 +++ public/icon.png | Bin 0 -> 43019 bytes public/index.html | 29 ++++ public/js/filebrowser.js | 262 +++++++++++++++++++++++++++++++++++++ public/js/jquery.min.js | 2 + public/js/kclient.js | 60 +++++++++ public/manifest.json | 17 +++ 14 files changed, 708 insertions(+) create mode 100644 README.md create mode 100644 index.js create mode 100644 package.json create mode 100644 public/css/filebrowser.css create mode 100644 public/css/files.svg create mode 100644 public/css/kclient.css create mode 100644 public/favicon.ico create mode 100644 public/filebrowser.html create mode 100644 public/icon.png create mode 100644 public/index.html create mode 100644 public/js/filebrowser.js create mode 100644 public/js/jquery.min.js create mode 100644 public/js/kclient.js create mode 100644 public/manifest.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..0337726 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Kclient + +ALPHA VERSION NO TOUCH diff --git a/index.js b/index.js new file mode 100644 index 0000000..62c7b4e --- /dev/null +++ b/index.js @@ -0,0 +1,136 @@ +// LinuxServer KasmVNC Client + +//// Env variables //// +var CUSTOM_USER = process.env.CUSTOM_USER || 'abc'; +var PASSWORD = process.env.PASSWORD || 'abc'; +var SUBFOLDER = process.env.SUBFOLDER || '/'; +var TITLE = process.env.TITLE || 'KasmVNC Client'; +var FM_HOME = process.env.FM_HOME || '/config'; + +//// Application Variables //// +var socketIO = require('socket.io'); +var express = require('express'); +var ejs = require('ejs'); +var app = require('express')(); +var http = require('http').Server(app); +var bodyParser = require('body-parser'); +var baseRouter = express.Router(); +var fsw = require('fs').promises; +var fs = require('fs'); + + +//// Server Paths Main //// +app.engine('html', require('ejs').renderFile); +app.engine('json', require('ejs').renderFile); +baseRouter.use('/public', express.static(__dirname + '/public')); +baseRouter.use('/vnc', express.static("/usr/share/kasmvnc/www/")); +baseRouter.get('/', function (req, res) { + res.render(__dirname + '/public/index.html', {title: TITLE}); +}); +baseRouter.get('/favicon.ico', function (req, res) { + res.sendFile(__dirname + '/public/favicon.ico'); +}); +baseRouter.get('/manifest.json', function (req, res) { + res.render(__dirname + '/public/manifest.json', {title: TITLE}); +}); + +//// Web File Browser //// +// Send landing page +baseRouter.get('/files', function (req, res) { + res.sendFile( __dirname + '/public/filebrowser.html'); +}); +// Websocket comms // +io = socketIO(http, {path: SUBFOLDER + 'files/socket.io',maxHttpBufferSize: 200000000}); +io.on('connection', async function (socket) { + let id = socket.id; + + //// Functions //// + + // Open default location + async function checkAuth(password) { + getFiles(FM_HOME); + } + + // Emit to user + function send(command, data) { + io.sockets.to(id).emit(command, data); + } + + // Get file list for directory + async function getFiles(directory) { + let items = await fsw.readdir(directory); + if (items.length > 0) { + let dirs = []; + let files = []; + for await (let item of items) { + let fullPath = directory + '/' + item; + if (fs.lstatSync(fullPath).isDirectory()) { + dirs.push(item); + } else { + files.push(item); + } + } + send('renderfiles', [dirs, files, directory]); + } else { + send('renderfiles', [[], [], directory]); + } + } + + // Send file to client + async function downloadFile(file) { + let fileName = file.split('/').slice(-1)[0]; + let data = await fsw.readFile(file); + send('sendfile', [data, fileName]); + } + + // Write client sent file + async function uploadFile(res) { + let directory = res[0]; + let filePath = res[1]; + let data = res[2]; + let render = res[3]; + let dirArr = filePath.split('/'); + let folder = filePath.replace(dirArr[dirArr.length - 1], '') + await fsw.mkdir(folder, { recursive: true }); + await fsw.writeFile(filePath, Buffer.from(data)); + if (render) { + getFiles(directory); + } + } + + // Delete files + async function deleteFiles(res) { + let item = res[0]; + let directory = res[1]; + item = item.replace("|","'"); + if (fs.lstatSync(item).isDirectory()) { + await fsw.rm(item, {recursive: true}); + } else { + await fsw.unlink(item); + } + getFiles(directory); + } + + // Create a folder + async function createFolder(res) { + let dir = res[0]; + let directory = res[1]; + if (!fs.existsSync(dir)){ + await fsw.mkdir(dir); + } + getFiles(directory); + } + + // Incoming socket requests + socket.on('open', checkAuth); + socket.on('getfiles', getFiles); + socket.on('downloadfile', downloadFile); + socket.on('uploadfile', uploadFile); + socket.on('deletefiles', deleteFiles); + socket.on('createfolder', createFolder); +}); + + +// Spin up application on 6900 +app.use(SUBFOLDER, baseRouter); +http.listen(6900); diff --git a/package.json b/package.json new file mode 100644 index 0000000..cac04fe --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "kclient", + "version": "0.1.0", + "description": "Kclient is a wrapper for KasmVNC to add functionality to a containerized environment", + "main": "index.js", + "dependencies": { + "ejs": "^3.1.8", + "express": "^4.18.2", + "socket.io": "^4.6.0" + }, + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/linuxserver/kclient.git" + }, + "keywords": [ + "VNC", + "Webtop", + "VDI", + "Docker" + ], + "author": "thelamer", + "license": "GPL-3.0-or-later", + "bugs": { + "url": "https://github.com/linuxserver/kclient/issues" + }, + "homepage": "https://github.com/linuxserver/kclient#readme" +} diff --git a/public/css/filebrowser.css b/public/css/filebrowser.css new file mode 100644 index 0000000..0fb5a1d --- /dev/null +++ b/public/css/filebrowser.css @@ -0,0 +1,72 @@ +html * { + font-family: Poppins,Helvetica !important; + color: white !important; +} + +.hidden { + display: none; +} + +.right { + float: right; + margin-right: 5px; +} + +.directory, .file { + cursor: pointer; +} + +button { + background-color: rgb(9 2 2 / 0.6); + border-radius: 5px; + border-style: inset; + border-color: rgb(255 255 255 / 0.6); + cursor: pointer; + margin: 5px; +} + +.deleteButton { + margin: 0px !important; + float: right; +} + +.fileTable { + border-collapse: collapse; + width: 100%; + margin-top: 10px; +} + +td, th { + border: 2px solid #ddd; + padding: 8px; +} + +tr:hover, button:hover { + background: rgba(255, 255, 255, 0.3) +} + +#dropzone { + position: fixed; top: 0; left: 0; + z-index: 9999999999; + width: 100%; height: 100%; + background-color: rgba(0,0,0,0.5); + transition: visibility 175ms, opacity 175ms; +} + +#loading { + display: inline-block; + width: 50px; + height: 50px; + border: 3px solid rgba(0,0,0,.3); + border-radius: 50%; + border-top-color: black; + animation: spin 1s ease-in-out infinite; + -webkit-animation: spin 1s ease-in-out infinite; +} + +@keyframes spin { + to { -webkit-transform: rotate(360deg); } +} +@-webkit-keyframes spin { + to { -webkit-transform: rotate(360deg); } +} diff --git a/public/css/files.svg b/public/css/files.svg new file mode 100644 index 0000000..6dc8302 --- /dev/null +++ b/public/css/files.svg @@ -0,0 +1,2 @@ + + diff --git a/public/css/kclient.css b/public/css/kclient.css new file mode 100644 index 0000000..d564244 --- /dev/null +++ b/public/css/kclient.css @@ -0,0 +1,74 @@ +.vnc { + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; + width: 100%; + height: 100%; + border: none; + margin: 0; + padding: 0; + overflow: hidden; +} + +#files { + display: none; + position: absolute; + left: 20vw; + top: 50%; + transform: translateY(-50%); + width: 60vw; + height: 60vh; + z-index: 2; + background-color: rgb(9 2 2 / 0.6); + border-radius: 10px; + border-style: inset; + border-color: rgb(255 255 255 / 0.6); +} + +#files_frame { + width: 100%; + height: 100%; +} + +.close { + position: absolute; + background: DimGray; + top: -10px; + right: -10px; + cursor: pointer; + border-radius:50%; + border-style: inset; + border-color: rgb(255 255 255 / 0.6); + width: 20px; + height: 20px; +} + +#lsbar { + position: absolute; + top: 0; + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; + width: max-content; + display: none; + background-color: rgb(9 2 2 / 0.6); + border-radius: 0 0 10px 10px; + border-style: inset; + border-color: rgb(255 255 255 / 0.6); +} + +.icons { + margin: 5px; + padding: 4px; + height: 4vh; + cursor: pointer; + border-radius: 3px; + filter: invert(100%) sepia(0%) saturate(0%) hue-rotate(82deg) brightness(105%) contrast(105%); +} + +.icons:hover { + background: rgba(0, 0, 0, 0.3); +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..6fe0291830d633f1befedbdd623b8321546b7a2c GIT binary patch literal 1150 zcmaKsTTc^V5Qe8MO{BZs^LDrA7Ae#qhZKx}5QBgREKmfLlL#Incx6Ngs4>xys5g4+ zh4GeOL86I&f;;~SJ|CFKmD{{K-#0VwJG1j`S0uvEvV?m{Hkw3IB2opo@SOTFBE($n z6es&Lw`?jU=kxi9l}wfmOy2X=&t218V?77103|6G%H=am5#P2Ya9z!{tWi_f zU&G%bcgC~rd16Z`$C+e3g4QgyyO>T#Gt^TTKJiRbx0A^^_+9WS_(qb6gtQh45x55E zg*S`sb4>G=rRzg&<#Hr+T`2@X6z(j&c9A0~*R`;dqqSOOdud5J+S&w;>Mx0Y-FMwV za&P0iNUuU}h4sGgI3YPFeDXH%kKiAp{{i4jG%EQ_CXRms4AJK-^>x;D+b~w>sST)F z7500e!5pTU=PJIJ?8idSGt`{msM$<|8g&eOo`-a$N9DHh|b3&d8?dQ0uc=si^P z!}g(9CsxA$jGnekOTbF` zQ#K42dpbKcVmY{vz}e9ctge4sbqF%H_fwykag_gTGMBWHfr5 zrhbvU8d{gg?MG|Kw=5g`jydnr$5QAxakw$wuPU~m-qjo?iC>O4H3{}ActYJfoS#KZ zR9$*Gq{h_o(UFwXY03J24Bate+n~Yoq~MK{ze69N>7fQk(!MYEXJ#UXuGiUbq&+VY zIF1l^)V#2Z=zL`U^PJ5A_|8llg)oewsg7K7K_J;6h)|~;zOCMC&oH9!GVrg%U8c?y zddt-BCO^u%bi)1L?bF_#R7xe`+@sZ&mRNVCB0W|0W%zgfHbs8KMBYb4?4NuyN~hnA YlRx_3v~ySm^+DMuPvIAw<1ZqA0HmpQOaK4? literal 0 HcmV?d00001 diff --git a/public/filebrowser.html b/public/filebrowser.html new file mode 100644 index 0000000..cdfc904 --- /dev/null +++ b/public/filebrowser.html @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + +
+ + + diff --git a/public/icon.png b/public/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cd8eae76da503f87a745b5f9b63ccf816cb2ed09 GIT binary patch literal 43019 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∨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ɩ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 literal 0 HcmV?d00001 diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..81120e4 --- /dev/null +++ b/public/index.html @@ -0,0 +1,29 @@ + + + + + + <%- title -%> + + + + + + + + + + +

+ +
+ +
+ +
+
+ + + + + diff --git a/public/js/filebrowser.js b/public/js/filebrowser.js new file mode 100644 index 0000000..76f9eb1 --- /dev/null +++ b/public/js/filebrowser.js @@ -0,0 +1,262 @@ +var host = window.location.hostname; +var port = window.location.port; +var protocol = window.location.protocol; +var path = window.location.pathname; +var socket = io(protocol + '//' + host + ':' + port, { path: path + '/socket.io'}); + +// Open default folder on connect +socket.on('connect',function(){ + $('#filebrowser').empty(); + $('#filebrowser').append($('
').attr('id','loading')); + socket.emit('open', ''); +}); + +// Get file list +function getFiles(directory) { + directory = directory.replace("//","/"); + directory = directory.replace("|","'"); + let directoryClean = directory.replace("'","|"); + if ((directory !== '/') && (directory.endsWith('/'))) { + directory = directory.slice(0, -1); + } + $('#filebrowser').empty(); + $('#filebrowser').append($('
').attr('id','loading')); + socket.emit('getfiles', directory); +} + +// Render file list +async function renderFiles(data) { + let dirs = data[0]; + let files = data[1]; + let directory = data[2]; + let baseName = directory.split('/').slice(-1)[0]; + let parentFolder = directory.replace(baseName,''); + let parentLink = $('').addClass('directory').attr('onclick', 'getFiles(\'' + parentFolder + '\');').text('..'); + let directoryClean = directory.replace("'","|"); + if (directoryClean == '/') { + directoryClean = ''; + } + let table = $('').addClass('fileTable'); + let tableHeader = $(''); + for await (name of ['Name', 'Type', 'Delete (NO WARNING)']) { + tableHeader.append($(''); + for await (item of [parentLink, $(''); + let dirClean = dir.replace("'","|"); + let link = $(''); + let fileClean = file.replace("'","|"); + let link = $('
').text(name)); + } + let parentRow = $('
').text('Parent'), $('')]) { + parentRow.append(item); + } + table.append(tableHeader,parentRow); + $('#filebrowser').empty(); + $('#filebrowser').data('directory', directory); + $('#filebrowser').append($('
').text(directory)); + $('#filebrowser').append(table); + if (dirs.length > 0) { + for await (let dir of dirs) { + let tableRow = $('
').addClass('directory').attr('onclick', 'getFiles(\'' + directoryClean + '/' + dirClean + '\');').text(dir); + let type = $('').text('Dir'); + let del = $('').append($('
').addClass('file').attr('onclick', 'downloadFile(\'' + directoryClean + '/' + fileClean + '\');').text(file); + let type = $('').text('File'); + let del = $('').append($('