Unverified Commit 39f977cb authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #100 from jumpserver/dev_beta

Dev beta
parents 93dc40c3 fd957e28
......@@ -8,6 +8,7 @@
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"static",
"theme/default/",
"favicon.ico"
......@@ -31,8 +32,6 @@
"../node_modules/metismenu/dist/metisMenu.js",
"../node_modules/jquery-sparkline/jquery.sparkline.js",
"../node_modules/tether/dist/js/tether.min.js",
"../node_modules/bootstrap/dist/js/bootstrap.min.js",
"assets/inspinia/inspinia.js",
"assets/slimscroll/jquery.slimscroll.min.js",
"../node_modules/xterm/dist/xterm.js",
"assets/ztree/jquery.ztree.all.min.js",
......
......@@ -9,5 +9,5 @@ RUN npm run-script build
FROM nginx:alpine
COPY --from=stage-build /data/dist /opt/luna/
COPY i18n /opt/luna/i18n
COPY ./src/assets/i18n /opt/luna/i18n
COPY nginx.conf /etc/nginx/conf.d/default.conf
{
"reset": "重置",
"submit": "提交",
"email subject prefix": "邮件主题前缀",
"basic setting": "基本设置",
"email setting": "邮件设置",
"ldap setting": "LDAP设置",
"terminal setting": "终端设置",
"current site url": "当前站点URL",
"user guide url": "用户向导URL",
"user first login update profile done redirect to it": "用户第一次登录,修改profile后重定向到地址",
"server": "服务器",
"view": "视图",
"help": "帮助",
"hide left manager": "隐藏左边栏",
"show left manager": "显示左边栏",
"disconnect all": "断开所有链接",
"disconnect": "断开链接",
"website": "官网",
"search": "搜索",
"settings": "系统设置",
"job center": "作业中心",
"sessions": "会话管理",
"perms": "权限管理",
"assets": "资产管理",
"users": "用户管理",
"dashboard": "仪表盘",
"task": "任务",
"session online": "在线会话",
"session offline": "离线会话",
"commands": "命令记录",
"terminal": "终端管理",
"asset perminssion": "资产授权",
"asset": "资产",
"asset group": "资产组",
"cluster": "集群",
"admin user": "管理用户",
"system user": "系统用户",
"labels": "标签管理",
"user": "用户",
"user group": "用户组",
"login logs": "登陆日志",
"language": "语言选择",
"found": "发现",
"users ": "用户",
"choose a user": "选择一个用户",
"please choose a user": "请选择一个用户",
"cancel": "取消",
"confirm": "确认",
"document": "文档",
"support": "商业支持",
"speed": "速度",
"file manager": "文件管理",
"file": "文件管理",
"new connection": "连接",
"connect": "连接",
"rdp resolution": "RDP分辨率",
"set rdp solution": "设置分辨率",
"select a solution": "选择分辨率",
"set font": "设置字体",
"font": "字体",
"font size": "字体大小",
"full screen": "全屏显示",
"please input password": "请输入密码",
"username": "用户名",
"password": "密码",
"tab list": "窗口列表",
"reconnect": "重新连接",
"are you sure to reconnect it?(rdp not support)": "确定要重新连接吗? (RDP暂不支持)"
}
{
"reset": "重置",
"submit": "提交",
"email subject prefix": "邮件主题前缀",
"basic setting": "基本设置",
"email setting": "邮件设置",
"ldap setting": "LDAP设置",
"terminal setting": "终端设置",
"current site url": "当前站点URL",
"user guide url": "用户向导URL",
"user first login update profile done redirect to it": "用户第一次登录,修改profile后重定向到地址",
"server": "服务器",
"view": "视图",
"help": "帮助",
"hide left manager": "隐藏左边栏",
"show left manager": "显示左边栏",
"disconnect all": "断开所有链接",
"disconnect": "断开链接",
"website": "官网",
"search": "搜索",
"settings": "系统设置",
"job center": "作业中心",
"sessions": "会话管理",
"perms": "权限管理",
"assets": "资产管理",
"users": "用户管理",
"dashboard": "仪表盘",
"task": "任务",
"session online": "在线会话",
"session offline": "离线会话",
"commands": "命令记录",
"terminal": "终端管理",
"asset perminssion": "资产授权",
"asset": "资产",
"asset group": "资产组",
"cluster": "集群",
"admin user": "管理用户",
"system user": "系统用户",
"labels": "标签管理",
"user": "用户",
"user group": "用户组",
"login logs": "登陆日志",
"language": "语言选择",
"found": "发现",
"users ": "用户",
"choose a user": "选择一个用户",
"please choose a user": "请选择一个用户",
"cancel": "取消",
"confirm": "确认",
"document": "文档",
"support": "商业支持",
"speed": "速度",
"file manager": "文件管理",
"file": "文件管理",
"new connection": "连接",
"connect": "连接",
"rdp resolution": "RDP分辨率",
"set rdp solution": "设置分辨率",
"select a solution": "选择分辨率",
"set font": "设置字体",
"font": "字体",
"font size": "字体大小",
"full screen": "全屏显示",
"please input password": "请输入密码",
"username": "用户名",
"password": "密码",
"tab list": "窗口列表",
"reconnect": "重新连接",
"are you sure to reconnect it?(rdp not support)": "确定要重新连接吗? (RDP暂不支持)"
}
{
"name": "luna",
"version": "1.4.7",
"version": "1.5.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
......@@ -232,31 +232,31 @@
}
},
"@angular/common": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-5.2.0.tgz",
"integrity": "sha512-yMFn2isC7/XOs56/2Kzzbb1AASHiwipAPOVFtKe7TdZQClO8fJXwCnk326rzr615+CG0eSBNQWeiFGyWN2riBA==",
"version": "5.2.11",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-5.2.11.tgz",
"integrity": "sha512-LniJjGAeftUJDJh+2+LEjltcGen08C/VMxQ/eUYmesytKy1sN+MWzh3GbpKfEWtWmyUsYTG9lAAJNo3L3jPwsw==",
"requires": {
"tslib": "^1.7.1"
}
},
"@angular/compiler": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-5.2.0.tgz",
"integrity": "sha512-RfYa4ESgjGX0T0ob/Xz00IF7nd2xZkoyRy6oKgL82q42uzB3xZUDMrFNgeGxAUs3H22IkL46/5SSPOMOTMZ0NA==",
"version": "5.2.11",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-5.2.11.tgz",
"integrity": "sha512-ICvB1ud1mxaXUYLb8vhJqiLhGBVocAZGxoHTglv6hMkbrRYcnlB3FZJFOzBvtj+krkd1jamoYLI43UAmesqQ6Q==",
"requires": {
"tslib": "^1.7.1"
}
},
"@angular/compiler-cli": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-5.2.0.tgz",
"integrity": "sha512-+Kef4NjuHwLj2hRPdVo4yAtPmv+kn0Nu/ShmKfaEK4mt9aaQMFxKNt6beUuDK5iUPMB5nuTCZ4bqEFpqd+EtxQ==",
"version": "5.2.11",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-5.2.11.tgz",
"integrity": "sha512-dwrQ0yxoCM/XzKzlm7pTsyg4/6ECjT9emZufGj8t12bLMO8NDn1IJOsqXJA1+onEgQKhlr0Ziwi+96TvDTb1Cg==",
"dev": true,
"requires": {
"chokidar": "^1.4.2",
"minimist": "^1.2.0",
"reflect-metadata": "^0.1.2",
"tsickle": "^0.26.0"
"tsickle": "^0.27.2"
},
"dependencies": {
"minimist": {
......@@ -268,33 +268,33 @@
}
},
"@angular/core": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-5.2.0.tgz",
"integrity": "sha512-s2ne45DguNUubhC1YgybGECC4Tyx3G4EZCntUiRMDWWkmKXSK+6dgHMesyDo8R5Oat8VfN4Anf8l3JHS1He8kg==",
"version": "5.2.11",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-5.2.11.tgz",
"integrity": "sha512-h2vpvXNAdOqKzbVaZcHnHGMT5A8uDnizk6FgGq6SPyw9s3d+/VxZ9LJaPjUk3g2lICA7og1tUel+2YfF971MlQ==",
"requires": {
"tslib": "^1.7.1"
}
},
"@angular/flex-layout": {
"version": "2.0.0-beta.10-4905443",
"resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-2.0.0-beta.10-4905443.tgz",
"integrity": "sha512-jjr6mQ3X2vdEQbsyHD/mz1hfTBUUEOZVLFWEz/sbNoeU7uiA4lvqdp/ASrkZydGJHmTDUYrbBE/9kx0lherZ8Q==",
"version": "5.0.0-beta.15",
"resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-5.0.0-beta.15.tgz",
"integrity": "sha512-rGvvjDu0PMrsfTsNX+qcNc4EloKGXTxW8FT1tKcdIxOUMRQkvflkwAnKW2kRWNfEgDtBNt+VNP98u2w+athvZQ==",
"requires": {
"tslib": "^1.7.1"
}
},
"@angular/forms": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-5.2.0.tgz",
"integrity": "sha512-g1/SF9lY0ZwzJ0w4NXbFsTGGEuUdgtaZny8DmkaqtmA7idby3FW398X0tv25KQfVYKtL+p9Jp1Y8EI0CvrIsvw==",
"version": "5.2.11",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-5.2.11.tgz",
"integrity": "sha512-wBllFlIubPclAFRXUc84Kc7TMeKOftzrQraVZ7ooTNeFLLa/FZLN2K8HGyRde8X/XDsMu1XAmjNfkz++spwTzA==",
"requires": {
"tslib": "^1.7.1"
}
},
"@angular/http": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@angular/http/-/http-5.2.0.tgz",
"integrity": "sha512-V5Cl24dP3rCXTTQvDc0TIKoWqBRAa0DWAQbtr7iuDAt5a1vPGdKz5K1sEiiV6ziwX6gzjiwHjUvL+B+WbIUrQA==",
"version": "5.2.11",
"resolved": "https://registry.npmjs.org/@angular/http/-/http-5.2.11.tgz",
"integrity": "sha512-eR7wNXh1+6MpcQNb3sq4bJVX03dx50Wl3kpPG+Q7N1VSL0oPQSobaTrR17ac3oFCEfSJn6kkUCqtUXha6wcNHg==",
"requires": {
"tslib": "^1.7.1"
}
......@@ -314,25 +314,25 @@
}
},
"@angular/platform-browser": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-5.2.0.tgz",
"integrity": "sha512-c6cR15MfopPwGZ097HdRuAi9+R9BhA3bRRFpP2HmrSSB/BW4ZNovUYwB2QUMSYbd9s0lYTtnavqGm6DKcyF2QA==",
"version": "5.2.11",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-5.2.11.tgz",
"integrity": "sha512-6YZ4IpBFqXx88vEzBZG2WWnaSYXbFWDgG0iT+bZPHAfwsbmqbcMcs7Ogu+XZ4VmK02dTqbrFh7U4P2W+sqrzow==",
"requires": {
"tslib": "^1.7.1"
}
},
"@angular/platform-browser-dynamic": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.2.0.tgz",
"integrity": "sha512-xG1eNoi8sm4Jcly2y98r5mqYVe3XV8sUJCtOhvGBYtvt4dKEQ5tOns6fWQ0nUbl6Vv3Y0xgGUS1JCtfut3DuaQ==",
"version": "5.2.11",
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.2.11.tgz",
"integrity": "sha512-5kKPNULcXNwkyBjpHfF+pq+Yxi8Zl866YSOK9t8txoiQ9Ctw97kMkEJcTetk6MJgBp/NP3YyjtoTAm8oXLerug==",
"requires": {
"tslib": "^1.7.1"
}
},
"@angular/router": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-5.2.0.tgz",
"integrity": "sha512-VXDXtp2A1GQEUEhXg0ZzqHdTUERLgDSo3/Mmpzt+dgLMKlXDSCykcm4gINwE5VQLGD1zQvDFCCRv3seGRNfrqA==",
"version": "5.2.11",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-5.2.11.tgz",
"integrity": "sha512-NT8xYl7Vr3qPygisek3PlXqNROEjg48GXOEsDEc7c8lDBo3EB9Tf328fWJD0GbLtXZNhmmNNxwIe+qqPFFhFAA==",
"requires": {
"tslib": "^1.7.1"
}
......@@ -406,6 +406,14 @@
"normalize.css": "^5.0.0"
},
"dependencies": {
"@angular/flex-layout": {
"version": "2.0.0-beta.10-4905443",
"resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-2.0.0-beta.10-4905443.tgz",
"integrity": "sha512-jjr6mQ3X2vdEQbsyHD/mz1hfTBUUEOZVLFWEz/sbNoeU7uiA4lvqdp/ASrkZydGJHmTDUYrbBE/9kx0lherZ8Q==",
"requires": {
"tslib": "^1.7.1"
}
},
"moment": {
"version": "2.22.2",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
......@@ -563,6 +571,7 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
"integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
"dev": true,
"requires": {
"kind-of": "^3.0.2",
"longest": "^1.0.1",
......@@ -572,7 +581,8 @@
"amdefine": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
"dev": true
},
"amqplib": {
"version": "0.5.2",
......@@ -1356,9 +1366,9 @@
}
},
"bootstrap": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.1.3.tgz",
"integrity": "sha512-rDFIzgXcof0jDyjNosjv4Sno77X4KuPeFxG2XZZv1/Kc8DRVGVADdoQyyOVDwPqL36DDmtCQbrpMCqvpPLJQ0w=="
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.3.1.tgz",
"integrity": "sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag=="
},
"brace-expansion": {
"version": "1.1.8",
......@@ -1858,6 +1868,7 @@
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
"integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
"dev": true,
"requires": {
"align-text": "^0.1.3",
"lazy-cache": "^1.0.3"
......@@ -1866,7 +1877,8 @@
"lazy-cache": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
"integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4="
"integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
"dev": true
}
}
},
......@@ -2698,7 +2710,8 @@
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"dev": true
},
"decode-uri-component": {
"version": "0.2.0",
......@@ -3156,10 +3169,6 @@
"integrity": "sha512-h3zEBLdHvsKfaXv1SHAtykJyNtwYFEKkrWGSFyW1BzGgPQ4ykAzD5Hd8C5MZGTAEhkCKmtyIwYUrapsI0xfKww==",
"dev": true
},
"elfinder": {
"version": "git+https://github.com/Studio-42/elFinder.git#2d3a31f70e3c817417913ab54035717f26e3efce",
"from": "git+https://github.com/Studio-42/elFinder.git#2.1.33"
},
"elliptic": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
......@@ -3933,11 +3942,6 @@
"minimatch": "^3.0.3"
}
},
"filetree-css": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/filetree-css/-/filetree-css-1.0.0.tgz",
"integrity": "sha1-7bfoiHH+ere+CeaZXNouodVzXUw="
},
"fill-range": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz",
......@@ -5031,82 +5035,25 @@
"dev": true
},
"handlebars": {
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz",
"integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=",
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz",
"integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==",
"requires": {
"async": "^1.4.0",
"neo-async": "^2.6.0",
"optimist": "^0.6.1",
"source-map": "^0.4.4",
"uglify-js": "^2.6"
"source-map": "^0.6.1",
"uglify-js": "^3.1.4"
},
"dependencies": {
"async": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
},
"camelcase": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
"integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
"optional": true
},
"cliui": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
"integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
"optional": true,
"requires": {
"center-align": "^0.1.1",
"right-align": "^0.1.1",
"wordwrap": "0.0.2"
}
},
"source-map": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
"requires": {
"amdefine": ">=0.0.4"
}
},
"uglify-js": {
"version": "2.8.29",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
"integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
"optional": true,
"requires": {
"source-map": "~0.5.1",
"uglify-to-browserify": "~1.0.0",
"yargs": "~3.10.0"
"neo-async": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
"integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw=="
},
"dependencies": {
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"optional": true
}
}
},
"wordwrap": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
"integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
"optional": true
},
"yargs": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
"integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
"optional": true,
"requires": {
"camelcase": "^1.0.2",
"cliui": "^2.1.0",
"decamelize": "^1.0.0",
"window-size": "0.1.0"
}
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
}
},
......@@ -5872,7 +5819,8 @@
"is-buffer": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
"integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw="
"integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=",
"dev": true
},
"is-builtin-module": {
"version": "1.0.0",
......@@ -6330,9 +6278,9 @@
"dev": true
},
"jquery": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz",
"integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c="
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz",
"integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw=="
},
"jquery-slimscroll": {
"version": "1.3.8",
......@@ -6749,6 +6697,7 @@
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
"dev": true,
"requires": {
"is-buffer": "^1.1.5"
}
......@@ -6921,9 +6870,9 @@
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
"lodash.assign": {
"version": "4.2.0",
......@@ -7114,7 +7063,8 @@
"longest": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
"integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
"integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
"dev": true
},
"loose-envify": {
"version": "1.3.1",
......@@ -9884,9 +9834,9 @@
"optional": true
},
"reflect-metadata": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.12.tgz",
"integrity": "sha512-n+IyV+nGz3+0q3/Yf1ra12KpCyi001bi4XFxSjbiWWjfqb52iTTtpGXmCCAOWWIAn9KEuFZKGqBERHmrtScZ3A==",
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
"integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
"dev": true
},
"regenerate": {
......@@ -9989,7 +9939,8 @@
"repeat-string": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
"integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
"integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
"dev": true
},
"repeating": {
"version": "2.0.1",
......@@ -10127,6 +10078,7 @@
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
"integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
"dev": true,
"requires": {
"align-text": "^0.1.1"
}
......@@ -11743,15 +11695,15 @@
}
},
"tsickle": {
"version": "0.26.0",
"resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.26.0.tgz",
"integrity": "sha512-eWJ2CUfttGK0LqF9iJ/Avnxbj4M+fCyJ50Zag3wm73Fut1hsasPRHKxKdrMWVj4BMHnQNx7TO+DdNmLmJTSuNw==",
"version": "0.27.5",
"resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.27.5.tgz",
"integrity": "sha512-NP+CjM1EXza/M8mOXBLH3vkFEJiu1zfEAlC5WdJxHPn8l96QPz5eooP6uAgYtw1CcKfuSyIiheNUdKxtDWCNeg==",
"dev": true,
"requires": {
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"source-map": "^0.5.6",
"source-map-support": "^0.4.2"
"source-map": "^0.6.0",
"source-map-support": "^0.5.0"
},
"dependencies": {
"minimist": {
......@@ -11759,6 +11711,22 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"source-map-support": {
"version": "0.5.13",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
"integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
}
}
},
......@@ -11888,7 +11856,6 @@
"version": "3.4.9",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
"integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
"dev": true,
"requires": {
"commander": "~2.17.1",
"source-map": "~0.6.1"
......@@ -11897,14 +11864,12 @@
"commander": {
"version": "2.17.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
"dev": true
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
}
},
......@@ -11912,6 +11877,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
"integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
"dev": true,
"optional": true
},
"uglifyjs-webpack-plugin": {
......@@ -14175,7 +14141,8 @@
"window-size": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
"integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0="
"integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
"dev": true
},
"wordwrap": {
"version": "0.0.3",
......
......@@ -12,17 +12,18 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^5.2.10",
"@angular/cdk": "^5.2.5",
"@angular/common": "5.2.0",
"@angular/compiler": "5.2.0",
"@angular/core": "5.2.0",
"@angular/forms": "5.2.0",
"@angular/http": "5.2.0",
"@angular/material": "^5.2.5",
"@angular/platform-browser": "5.2.0",
"@angular/platform-browser-dynamic": "5.2.0",
"@angular/router": "5.2.0",
"@angular/animations": "^5.2.11",
"@angular/cdk": "^5.2.4",
"@angular/common": "^5.2.11",
"@angular/compiler": "^5.2.11",
"@angular/core": "^5.2.11",
"@angular/flex-layout": "^5.0.0-beta.15",
"@angular/forms": "^5.2.11",
"@angular/http": "^5.2.11",
"@angular/material": "^5.2.0",
"@angular/platform-browser": "^5.2.11",
"@angular/platform-browser-dynamic": "^5.2.11",
"@angular/router": "^5.2.11",
"@swimlane/ngx-datatable": "^11.3.2",
"@swimlane/ngx-ui": "^20.2.1",
"@types/jquery": "^3.3.6",
......@@ -31,22 +32,20 @@
"ajv": "^6.5.0",
"animate.css": "^3.6.1",
"body-parser": "^1.18.2",
"bootstrap": "^4.1.1",
"bootstrap": "^4.3.1",
"clipboard": "^1.7.1",
"compass-mixins": "^0.12.10",
"core-js": "2.5.3",
"directory-encoder": "^0.9.2",
"elfinder": "git+https://github.com/Studio-42/elFinder.git#2.1.33",
"filetree-css": "^1.0.0",
"font-awesome": "4.7.0",
"guacamole-common-js": "0.9.14-b",
"handlebars": "^4.0.11",
"handlebars": "^4.1.2",
"intl": "1.2.5",
"jquery": "3.2.1",
"jquery": "^3.4.1",
"jquery-slimscroll": "^1.3.8",
"jquery-sparkline": "^2.4.0",
"jvectormap": "1.2.2",
"lodash": "^4.17.10",
"lodash": "^4.17.15",
"material-design-icons": "^3.0.1",
"materialize-css": "^0.100.2",
"metismenu": "^2.7.9",
......@@ -79,7 +78,7 @@
"@angular-devkit/core": "^0.4.9",
"@angular-devkit/schematics": "^0.4.9",
"@angular/cli": "^1.7.4",
"@angular/compiler-cli": "5.2.0",
"@angular/compiler-cli": "^5.2.11",
"@angular/language-service": "5.2.0",
"@types/jasmine": "2.8.4",
"@types/jasminewd2": "~2.0.2",
......
{
"/api": {
"target": "http://127.0.0.1:5001",
"target": "http://127.0.0.1:8080",
"secure": false
},
"/luna/i18n": {
......@@ -8,7 +8,7 @@
"secure": false
},
"/socket.io/": {
"target": "http://127.0.0.1:5001",
"target": "http://127.0.0.1:5000",
"secure": false,
"ws": true
},
......@@ -27,5 +27,13 @@
"^/rdp": ""
},
"secure": false
},
"/guacamole/": {
"target": "http://127.0.0.1:8083",
"secure": false,
"ws": true,
"pathRewrite": {
"^/guacamole": ""
}
}
}
/**
* app 模块
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {FormsModule, ReactiveFormsModule} from '@angular/forms'; // <-- NgModel lives here
import {NGXLogger} from 'ngx-logger';
import {HttpClientModule} from '@angular/common/http';
import {AppRouterModule} from './router/router.module';
import {AppComponent} from './pages/app.component';
// service
import {AppService, HttpService, LocalStorageService, LogService, UUIDService} from './app.service';
import {CookieService} from 'ngx-cookie-service';
import {MAT_LABEL_GLOBAL_OPTIONS} from '@angular/material';
// service
import {AppService, HttpService, LocalStorageService, NavService, LogService, UUIDService, TreeFilterService} from './app.service';
import {AppRouterModule} from './router/router.module';
import {Pipes} from './pipes/pipes';
import {AppComponent} from './pages/app.component';
import {PagesComponents} from './pages/pages.component';
import {ElementComponents} from './elements/elements.component';
import {ChangLanWarningDialogComponent, RDPSolutionDialogComponent, FontDialogComponent} from './elements/nav/nav.component';
import {DialogService, ElementDialogAlertComponent} from './elements/dialog/dialog.service';
import {PluginModules} from './plugins/plugins';
import {TestPageComponent} from './test-page/test-page.component';
import {AssetTreeDialogComponent, ManualPasswordDialogComponent} from './elements/asset-tree/asset-tree.component';
import {AssetTreeDialogComponent, ManualPasswordDialogComponent} from './elements/connect/connect.component';
import {SftpComponent} from './elements/sftp/sftp.component';
......@@ -66,13 +55,14 @@ import {SftpComponent} from './elements/sftp/sftp.component';
AppService,
HttpService,
LogService,
NavService,
UUIDService,
LocalStorageService,
DialogService,
CookieService,
TreeFilterService,
NGXLogger,
{provide: MAT_LABEL_GLOBAL_OPTIONS, useValue: {float: 'always'}}
]
})
export class AppModule {
......
/**
* 后台控制
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Injectable, OnInit} from '@angular/core';
import {EventEmitter, Injectable, OnInit} from '@angular/core';
import {Router} from '@angular/router';
import {CookieService} from 'ngx-cookie-service';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import {DataStore, User, Browser, i18n} from './globals';
import {environment} from '../environments/environment';
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
import {NGXLogger} from 'ngx-logger';
import {SystemUser, GuacObjAddResp, TreeNode, User as _User, NavEvt} from './model';
import {environment} from '../environments/environment';
import * as UUID from 'uuid-js/lib/uuid.js';
declare function unescape(s: string): string;
class GuacObjAddResp {
code: number;
result: string;
}
@Injectable()
export class HttpService {
......@@ -58,31 +49,45 @@ export class HttpService {
return this.http.options(url, options);
}
report_browser() {
reportBrowser() {
return this.http.post('/api/browser', JSON.stringify(Browser));
}
check_login(user: any) {
checkLogin(user: any) {
return this.http.post('/api/checklogin', user);
}
get_user_profile() {
return this.http.get('/api/users/v1/profile/');
getUserProfile() {
return this.http.get<_User>('/api/users/v1/profile/');
}
getMyGrantedAssets(keyword) {
const url = `/api/perms/v1/users/assets/tree/?search=${keyword}`;
return this.http.get<Array<TreeNode>>(url);
}
get_my_granted_nodes() {
return this.http.get<Array<Node>>('/api/perms/v1/users/nodes-with-assets/tree/?cache_policy=1');
getMyGrantedNodes(async: boolean, refresh?: boolean) {
const cachePolicy = refresh ? '2' : '1';
const syncUrl = `/api/perms/v1/users/nodes-with-assets/tree/?cache_policy=${cachePolicy}`;
const asyncUrl = `/api/perms/v1/users/nodes/children-with-assets/tree/?cache_policy=${cachePolicy}`;
const url = async ? asyncUrl : syncUrl;
return this.http.get<Array<TreeNode>>(url);
}
get_my_granted_remote_apps() {
return this.http.get<Array<Node>>('/api/perms/v1/user/remote-apps/tree/');
getMyGrantedRemoteApps() {
return this.http.get<Array<TreeNode>>('/api/perms/v1/user/remote-apps/tree/');
}
refresh_my_granted_nodes() {
return this.http.get<Array<Node>>('/api/perms/v1/users/nodes-with-assets/tree/?cache_policy=2');
getMyAssetSystemUsers(assetId: string) {
const url = `/api/v1/perms/users/assets/${assetId}/system-users/`;
return this.http.get<Array<SystemUser>>(url);
}
get_guacamole_token(user_id: string, authToken: string) {
refreshMyGrantedNodes() {
return this.http.get<Array<TreeNode>>('/api/perms/v1/user/nodes-assets/tree/?cache_policy=2');
}
getGuacamoleToken(user_id: string, authToken: string) {
const body = new HttpParams()
.set('username', user_id)
.set('password', 'jumpserver')
......@@ -100,17 +105,17 @@ export class HttpService {
{headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded')});
}
guacamole_add_asset(user_id: string, asset_id: string, system_user_id: string, system_user_username?: string, system_user_password?: string) {
guacamoleAddAsset(userId: string, assetId: string, systemUserId: string, systemUserUsername?: string, systemUserPassword?: string) {
let params = new HttpParams()
.set('user_id', user_id)
.set('asset_id', asset_id)
.set('system_user_id', system_user_id)
.set('token', DataStore.guacamole_token);
if (system_user_username) {
params = params.set('username', system_user_username);
}
if (system_user_password) {
params = params.set('password', system_user_password);
.set('user_id', userId)
.set('asset_id', assetId)
.set('system_user_id', systemUserId)
.set('token', DataStore.guacamoleToken);
let body = new HttpParams();
if (systemUserUsername && systemUserPassword) {
systemUserUsername = btoa(systemUserUsername);
systemUserPassword = btoa(systemUserPassword);
body = body.set('username', systemUserUsername).set('password', systemUserPassword);
}
const solution = localStorage.getItem('rdpSolution') || 'Auto';
if (solution !== 'Auto') {
......@@ -119,8 +124,9 @@ export class HttpService {
params = params.set('width', width).set('height', height);
}
return this.http.get<GuacObjAddResp>(
return this.http.post<GuacObjAddResp>(
'/guacamole/api/session/ext/jumpserver/asset/add',
body.toString(),
{
headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'),
params: params
......@@ -128,11 +134,17 @@ export class HttpService {
);
}
guacamole_add_remote_app(user_id: string, remote_app_id: string) {
guacamoleAddRemoteApp(userId: string, remoteAppId: string, systemUserUsername?: string, systemUserPassword?: string) {
let params = new HttpParams()
.set('user_id', user_id)
.set('remote_app_id', remote_app_id)
.set('token', DataStore.guacamole_token);
.set('user_id', userId)
.set('remote_app_id', remoteAppId)
.set('token', DataStore.guacamoleToken);
let body = new HttpParams();
if (systemUserUsername && systemUserPassword) {
systemUserUsername = btoa(systemUserUsername);
systemUserPassword = btoa(systemUserPassword);
body = body.set('username', systemUserUsername).set('password', systemUserPassword);
}
const solution = localStorage.getItem('rdpSolution') || 'Auto';
if (solution !== 'Auto') {
const width = solution.split('x')[0];
......@@ -140,8 +152,9 @@ export class HttpService {
params = params.set('width', width).set('height', height);
}
return this.http.get<GuacObjAddResp>(
return this.http.post<GuacObjAddResp>(
'/guacamole/api/session/ext/jumpserver/remote-app/add',
body.toString(),
{
headers: new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'),
params: params
......@@ -149,7 +162,7 @@ export class HttpService {
);
}
guacamole_token_add_asset(assetToken: string, token: string) {
guacamoleTokenAddAsset(assetToken: string, token: string) {
let params = new HttpParams()
.set('asset_token', assetToken)
.set('token', token);
......@@ -169,12 +182,11 @@ export class HttpService {
}
search(q: string) {
const params = new HttpParams()
.set('q', q);
const params = new HttpParams().set('q', q);
return this.http.get('/api/search', {params: params});
}
get_replay(token: string) {
getReplay(token: string) {
return this.http.get('/api/terminal/v1/sessions/' + token + '/replay');
}
......@@ -182,11 +194,11 @@ export class HttpService {
// return this.http.get('/api/terminal/v2/sessions/' + token + '/replay');
// }
get_replay_data(src: string) {
getReplayData(src: string) {
return this.http.get(src);
}
get_user_id_from_token(token: string) {
getUserIdFromToken(token: string) {
const params = new HttpParams()
.set('user-only', '1')
.set('token', token);
......@@ -267,94 +279,82 @@ export class AppService implements OnInit {
constructor(private _http: HttpService,
private _router: Router,
private _logger: LogService,
private _cookie: CookieService,
private _logger: LogService,
private _localStorage: LocalStorageService) {
if (this._cookie.get('loglevel')) {
// 0.- Level.OFF
// 1.- Level.ERROR
// 2.- Level.WARN
// 3.- Level.INFO
// 4.- Level.DEBUG
// 5.- Level.LOG
this._logger.level = parseInt(this._cookie.get('loglevel'), 10);
// this._logger.debug('Your debug stuff');
// this._logger.info('An info');
// this._logger.warn('Take care ');
// this._logger.error('Too late !');
// this._logger.log('log !');
} else {
this._cookie.set('loglevel', '0', 99, '/', document.domain);
// this._logger.level = parseInt(Cookie.getCookie('loglevel'));
this._logger.level = 0;
this.setLogLevel();
this.setLang();
this.checklogin();
}
if (environment.production) {
this._logger.level = 2;
this.checklogin();
ngOnInit() {
}
if (this._cookie.get('lang')) {
this.lang = this._cookie.get('lang');
} else {
this.lang = window.navigator.languages ? window.navigator.languages[0] : 'cn';
this._cookie.set('lang', this.lang);
setLogLevel() {
// 设置logger level
let logLevel = this._cookie.get('logLevel');
if (!logLevel) {
logLevel = environment.production ? '1' : '5';
}
this._logger.level = parseInt(logLevel, 10);
}
if (this.lang !== 'en') {
this._http.get('/luna/i18n/' + this.lang + '.json').subscribe(
setLang() {
let lang = this._cookie.get('lang');
if (!lang) {
lang = navigator.language;
}
lang = lang.substr(0, 2);
this.lang = lang;
if (lang !== 'en') {
let url = `/luna/i18n/zh.json`;
if (!environment.production) {
url = `/assets/i18n/zh.json`;
}
this._http.get(url).subscribe(
data => {
this._localStorage.set('lang', JSON.stringify(data));
},
err => {
this._logger.error('Load i18n file error: ', err.error);
}
);
}
const l = this._localStorage.get('lang');
if (l) {
try {
const data = JSON.parse(l);
Object.keys(data).forEach((k, _) => {
i18n.set(k, data[k]);
});
} catch (e) {
this._logger.error('Parse lang json failed');
}
}
ngOnInit() {
}
checklogin() {
this._logger.log('service.ts:AppService,checklogin');
if (DataStore.Path) {
if (!DataStore.Path) {
this._router.navigate(['FOF']);
}
if (document.location.pathname === '/luna/connect') {
} else {
return;
}
if (User.logined) {
if (document.location.pathname === '/login') {
this._router.navigate(['']);
} else {
this._router.navigate([document.location.pathname]);
}
// jQuery('angular2').show();
} else {
this._http.get_user_profile()
.subscribe(
data => {
User.id = data['id'];
User.name = data['name'];
User.username = data['username'];
User.email = data['email'];
User.is_active = data['is_active'];
User.is_superuser = data['is_superuser'];
User.role = data['role'];
// User.groups = data['groups'];
User.wechat = data['wechat'];
User.comment = data['comment'];
User.date_expired = data['date_expired'];
if (data['phone']) {
User.phone = data['phone'].toString();
}
User.logined = data['logined'];
this._logger.debug(User);
this._localStorage.set('user', data['id']);
return;
}
this._http.getUserProfile().subscribe(
user => {
Object.assign(User, user);
User.logined = true;
this._localStorage.set('user', user.id);
},
err => {
// this._logger.error(err);
......@@ -363,29 +363,11 @@ export class AppService implements OnInit {
document.location.pathname + document.location.search;
// this._router.navigate(['login']);
},
// () => {
// if (User.logined) {
// if (document.location.pathname === '/login') {
// this._router.navigate(['']);
// } else {
// this._router.navigate([document.location.pathname]);
// }
// } else {
// this._router.navigate(['login']);
// }
// jQuery('angular2').show();
// }
);
}
}
} else {
this._router.navigate(['FOF']);
// jQuery('angular2').show();
}
}
browser() {
this._http.report_browser();
this._http.reportBrowser();
}
getQueryString(name) {
......@@ -396,124 +378,6 @@ export class AppService implements OnInit {
}
return null;
}
//
//
// HideLeft() {
// DataStore.leftbarhide = true;
//
// DataStore.Nav.map(function (value, i) {
// for (var ii in value['children']) {
// if (DataStore.Nav[i]['children'][ii]['id'] === 'HideLeftManager') {
// DataStore.Nav[i]['children'][ii] = {
// 'id': 'ShowLeftManager',
// 'click': 'ShowLeft',
// 'name': 'Show left manager'
// };
// }
// }
// });
//
// }
//
// ShowLeft() {
// DataStore.leftbarhide = false;
//
// DataStore.Nav.map(function (value, i) {
// for (var ii in value['children']) {
// if (DataStore.Nav[i]['children'][ii]['id'] === 'ShowLeftManager') {
// DataStore.Nav[i]['children'][ii] = {
// 'id': 'HideLeftManager',
// 'click': 'HideLeft',
// 'name': 'Hide left manager'
// };
// }
// }
// });
//
//
// }
//
// setMyinfo(user:User) {
// // Update data store
// this._dataStore.user = user;
// this._logger.log("service.ts:AppService,setMyinfo");
// this._logger.debug(user);
// // Push the new list of todos into the Observable stream
// // this._dataObserver.next(user);
// // this.myinfo$ = new Observable(observer => this._dataObserver = observer).share()
// }
//
// getMyinfo() {
// this._logger.log('service.ts:AppService,getMyinfo');
// return this.http.get('/api/userprofile')
// .map(res => res.json())
// .subscribe(response => {
// DataStore.user = response;
// // this._logger.warn(this._dataStore.user);
// // this._logger.warn(DataStore.user)
// });
// }
//
// getUser(id: string) {
// this._logger.log('service.ts:AppService,getUser');
// return this.http.get('/api/userprofile')
// .map(res => res.json());
// }
//
// gettest() {
// this._logger.log('service.ts:AppService,gettest');
// this.http.get('/api/userprofile')
// .map(res => res.json())
// .subscribe(res => {
// return res;
// });
// }
//
// getGrouplist() {
// this._logger.log('service.ts:AppService,getGrouplist');
// return this.http.get('/api/grouplist')
// .map(res => res.json());
// }
//
// getUserlist(id: string) {
// this._logger.log('service.ts:AppService,getUserlist');
// if (id)
// return this.http.get('/api/userlist/' + id)
// .map(res => res.json());
// else
// return this.http.get('/api/userlist')
// .map(res => res.json());
// }
//
// delGroup(id) {
//
// }
//
//
// copy() {
// var clipboard = new Clipboard('#Copy');
//
// clipboard.on('success', function (e) {
// console.info('Action:', e.action);
// console.info('Text:', e.text);
// console.info('Trigger:', e.trigger);
//
// e.clearSelection();
// });
// console.log('ffff');
// console.log(window.getSelection().toString());
//
// var copy = new Clipboard('#Copy', {
// text: function () {
// return window.getSelection().toString();
// }
// });
// copy.on('success', function (e) {
// layer.alert('Lucky Copyed!');
// });
//
// }
}
@Injectable()
......@@ -527,3 +391,54 @@ export class UUIDService {
}
}
@Injectable()
export class NavService {
onNavClick: EventEmitter<NavEvt> = new EventEmitter<NavEvt>();
constructor(private store: LocalStorageService) {}
disconnectAllConnection() {
const evt = new NavEvt('disconnectAll', '');
this.onNavClick.emit(evt);
}
disconnectConnection() {
const evt = new NavEvt('disconnect', '');
this.onNavClick.emit(evt);
}
changeLang(value) {
const evt = new NavEvt('changeLang', value);
this.onNavClick.emit(evt);
}
get treeLoadAsync() {
const value = this.store.get('LoadTreeAsync');
return value === '1';
}
set treeLoadAsync(v: boolean) {
const value = v ? '1' : '0';
this.store.set('LoadTreeAsync', value);
}
get skipAllManualPassword() {
const value = this.store.get('SkipAllManualPassword');
return value === '1';
}
set skipAllManualPassword(v) {
const value = v ? '1' : '0';
this.store.set('SkipAllManualPassword', value);
}
}
@Injectable()
export class TreeFilterService {
onFilter: EventEmitter<string> = new EventEmitter<string>();
filter(q: string) {
this.onFilter.emit(q);
}
}
<ul id="ztree" class="ztree"></ul>
<div>
<ul id="assetsTree" class="ztree">
{{ "Loading"|trans }} ...
</ul>
<ul id="remoteAppsTree" class="ztree">
{{ "Loading"|trans }} ...
</ul>
</div>
<div #rMenu *ngIf="isShowRMenu" class="basicContext" [style.top]="pos.top" [style.left]="pos.left">
<table>
......
#ztree .fa {
.tree-refresh .fa {
width: 24px;
height: 24px;
line-height: 24px;
......
import {Component, Input, Output, OnInit, Inject, SimpleChanges, OnChanges, ElementRef, ViewChild, EventEmitter} from '@angular/core';
import {NavList, View} from '../../pages/control/control/control.component';
import {AppService, HttpService, LogService} from '../../app.service';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {FormControl, Validators} from '@angular/forms';
import {Component, Input, OnInit, OnDestroy, ElementRef, ViewChild} from '@angular/core';
import {MatDialog} from '@angular/material';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {ActivatedRoute} from '@angular/router';
import * as jQuery from 'jquery/dist/jquery.min';
import {AppService, HttpService, LogService, NavService, TreeFilterService} from '@app/app.service';
import {connectEvt, translate} from '@app/globals';
import {TreeNode, ConnectEvt} from '@app/model';
declare var $: any;
@Component({
selector: 'elements-asset-tree',
templateUrl: './asset-tree.component.html',
styleUrls: ['./asset-tree.component.scss']
styleUrls: ['./asset-tree.component.scss'],
})
export class ElementAssetTreeComponent implements OnInit, OnChanges {
export class ElementAssetTreeComponent implements OnInit, OnDestroy {
@Input() query: string;
@Input() searchEvt$: BehaviorSubject<string>;
@ViewChild('rMenu') rMenu: ElementRef;
......@@ -33,106 +33,123 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
title: 'title'
}
},
callback: {
onClick: this.onCzTreeOnClick.bind(this),
onRightClick: this.onRightClick.bind(this)
},
};
pos = {left: '100px', top: '200px'};
hiddenNodes: any;
expandNodes: any;
zTree: any;
assetsTree: any;
remoteAppsTree: any;
isShowRMenu = false;
rightClickSelectNode: any;
hasLoginTo = false;
treeFilterSubscription: any;
onCzTreeOnClick(event, treeId, treeNode, clickFlag) {
if (treeNode.isParent) {
const zTreeObj = $.fn.zTree.getZTreeObj('ztree');
zTreeObj.expandNode(treeNode);
} else {
this._http.get_user_profile().subscribe();
this.Connect(treeNode);
}
}
constructor(private _appService: AppService,
constructor(private _appSvc: AppService,
private _treeFilterSvc: TreeFilterService,
public _dialog: MatDialog,
public _logger: LogService,
private activatedRoute: ActivatedRoute,
private _http: HttpService
) {
this.searchEvt$ = new BehaviorSubject<string>(this.query);
private _http: HttpService,
private _navSvc: NavService
) {}
ngOnInit() {
this.initTree();
document.addEventListener('click', this.hideRMenu.bind(this), false);
this.treeFilterSubscription = this._treeFilterSvc.onFilter.subscribe(
keyword => {
this._logger.debug('Filter tree: ', keyword);
this.filterAssets(keyword);
this.filterRemoteApps(keyword);
}
);
}
getGrantedAssetsNodes() {
this._http.get_my_granted_nodes()
.subscribe(response => {
this.Data = [...response, ...this.Data];
this.draw();
});
ngOnDestroy(): void {
this.treeFilterSubscription.unsubscribe();
}
refreshGrantedAssetsNodes() {
this._http.refresh_my_granted_nodes()
.subscribe(response => {
this.Data = [...response, ...this.Data];
this.draw();
});
onAssetsNodeClick(event, treeId, treeNode, clickFlag) {
if (treeNode.isParent) {
this.assetsTree.expandNode(treeNode);
} else {
this._http.getUserProfile().subscribe();
this.connectAsset(treeNode);
}
}
getGrantedRemoteApps() {
this._http.get_my_granted_remote_apps()
.subscribe(response => {
if (response.length > 1) {
this.Data = [...this.Data, ...response];
this.draw();
refreshAssetsTree() {
this.assetsTree.destroy();
this.initAssetsTree(true);
}
});
initAssetsTree(refresh?: boolean) {
const setting = Object.assign({}, this.setting);
setting['callback'] = {
onClick: this.onAssetsNodeClick.bind(this),
onRightClick: this.onRightClick.bind(this)
};
if (this._navSvc.treeLoadAsync) {
setting['async'] = {
enable: true,
url: '/api/perms/v1/users/nodes/children-with-assets/tree/',
autoParam: ['id=key', 'name=n', 'level=lv'],
type: 'get'
};
}
ngOnInit() {
this.getGrantedAssetsNodes();
this.getGrantedRemoteApps();
document.addEventListener('click', this.hideRMenu.bind(this), false);
this.searchEvt$.asObservable()
.debounceTime(300)
.distinctUntilChanged()
.subscribe((n) => {
this.filter();
this._http.getMyGrantedNodes(this._navSvc.treeLoadAsync, refresh).subscribe(resp => {
const assetsTree = $.fn.zTree.init($('#assetsTree'), setting, resp);
this.assetsTree = assetsTree;
this.rootNodeAddDom(assetsTree, () => {
this.refreshAssetsTree();
});
});
}
ngOnChanges(changes: SimpleChanges) {
if (changes['Data'] && this.Data) {
this.draw();
refreshRemoteAppsTree() {
this.remoteAppsTree.destroy();
this.initRemoteAppsTree();
}
if (changes['query'] && !changes['query'].firstChange) {
this.searchEvt$.next(this.query);
onRemoteAppsNodeClick(event, treeId, treeNode, clickFlag) {
if (treeNode.isParent) {
this.remoteAppsTree.expandNode(treeNode);
} else {
this._http.getUserProfile().subscribe();
this.connectAsset(treeNode);
}
}
refreshNodes() {
this.zTree.destroy();
this.Data = [];
this.refreshGrantedAssetsNodes();
this.getGrantedRemoteApps();
initRemoteAppsTree() {
const setting = Object.assign({}, this.setting);
setting['callback'] = {
onClick: this.onRemoteAppsNodeClick.bind(this),
};
this._http.getMyGrantedRemoteApps().subscribe(
resp => {
const tree = $.fn.zTree.init($('#remoteAppsTree'), setting, resp);
this.remoteAppsTree = tree;
this.rootNodeAddDom(tree, () => {
this.refreshRemoteAppsTree();
});
}
);
}
draw() {
$.fn.zTree.init($('#ztree'), this.setting, this.Data);
this.zTree = $.fn.zTree.getZTreeObj('ztree');
this.rootNodeAddDom(this.zTree, () => {
this.refreshNodes();
});
initTree() {
this.initAssetsTree();
this.initRemoteAppsTree();
// Todo: connect to some asset, direct
this.activatedRoute.queryParams.subscribe(params => {
const login_to = params['login_to'];
if (login_to && !this.hasLoginTo) {
this.Data.forEach(t => {
if (login_to === t.id && t.isParent === false) {
this.hasLoginTo = true;
this.Connect(t);
this.connectAsset(t);
return;
}
});
......@@ -140,12 +157,19 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
});
}
connectAsset(node: TreeNode) {
const evt = new ConnectEvt(node, 'asset');
connectEvt.next(evt);
}
rootNodeAddDom(ztree, callback) {
const refreshIcon = '<a id="tree-refresh"><i class="fa fa-refresh"></i></a>';
const tId = ztree.setting.treeId + '_tree_refresh';
const refreshIcon = '<a id=' + tId + ' class="tree-refresh">' +
'<i class="fa fa-refresh" style="font-family: FontAwesome !important;" ></i></a>';
const rootNode = ztree.getNodes()[0];
const $rootNodeRef = $('#' + rootNode.tId + '_a');
$rootNodeRef.after(refreshIcon);
const refreshIconRef = $('#tree-refresh');
const refreshIconRef = $('#' + tId);
refreshIconRef.bind('click', function () {
callback();
});
......@@ -186,154 +210,124 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
}
if (!treeNode && event.target.tagName.toLowerCase() !== 'button' && $(event.target).parents('a').length === 0) {
this.zTree.cancelSelectedNode();
this.assetsTree.cancelSelectedNode();
this.showRMenu(event.clientX, event.clientY);
} else if (treeNode && !treeNode.noR) {
this.zTree.selectNode(treeNode);
this.assetsTree.selectNode(treeNode);
this.showRMenu(event.clientX, event.clientY);
this.rightClickSelectNode = treeNode;
}
}
connectAsset(node) {
const system_users = node.meta.system_users;
const host = node.meta.asset;
let user: any;
if (system_users.length > 1) {
user = this.checkPriority(system_users);
if (user) {
this.login(host, user);
} else {
const dialogRef = this._dialog.open(AssetTreeDialogComponent, {
height: '200px',
width: '300px',
data: {users: system_users}
});
connectFileManager() {
const node = this.rightClickSelectNode;
const evt = new ConnectEvt(node, 'sftp');
connectEvt.next(evt);
}
dialogRef.afterClosed().subscribe(result => {
if (result) {
for (const i of system_users) {
if (i.id.toString() === result.toString()) {
user = i;
break;
connectTerminal() {
const host = this.rightClickSelectNode;
this.connectAsset(host);
}
filterAssets(keyword) {
if (this._navSvc.treeLoadAsync) {
this._logger.debug('Filter assets server');
this.filterAssetsServer(keyword);
} else {
this._logger.debug('Filter assets local');
this.filterAssetsLocal(keyword);
}
this.login(host, user);
}
});
filterTree(keyword, tree, filterCallback) {
const nodes = tree.transformToArray(tree.getNodes());
if (!keyword) {
if (tree.hiddenNodes) {
tree.showNodes(tree.hiddenNodes);
tree.hiddenNodes = null;
}
} else if (system_users.length === 1) {
user = system_users[0];
this.login(host, user);
} else {
alert('该主机没有授权登录用户');
if (tree.expandNodes) {
tree.expandNodes.forEach((node) => {
if (node.id !== nodes[0].id) {
tree.expandNode(node, false);
}
});
tree.expandNodes = null;
}
connectRemoteApp(node) {
const id = NavList.List.length - 1;
if (node) {
NavList.List[id].nick = node.name;
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].remoteApp = node.id;
NavList.List[id].type = 'rdp';
NavList.List.push(new View());
NavList.Active = id;
return null;
}
let shouldShow = [];
const matchedNodes = tree.getNodesByFilter(filterCallback);
matchedNodes.forEach((node) => {
const parents = this.recurseParent(node);
const children = this.recurseChildren(node);
shouldShow = [...shouldShow, ...parents, ...children, node];
});
tree.hiddenNodes = nodes;
tree.expandNodes = shouldShow;
tree.hideNodes(nodes);
tree.showNodes(shouldShow);
shouldShow.forEach((node) => {
if (node.isParent) {
tree.expandNode(node, true);
}
this._logger.debug(NavList);
});
}
Connect(node) {
switch (node.meta.type) {
case 'asset':
this.connectAsset(node);
break;
case 'remote_app':
this.connectRemoteApp(node);
break;
default:
alert('Unknown type: ' + node.meta.type);
filterRemoteApps(keyword) {
if (!this.remoteAppsTree) {
return null;
}
function filterCallback(node: TreeNode) {
return node.name.toLowerCase().indexOf(keyword) !== -1;
}
return this.filterTree(keyword, this.remoteAppsTree, filterCallback);
}
connectFileManager() {
const host = this.rightClickSelectNode.meta.asset;
const id = NavList.List.length - 1;
if (host) {
NavList.List[id].nick = '[FILE]' + host.hostname;
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].host = host;
NavList.List[id].type = 'sftp';
NavList.List.push(new View());
NavList.Active = id;
filterAssetsServer(keyword) {
if (!this.assetsTree) {
return;
}
this._logger.debug(NavList);
if (!keyword) {
const searchNode = this.assetsTree.getNodesByFilter((node) => node.id === 'search');
if (searchNode) {
this.assetsTree.removeChildNodes(searchNode[0]);
this.assetsTree.removeNode(searchNode[0]);
}
connectTerminal() {
const host = this.rightClickSelectNode;
this.Connect(host);
const treeNodes = this.assetsTree.getNodes();
if (treeNodes.length !== 0) {
this.assetsTree.showNode(treeNodes[0]);
}
manualSetUserAuthLogin(host, user) {
user = Object.assign({}, user);
const dialogRef = this._dialog.open(ManualPasswordDialogComponent, {
height: '250px',
width: '400px',
data: {username: user.username}
});
dialogRef.afterClosed().subscribe(result => {
if (!result) {
return;
}
user.username = btoa(result.username);
user.password = btoa(result.password);
return this.login(host, user);
this._http.getMyGrantedAssets(keyword).subscribe(nodes => {
const treeNodes = this.assetsTree.getNodes();
if (treeNodes.length !== 0) {
this.assetsTree.hideNode(treeNodes[0]);
}
const newNode = {id: 'search', name: translate('Search'), isParent: true, open: true, zAsync: true};
const parentNode = this.assetsTree.addNodes(null, newNode)[0];
parentNode.zAsync = true;
this.assetsTree.addNodes(parentNode, nodes);
parentNode.open = true;
});
return;
}
login(host, user) {
const id = NavList.List.length - 1;
this._logger.debug(NavList);
this._logger.debug(host);
if (user.login_mode === 'manual' && !user.password && user.protocol === 'rdp') {
return this.manualSetUserAuthLogin(host, user);
}
if (user) {
NavList.List[id].nick = host.hostname;
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].host = host;
NavList.List[id].user = user;
if (user.protocol === 'ssh' || user.protocol === 'telnet') {
NavList.List[id].type = 'ssh';
} else if (user.protocol === 'rdp' || user.protocol === 'vnc') {
NavList.List[id].type = 'rdp';
}
NavList.List.push(new View());
NavList.Active = id;
jQuery('.tabs').animate({'scrollLeft': 150 * id}, 400);
}
this._logger.debug(NavList);
}
checkPriority(sysUsers) {
let priority = -1;
let user: any;
for (const u of sysUsers) {
if (u.priority > priority) {
user = u;
priority = u.priority;
} else if (u.priority === priority) {
filterAssetsLocal(keyword) {
if (!this.assetsTree) {
return null;
}
function filterAssetsCallback(node) {
if (node.isParent) {
return false;
}
const host = node.meta.asset;
return host.hostname.toLowerCase().indexOf(keyword) !== -1 || host.ip.indexOf(keyword) !== -1;
}
return user;
return this.filterTree(keyword, this.assetsTree, filterAssetsCallback);
// zTreeObj.expandAll(true);
}
recurseParent(node) {
......@@ -355,113 +349,11 @@ export class ElementAssetTreeComponent implements OnInit, OnChanges {
if (!children) {
return [];
}
let all_children = [];
let allChildren = [];
children.forEach((n) => {
all_children = [...children, ...this.recurseChildren(n)];
});
return all_children;
}
filter() {
const zTreeObj = $.fn.zTree.getZTreeObj('ztree');
if (!zTreeObj) {
return null;
}
let _keywords = this.query;
const nodes = zTreeObj.transformToArray(zTreeObj.getNodes());
if (!_keywords) {
if (this.hiddenNodes) {
zTreeObj.showNodes(this.hiddenNodes);
this.hiddenNodes = null;
}
if (this.expandNodes) {
this.expandNodes.forEach((node) => {
if (node.id !== nodes[0].id) {
zTreeObj.expandNode(node, false);
}
});
this.expandNodes = null;
}
return null;
}
_keywords = _keywords.toLowerCase();
let shouldShow = [];
const matchedNodes = zTreeObj.getNodesByFilter(function (node) {
if (node.meta.type === 'asset') {
const host = node.meta.asset;
return host.hostname.toLowerCase().indexOf(_keywords) !== -1 || host.ip.indexOf(_keywords) !== -1;
} else {
return node.name.toLowerCase().indexOf(_keywords) !== -1;
}
});
matchedNodes.forEach((node) => {
const parents = this.recurseParent(node);
const children = this.recurseChildren(node);
shouldShow = [...shouldShow, ...parents, ...children, node];
});
this.hiddenNodes = nodes;
this.expandNodes = shouldShow;
zTreeObj.hideNodes(nodes);
zTreeObj.showNodes(shouldShow);
shouldShow.forEach((node) => {
if (node.isParent) {
zTreeObj.expandNode(node, true);
}
allChildren = [...children, ...this.recurseChildren(n)];
});
// zTreeObj.expandAll(true);
}
}
@Component({
selector: 'elements-asset-tree-dialog',
templateUrl: 'dialog.html',
})
export class AssetTreeDialogComponent implements OnInit {
UserSelectControl = new FormControl('', [Validators.required]);
selected: any;
constructor(public dialogRef: MatDialogRef<AssetTreeDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private _logger: LogService) {
}
ngOnInit() {
this.selected = this.data.users[0].id;
this.UserSelectControl.setValue(this.selected);
// this._logger.debug(this.UserSelectControl);
}
onNoClick(): void {
this.dialogRef.close();
}
compareFn: ((f1: any, f2: any) => boolean) | null = this.compareByValue;
compareByValue(f1: any, f2: any) {
return f1 && f2 && f1.value === f2.value;
return allChildren;
}
}
@Component({
selector: 'elements-manual-password-dialog',
templateUrl: 'manual-password-dialog.html',
})
export class ManualPasswordDialogComponent implements OnInit {
PasswordControl = new FormControl('', [Validators.required]);
constructor(@Inject(MAT_DIALOG_DATA) public data: any,
public dialogRef: MatDialogRef<ManualPasswordDialogComponent>) {
}
onNoClick() {
this.dialogRef.close();
}
onEnter() {
this.dialogRef.close(this.data);
}
ngOnInit(): void {
}
}
import {Component, Input, OnInit, Output, Inject, OnDestroy, EventEmitter} from '@angular/core';
import {connectEvt} from '@app/globals';
import {AppService, HttpService, LogService, NavService} from '@app/app.service';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {FormControl, Validators} from '@angular/forms';
import {ActivatedRoute} from '@angular/router';
import {SystemUser, TreeNode, Asset} from '@app/model';
import {View} from '@app/model';
import * as jQuery from 'jquery/dist/jquery.min';
declare var $: any;
@Component({
selector: 'elements-connect',
templateUrl: './connect.component.html',
})
export class ElementConnectComponent implements OnInit, OnDestroy {
@Output() onNewView: EventEmitter<View> = new EventEmitter<View>();
pos = {left: '100px', top: '200px'};
hasLoginTo = false;
constructor(private _appSvc: AppService,
public _dialog: MatDialog,
public _logger: LogService,
private activatedRoute: ActivatedRoute,
private _http: HttpService,
) {
}
ngOnInit(): void {
connectEvt.asObservable().subscribe(evt => {
switch (evt.action) {
case 'asset': {
this.Connect(evt.node);
break;
}
case 'sftp': {
this.connectFileManager(evt.node);
break;
}
}
});
}
ngOnDestroy(): void {
connectEvt.unsubscribe();
}
Connect(node: TreeNode) {
switch (node.meta.type) {
case 'asset':
this.connectAsset(node);
break;
case 'remote_app':
this.connectRemoteApp(node);
break;
default:
alert('Unknown type: ' + node.meta.type);
}
}
connectAsset(node: TreeNode) {
const host = node.meta.asset as Asset;
this._http.getMyAssetSystemUsers(host.id).subscribe(systemUsers => {
let user: SystemUser;
if (systemUsers.length > 1) {
// 检查系统用户优先级,获取最高优先级的
user = this.checkPriority(systemUsers);
if (user) {
return this.manualSetUserAuthLoginIfNeed(host, user, this.loginAsset.bind(this));
}
const dialogRef = this._dialog.open(AssetTreeDialogComponent, {
height: '200px',
width: '300px',
data: {users: systemUsers}
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
for (const i of systemUsers) {
if (i.id.toString() === result.toString()) {
user = i;
break;
}
}
return this.manualSetUserAuthLoginIfNeed(host, user, this.loginAsset.bind(this));
}
});
} else if (systemUsers.length === 1) {
user = systemUsers[0];
this.manualSetUserAuthLoginIfNeed(host, user, this.loginAsset.bind(this));
} else {
alert('该主机没有授权登录用户');
}
});
}
connectRemoteApp(node: TreeNode) {
const user = node.meta.user as SystemUser;
return this.manualSetUserAuthLoginIfNeed(node, user, this.loginRemoteApp.bind(this));
}
loginRemoteApp(node: TreeNode, user: SystemUser) {
if (node) {
const view = new View();
view.nick = node.name;
view.connected = true;
view.editable = false;
view.closed = false;
view.remoteApp = node.id;
view.user = user;
view.type = 'rdp';
this.onNewView.emit(view);
}
}
connectFileManager(node: TreeNode) {
const host = node.meta.asset as Asset;
if (host) {
const view = new View();
view.nick = '[FILE] ' + host.hostname;
view.connected = true;
view.editable = false;
view.closed = false;
view.host = host;
view.type = 'sftp';
this.onNewView.emit(view);
// jQuery('.tabs').animate({'scrollLeft': 150 * id}, 400);
}
}
connectTerminal(node: TreeNode) {
this.Connect(node);
}
manualSetUserAuthLoginIfNeed(node: any, user: SystemUser, callback) {
if (user.login_mode !== 'manual' || user.protocol !== 'rdp') {
return callback(node, user);
}
user = Object.assign({}, user);
const dialogRef = this._dialog.open(ManualPasswordDialogComponent, {
height: '250px',
width: '500px',
data: {username: user.username}
});
dialogRef.afterClosed().subscribe(result => {
if (!result) {
return;
}
if (result.skip) {
return callback(node, user);
}
user.username = result.username;
user.password = result.password;
return callback(node, user);
});
}
loginAsset(host: Asset, user: SystemUser) {
if (user) {
const view = new View();
view.nick = host.hostname;
view.connected = true;
view.editable = false;
view.closed = false;
view.host = host;
view.user = user;
if (user.protocol === 'ssh' || user.protocol === 'telnet') {
view.type = 'ssh';
} else if (user.protocol === 'rdp' || user.protocol === 'vnc') {
view.type = 'rdp';
}
this.onNewView.emit(view);
}
}
checkPriority(sysUsers: Array<SystemUser>) {
let priority = -1;
let user: any;
for (const u of sysUsers) {
if (u.priority > priority) {
user = u;
priority = u.priority;
} else if (u.priority === priority) {
return null;
}
}
return user;
}
}
@Component({
selector: 'elements-asset-tree-dialog',
templateUrl: 'dialog.html',
})
export class AssetTreeDialogComponent implements OnInit {
UserSelectControl = new FormControl('', [Validators.required]);
selected: any;
constructor(public dialogRef: MatDialogRef<AssetTreeDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private _logger: LogService) {
}
ngOnInit() {
this.selected = this.data.users[0].id;
this.UserSelectControl.setValue(this.selected);
// this._logger.debug(this.UserSelectControl);
}
onNoClick(): void {
this.dialogRef.close();
}
compareFn: ((f1: any, f2: any) => boolean) | null = this.compareByValue;
compareByValue(f1: any, f2: any) {
return f1 && f2 && f1.value === f2.value;
}
}
@Component({
selector: 'elements-manual-password-dialog',
templateUrl: 'manual-password-dialog.html',
})
export class ManualPasswordDialogComponent implements OnInit {
PasswordControl = new FormControl('', [Validators.required]);
constructor(@Inject(MAT_DIALOG_DATA) public data: any,
public dialogRef: MatDialogRef<ManualPasswordDialogComponent>) {
}
onSkip() {
this.data.skip = true;
this.dialogRef.close(this.data);
}
onSkipAll() {
this.data.skipAll = true;
this.dialogRef.close(this.data);
}
onNoClick() {
this.dialogRef.close();
}
onEnter() {
this.dialogRef.close(this.data);
}
ngOnInit(): void {
}
}
......@@ -11,6 +11,7 @@
</mat-form-field>
<div style="float: right">
<button mat-raised-button (click)="onSkip()" >{{"Skip"|trans}}</button>
<button mat-raised-button (click)="onNoClick()" >{{"Cancel"|trans}}</button>
<button mat-raised-button color="primary" [type]="'submit'" [mat-dialog-close]="data" >{{"Confirm"|trans}}</button>
</div>
......
.tabs {
height: 30px;
overflow-y: hidden;
overflow-x: hidden;
position: relative;
.window {
display: none;
/*padding: 15px;*/
}
.tabs ul li.disconnected {
li.disconnected {
background-color: darkgray;
}
.tabs ul li.hidden {
li.hidden {
display: none;
}
.tabs ul {
list-style-type: none;
height: 30px;
background-color: #3a3333;
display: block;
min-width: 100%;
padding-left: 0;
}
.tabs ul li {
li {
display: inline-table;
width: 150px;
height: 30px;
position: relative;
box-sizing: content-box;
float: left;;
background-color: #463a3a66;
border-right: 1px solid #6b6565c2;
border-radius: 2px;
}
.tabs ul li.active {
li.active {
box-sizing: border-box;
border-bottom: 3px solid #19aa8d !important;
border-bottom: 3px solid #19aa8d;
}
.tabs ul li span {
li span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: gray;
color: #f1f0f0;
font-family: 'Roboto', sans-serif;
font-size: 13px;
text-decoration: none;
......@@ -53,7 +45,7 @@
display: block;
}
.tabs ul li a.close {
li a.close {
font-family: 'Roboto', sans-serif;
font-size: 13px;
position: absolute;
......@@ -65,18 +57,18 @@
display: inline-block;
}
.tabs ul li.active a {
li.active a {
color: white;
}
.tabs ul li.active span {
li.active span {
padding-left: 12px;
line-height: 26px;
color: white;
height: 26px;
}
.tabs ul li input {
li input {
font-family: 'Roboto', sans-serif;
font-size: 13px;
width: 120px;
......@@ -87,27 +79,3 @@
height: 26px;
}
/*
* scrollbar
*/
.tabs::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
background-color: #676a6c;
}
.tabs::-webkit-scrollbar {
height: 2px;
}
.tabs::-webkit-scrollbar-thumb {
background-color: #F5F5F5;
}
.scroll-botton {
font-size: 20px;
float: left;
height: 30px;
overflow: hidden;
background-color: #3a3333;
color: white
}
<li
[ngClass]="{'active': view.active,'disconnected': !view.connected, 'hidden': view.closed != false}"
[id]="view.id" (click)="setActive()" (dblclick)="view.editable=true;setActive()"
>
<span *ngIf="view.nick">{{view.nick | truncatechars:25 }}</span>
<input *ngIf="view.editable" [(ngModel)]="view.nick" (blur)="view.editable=false" (keyup.enter)="view.editable=false" autofocus="true"/>
<a class="close" (click)="close()">&times;</a>
</li>
import {Component, OnInit, Input, Output, EventEmitter} from '@angular/core';
import {View, ViewAction} from '@app/model';
@Component({
selector: 'elements-content-tab',
templateUrl: './content-tab.component.html',
styleUrls: ['./content-tab.component.css'],
})
export class ElementContentTabComponent implements OnInit {
@Input() view: View;
@Output() onAction: EventEmitter<ViewAction> = new EventEmitter<ViewAction>();
ngOnInit(): void {
}
close() {
const action = new ViewAction(this.view, 'close');
this.onAction.emit(action);
}
setActive() {
const action = new ViewAction(this.view, 'active');
this.onAction.emit(action);
}
}
div, elements-term, elements-guacamole, elements-settings {
div, elements-term, elements-guacamole {
height: 100%;
}
elements-term, elements-guacamole, elements-settings {
elements-term, elements-guacamole {
/*padding-bottom: 30px;*/
}
.window {
display: none;
height: 100%;
/*padding: 15px;*/
}
.active {
display: block;
}
.right-side {
height: 100%;
width: 100%;
background-color: gray;
}
<div class="window" [ngClass]="{'active':view.active}" style="height: 100%">
<elements-ssh-term
[view]="view"
[host]="view.host"
[sysUser]="view.user"
*ngIf="view.type=='ssh'"
>
</elements-ssh-term>
<elements-guacamole
[view]="view"
[host]="view.host"
[sysUser]="view.user"
[remoteAppId]="view.remoteApp"
*ngIf="view.type=='rdp'"
>
</elements-guacamole>
<app-sftp *ngIf="view.type=='sftp'" [host]="view.host"></app-sftp>
</div>
import {Component, OnInit, Input} from '@angular/core';
import {View} from '@app/model';
@Component({
selector: 'elements-content-window',
templateUrl: './content-window.component.html',
styleUrls: ['./content-window.component.css']
})
export class ElementContentViewComponent implements OnInit {
@Input() view: View;
static active() {
// viewList.List.forEach((v, k) => {
// v.hide = id.toString() !== k;
// });
// viewList.Active = id;
}
static TerminalDisconnect(id) {
// if (viewList.List[id].connected) {
// viewList.List[id].connected = false;
// viewList.List[id].Term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
// TermWS.emit('logout', viewList.List[id].room);
// }
}
static RdpDisconnect(id) {
// viewList.List[id].connected = false;
}
static DisconnectAll() {
// for (let i = 0; i < viewList.List.length; i++) {
// Todo:
// ContentComponent.TerminalDisconnect(i);
// }
}
constructor() {
}
ngOnInit() {
}
// trackByFn(index: number, item: View) {
// return item.id;
// }
}
<div id="content">
<div>
<div class="scroll-button">
<a class="left" (click)="scrollLeft()"><i class="fa fa-caret-left"></i></a>
<a class="right" (click)="scrollRight()"><i class="fa fa-caret-right"></i></a>
</div>
<div class="tabs" #tabs>
<ul [ngStyle]="{'width':tabsWidth+'px'}">
<elements-content-tab
*ngFor="let view of viewList"
[view]="view" (onAction)="onViewAction($event)">
</elements-content-tab>
</ul>
</div>
</div>
<div id="winContainer">
<elements-content-window *ngFor="let view of viewList" [view]="view" ></elements-content-window>
</div>
</div>
<elements-connect [ngStyle]="{'display': 'none'}" (onNewView)="onNewView($event)" ></elements-connect>
.tabs {
height: 30px;
overflow-y: hidden;
overflow-x: hidden;
position: relative;
}
.tabs ul {
list-style-type: none;
height: 30px;
background-color: #3a3333;
//display: block;
display: inline-block;
min-width: 100%;
padding-left: 0;
margin: 0;
}
/*
* scrollbar
*/
.tabs::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
background-color: #676a6c;
}
.tabs::-webkit-scrollbar {
height: 2px;
}
.tabs::-webkit-scrollbar-thumb {
background-color: #F5F5F5;
}
.scroll-button {
font-size: 20px;
float: left;
height: 30px;
overflow: hidden;
background-color: #3a3333;
color: white;
padding: 0 5px;
}
.scroll-button a.disabled {
color: #676A6D;
cursor: not-allowed;
}
#content {
height: 100%;
}
#winContainer {
height: calc(100% - 30px);
}
//
//.window {
// height: 100%;
//}
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {View, ViewAction} from '@app/model';
@Component({
selector: 'elements-content',
templateUrl: './content.component.html',
styleUrls: ['./content.component.scss']
})
export class ElementContentComponent implements OnInit {
@ViewChild('tabs') tabsRef: ElementRef;
viewList: Array<View> = [];
static DisconnectAll() {
}
get tabsWidth() {
return (this.viewList.length + 1) * 151 + 10;
}
constructor() {
}
ngOnInit() {
}
onNewView(view) {
this.scrollToEnd();
setTimeout(() => {
this.viewList.push(view);
this.setViewActive(view);
}, 100);
}
onViewAction(action: ViewAction) {
switch (action.name) {
case 'active': {
this.setViewActive(action.view);
break;
}
case 'close': {
this.closeView(action.view);
break;
}
}
}
setViewActive(view) {
this.viewList.forEach((v, k) => {
v.active = v === view;
});
}
closeView(view) {
let nextActiveView = null;
const index = this.viewList.indexOf(view);
if (view.active) {
// 如果关掉的是最后一个, 存在上一个
if (index === this.viewList.length - 1 && index !== 0) {
nextActiveView = this.viewList[index - 1];
} else if (index < this.viewList.length) {
nextActiveView = this.viewList[index + 1];
}
}
this.viewList.splice(index, 1);
if (nextActiveView) {
this.setViewActive(nextActiveView);
}
}
scrollLeft() {
this.tabsRef.nativeElement.scrollLeft -= 150 * 2;
}
scrollRight() {
this.tabsRef.nativeElement.scrollLeft += 150 * 2;
}
scrollToEnd() {
this.tabsRef.nativeElement.scrollLeft = this.tabsRef.nativeElement.scrollWidth;
}
}
import {Component, Inject, Injectable, OnInit} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {LogService} from '../../app.service';
import {LogService} from '@app/app.service';
import {FormControl, Validators} from '@angular/forms';
// import * as layer from 'layui-layer/src/layer.js';
......
// Elements
import {ElementTableComponent} from './table/table.component';
import {ElementLeftbarComponent} from './leftbar/leftbar.component';
import {ElementOfooterComponent} from './ofooter/ofooter.component';
import {ElementLeftBarComponent} from './left-bar/left-bar.component';
import {ElementContentComponent} from './content/content.component';
import {ElementContentViewComponent} from './content-window/content-window.component';
import {ElementContentTabComponent} from './content-tab/content-tab.component';
import {ElementAssetTreeComponent} from './asset-tree/asset-tree.component';
import {ElementTreeFilterComponent} from './tree-filter/tree-filter.component';
import {ElementFooterComponent} from './footer/footer.component';
import {ElementTermComponent} from './term/term.component';
import {ElementInteractiveComponent} from './interactive/interactive.component';
......@@ -13,17 +17,22 @@ import {ElementIframeComponent} from './iframe/iframe.component';
import {ElementDialogAlertComponent} from './dialog/dialog.service';
import {ElementGuacamoleComponent} from './guacamole/guacamole.component';
import {ElementSshTermComponent} from './ssh-term/ssh-term.component';
import {AssetTreeDialogComponent, ElementAssetTreeComponent, ManualPasswordDialogComponent} from './asset-tree/asset-tree.component';
import {ElementConnectComponent, AssetTreeDialogComponent, ManualPasswordDialogComponent} from './connect/connect.component';
import {RDPSolutionDialogComponent, FontDialogComponent} from './nav/nav.component';
export const ElementComponents = [
ElementLeftbarComponent,
ElementOfooterComponent,
ElementLeftBarComponent,
ElementContentComponent,
ElementContentTabComponent,
ElementContentViewComponent,
ElementConnectComponent,
ElementTreeFilterComponent,
ElementTableComponent,
ElementFooterComponent,
ElementTermComponent,
ElementInteractiveComponent,
ElementNavComponent, ChangLanWarningDialogComponent,
ElementNavComponent,
ChangLanWarningDialogComponent,
ElementPopupComponent,
ElementRdpComponent,
ElementServerMenuComponent,
......@@ -32,6 +41,7 @@ export const ElementComponents = [
ElementGuacamoleComponent,
ElementAssetTreeComponent,
ElementSshTermComponent,
ElementConnectComponent,
AssetTreeDialogComponent,
ManualPasswordDialogComponent,
RDPSolutionDialogComponent,
......
......@@ -5,9 +5,9 @@
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {AppService, LogService} from '../../app.service';
import {DataStore, User} from '../../globals';
import {version} from '../../../environments/environment';
import {AppService, LogService} from '@app/app.service';
import {DataStore, User} from '@app/globals';
import {version} from '@src/environments/environment';
@Component({
selector: 'elements-footer',
......
<!--<iframe #rdp [src]="trust('https://inews.gtimg.com/newsapp_bt/0/3460971429/1000')" width="100%" height="100%" (mouseenter)="active()"></iframe>-->
<iframe *ngIf="target" #rdp [src]="trust(target)" width="100%" height="100%" (mouseenter)="active()"></iframe>
<div class="rdpIframe">
<iframe #rdpRef *ngIf="target" [src]="trust(target)" width="100%" height="100%" (mouseenter)="active()"></iframe>
</div>
......@@ -2,3 +2,7 @@ iframe {
border: none;
background-color: white;
}
.rdpIframe {
height: 100%;
}
import {Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core';
import {CookieService} from 'ngx-cookie-service';
import {HttpService, LogService} from '../../app.service';
import {DataStore, User} from '../../globals';
import {HttpService, LogService} from '@app/app.service';
import {DataStore, User} from '@app/globals';
import {DomSanitizer} from '@angular/platform-browser';
import {environment} from '../../../environments/environment';
import {NavList} from '../../pages/control/control/control.component';
import {View} from '@app/model';
@Component({
selector: 'elements-guacamole',
......@@ -12,12 +11,13 @@ import {NavList} from '../../pages/control/control/control.component';
styleUrls: ['./guacamole.component.scss']
})
export class ElementGuacamoleComponent implements OnInit {
@Input() view: View;
@Input() host: any;
@Input() sysUser: any;
@Input() remoteAppId: string;
@Input() target: string;
@Input() index: number;
@ViewChild('rdp') el: ElementRef;
@ViewChild('rdpRef') el: ElementRef;
registered = false;
constructor(private sanitizer: DomSanitizer,
......@@ -29,15 +29,14 @@ export class ElementGuacamoleComponent implements OnInit {
registerHost() {
let action: any;
if (this.remoteAppId) {
action = this._http.guacamole_add_remote_app(User.id, this.remoteAppId);
action = this._http.guacamoleAddRemoteApp(User.id, this.remoteAppId, this.sysUser.username, this.sysUser.password);
} else {
action = this._http.guacamole_add_asset(User.id, this.host.id, this.sysUser.id, this.sysUser.username, this.sysUser.password);
action = this._http.guacamoleAddAsset(User.id, this.host.id, this.sysUser.id, this.sysUser.username, this.sysUser.password);
}
action.subscribe(
data => {
const base = data.result;
this.target = document.location.origin + '/guacamole/#/client/' + base + '?token=' + DataStore.guacamole_token;
NavList.List[this.index].Rdp = this.el.nativeElement;
this.target = document.location.origin + '/guacamole/#/client/' + base + '?token=' + DataStore.guacamoleToken;
},
error => {
if (!this.registered) {
......@@ -52,11 +51,11 @@ export class ElementGuacamoleComponent implements OnInit {
const now = new Date();
const nowTime = now.getTime() / 1000;
this.registered = true;
this._http.get_guacamole_token(User.id, '').subscribe(
this._http.getGuacamoleToken(User.id, '').subscribe(
data => {
// /guacamole/client will redirect to http://guacamole/#/client
DataStore.guacamole_token = data['authToken'];
DataStore.guacamole_token_time = nowTime;
DataStore.guacamoleToken = data['authToken'];
DataStore.guacamoleTokenTime = nowTime;
this.registerHost();
},
error => {
......@@ -68,8 +67,8 @@ export class ElementGuacamoleComponent implements OnInit {
ngOnInit() {
// /guacamole/api/tokens will redirect to http://guacamole/api/tokens
this.view.type = 'rdp';
if (this.target) {
NavList.List[this.index].Rdp = this.el.nativeElement;
return null;
}
......@@ -86,7 +85,8 @@ export class ElementGuacamoleComponent implements OnInit {
}
Disconnect() {
NavList.List[this.index].connected = false;
// TOdo:
return;
}
active() {
......
import {Component, Input, OnInit} from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {DomSanitizer} from '@angular/platform-browser';
import {NavList} from '../../pages/control/control/control.component';
import {User, DataStore} from '../../globals';
import {HttpService, LogService} from '../../app.service';
import {environment} from '../../../environments/environment';
import {CookieService} from 'ngx-cookie-service';
@Component({
selector: 'elements-iframe',
......@@ -13,15 +8,9 @@ import {CookieService} from 'ngx-cookie-service';
styleUrls: ['./iframe.component.scss']
})
export class ElementIframeComponent implements OnInit {
@Input() host: any;
@Input() userid: any;
@Input() index: number;
target: string;
constructor(private sanitizer: DomSanitizer,
private _http: HttpService,
private _cookie: CookieService,
private _logger: LogService) {
constructor(private sanitizer: DomSanitizer) {
}
ngOnInit() {
......@@ -30,8 +19,4 @@ export class ElementIframeComponent implements OnInit {
trust(url) {
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
Disconnect() {
NavList.List[this.index].connected = false;
}
}
<div class="sidebar" fxLayout="column" >
<div fxflex="1 1 30px" class="tree-filter">
<elements-tree-filter ></elements-tree-filter>
</div>
<div class="overflow ngx-scroll-overlay" fxflex="1 1 calc(100% - 60px)">
<elements-asset-tree ></elements-asset-tree>
</div>
<div class="footer-version" fxflex="1 1 30px">
<p> Version <strong>{{version}}</strong></p>
</div>
</div>
.sidebar {
height: 100%;
width: 100%;
overflow: auto;
}
label {
margin-bottom: 0;
}
.fa.fa-undefined:before {
content: "\f26c";
}
.overflow {
display: flex;
width: 100%;
height: 100%;
float: left;
position: inherit;
background: #2f2a2a;
color: #d6cbcb;
}
.footer-version {
background: #2f2a2a;
font-size: 9pt;
left: 0;
width: 100%;
padding: 1px 20px 0 20px;
border-top: 1px solid #e7eaec;
bottom: 0;
height: 30px;
}
.footer-version > p {
height: 8px;
font-size: 13px;
padding-top: 2px;
padding-bottom: 2px;
color: #d6cbcb;
}
.ngx-scroll-overlay {
overflow: auto; // for FF
}
/**
* 控制页面的左边栏主机树状页
*
* 以树状方式列出所有主机
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, Inject, OnInit, ViewChild, ElementRef} from '@angular/core';
import {AppService, HttpService, LogService} from '../../../app.service';
import {SearchComponent} from '../search/search.component';
import {DataStore} from '../../../globals';
import {version} from '../../../../environments/environment';
import {NavList, View} from '../control/control.component';
import {AppService, HttpService, LogService} from '@app/app.service';
// import {ElementTreeFilterComponent} from '../tree-filter/tree-filter.component';
import {DataStore} from '@app/globals';
import {version} from '@src/environments/environment';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
import {ElementServerMenuComponent} from '../../../elements/server-menu/server-menu.component';
import {DialogService} from '../../../elements/dialog/dialog.service';
export interface Node {
......@@ -37,12 +25,12 @@ export class Host {
}
@Component({
selector: 'pages-control-cleftbar',
templateUrl: './cleftbar.component.html',
styleUrls: ['./cleftbar.component.scss'],
providers: [SearchComponent, ElementServerMenuComponent]
selector: 'elements-left-bar',
templateUrl: './left-bar.component.html',
styleUrls: ['./left-bar.component.scss'],
// providers: [ElementTreeFilterComponent]
})
export class CleftbarComponent {
export class ElementLeftBarComponent {
DataStore = DataStore;
version = version;
q: string;
......@@ -55,7 +43,7 @@ export class CleftbarComponent {
}
static Hide() {
DataStore.leftbarshow = false;
DataStore.showLeftBar = false;
DataStore.Nav.map(function (value, i) {
value['children'].forEach((v, key) => {
if (DataStore.Nav[i]['children'][key]['id'] === 'HideLeftManager') {
......@@ -71,7 +59,7 @@ export class CleftbarComponent {
}
static Show() {
DataStore.leftbarshow = true;
DataStore.showLeftBar = true;
DataStore.Nav.map(function (value, i) {
value['children'].forEach((v, key) => {
if (DataStore.Nav[i]['children'][key]['id'] === 'ShowLeftManager') {
......@@ -88,16 +76,15 @@ export class CleftbarComponent {
constructor(private _appService: AppService,
private _http: HttpService,
private _search: SearchComponent,
private _logger: LogService,
private _menu: ElementServerMenuComponent,
public _dialog: MatDialog,
private _layer: DialogService) {
) {
this._logger.log('nav.ts:NavComponent');
}
Search(q) {
this._search.Search(q);
// Todo:
// this._search.Search(q);
}
onRightClick(event: MouseEvent): void {
......
<mat-sidenav-container>
<mat-sidenav #sidenav mode="side" opened="true" class="example-sidenav"
[fixedInViewport]="options.value.fixed" [fixedTopGap]="options.value.top"
[fixedBottomGap]="options.value.bottom">
<nav>
<div class="header">
<a href="http://www.jumpserver.org" target="_blank">
<img alt="image" height="55" src="/static/imgs/logo-text.png" style="margin-left: 10px">
</a>
</div>
<div class="body">
<ul class="nav metismenu nav-frist-level">
<li *ngFor="let bar of leftbar;let i = index" [ngClass]="{'active':i==active}">
<a (click)="gotoLink(bar.link,i,-1)">
<i class="{{bar.class}}"></i>
<span class="nav-label">{{bar.name|trans}}</span>
<span class="{{bar.label}}"></span>
</a>
<ul class="nav nav-second-level collapse">
<li *ngFor="let child of bar.child;let ii = index" [ngClass]="{'active':ii==active2}">
<a (click)="gotoLink(child.link,i,ii)">{{child.name|trans}}</a>
</li>
</ul>
</li>
</ul>
</div>
</nav>
</mat-sidenav>
<div class="navbar-header">
<button class="navbar-minimalize minimalize-styl-2 btn btn-primary " (click)="sidenav.toggle()"><i
class="fa fa-bars"></i></button>
</div>
</mat-sidenav-container>
li a {
background-color: rgba(0, 0, 0, 0);
box-sizing: border-box;
color: rgb(167, 177, 194);
cursor: pointer;
display: block;
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
font-weight: 600;
height: 46px;
line-height: 18.5714px;
list-style-image: none;
list-style-position: outside;
list-style-type: none;
padding-bottom: 14px;
padding-left: 25px;
padding-right: 20px;
padding-top: 14px;
position: relative;
text-align: left;
text-decoration-color: rgb(167, 177, 194);
text-decoration-line: none;
text-decoration-style: solid;
text-size-adjust: 100%;
width: 220px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
li.active > a {
width: 216px;
color: white;
}
li.active ul.nav.nav-second-level li {;
border-bottom-style: none;
border-bottom-width: 0px;
box-sizing: border-box;
color: rgb(103, 106, 108);
display: block;
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
height: 32px;
line-height: 18.5714px;
list-style-image: none;
list-style-position: outside;
list-style-type: none;
position: relative;
text-align: left;
text-size-adjust: 100%;
visibility: visible;
width: 216px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
li.active ul.nav.nav-second-level {
box-sizing: border-box;
color: rgb(103, 106, 108);
display: block;
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
line-height: 18.5714px;
list-style-image: none;
list-style-position: outside;
list-style-type: none;
margin-bottom: 0px;
margin-top: 0px;
padding-left: 0px;
text-align: left;
text-size-adjust: 100%;
visibility: visible;
width: 216px;
-webkit-margin-after: 0px;
-webkit-margin-before: 0px;
-webkit-margin-end: 0px;
-webkit-margin-start: 0px;
-webkit-padding-start: 0px;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
mat-sidenav {
background-color: #2f4050;
}
nav {
width: 220px;
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
}
div.header {
background-color: #202c37;
}
div.body {
background-color: #2f4050;
height: 100%;
}
li:hover {
color: white !important;
}
.nav-second-level li a {
padding: 7px 10px 7px 52px;
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ElementLeftbarComponent } from './leftbar.component';
describe('ElementLeftbarComponent', () => {
let component: ElementLeftbarComponent;
let fixture: ComponentFixture<ElementLeftbarComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ElementLeftbarComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ElementLeftbarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup} from '@angular/forms';
import {Router} from '@angular/router';
@Component({
selector: 'elements-leftbar',
templateUrl: './leftbar.component.html',
styleUrls: ['./leftbar.component.scss']
})
export class ElementLeftbarComponent implements OnInit {
leftbar = [
{
'name': 'Dashboard',
'class': 'fa fa-dashboard',
'label': '',
'link': '/'
},
{
'name': 'Users',
'class': 'fa fa-group',
'label': 'fa arrow',
'child': [
{
'name': 'User',
}, {
'name': 'User group',
}, {
'name': 'Login logs',
}
]
},
{
'name': 'Assets',
'class': 'fa fa-inbox',
'label': 'fa arrow',
'child': [
{
'name': 'Asset',
}, {
'name': 'Asset group',
}, {
'name': 'Cluster',
}, {
'name': 'Admin user',
}, {
'name': 'System user',
}, {
'name': 'Labels',
}
]
},
{
'name': 'Perms',
'class': 'fa fa-edit',
'label': 'fa arrow',
'child': [{'name': 'Asset permission'}]
},
{
'name': 'Sessions',
'class': 'fa fa-rocket',
'label': 'fa arrow',
'child': [
{
'name': 'Session online'
},
{
'name': 'Session offline'
},
{
'name': 'Commands'
},
{
'name': 'Terminal'
},
]
},
{
'name': 'Job Center',
'class': 'fa fa-coffee',
'label': 'fa arrow',
'child': [
{
'name': 'Task',
'link': '/task'
},
]
},
{
'name': 'Settings',
'class': 'fa fa-gears',
'label': '',
'link': '/luna/setting'
},
];
options: FormGroup;
active: number;
active2: number;
constructor(fb: FormBuilder,
private _router: Router) {
this.options = fb.group({
'fixed': true,
'top': 0,
'bottom': 0,
});
}
ngOnInit() {
this.active = 6;
this.active2 = 0;
}
gotoLink(link: string, index: number, index2: number) {
if (link) {
if (link === '/luna/setting') {
this._router.navigate(['setting']);
} else {
window.location.href = link;
}
}
this.active = index;
this.active2 = index2;
}
}
.nav {
display: block;
margin-top: 2px;
height: 30px
height: 30px;
padding-top: 0;
background-color: #463e3e;
list-style: none;
}
.nav ul {
list-style-type: none;
line-height: 24px;
margin: 0;
box-sizing: border-box;
font-weight: 400;
text-align: left;
}
.nav li {
display: inline-block;
}
.nav a {
.nav .dropdown a {
color: #f0f0f1;
font-family: Roboto,sans-serif;
font-size: 13px;
font-weight: 300;
text-decoration: none;
padding: 6px 15px 6px 15px;
padding: 6px 15px;
height: 30px;
}
.nav a:hover {
......@@ -46,9 +56,9 @@
direction: ltr;
width: auto;
top: auto;
left: 0px;
margin-left: 0px;
margin-top: 0px;
left: 0;
margin-left: 0;
margin-top: 0;
min-width: 150px;
}
......
<script src="../../trans.pipe.spec.ts"></script>
<div class="nav">
<ul>
<li><a href="/"><img src="static/imgs/logo.png" height="26px"/></a>
<ul class="nav-main">
<li>
<a href="/"><img src="static/imgs/logo.png" height="26px"/></a>
</li>
<li *ngFor="let v of DataStore.Nav" [ngClass]="{'dropdown': v.children}">
<li *ngFor="let v of navs" [ngClass]="{'dropdown': v.children}" >
<a>{{v.name|trans}}</a>
<ul [ngClass]="{'dropdown-content': v.children}">
<li *ngFor="let vv of v.children" [ngClass]="{'disabled': vv.disable}">
<a *ngIf="vv.href" [routerLink]="[vv.href]">{{vv.name|trans}}</a>
<a id="{{vv.id}}" *ngIf="vv.click" (click)="click(vv.click)">{{vv.name|trans}}</a>
<a id="{{vv.id}}" *ngIf="vv.click && !vv.hide" (click)="click(vv.click)">{{vv.name|trans}}</a>
</li>
</ul>
</li>
<li [ngClass]="{'dropdown': true}">
<a>{{"Tab List"|trans}}</a>
<ul *ngIf="NavList.List.length>1" [ngClass]="{'dropdown-content': true}">
<ng-container *ngFor="let t of NavList.List, let idx= index" >
<li *ngIf="t.nick!=null" [ngClass]="{'active':idx==NavList.Active,'disconnected':!t.connected, 'hidden': t.closed != false}">
<a id="{{ 'tab' + idx }}" (click)="toTab(idx)">{{t.nick}}</a>
</li>
</ng-container>
</ul>
</li>
</ul>
</div>
......@@ -6,10 +6,8 @@
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, Inject, OnInit} from '@angular/core';
import {AppService, HttpService, LocalStorageService, LogService} from '../../app.service';
import {CleftbarComponent} from '../../pages/control/cleftbar/cleftbar.component';
import {ControlComponent, NavList, View} from '../../pages/control/control/control.component';
import {DataStore, i18n} from '../../globals';
import {HttpService, LocalStorageService, NavService, LogService} from '@app/app.service';
import {DataStore, i18n} from '@app/globals';
import * as jQuery from 'jquery/dist/jquery.min.js';
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
declare let layer: any;
......@@ -21,50 +19,52 @@ declare let layer: any;
})
export class ElementNavComponent implements OnInit {
DataStore = DataStore;
NavList = NavList;
ChangeLanWarningDialog: any;
navs: Array<any>;
_asyncTree = false;
static Hide() {
jQuery('elements-nav').hide();
}
constructor(private _appService: AppService,
private _http: HttpService,
constructor(private _http: HttpService,
private _logger: LogService,
public _dialog: MatDialog,
public _navSvc: NavService,
private _localStorage: LocalStorageService) {
this._logger.log('nav.ts:NavComponent');
this.getNav();
}
ngOnInit() {
this.navs = this.getNav();
}
toTab(idx) {
ControlComponent.active(idx);
get treeLoadAsync() {
return this._asyncTree;
}
set treeLoadAsync(value) {
this._asyncTree = value;
}
click(event) {
this._logger.debug('nav.ts:NavComponent,click', event);
switch (event) {
case 'ReloadLeftbar': {
CleftbarComponent.Reload();
break;
}
case 'ConnectSFTP': {
window.open('/coco/elfinder/sftp/');
break;
}
case 'HideLeft': {
CleftbarComponent.Hide();
DataStore.showLeftBar = false;
this.refreshNav();
break;
}
case 'Settings': {
this.Settings();
case 'ShowLeft': {
DataStore.showLeftBar = true;
this.refreshNav();
break;
}
case 'ShowLeft': {
CleftbarComponent.Show();
case 'Settings': {
this.Settings();
break;
}
case 'Copy': {
......@@ -72,8 +72,10 @@ export class ElementNavComponent implements OnInit {
break;
}
case 'FullScreen': {
let ele:any = document.getElementsByClassName("window active ")[0];
const ele: any = document.getElementsByClassName('window active')[0];
if (!ele) {
return;
}
if (ele.requestFullscreen) {
ele.requestFullscreen();
} else if (ele.mozRequestFullScreen) {
......@@ -85,41 +87,29 @@ export class ElementNavComponent implements OnInit {
} else {
throw new Error('不支持全屏api');
}
window.dispatchEvent(new Event('resize'));
break;
}
case 'Reconnect': {
if (NavList.List[NavList.Active].termComp) {
NavList.List[NavList.Active].termComp.reconnect();
case'Disconnect': {
if (!confirm('断开当前连接?')) {
return
}
}
case 'Reconnect': {
break;
}
case 'Disconnect': {
if (!confirm('断开当前连接? (RDP暂不支持)')) {
break;
}
switch (NavList.List[NavList.Active].type) {
case 'ssh': {
ControlComponent.TerminalDisconnect(NavList.Active);
break;
}
case 'rdp': {
ControlComponent.RdpDisconnect(NavList.Active);
break;
}
default: {
// statements;
break;
}
}
this._navSvc.disconnectConnection();
break;
}
case'DisconnectAll': {
if (!confirm('断开所有连接? (RDP暂不支持)')) {
if (!confirm('断开所有连接?')) {
break;
}
ControlComponent.DisconnectAll();
this._navSvc.disconnectAllConnection();
break;
}
case 'Website': {
......@@ -162,10 +152,6 @@ export class ElementNavComponent implements OnInit {
});
break;
}
case 'EnterLicense': {
this.EnterLicense();
break;
}
case 'English': {
const dialog = this._dialog.open(
ChangLanWarningDialogComponent,
......@@ -206,6 +192,16 @@ export class ElementNavComponent implements OnInit {
});
break;
}
case 'LoadTreeAsync': {
this._navSvc.treeLoadAsync = !this._navSvc.treeLoadAsync;
this.refreshNav();
break;
}
case 'SkipManualPassword': {
this._navSvc.skipAllManualPassword = !this._navSvc.skipAllManualPassword;
this.refreshNav();
break;
}
default: {
break;
}
......@@ -213,45 +209,12 @@ export class ElementNavComponent implements OnInit {
}
EnterLicense() {
layer.prompt({
formType: 2,
maxlength: 500,
title: 'Please Input Code',
scrollbar: false,
area: ['400px', '300px'],
moveOut: true,
moveType: 1
}, function (value, index) {
DataStore.socket.emit('key', value);
// layer.msg(value); //得到value
layer.close(index);
});
refreshNav() {
this.navs = this.getNav();
}
getNav() {
DataStore.Nav = [{
'id': 'File',
'name': 'Server',
'children': [
{
'id': 'Disconnect',
'click': 'Disconnect',
'name': 'Disconnect'
},
{
'id': 'DisconnectAll',
'click': 'DisconnectAll',
'name': 'Disconnect all'
},
{
'id': 'Reconnect',
'click': 'Reconnect',
'name': 'Reconnect'
},
]
}, {
return [{
'id': 'FileManager',
'name': 'File Manager',
'children': [
......@@ -269,7 +232,14 @@ export class ElementNavComponent implements OnInit {
{
'id': 'HideLeftManager',
'click': 'HideLeft',
'name': 'Hide left manager'
'name': 'Hide left manager',
'hide': !DataStore.showLeftBar
},
{
'id': 'ShowLeftManager',
'click': 'ShowLeft',
'name': 'Show left manager',
'hide': DataStore.showLeftBar
},
{
'id': 'RDPResolution',
......@@ -303,6 +273,30 @@ export class ElementNavComponent implements OnInit {
'id': 'FullScreen',
'click': 'FullScreen',
'name': 'Full Screen'
},
{
'id': 'LoadTreeAsync',
'click': 'LoadTreeAsync',
'name': 'Load Tree Async',
'hide': this._navSvc.treeLoadAsync
},
{
'id': 'LoadTreeSync',
'click': 'LoadTreeAsync',
'name': 'Load Tree Sync',
'hide': !this._navSvc.treeLoadAsync
},
{
'id': 'SkipManualPassword',
'click': 'SkipManualPassword',
'name': 'Skip manual password',
'hide': this._navSvc.skipAllManualPassword
},
{
'id': 'ShowManualPassword',
'click': 'SkipManualPassword',
'name': 'Show manual password',
'hide': !this._navSvc.skipAllManualPassword
}
]
}, {
......@@ -343,21 +337,6 @@ export class ElementNavComponent implements OnInit {
];
}
Connect() {
layer.prompt({
formType: 2,
maxlength: 500,
title: 'Please Input Code',
scrollbar: false,
area: ['400px', '300px'],
moveOut: true,
moveType: 1
}, function (value, index) {
DataStore.socket.emit('key', value);
layer.close(index);
});
}
English() {
this._localStorage.delete('lang');
i18n.clear();
......@@ -381,14 +360,6 @@ export class ElementNavComponent implements OnInit {
}
Settings() {
const id = NavList.List.length - 1;
NavList.List[id].nick = 'Setting';
NavList.List[id].connected = true;
NavList.List[id].edit = false;
NavList.List[id].closed = false;
NavList.List[id].type = 'settings';
NavList.List.push(new View());
NavList.Active = id;
}
}
......
<div class="footer fixed">
<div class="pull-right">
Version <strong>{{version}}</strong> GPLv2.
<img style="display: none" src="http://www.jumpserver.org/img/evaluate_avatar1.jpg">
</div>
<div>
<strong>Copyright</strong> 北京堆栈科技有限公司 &copy; 2014-2018
</div>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ElementOfooterComponent } from './ofooter.component';
describe('ElementOfooterComponent', () => {
let component: ElementOfooterComponent;
let fixture: ComponentFixture<ElementOfooterComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ElementOfooterComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ElementOfooterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import {Component, OnInit} from '@angular/core';
import {version} from '../../../environments/environment';
import {DataStore} from '../../globals';
@Component({
selector: 'elements-ofooter',
templateUrl: './ofooter.component.html',
styleUrls: ['./ofooter.component.scss']
})
export class ElementOfooterComponent implements OnInit {
version = version;
constructor() {
DataStore.NavShow = false;
}
ngOnInit() {
}
}
......@@ -6,7 +6,7 @@
*/
import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute, Params} from '@angular/router';
import {DataStore} from '../../globals';
import {DataStore} from '@app/globals';
declare let Mstsc: any;
......
import {Component, OnInit, Input, ElementRef, ViewChild} from '@angular/core';
import {DataStore} from '../../globals';
import {DataStore} from '@app/globals';
import {DomSanitizer} from '@angular/platform-browser';
@Component({
......
import {AfterViewInit, Component, Input, OnInit, OnDestroy} from '@angular/core';
import {Component, Input, OnInit, OnDestroy } from '@angular/core';
import {Terminal} from 'xterm';
import {NavList, View} from '../../pages/control/control/control.component';
import {UUIDService} from '../../app.service';
import {CookieService} from 'ngx-cookie-service';
import {Socket} from '../../utils/socket';
import {getWsSocket} from '../../globals';
import {TransPipe} from '../../pipes/trans.pipe';
import {View} from '@app/model';
import {LogService, UUIDService} from '@app/app.service';
import {Socket} from '@app/utils/socket';
import {getWsSocket, translate} from '@app/globals';
@Component({
......@@ -13,8 +11,9 @@ import {TransPipe} from '../../pipes/trans.pipe';
templateUrl: './ssh-term.component.html',
styleUrls: ['./ssh-term.component.scss']
})
export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy {
export class ElementSshTermComponent implements OnInit, OnDestroy {
@Input() host: any;
@Input() view: View;
@Input() sysUser: any;
@Input() index: number;
@Input() token: string;
......@@ -23,24 +22,18 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
secret: string;
ws: Socket;
roomID: string;
view: View;
transPipe: TransPipe;
constructor(private _uuid: UUIDService, private _cookie: CookieService) {
constructor(private _uuid: UUIDService, private _logger: LogService) {
}
ngOnInit() {
this.view = NavList.List[this.index];
this.secret = this._uuid.gen();
this.newTerm();
this.transPipe = new TransPipe();
getWsSocket().then(sock => {
this.ws = sock;
this.connectHost();
});
}
ngAfterViewInit() {
this.view.type = 'ssh';
}
newTerm() {
......@@ -54,7 +47,6 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
}
});
this.view.Term = this.term;
this.view.termComp = this;
}
changeWinSize(size: Array<number>) {
......@@ -64,12 +56,10 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
}
reconnect() {
if (NavList.List[this.index].connected === true) {
if (!confirm(this.transPipe.transform('Are you sure to reconnect it?(RDP not support)'))) {
if (this.view.connected === true) {
if (!confirm(translate('Are you sure to reconnect it?(RDP not support)'))) {
return;
}
this.close();
}
this.secret = this._uuid.gen();
this.emitHostAndTokenData();
......@@ -90,7 +80,7 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
'token': this.token, 'secret': this.secret,
'size': [this.term.cols, this.term.rows]
};
console.log('On token event trigger');
this._logger.debug('On token event trigger');
this.ws.emit('token', data);
}
}
......@@ -111,20 +101,20 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
// 服务器主动断开
this.ws.on('disconnect', () => {
console.log('On disconnect event trigger');
this.close();
this._logger.debug('On disconnect event trigger');
this.view.connected = false;
});
this.ws.on('logout', data => {
if (data.room === this.roomID) {
console.log('On logout event trigger: ', data.room, this.roomID);
this._logger.debug('On logout event trigger: ', data.room, this.roomID);
this.view.connected = false;
}
});
this.ws.on('room', data => {
if (data.secret === this.secret && data.room) {
console.log('On room', data);
this._logger.debug('On room', data);
this.roomID = data.room;
this.view.room = data.room;
this.view.connected = true;
......@@ -132,19 +122,15 @@ export class ElementSshTermComponent implements OnInit, AfterViewInit, OnDestroy
});
}
// 客户端主动关闭
close() {
if (this.view && (this.view.room === this.roomID)) {
this.view.connected = false;
this.ws.emit('logout', this.roomID);
}
}
active() {
this.term.focus();
}
ngOnDestroy(): void {
this.close();
this._logger.debug('Close view');
if (this.view && (this.view.room === this.roomID)) {
this.view.connected = false;
this.ws.emit('logout', this.roomID);
}
}
}
import {Component, Input, OnInit, ViewChild} from '@angular/core';
import {DatatableComponent} from '@swimlane/ngx-datatable';
import {MatPaginator} from '@angular/material';
import {LogService} from '../../app.service';
import {LogService} from '@app/app.service';
export let Config: {
search: boolean,
......
......@@ -2,15 +2,13 @@ import {AfterViewInit, Component, Input, Output, OnInit, ViewChild, EventEmitter
import {ElementRef} from '@angular/core';
import {Terminal} from 'xterm';
import {fit} from 'xterm/lib/addons/fit/fit';
import {LogService} from '@app/app.service';
import {Observable} from 'rxjs/Rx';
import {CookieService} from 'ngx-cookie-service';
import * as $ from 'jquery/dist/jquery.min.js';
import 'rxjs/Observable';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import {NavList} from '../../pages/control/control/control.component';
@Component({
selector: 'elements-term',
......@@ -24,7 +22,8 @@ export class ElementTermComponent implements OnInit, AfterViewInit {
@Output() winSizeChangeTrigger = new EventEmitter<Array<number>>();
winSizeChange$: Observable<any>;
constructor(private _cookie: CookieService) {
constructor(private _logger: LogService){
}
ngOnInit() {
......@@ -34,9 +33,7 @@ export class ElementTermComponent implements OnInit, AfterViewInit {
this.winSizeChange$
.subscribe(() => {
if (NavList.List[NavList.Active].type !== 'rdp') {
this.resizeTerm();
}
});
}
......@@ -66,10 +63,18 @@ export class ElementTermComponent implements OnInit, AfterViewInit {
availableWidth = activeEle.width() - elementPaddingHor - (<any>this.term).viewport.scrollBarWidth;
}
const dimensions = (<any>this.term).renderer.dimensions;
const geometry = [
Math.floor(availableWidth / (<any>this.term).renderer.dimensions.actualCellWidth) - 1,
Math.floor(availableHeight / (<any>this.term).renderer.dimensions.actualCellHeight) - 1
Math.floor(availableWidth / dimensions.actualCellWidth) - 1,
Math.floor(availableHeight / dimensions.actualCellHeight) - 1
];
if (!isFinite(geometry[0])) {
geometry[0] = 80;
}
if (!isFinite(geometry[1])) {
geometry[1] = 24;
}
return geometry;
}
......
/*.left-search {*/
/*padding-left: 14px;*/
/*width: 100%;*/
/*border: none;*/
/*}*/
.search {
border: none;
border-left-width: 0;
border-bottom: #19aa8d 1px inset;
width: 100%;
background: #2f2a2a;
color: #d6cbcb;
height: 30px;
padding-left: 10px;
/* padding-top: 30px;
//position: fixed;
//height: 28px;
*/
}
<input id="search" class="search"
[formControl]="searchControl"
placeholder=" {{'Search'| trans }} ..."
maxlength="2048"
name="keyword"
autocomplete="off"
title="Search"
type="text" tabindex="1" spellcheck="false"
>
/**
* 控制页面的搜索框
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnChanges, Input, Pipe, PipeTransform} from '@angular/core';
import {Component, OnInit, Pipe, PipeTransform} from '@angular/core';
import {FormControl} from '@angular/forms';
import {debounceTime, distinctUntilChanged} from 'rxjs/operators';
import {AppService, HttpService, LogService} from '../../../app.service';
export let Q = '';
@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.css']
})
export class SearchComponent implements OnChanges {
q: string;
@Input() input;
searchrequest: any;
constructor(private _appService: AppService,
private _http: HttpService,
private _logger: LogService) {
this._logger.log('LeftbarComponent.ts:SearchBar');
}
ngOnChanges(changes) {
Q = changes.input.currentValue;
}
modelChange($event) {
this.Search(Q);
}
public Search(q) {
if (this.searchrequest) {
this.searchrequest.unsubscribe();
}
this.searchrequest = this._http.search(q)
.subscribe(
data => {
this._logger.log(data);
},
err => {
this._logger.error(err);
},
() => {
}
);
this._logger.log(q);
}
}
import {LogService, TreeFilterService} from '@app/app.service';
@Pipe({name: 'SearchFilter'})
......@@ -68,3 +18,30 @@ export class SearchFilter implements PipeTransform {
return value;
}
}
@Component({
selector: 'elements-tree-filter',
templateUrl: './tree-filter.component.html',
styleUrls: ['./tree-filter.component.css'],
providers: [SearchFilter],
})
export class ElementTreeFilterComponent implements OnInit {
searchControl: FormControl;
private debounce = 400;
constructor(private _treeFilterService: TreeFilterService,
private _logger: LogService) {
}
ngOnInit(): void {
this.searchControl = new FormControl('');
this.searchControl.valueChanges
.pipe(debounceTime(this.debounce), distinctUntilChanged())
.subscribe(query => {
this._logger.debug('Tree filter: ', query);
this._treeFilterService.filter(query);
});
}
}
import {Component, Input, OnInit} from '@angular/core';
import {HttpService} from '../../app.service';
import {HttpService} from '@app/app.service';
export interface Assets {
name: string;
......
'use strict';
import {EventEmitter} from 'events/events';
import * as io from 'socket.io-client';
import * as neffos from 'neffos.js';
import {Terminal} from 'xterm';
// const abc = io.connect('/ssh');
import {Socket} from './utils/socket';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {ConnectEvt, User as _User } from './model';
import {DataStore as _DataStore, Browser as _Browser, Video as _Video, Monitor as _Monitor} from './model';
const scheme = document.location.protocol === 'https:' ? 'wss' : 'ws';
const port = document.location.port ? ':' + document.location.port : '';
const wsURL = scheme + '://' + document.location.hostname + port + '/socket.io/';
const hostname = document.location.hostname;
const wsURL = `${scheme}://${hostname}${port}/socket.io/`;
export let TermWS = null;
export const emitter = new(EventEmitter);
export const sep = '/';
export let Video: {
id: string,
src: string,
type: string,
height: number,
width: number,
json: object;
timelist: Array<number>;
totalTime: number;
} = {
id: '',
src: '',
type: '',
width: 0,
height: 0,
json: {},
timelist: [],
totalTime: 0,
};
export let Monitor: {
token: string,
room: string,
type: string,
} = {
token: '',
room: '',
type: 'term',
};
export class Group {
id: string;
name: string;
membercount: number;
comment: string;
}
export let User: {
id: string;
name: string;
username: string;
password: string;
phone: string;
avatar: string;
role: string;
email: string;
wechat: string;
comment: string;
is_active: boolean;
is_superuser: boolean;
date_joined: string;
last_login: string;
date_expired: string;
groups: Array<Group>;
logined: boolean;
} = {
id: '',
name: 'nobody',
username: '',
password: '',
phone: '',
avatar: '',
role: '',
email: '',
wechat: '',
comment: '',
is_active: false,
is_superuser: false,
date_joined: '',
last_login: '',
date_expired: '',
groups: [],
logined: false,
};
export let DataStore: {
socket: any;
Nav: Array<{}>;
NavShow: boolean;
Path: {};
error: {};
msg: {};
loglevel: number;
leftbarshow: boolean;
windowsize: Array<number>;
autologin: boolean;
guacamole_token: string;
guacamole_token_time: number;
} = {
export let Video = new _Video();
export let Monitor = new _Monitor();
export let User = new _User();
export const DataStore: _DataStore = {
socket: TermWS,
Nav: [{}],
NavShow: true,
Path: {},
error: {},
msg: {},
loglevel: 0,
leftbarshow: true,
windowsize: [],
autologin: false,
guacamole_token: '',
guacamole_token_time: 0
};
export let CSRF = '';
export let Browser: {
userAgent: string;
appCodeName: string;
appName: string;
appVersion: string;
language: string;
platform: string;
product: string;
productSub: string;
vendor: string;
} = {
userAgent: navigator.userAgent,
appCodeName: navigator.appCodeName,
appName: navigator.appName,
appVersion: navigator.appVersion,
language: navigator.language,
platform: navigator.platform,
product: navigator.product,
productSub: navigator.productSub,
vendor: navigator.vendor,
logLevel: 4,
showLeftBar: true,
windowSize: [],
autoLogin: false,
guacamoleToken: '',
guacamoleTokenTime: 0
};
export let Browser = new _Browser();
export const i18n = new Map();
export async function getWsSocket() {
......@@ -155,4 +49,13 @@ export async function getWsSocket() {
return TermWS;
}
export const connectEvt = new BehaviorSubject<ConnectEvt>(new ConnectEvt(null, null));
export function translate(value) {
if (i18n.has(value.toLowerCase())) {
return i18n.get(value.toLowerCase());
} else {
return value;
}
}
export class UserGroup {
id: string;
name: string;
comment: string;
}
export class User {
id: string;
name: string;
username: string;
password: string;
phone: string;
avatar: string;
role: string;
email: string;
wechat: string;
comment: string;
is_active: boolean;
is_superuser: boolean;
date_joined: string;
last_login: string;
date_expired: string;
groups: Array <UserGroup>;
logined: boolean;
}
export class SystemUser {
id: string;
name: string;
login_mode: string;
username: string;
priority: number;
protocol: string;
password: string;
actions: Array<string>;
}
export class TreeNode {
id: string;
name: string;
comment: string;
title: string;
isParent: boolean;
pId: string;
open: boolean;
iconSkin: string;
meta: any;
}
export class Node {
id: string;
key: string;
value: string;
}
export class Asset {
id: string;
hostname: string;
ip: string;
comment: string;
domain: string;
os: string;
platform: string;
protocols: Array<string>;
}
export class GuacObjAddResp {
code: number;
result: string;
}
export class ConnectEvt {
node: TreeNode;
action: string;
constructor(node: TreeNode, action: string) {
this.node = node;
this.action = action;
}
}
export class NavEvt {
name: string;
value: any;
constructor(name: string, value: any) {
this.name = name;
this.value = value;
}
}
export class View {
id: string;
nick: string;
type: string;
editable: boolean;
active: boolean;
connected: boolean;
hide: boolean;
closed: boolean;
host: any;
user: any;
remoteApp: string;
room: string;
Rdp: any;
Term: any;
}
export class ViewAction {
view: View;
name: string;
constructor(view: View, name: string) {
this.view = view;
this.name = name;
}
}
export class DataStore {
socket: any;
Nav: Array<object>;
NavShow = true;
Path: {};
error: {};
msg: {};
logLevel: number;
showLeftBar = true;
windowSize: Array<number>;
autoLogin: boolean;
guacamoleToken: string;
guacamoleTokenTime: number;
}
export class Browser {
userAgent: string;
appCodeName: string;
appName: string;
appVersion: string;
language: string;
platform: string;
product: string;
productSub: string;
vendor: string;
constructor() {
this.userAgent = navigator.userAgent;
this.appCodeName = navigator.appCodeName;
this.appName = navigator.appName;
this.appVersion = navigator.appVersion;
this.language = navigator.language;
this.platform = navigator.platform;
this.product = navigator.product;
this.productSub = navigator.productSub;
this.vendor = navigator.vendor;
}
}
export class Video {
id: string;
src: string;
type: string;
height: number;
width: number;
json: object;
timeList: Array<number>;
totalTime: number;
}
export class Monitor {
token: string;
room: string;
type: string;
}
<ng-progress></ng-progress>
<elements-nav *ngIf="DataStore.NavShow"></elements-nav>
<router-outlet></router-outlet>
<!--<elements-interactive></elements-interactive>-->
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'app'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
}));
});
/**
* 控制主页
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, HostListener} from '@angular/core';
import {Component} from '@angular/core';
import {DataStore} from '../globals';
import { environment } from '../../environments/environment';
import {AppService} from '../app.service';
@Component({
selector: 'app-root',
......@@ -18,16 +11,7 @@ import { environment } from '../../environments/environment';
export class AppComponent {
DataStore = DataStore;
constructor() {}
@HostListener('window:beforeunload', ['$event'])
unloadNotification($event: any) {
const notInIframe = window.self === window.top;
const notInReplay = location.pathname.indexOf('/luna/replay') === -1;
if (environment.production && notInIframe && notInReplay) {
return false;
}
return true;
constructor(appSrv: AppService) {
}
}
import {Component, OnInit} from '@angular/core';
import {DataStore} from '../../globals';
import {DataStore} from '@app/globals';
@Component({
selector: 'pages-blank',
......
import {Component, OnInit} from '@angular/core';
import {AppService, HttpService, LocalStorageService} from '../../app.service';
import {DataStore} from '../../globals';
import {AppService, HttpService, LocalStorageService} from '@app/app.service';
import {DataStore} from '@app/globals';
import * as jQuery from 'jquery/dist/jquery.min.js';
@Component({
......@@ -33,7 +33,7 @@ export class PagesConnectComponent implements OnInit {
jQuery('body').css('background-color', '#1f1b1b');
if (this.system === 'windows') {
if (!this.userid) {
this._http.get_user_id_from_token(this.token)
this._http.getUserIdFromToken(this.token)
.subscribe(
data => {
this._localStorage.set('user-' + this.token, data['user']);
......@@ -49,7 +49,7 @@ export class PagesConnectComponent implements OnInit {
getAuthToken() {
if (!this.authToken) {
this._http.get_guacamole_token(this.userid, this.token).subscribe(
this._http.getGuacamoleToken(this.userid, this.token).subscribe(
data => {
if (data['authToken']) {
this._localStorage.set('authToken-' + this.token, data['authToken']);
......@@ -65,7 +65,7 @@ export class PagesConnectComponent implements OnInit {
getBase() {
if (!this.base) {
this._http.guacamole_token_add_asset(this.token, this.authToken).subscribe(
this._http.guacamoleTokenAddAsset(this.token, this.authToken).subscribe(
data => {
if (data['result']) {
this._localStorage.set('base-' + this.token, data['result']);
......
<div class="sidebar" fxLayout="column" ngxSplit="column">
<div fxflex="0 0 30px" class="search">
<input #keyword id="keyword" class="left-search"
placeholder=" {{'Search'| trans }} ..."
maxlength="2048"
name="q"
autocomplete="off"
title="Search"
type="text" tabindex="1" spellcheck="false" [(ngModel)]="q">
</div>
<div class="overflow ngx-scroll-overlay" fxflex="1 1 90%">
<elements-asset-tree [query]="q" ></elements-asset-tree>
</div>
<div class="footer-version" fxflex="0 0 26px">
<p> Version <strong>{{version}}</strong></p>
</div>
</div>
<!--<elements-server-menu></elements-server-menu>-->
.sidebar {
height: 100%;
width: 100%;
overflow: auto;
}
:root {
font-family: "Hiragino Kaku Gothic ProN", Meiryo, sans-serif;
}
label {
margin-bottom: 0;
}
.filetree {
padding-left: 20px;
height: inherit;
}
.filetree input[type="checkbox"] {
display: none;
}
.filetree input[type=checkbox] + label:before {
font-family: FontAwesome;
display: inline-block;
}
.filetree input[type=checkbox] + label:before {
content: "\f114";
letter-spacing: 10px;
width: 30px;
}
.filetree input[type=checkbox]:checked + label:before {
content: "\f115";
}
.filetree ul {
height: 0;
overflow: hidden;
}
.filetree > li input:checked ~ ul, .filetree > li ul.insearch {
height: auto;
}
.filetree li {
list-style: none;
cursor: pointer;
color: #ffffff;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.filetree label {
line-height: 33px;
display: inline-block;
}
.fa.fa-undefined:before {
content: "\f26c";
}
.left-search {
padding-left: 14px;
width: 100%;
border: none;
background: #2f2a2a;
color: #ffffff;
}
.search {
border-left-width: 0;
border-bottom: #19aa8d 2px inset;
//padding-top: 30px;
width: 100%;
//position: fixed;
//height: 28px;
}
.search > input {
height: 30px;
}
.overflow {
height: 100%;
width: 100%;
float: left;
position: inherit;
background: #2f2a2a;
color: #d6cbcb;
}
.footer-version {
background: #2f2a2a;
font-size: 9pt;
left: 0;
width: 100%;
padding: 1px 20px 0 20px;
border-top: 1px solid #e7eaec;
bottom: 0;
//height: 30px;
//position: fixed;
}
.footer-version > p {
height: 8px;
padding-top: 2px;
padding-bottom: 2px;
}
//@import "~@swimlane/ngx-ui/release/styles/components/scrollbars";
.ngx-scroll-overlay {
overflow: auto; // for FF
//-ms-overflow-style: -ms-autohiding-scrollbar;
//
//&::-webkit-scrollbar {
// display: none;
//}
//
//&:hover::-webkit-scrollbar {
// display: initial;
//}
}
//.sidebar::-webkit-scrollbar-track {
// -webkit-box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.3);
// background-color: #676a6c;
//}
//
//.sidebar::-webkit-scrollbar {
// width: 8px;
//}
//
//.sidebar::-webkit-scrollbar-thumb {
// background-color: #F5F5F5;
// border-radius: 6px;
// border: 2px solid transparent;
//}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CleftbarComponent } from './cleftbar.component';
describe('CleftbarComponent', () => {
let component: CleftbarComponent;
let fixture: ComponentFixture<CleftbarComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CleftbarComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CleftbarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
<div class="container-fluid row" fxLayout="row" ngxSplit="row">
<div fxFlex="1 1 20%" minBasis="100px" maxBasis="800px" fxFlexFill ngxSplitArea *ngIf="DataStore.leftbarshow">
<pages-control-cleftbar></pages-control-cleftbar>
</div>
<div fxFlex="0" ngxSplitHandle [style.display]="activeViewIsRdp() ? 'none' : 'block'" (mouseup)="dragSplitBtn($event)"></div>
<div [fxFlex]="DataStore.leftbarshow ? '1 1 80%' : '1 1 100%'" ngxSplitArea class="content">
<pages-control-control></pages-control-control>
</div>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PagesControlComponent } from './control.component';
describe('ControlPageComponent', () => {
let component: PagesControlComponent;
let fixture: ComponentFixture<PagesControlComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PagesControlComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PagesControlComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* 控制页面
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {DataStore, User} from '../../globals';
import {NavList} from './control/control.component';
@Component({
selector: 'pages-control',
templateUrl: './control.component.html',
styleUrls: ['./control.component.css'],
})
export class PagesControlComponent implements OnInit {
DataStore = DataStore;
User = User;
constructor() {
}
activeViewIsRdp() {
return NavList.List[NavList.Active].type === 'rdp';
}
dragSplitBtn(evt) {
window.dispatchEvent(new Event('resize'));
}
ngOnInit() {
}
}
<div fxLayout="column" ngxSplit="column" style="width: 100%;height: 100%;">
<div fxFlex="0 0 30px" class="search">
<pages-control-nav></pages-control-nav>
</div>
<div fxFlex="0 0 calc(100%-35px)" id="winContainer">
<div class="window" *ngFor="let m of NavList.List;let i=index"
[ngClass]="{'active':i==NavList.Active}" style="height: 100%">
<elements-ssh-term [index]="i"
[host]="m.host"
[sysUser]="m.user"
*ngIf="m.type=='ssh'">
</elements-ssh-term>
<elements-guacamole [index]="i"
[host]="m.host"
[sysUser]="m.user"
[remoteAppId]="m.remoteApp"
*ngIf="m.type=='rdp'">
</elements-guacamole>
<app-sftp *ngIf="m.type=='sftp'" [host]="m.host">
</app-sftp>
<!--<elements-settings [index]="i"-->
<!--*ngIf="m.type=='settings'">-->
<!--</elements-settings>-->
</div>
</div>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ControlComponent } from './control.component';
describe('ControlComponent', () => {
let component: ControlComponent;
let fixture: ComponentFixture<ControlComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ControlComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ControlComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* 控制页面
*
* 主管已连接的主机标签卡,各连接方式的web展现(WebTerminal、RDP、VNC等)
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {TermWS} from '../../../globals';
import * as jQuery from 'jquery/dist/jquery.min';
export class View {
nick: string;
type: string;
edit: boolean;
connected: boolean;
hide: boolean;
closed: boolean;
host: any;
user: any;
remoteApp: string;
room: string;
Rdp: any;
Term: any;
termComp: any;
}
export let NavList: {
List: Array<View>;
Active: number;
} = {
List: [new View()],
Active: 0,
};
@Component({
selector: 'pages-control-control',
templateUrl: './control.component.html',
styleUrls: ['./control.component.css']
})
export class ControlComponent implements OnInit {
NavList = NavList;
static active(id) {
NavList.List.forEach((v, k) => {
v.hide = id.toString() !== k;
});
NavList.Active = id;
jQuery('.tabs').animate({'scrollLeft': 150 * id}, 400);
}
static TerminalDisconnect(id) {
if (NavList.List[id].connected) {
NavList.List[id].connected = false;
NavList.List[id].Term.write('\r\n\x1b[31mBye Bye!\x1b[m\r\n');
TermWS.emit('logout', NavList.List[id].room);
}
}
static RdpDisconnect(id) {
NavList.List[id].connected = false;
}
static DisconnectAll() {
for (let i = 0; i < NavList.List.length; i++) {
ControlComponent.TerminalDisconnect(i);
}
}
constructor() {
}
ngOnInit() {
}
// trackByFn(index: number, item: View) {
// return item.id;
// }
}
<div class="scroll-botton" style="padding: 0 5px">
<a class="left" (click)="scrollleft()"><i class="fa fa-caret-left"></i></a>
<a class="right" (click)="scrollright()"><i class="fa fa-caret-right"></i></a>
</div>
<div class="tabs">
<ul [ngStyle]="{'width':150*NavList.List.length+'px'}">
<li *ngFor="let m of NavList.List;let i = index"
[ngClass]="{'active':i==NavList.Active,'disconnected':!m.connected, 'hidden': m.closed != false}"
id="termnav-{{i}}" (click)="setActive(i)" (dblclick)="m.edit=true;setActive(i)">
<span *ngIf="!m.edit" [attr.title]="m.nick">{{m.nick | truncatechars:17 }}</span>
<input *ngIf="m.edit" [(ngModel)]="m.nick" (blur)="m.edit=false" (keyup.enter)="m.edit=false" autofocus="true"/>
<a class="close" (click)="close(m,i)">&times;</a>
</li>
</ul>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PagesControlNavComponent } from './nav.component';
describe('ControlPagesControlNavComponent', () => {
let component: PagesControlNavComponent;
let fixture: ComponentFixture<PagesControlNavComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PagesControlNavComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PagesControlNavComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* 控制页面的已连接主机选项卡
*
* 展示所有已连接的主机
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {ControlComponent, NavList} from '../control.component';
import * as jQuery from 'jquery/dist/jquery.min.js';
@Component({
selector: 'pages-control-nav',
templateUrl: './nav.component.html',
styleUrls: ['./nav.component.css'],
})
export class PagesControlNavComponent implements OnInit {
setActive = PagesControlNavComponent.setActive;
NavList = NavList;
static checkActive(index) {
const len = NavList.List.length;
if (len === 1) {
// 唯一一个
NavList.Active = 0;
} else if (len - 1 === index) {
// 删了最后一个
NavList.Active = len - 2;
} else {
NavList.Active = index;
}
PagesControlNavComponent.setActive(NavList.Active);
}
static setActive(index) {
NavList.List.forEach((value, key) => {
NavList.List[key].hide = true;
});
NavList.List[index].hide = false;
NavList.Active = index;
if (!NavList.List[index].edit) {
if (NavList.List[index].type === 'ssh') {
NavList.List[index].Term.focus();
} else if (NavList.List[index].type === 'rdp') {
// NavList.List[index].Rdp.focus();
}
} else {
}
}
constructor() {
}
ngOnInit() {
}
close(host, index) {
if (host.type === 'rdp') {
ControlComponent.RdpDisconnect(index);
} else if (host.type === 'ssh') {
ControlComponent.TerminalDisconnect(index);
}
NavList.List.splice(index, 1);
PagesControlNavComponent.checkActive(index);
}
scrollleft() {
jQuery('.tabs').scrollLeft(jQuery('.tabs').scrollLeft() - 100);
}
scrollright() {
jQuery('.tabs').scrollLeft(jQuery('.tabs').scrollLeft() + 100);
}
}
.left-search {
padding-left: 14px;
width: 100%;
border: none;
}
<input class="left-search" placeholder=" Search ..." maxlength="2048" name="q" autocomplete="off"
title="Search"
type="text" tabindex="1" spellcheck="false" autofocus [(ngModel)]="q" (keyup.enter)="Search(q)"
(ngModelChange)="modelChange($event)">
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SearchComponent } from './search.component';
describe('SearchComponent', () => {
let component: SearchComponent;
let fixture: ComponentFixture<SearchComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SearchComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SearchComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
<div class="container" *ngIf="User.logined">
<div class="row row-offcanvas row-offcanvas-right">
<div class="col-12 col-md-9">
<p class="float-right hidden-md-up">
<button type="button" class="btn btn-primary btn-sm" data-toggle="offcanvas">Toggle nav</button>
</p>
<div class="jumbotron">
<h1>Hello, world! {{User.username}}</h1>
<p>This is an example to show the potential of an offcanvas layout pattern in Bootstrap. Try some
responsive-range viewport sizes to see it in action.</p>
</div>
<div class="row">
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
<div class="col-6 col-lg-4">
<h2>Heading</h2>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
Donec sed odio dui. </p>
<p><a class="btn btn-secondary" href="#" role="button">View details »</a></p>
</div><!--/span-->
</div><!--/row-->
</div><!--/span-->
<div class="col-6 col-md-3 sidebar-offcanvas" id="sidebar">
<div class="list-group">
<a href="#" class="list-group-item active">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
<a href="#" class="list-group-item">Link</a>
</div>
</div><!--/span-->
</div><!--/row-->
<hr>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PagesIndexComponent } from './index.component';
describe('PagesIndexComponent', () => {
let component: PagesIndexComponent;
let fixture: ComponentFixture<PagesIndexComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PagesIndexComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PagesIndexComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
/**
* 主页
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {AppService} from '../../app.service';
import {User} from '../../globals';
@Component({
selector: 'pages-index',
templateUrl: './index.component.html',
styleUrls: ['./index.component.css'],
})
export class PagesIndexComponent implements OnInit {
User = User;
constructor() {
}
ngOnInit() {
}
}
......@@ -6,10 +6,10 @@
* @author liuzheng <liuzheng712@gmail.com>
*/
import {Component, OnInit} from '@angular/core';
import {AppService, HttpService, LogService} from '../../app.service';
import {AppService, HttpService, LogService} from '@app/app.service';
import {NgForm} from '@angular/forms';
import {Router} from '@angular/router';
import {DataStore, User} from '../../globals';
import {DataStore, User} from '@app/globals';
import * as jQuery from 'jquery/dist/jquery.min.js';
@Component({
......@@ -44,7 +44,7 @@ export class PagesLoginComponent implements OnInit {
DataStore.error['login'] = '';
this._logger.log(User);
if (User.username.length > 0 && User.password.length > 6 && User.password.length < 100) {
this._http.check_login(JSON.stringify(User))
this._http.checkLogin(JSON.stringify(User))
.subscribe(
data => {
User.logined = data['logined'];
......
div {
.content {
height: 100%;
width: 100%;
padding: 0;
background-color: #1f1b1b;
/*background-color: red;*/
margin: 0;
position: initial;
}
pages-control-cleftbar, pages-control-control {
background: #2f2a2a;
color: #d6cbcb;
/*.content {*/
/*overflow: hidden !important;*/
/*}*/
elements-nav {
height: 30px;
}
.container-fluid {
padding-top: 30px;
#container {
padding: 0;
margin: 0;
height: calc(100% - 30px);
}
/*.container-fluid {*/
/*padding-top: 30px;*/
/*}*/
<elements-nav></elements-nav>
<div fxLayout="row" id="container" class="container-fluid row" ngxSplit="row">
<div fxFlex="1 1 20%" minBasis="100px" maxBasis="800px" fxFlexFill ngxSplitArea *ngIf="DataStore.showLeftBar">
<elements-left-bar></elements-left-bar>
</div>
<div fxFlex="0" ngxSplitHandle (mouseup)="dragSplitBtn($event)"></div>
<div [fxFlex]="DataStore.showLeftBar ? '1 1 80%' : '1 1 100%'" ngxSplitArea class="content">
<elements-content></elements-content>
</div>
</div>
import {Component, HostListener, OnInit} from '@angular/core';
import {DataStore, User} from '@app/globals';
import {environment} from '@src/environments/environment';
@Component({
selector: 'pages-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css'],
})
export class PageMainComponent implements OnInit {
User = User;
DataStore = DataStore;
ngOnInit(): void {
}
dragSplitBtn(evt) {
window.dispatchEvent(new Event('resize'));
}
@HostListener('window:beforeunload', ['$event'])
unloadNotification($event: any) {
const notInIframe = window.self === window.top;
const notInReplay = location.pathname.indexOf('/luna/replay') === -1;
return !(environment.production && notInIframe && notInReplay);
}
}
import {Component, OnInit} from '@angular/core';
import {Monitor} from '../../../globals';
import {Monitor} from '@app/globals';
@Component({
selector: 'pages-monitor-linux',
......
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Params} from '@angular/router';
import {DataStore, Monitor} from '../../globals';
import {DataStore, Monitor} from '@app/globals';
@Component({
selector: 'pages-monitor',
......
import {Component, OnInit} from '@angular/core';
import {DataStore} from '../../globals';
import {DataStore} from '@app/globals';
@Component({
selector: 'pages-not-found',
......
import {PageMainComponent} from './main/main.component';
import {PagesBlankComponent} from './blank/blank.component';
import {PagesConnectComponent} from './connect/connect.component';
import {PagesControlComponent} from './control/control.component';
import {PagesIndexComponent} from './index/index.component';
import {PagesMonitorComponent} from './monitor/monitor.component';
import {PagesReplayComponent} from './replay/replay.component';
// import {PagesSettingComponent} from './setting/setting.component';
import {PagesNotFoundComponent} from './not-found/not-found.component';
import {PagesLoginComponent} from './login/login.component';
import {CleftbarComponent} from './control/cleftbar/cleftbar.component';
import {JsonComponent} from './replay/json/json.component';
import {ControlComponent} from './control/control/control.component';
import {PagesControlNavComponent} from './control/control/controlnav/nav.component';
import {SearchComponent, SearchFilter} from './control/search/search.component';
import {PagesMonitorLinuxComponent} from './monitor/linux/linux.component';
import {PagesMonitorWindowsComponent} from './monitor/windows/windows.component';
import {ReplayGuacamoleComponent} from './replay/guacamole/guacamole.component';
export const PagesComponents = [
PageMainComponent,
PagesBlankComponent,
PagesConnectComponent,
PagesControlComponent, ControlComponent, PagesControlNavComponent,
CleftbarComponent,
PagesIndexComponent,
PagesMonitorComponent,
PagesReplayComponent, JsonComponent,
// PagesSettingComponent,
PagesNotFoundComponent,
PagesLoginComponent,
SearchComponent,
SearchFilter,
PagesMonitorLinuxComponent,
PagesMonitorWindowsComponent,
ReplayGuacamoleComponent
......
import {Component, Input, OnInit} from '@angular/core';
import {Terminal} from 'xterm';
import {HttpService, LogService} from '../../../app.service';
import {HttpService, LogService} from '@app/app.service';
import {Replay} from '../replay.model';
function zeroPad(num, minLength) {
......@@ -86,7 +86,7 @@ export class JsonComponent implements OnInit {
}
});
if (this.replay.src !== 'READY') {
this._http.get_replay_data(this.replay.src)
this._http.getReplayData(this.replay.src)
.subscribe(
data => {
this.replayData = data;
......
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Params} from '@angular/router';
import {HttpService, LogService} from '../../app.service';
import {DataStore} from '../../globals';
import {HttpService, LogService} from '@app/app.service';
import {DataStore} from '@app/globals';
import {Replay} from './replay.model';
@Component({
......@@ -24,7 +24,7 @@ export class PagesReplayComponent implements OnInit {
.subscribe(params => {
token = params['token'];
});
this._http.get_replay(token)
this._http.getReplay(token)
.subscribe(
data => {
this.replay.type = data['type'];
......
import {TransPipe} from './trans.pipe';
import {UtcDatePipe} from './date.pipe';
import {TruncatecharsPipe} from './truncatechars.pipe';
import {SearchFilter} from '@app/elements/tree-filter/tree-filter.component';
export const Pipes = [
UtcDatePipe,
TransPipe,
TruncatecharsPipe
TruncatecharsPipe,
SearchFilter
];
import {Pipe, PipeTransform} from '@angular/core';
import {i18n} from '../globals';
import {translate} from '../globals';
@Pipe({
name: 'trans'
......@@ -7,10 +7,6 @@ import {i18n} from '../globals';
export class TransPipe implements PipeTransform {
transform(value: any, args?: any): any {
if (i18n.has(value.toLowerCase())) {
return i18n.get(value.toLowerCase());
} else {
return value;
}
return translate(value);
}
}
......@@ -4,12 +4,15 @@ import {LoggerModule, NgxLoggerLevel} from 'ngx-logger';
import {NgxDatatableModule} from '@swimlane/ngx-datatable';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {NgProgressModule} from 'ngx-progressbar';
import {FlexLayoutModule} from '@angular/flex-layout';
export const PluginModules = [
BrowserAnimationsModule,
NgProgressModule,
MaterialModule,
LoggerModule.forRoot({serverLoggingUrl: '/api/logs', level: NgxLoggerLevel.DEBUG, serverLogLevel: NgxLoggerLevel.ERROR}),
FlexLayoutModule,
LoggerModule.forRoot({level: NgxLoggerLevel.DEBUG, serverLogLevel: NgxLoggerLevel.ERROR}),
NgxDatatableModule,
NgxUIModule,
SplitModule
......
/**
* app路由
*
*
* @date 2017-11-07
* @author liuzheng <liuzheng712@gmail.com>
*/
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
......@@ -12,8 +5,7 @@ import {PagesBlankComponent} from '../pages/blank/blank.component';
import {TestPageComponent} from '../test-page/test-page.component';
import {PagesConnectComponent} from '../pages/connect/connect.component';
import {PagesReplayComponent} from '../pages/replay/replay.component';
import {PagesControlComponent} from '../pages/control/control.component';
import {PagesNotFoundComponent} from '../pages/not-found/not-found.component';
import {PageMainComponent} from '../pages/main/main.component';
import {PagesMonitorComponent} from '../pages/monitor/monitor.component';
import {SftpComponent} from '../elements/sftp/sftp.component';
......@@ -25,8 +17,8 @@ const appRoutes: Routes = [
{path: 'connect', component: PagesConnectComponent},
{path: 'sftp', component: SftpComponent},
{path: 'undefined', component: PagesBlankComponent},
{path: '', component: PagesControlComponent},
{path: '**', component: PagesNotFoundComponent}
{path: '', component: PageMainComponent},
// {path: '**', component: PagesNotFoundComponent}
];
@NgModule({
......@@ -34,7 +26,6 @@ const appRoutes: Routes = [
RouterModule.forRoot(
appRoutes,
{enableTracing: false} // <-- debugging purposes only
// {enableTracing: !environment.production} // <-- debugging purposes only
)
],
exports: [
......
......@@ -17,6 +17,7 @@ export class Socket {
async connect() {
const emitter = new EventEmitter();
emitter.setMaxListeners(20);
this.emitter = emitter;
const events = {
};
......
......@@ -64,7 +64,10 @@
"please input password": "请输入密码",
"username": "用户名",
"password": "密码",
"tab list": "窗口列表",
"reconnect": "重新连接",
"are you sure to reconnect it?(rdp not support)": "确定要重新连接吗? (RDP暂不支持)"
"skip": "跳过",
"loading": "加载中",
"load tree async": "异步加载树",
"load tree sync": "同步加载树",
"show manual password": "显示手动密码窗",
"skip manual password": "跳过手动密码窗"
}
......@@ -3,6 +3,7 @@ $fa-font-path: '~font-awesome/fonts';
@import '~font-awesome/scss/font-awesome';
// bootstrap
// Todo: 去掉依赖
@import '~bootstrap/scss/bootstrap';
$FontPathOpenSans: '~npm-font-open-sans/fonts';
......@@ -17,6 +18,7 @@ $roboto-font-path: '~roboto-fontface/fonts';
*
*/
$asset-path: '../static/imgs/inspinia';
// Todo: 去掉依赖
@import '../assets/inspinia/style';
@import '~@swimlane/ngx-datatable/release/index.css';
......
......@@ -10,6 +10,7 @@ body {
width: 100%;
background-color: rgb(243, 243, 244);
overflow-y: hidden;
margin: 0;
}
app-root {
......@@ -90,7 +91,7 @@ body ::-webkit-scrollbar-thumb {
border-radius: 6px;
}
.ztree * {
.ztree *:not(.fa) {
font-family: 'Monaco', 'Consolas', 'monospace' !important;
font-size: 13px !important;
}
......
......@@ -6,6 +6,6 @@ npm run-script build
rm -fr luna*
mv dist luna
cp -R i18n luna/
cp -R src/assets/i18n luna/
tar czf luna.tar.gz luna
md5 luna.tar.gz
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "src",
"paths": {
"@src/*": ["*"],
"@app/*": ["app/*"]
},
"allowJs": true,
"outDir": "./dist/out-tsc",
"sourceMap": true,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment