├── .gitignore ├── CHANGELOG.md ├── README.md ├── bin ├── Guacamole桌面虚拟化介绍和安装使用.pdf ├── browse.jpg ├── desktop.jpg ├── guacamole-0.9.3.war ├── guacamole-server-0.9.3.tar.gz ├── guacamole.sh ├── noauth-config.xml ├── probe.asp ├── program.jpg ├── proxy.reg ├── server.jpg └── tomcat.sh ├── dcloud.sql ├── intro.md ├── nginx.conf ├── package.json ├── pm2.json ├── src ├── admin │ ├── config │ │ └── config.js │ ├── controller │ │ ├── app.js │ │ ├── base.js │ │ ├── count.js │ │ ├── download.js │ │ ├── index.js │ │ ├── install.js │ │ ├── server.js │ │ ├── system.js │ │ └── user.js │ ├── logic │ │ ├── index.js │ │ ├── install.js │ │ └── system.js │ ├── model │ │ ├── index.js │ │ ├── program.js │ │ └── user.js │ └── service │ │ └── user.js ├── common │ ├── bootstrap │ │ ├── global.js │ │ └── middleware.js │ ├── config │ │ ├── config.js │ │ ├── db.js │ │ ├── env │ │ │ ├── development.js │ │ │ ├── production.js │ │ │ └── testing.js │ │ ├── error.js │ │ ├── hook.js │ │ ├── locale │ │ │ ├── en.js │ │ │ └── zh-cn.js │ │ ├── session.js │ │ ├── view.js │ │ └── websocket.js │ └── controller │ │ └── error.js └── home │ ├── config │ └── config.js │ ├── controller │ ├── api.js │ ├── api │ │ ├── app.js │ │ ├── applog.js │ │ ├── config.js │ │ ├── hosts.js │ │ ├── js.js │ │ ├── server.js │ │ └── user.js │ ├── base.js │ ├── desktop.js │ ├── index.js │ ├── install.js │ ├── openapp.js │ ├── proxy.js │ ├── signin.js │ └── user.js │ ├── logic │ ├── api.js │ ├── api │ │ ├── server.js │ │ └── user.js │ ├── index.js │ ├── install.js │ ├── openapp.js │ ├── proxy.js │ └── signin.js │ └── model │ ├── appusers.js │ ├── index.js │ ├── program.js │ └── proxy.js ├── view ├── admin │ ├── app_add.html │ ├── app_edit.html │ ├── app_index.html │ ├── common │ │ ├── container.html │ │ ├── footer.html │ │ ├── header.html │ │ ├── script.html │ │ ├── silder-menu.html │ │ └── style.html │ ├── index_index.html │ ├── install_index.html │ ├── server_add.html │ ├── server_edit.html │ ├── server_index.html │ ├── system_index.html │ └── user_index.html ├── common │ ├── error_400.html │ ├── error_403.html │ ├── error_404.html │ ├── error_500.html │ └── error_503.html └── home │ ├── api_index.html │ ├── apps_index.html │ ├── common │ ├── footer.html │ └── header.html │ ├── index_index.html │ ├── install_index.html │ ├── signin_index.html │ └── user_setting.html └── www ├── README.md ├── development.js ├── production.js ├── static ├── admin │ ├── css │ │ ├── animate.min.css │ │ ├── bootstrap-slider.css │ │ ├── datepicker.css │ │ ├── font-awesome.min.css │ │ ├── ionicons.min.css │ │ ├── jquery.tagsinput.css │ │ ├── morris.css │ │ ├── owl.carousel.min.css │ │ ├── owl.theme.default.min.css │ │ ├── simplify.css │ │ └── simplify.min.css │ └── js │ │ ├── modernizr.min.js │ │ ├── morris.min.js │ │ ├── owl.carousel.min.js │ │ ├── rapheal.min.js │ │ ├── simplify │ │ ├── simplify.js │ │ └── simplify_dashboard.js │ │ ├── socket.io.js │ │ ├── sparkline.min.js │ │ ├── tongji.js │ │ ├── uncompressed │ │ ├── datepicker.js │ │ ├── jquery.sortable.js │ │ └── skycons.js │ │ └── waypoints.min.js ├── css │ ├── animate.min.css │ ├── bootstrap-slider.css │ ├── datepicker.css │ ├── font-awesome.min.css │ ├── ionicons.min.css │ ├── jquery.tagsinput.css │ ├── morris.css │ ├── owl.carousel.min.css │ ├── owl.theme.default.min.css │ ├── simplify.css │ └── simplify.min.css ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── fontawesome-webfont.woff2 │ ├── ionicons.eot │ ├── ionicons.svg │ ├── ionicons.ttf │ └── ionicons.woff ├── home │ ├── .eslintrc │ ├── .gitignore │ ├── README.md │ ├── dist │ │ ├── css │ │ │ └── main.css │ │ ├── img │ │ │ ├── 1FQwjQx.png │ │ │ ├── 1N5sAtp.png │ │ │ ├── 1V4SDVd.png │ │ │ ├── 1Ybvq1U.png │ │ │ ├── 1i7E9zO.png │ │ │ ├── 1vM7g2p.png │ │ │ ├── 22evHO-.png │ │ │ ├── 299xJ8e.png │ │ │ ├── 2CU4Idb.png │ │ │ ├── 2i2fdwz.jpg │ │ │ ├── 3Q_wqMC.png │ │ │ ├── 3mCKcZU.png │ │ │ ├── 3s71Wf2.png │ │ │ ├── 3zJXP6G.png │ │ │ ├── KxOzIex.png │ │ │ ├── LzfhU0_.png │ │ │ ├── Xi_hJy_.png │ │ │ └── ix3EeZk.png │ │ └── js │ │ │ ├── main.js │ │ │ └── vendor.js │ ├── images │ │ ├── 2345浏览器.png │ │ ├── 360极速浏览器.png │ │ ├── 360浏览器.png │ │ ├── IE 10.png │ │ ├── IE 11.png │ │ ├── IE 6.png │ │ ├── IE 7.png │ │ ├── IE 8.png │ │ ├── IE 9.png │ │ ├── QQ浏览器.png │ │ ├── Safari.png │ │ ├── UC浏览器.png │ │ ├── chrome.png │ │ ├── computer.png │ │ ├── desktop.jpg │ │ ├── download.jpg │ │ ├── firefox.png │ │ ├── opera.png │ │ ├── 世界之窗.png │ │ ├── 傲游浏览器.png │ │ ├── 搜狗浏览器.png │ │ └── 猎豹浏览器.png │ ├── index.html │ ├── package.json │ ├── scripts │ │ ├── actions │ │ │ ├── app.js │ │ │ ├── authed.js │ │ │ ├── navigator.js │ │ │ └── startmenu.js │ │ ├── components │ │ │ ├── AppCard.js │ │ │ ├── Desktop.js │ │ │ ├── Proxy.js │ │ │ ├── StartMenu.js │ │ │ └── TaskBar.js │ │ ├── constants │ │ │ ├── ActionTypes.js │ │ │ └── StartMenu.js │ │ ├── containers │ │ │ ├── App.js │ │ │ ├── DesktopContainer.js │ │ │ ├── StartMenuContainer.js │ │ │ └── TaskBarContainer.js │ │ ├── main.js │ │ ├── reducers │ │ │ ├── applists.js │ │ │ ├── authed.js │ │ │ ├── index.js │ │ │ ├── navigator.js │ │ │ ├── proxys.js │ │ │ └── startmenu.js │ │ ├── store │ │ │ └── configureStore.js │ │ └── utils │ │ │ └── DesktopUtils.js │ ├── styles │ │ ├── custom │ │ │ ├── components │ │ │ │ ├── desktop.scss │ │ │ │ ├── dialog.scss │ │ │ │ ├── reset.scss │ │ │ │ ├── switch.scss │ │ │ │ ├── table.scss │ │ │ │ ├── taskbar.scss │ │ │ │ └── type.scss │ │ │ └── custom.scss │ │ └── main.scss │ ├── webpack.config.js │ └── webpack.prod.config.js └── lib │ ├── bootstrap │ ├── css │ │ └── bootstrap.min.css │ └── js │ │ └── bootstrap.min.js │ ├── jquery-1.11.1.min.js │ ├── jquery.easypiechart.min.js │ ├── jquery.flot.min.js │ ├── jquery.localScroll.min.js │ ├── jquery.popupoverlay.min.js │ ├── jquery.scrollTo.min.js │ ├── jquery.slimscroll.min.js │ ├── js.cookie.js │ └── xterm │ ├── addons │ ├── attach │ │ ├── attach.js │ │ ├── index.html │ │ └── package.json │ ├── fit │ │ ├── fit.js │ │ └── package.json │ ├── fullscreen │ │ ├── fullscreen.css │ │ ├── fullscreen.js │ │ └── package.json │ └── linkify │ │ ├── index.html │ │ ├── linkify.js │ │ └── package.json │ ├── xterm.css │ └── xterm.js └── testing.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea 3 | runtime/ 4 | app/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v0.0.8 2 | 优化 3 | 4 | # v0.0.7 5 | 6 | 前端用户登陆授权 7 | 8 | # v0.0.6 9 | 10 | 前端页面改为react -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # web云桌面 2 | 3 | HTML5 远程桌面解决方案,通过访问浏览器就能操作远程服务器。 4 | 5 | ### 效果图 6 | ![image](https://raw.githubusercontent.com/stbui/dcloud/master/bin/desktop.jpg) 7 | ![image](https://raw.githubusercontent.com/stbui/dcloud/master/bin/browse.jpg) 8 | ![image](https://raw.githubusercontent.com/stbui/dcloud/master/bin/server.jpg) 9 | ![image](https://raw.githubusercontent.com/stbui/dcloud/master/bin/program.jpg) 10 | 11 | ### Demo 12 | ```bash 13 | http://dcloud.stbui.com 14 | ``` 15 | 16 | 17 | ### 安装依赖 18 | 19 | ``` 20 | npm install 21 | ``` 22 | 23 | ### 启动服务 24 | 25 | ``` 26 | npm start 27 | ``` 28 | 29 | ### 访问地址 30 | 31 | ``` 32 | http://127.0.0.1:8361 33 | ``` 34 | 35 | ## pm2 管理 36 | ``` 37 | pm2 start pm2.json 38 | ``` 39 | 40 | ### 关联项目 41 | 42 | 服务器探针 43 | ``` 44 | https://github.com/stbui/dcloud-probe 45 | ``` 46 | 47 | ### 开源项目 48 | - [x] nw 49 | - [x] socketio 50 | - [x] bootstrap 51 | - [x] react 52 | - [x] thinkjs 53 | - [x] express 54 | - [x] scss 55 | 56 | 57 | ### 即将实现 58 | - [x] 初始化安装 59 | - [x] 服务器账号同步 60 | - [x] 应用程序远程执行 61 | - [x] Guacamole 服务管理 62 | - [x] Guacamole 配置文件管理 63 | - [x] 服务器探针 64 | - [ ] jslint 65 | - [ ] webdrive 66 | - [ ] ssh 67 | 68 | 69 | ### help 70 | 查看bin目录 -------------------------------------------------------------------------------- /bin/Guacamole桌面虚拟化介绍和安装使用.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/bin/Guacamole桌面虚拟化介绍和安装使用.pdf -------------------------------------------------------------------------------- /bin/browse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/bin/browse.jpg -------------------------------------------------------------------------------- /bin/desktop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/bin/desktop.jpg -------------------------------------------------------------------------------- /bin/guacamole-0.9.3.war: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/bin/guacamole-0.9.3.war -------------------------------------------------------------------------------- /bin/guacamole-server-0.9.3.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/bin/guacamole-server-0.9.3.tar.gz -------------------------------------------------------------------------------- /bin/guacamole.sh: -------------------------------------------------------------------------------- 1 | # /bin/bash 2 | 3 | # centos 32 4 | 5 | # 导入第三方软件源 6 | 7 | # 安装依赖软件包 8 | 9 | yum install cairo-devel libpng-devel uuid-devel freerdp* libvncserver-devel openssl-devel 10 | 11 | ln -s /usr/local/lib/freerdp/guacsnd.so /usr/lib/freerdp/ 12 | ln -s /usr/local/lib/freerdp/guacdr.so /usr/lib/freerdp/ 13 | 14 | mkdir -p /home/guacdshare 15 | chmod 777 /home/guacdshare 16 | 17 | # server 18 | wget http://jaist.dl.sourceforge.net/project/guacamole/current/source/guacamole-server-0.9.7.tar.gz 19 | tar -xzf guacamole-server-0.9.3.tar.gz 20 | cd guacamole-server-0.9.3 21 | ./configure --with-init-dir=/etc/init.d 22 | make 23 | make install 24 | ldconfig 25 | 26 | chkconfig --add guacd 27 | chkconfig guacd on 28 | 29 | # 下载Guacamole客户端 30 | 31 | wget http://jaist.dl.sourceforge.net/project/guacamole/current/binary/guacamole-0.9.7.war 32 | mkdir /var/lib/guacamole 33 | mv /root/Downloads/guacamole-0.9.7.war /var/lib/guacamole/guacamole.war 34 | 35 | # 新建Guacamole配置文件 36 | mkdir /etc/guacamole 37 | mkdir /root/.guacamole 38 | cp /root/Downloads/guacamole/doc/example/guacamole.properties /etc/guacamole/guacamole.properties 39 | cp /root/Downloads/guacamole/doc/example/user-mapping.xml /etc/guacamole/user-mapping.xml 40 | ln -s /etc/guacamole/guacamole.properties /root/.guacamole/ 41 | 42 | # 配置guacamole.properties文件 43 | # vi /etc/guacamole/noauth-config.xml 44 | # 45 | # 46 | # 47 | # 48 | # 49 | # 50 | # 51 | # 52 | 53 | # 部署Guacamole客户端 54 | ln -s /var/lib/guacamole/guacamole.war /usr/local/Tomcat/webapps 55 | 56 | # 重启Tomcat 57 | 58 | # 启动guacd 59 | 60 | # 验证Guacamole安装 61 | 62 | echo "http://127.0.0.1:8080/guacamole/" -------------------------------------------------------------------------------- /bin/noauth-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /bin/probe.asp: -------------------------------------------------------------------------------- 1 | 2 | <%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%> 3 | <% 4 | Response.CharSet= "UTF-8" 5 | 6 | dim apiKey, domain 7 | apiKey = "undefined" 8 | domain = "undefined" 9 | 10 | dim shellName, shellPath 11 | 12 | 13 | dim key, username, password, appId 14 | 15 | key = request.QueryString("key") 16 | username = request.QueryString("username") 17 | password = request.QueryString("password") 18 | shellName = request.QueryString("shellName") 19 | shellPath = request.QueryString("shellPath") 20 | appId = request.QueryString("appId") 21 | 22 | if apiKey <> "" and key = apiKey and username <> "" then 23 | setUserPassword username,password 24 | if err <> 0 then 25 | response.write "{""resultCode"":""5000"",""resultMsg"":""注册失败""}" 26 | else 27 | response.write "{""resultCode"":""0"",""resultMsg"":""注册成功""}" 28 | end if 29 | 30 | elseif appid <> "" then 31 | response.write file_get_contents(domain&"?userId="&username&"&appId="&appid, "userId="&username&"&appId="&appid) 32 | 33 | elseif shellName <> "" and shellPath <> "" then 34 | shell_content shellName,shellPath 35 | response.write "{""resultCode"":""0"",""resultMsg"":""创建成功""}" 36 | else 37 | response.write "{""resultCode"":""5000"",""resultMsg"":""校验失败""}" 38 | end if 39 | 40 | function setUserPassword(username, password) 41 | On Error Resume Next 42 | dim oSystem,oUser,oGroup 43 | 44 | Set oSystem=GetObject("WinNT://127.0.0.1") 45 | 46 | Set oUser=oSystem.GetObject("user",username) 47 | 48 | if err <> 0 then 49 | err = 0 50 | Set oUser=oSystem.Create("user",username) 51 | oUser.SetPassword password 52 | oUser.Put "userFlags", &h10040 53 | oUser.Setinfo 54 | 55 | Set oGroup=oSystem.GetObject("Group","Users") 56 | oGroup.Add ("winnt://"&username) 57 | else 58 | oUser.SetPassword password 59 | oUser.Setinfo 60 | end if 61 | end function 62 | 63 | Function file_get_contents(url,data) 64 | Dim objXML:Set objXML = server.CreateObject( "Microsoft.XMLHTTP") 65 | 'objXML.open "GET ", url, False 66 | objXML.open "POST", url, False 67 | objXML.send(data) 68 | If objXml.Readystate=4 Then 69 | file_get_contents= objXML.responSetext 70 | Else 71 | file_get_contents=0 72 | End If 73 | Set objXML=Nothing 74 | End Function 75 | 76 | Function shell_content(name, path) 77 | dim fileName 78 | dim content 79 | 80 | fileName = name&".bat" 81 | 82 | 83 | content =":: Author: dCloud "&vbcrlf 84 | content =content&":: WebSite: http://dcloud.stbui.com"&vbcrlf 85 | content =content&":: 2016.06.30"&vbcrlf 86 | content =content&" "&vbcrlf 87 | content =content&" "&vbcrlf 88 | 89 | content =content&"set f2etestDomain=undefined"&vbcrlf 90 | content =content&"set appid=ie6"&vbcrlf 91 | content =content&""&vbcrlf 92 | content =content&""&vbcrlf 93 | content =content&"start /MAX """" "&""""&path&"""" &" ""http://www.baidu.com"" """" "&vbcrlf 94 | content =content&""&vbcrlf 95 | content =content&""&vbcrlf 96 | 97 | CreateFile fileName, content 98 | end Function 99 | 100 | Function CreateFile(FileName,Content) 101 | on error resume next 102 | 103 | FileName=Server.Mappath(FileName) 104 | Set FSO = Server.CreateObject("Scripting.FileSystemObject") 105 | set fd=FSO.createtextfile(FileName,true) 106 | fd.writeline Content 107 | 108 | if err>0 then 109 | err.clear 110 | CreateFile=False 111 | else 112 | CreateFile=True 113 | end if 114 | End function 115 | %> 116 | -------------------------------------------------------------------------------- /bin/program.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/bin/program.jpg -------------------------------------------------------------------------------- /bin/proxy.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/bin/proxy.reg -------------------------------------------------------------------------------- /bin/server.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/bin/server.jpg -------------------------------------------------------------------------------- /bin/tomcat.sh: -------------------------------------------------------------------------------- 1 | # /bin/bash 2 | 3 | echo "Tomcat" -------------------------------------------------------------------------------- /intro.md: -------------------------------------------------------------------------------- 1 | ### 客户端服务器安装和配置 2 | 3 | ##### dcloud-probe 使用 4 | 5 | - 下载探针软件 dcloud-probe http://github/stbui/dcloud-probe 6 | - 将下载的文件解压到系统c盘。(非中文目录路径下面) 7 | - 启动软件之后,点击同步系统信息 8 | - 开启服务器远程访问(右键点击 我的电脑 属性 远程 勾选 远程桌面,然后点击确定按钮) 9 | 10 | ##### dcloud 使用 11 | 12 | - 同步 用户账号 13 | - 同步 应用程序 14 | 15 | 16 | #### dcloud 配置 17 | 18 | 添加服务器 19 | 20 | 生成配置文件 21 | 22 | 重启服务 23 | 24 | 结束 25 | 26 | 27 | 28 | 29 | # 虚拟主机配置 30 | 31 | ## guacamole server 32 | 33 | IP 地址:192.168.159.133 34 | 35 | ## win2003 client 36 | 37 | IP 地址:192.168.159.137 38 | 39 | # 宿主主机端口转发 40 | 41 | 192.168.159.133:8080 172.16.97.13:20000 guacamole server 42 | 43 | 192.168.159.133:80 172.16.97.13:20001 web client 44 | 45 | 46 | 192.168.159.137:80 172.16.97.13:20002 client 47 | 48 | 192.168.159.137:3389 172.16.97.13:3389 49 | 50 | 51 | 52 | 代理软件 53 | 54 | 第1个参数 dCloud 访问地址,形式:http://127.0.0.1/ 55 | 第2个参数 用户名 User1467004117978 56 | 第3个参数 用户的apikey 43ade7073549b78cec5308dfd856abec 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name dcloud.stbui.com; 4 | root /root/www/dcloud; 5 | set $node_port 8361; 6 | 7 | index index.js index.html index.htm; 8 | if ( -f $request_filename/index.html ){ 9 | rewrite (.*) $1/index.html break; 10 | } 11 | if ( !-f $request_filename ){ 12 | rewrite (.*) /index.js; 13 | } 14 | location = /index.js { 15 | proxy_http_version 1.1; 16 | proxy_set_header X-Real-IP $remote_addr; 17 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 18 | proxy_set_header Host $http_host; 19 | proxy_set_header X-NginX-Proxy true; 20 | proxy_set_header Upgrade $http_upgrade; 21 | proxy_set_header Connection "upgrade"; 22 | proxy_pass http://127.0.0.1:$node_port$request_uri; 23 | proxy_redirect off; 24 | } 25 | 26 | location = /development.js { 27 | deny all; 28 | } 29 | 30 | location = /testing.js { 31 | deny all; 32 | } 33 | 34 | location = /production.js { 35 | deny all; 36 | } 37 | 38 | location ~ /static/ { 39 | etag on; 40 | expires max; 41 | } 42 | } 43 | 44 | 45 | 46 | 47 | ## http/2 nginx conf 48 | 49 | # server { 50 | # listen 80; 51 | # server_name example.com www.example.com; 52 | # rewrite ^(.*) https://example.com$1 permanent; 53 | # } 54 | # 55 | # server { 56 | # listen 443 ssl http2 fastopen=3 reuseport; 57 | # server_name www.thinkjs.org thinkjs.org; 58 | # set $node_port 8360; 59 | # 60 | # root E:\431103\dcloud/www; 61 | # 62 | # keepalive_timeout 70; 63 | # 64 | # ssl_certificate /path/to/certificate; 65 | # ssl_certificate_key /path/to/certificate.key; 66 | # ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 67 | # ssl_ciphers "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"; 68 | # ssl_prefer_server_ciphers on; 69 | 70 | # # openssl dhparam -out dhparams.pem 2048 71 | # ssl_dhparam /path/to/dhparams.pem; 72 | # 73 | # ssl_session_cache shared:SSL:10m; 74 | # ssl_session_timeout 10m; 75 | # 76 | # ssl_session_ticket_key /path/to/tls_session_ticket.key; 77 | # ssl_session_tickets on; 78 | # 79 | # ssl_stapling on; 80 | # ssl_stapling_verify on; 81 | # ssl_trusted_certificate /path/to/startssl_trust_chain.crt; 82 | # 83 | # 84 | # add_header x-Content-Type-Options nosniff; 85 | # add_header X-Frame-Options deny; 86 | # add_header Strict-Transport-Security "max-age=16070400"; 87 | # 88 | # index index.js index.html index.htm; 89 | # if ( -f $request_filename/index.html ){ 90 | # rewrite (.*) $1/index.html break; 91 | # } 92 | # if ( !-f $request_filename ){ 93 | # rewrite (.*) /index.js; 94 | # } 95 | # location = /index.js { 96 | # proxy_http_version 1.1; 97 | # proxy_set_header X-Real-IP $remote_addr; 98 | # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 99 | # proxy_set_header Host $http_host; 100 | # proxy_set_header X-NginX-Proxy true; 101 | # proxy_set_header Upgrade $http_upgrade; 102 | # proxy_set_header Connection "upgrade"; 103 | # proxy_pass http://127.0.0.1:$node_port$request_uri; 104 | # proxy_redirect off; 105 | # } 106 | # 107 | # location = /production.js { 108 | # deny all; 109 | # } 110 | # 111 | # location = /testing.js { 112 | # deny all; 113 | # } 114 | # 115 | # location ~ /static/ { 116 | # etag on; 117 | # expires max; 118 | # } 119 | #} 120 | 121 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dcloud", 3 | "description": "", 4 | "version": "1.0.0", 5 | "scripts": { 6 | "start": "node www/development.js", 7 | "compile": "babel --presets es2015-loose,stage-1 --plugins transform-runtime src/ --out-dir app/ --source-maps", 8 | "watch-compile": "node -e \"console.log(' no longer need, use command direct.');console.log();\"", 9 | "watch": "npm run watch-compile" 10 | }, 11 | "dependencies": { 12 | "babel-runtime": "6.x.x", 13 | "socket.io": "^1.3.7", 14 | "source-map-support": "0.4.0", 15 | "thinkjs": "2.2.x" 16 | }, 17 | "devDependencies": { 18 | "babel-cli": "6.x.x", 19 | "babel-core": "6.x.x", 20 | "babel-plugin-transform-runtime": "6.x.x", 21 | "babel-preset-es2015-loose": "6.x.x", 22 | "babel-preset-stage-1": "6.x.x", 23 | "request": "^2.72.0" 24 | }, 25 | "repository": "", 26 | "license": "MIT" 27 | } 28 | -------------------------------------------------------------------------------- /pm2.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps": [{ 3 | "name": "dcloud", 4 | "script": "www/production.js", 5 | "cwd": "/root/dCloud", 6 | "exec_mode": "cluster", 7 | "instances": 0, 8 | "max_memory_restart": "1G", 9 | "autorestart": true, 10 | "node_args": [], 11 | "args": [], 12 | "env": { 13 | 14 | } 15 | }] 16 | } -------------------------------------------------------------------------------- /src/admin/config/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * config 4 | */ 5 | export default { 6 | //key: value 7 | }; -------------------------------------------------------------------------------- /src/admin/controller/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from './base.js'; 4 | 5 | export default class extends Base { 6 | __before() { 7 | this.navType = 'app'; 8 | } 9 | 10 | async indexAction() { 11 | let program = await this.model('program').getList(); 12 | 13 | this.assign('app', program); 14 | 15 | return this.display(); 16 | } 17 | 18 | addAction() { 19 | if (this.isPost()) { 20 | const data = this.post(); 21 | this.model('program').add(data); 22 | } 23 | 24 | return this.display(); 25 | } 26 | 27 | async editAction() { 28 | const _get = this.get(); 29 | 30 | let programData, serverData; 31 | 32 | if (this.isPost()) { 33 | const _post = this.post(); 34 | this.model('program').where(_get).update(_post); 35 | 36 | this.action('home/proxy', 'remotegeneratecmdsingle'); 37 | this.redirect('/admin/app/index'); 38 | } 39 | 40 | programData = await this.model('program').getSingleList({'program.id': _get.id}); 41 | this.assign('app', programData); 42 | 43 | serverData = await this.model('server').select(); 44 | this.assign('server', serverData); 45 | 46 | return this.display(); 47 | } 48 | 49 | delAction() { 50 | const _get = this.get(); 51 | this.model('program').where(_get).delete(); 52 | 53 | this.redirect('/admin/app/index'); 54 | } 55 | 56 | async shownAction() { 57 | const _get = this.get(); 58 | 59 | if (think.isEmpty(_get)) { 60 | return this.fail() 61 | } 62 | 63 | const programData = this.model('program'); 64 | const row = await programData.where({id: _get.id}).find(); 65 | 66 | if (row.status == 1) { 67 | await programData.where({id: _get.id}).update({status: 0}); 68 | } else { 69 | await programData.where({id: _get.id}).update({status: 1}); 70 | } 71 | 72 | return this.success(row.status, this.locale('query_success')); 73 | } 74 | } -------------------------------------------------------------------------------- /src/admin/controller/base.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default class extends think.controller.base { 4 | /** 5 | * some base method in here 6 | */ 7 | } -------------------------------------------------------------------------------- /src/admin/controller/count.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from './base.js'; 4 | 5 | export default class extends Base { 6 | /** 7 | * index action 8 | * @return {Promise} [] 9 | */ 10 | indexAction() { 11 | return this.display('index/index'); 12 | } 13 | 14 | 15 | } -------------------------------------------------------------------------------- /src/admin/controller/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from './base.js'; 4 | 5 | export default class extends Base { 6 | __before() { 7 | this.navType = 'index'; 8 | } 9 | 10 | indexAction() { 11 | //auto render template file index_index.html 12 | return this.display(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/admin/controller/install.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from './base.js'; 4 | import child_process from 'child_process' 5 | 6 | export default class extends Base { 7 | __before() { 8 | this.navType = 'install'; 9 | } 10 | 11 | /** 12 | * index action 13 | * @return {Promise} [] 14 | */ 15 | indexAction(appType) { 16 | 17 | return this.display(); 18 | } 19 | 20 | /** 21 | * guacamole action 22 | * @return {json} 23 | */ 24 | guacamoleAction() { 25 | return this.guacamole(); 26 | } 27 | 28 | /** 29 | * tomcat action 30 | * @return {json} 31 | */ 32 | tomcatAction() { 33 | return this.tomcat(); 34 | } 35 | 36 | guacamole() { 37 | let cmd = 'sh', options = ['guacamole.sh']; 38 | let result = this.cli(cmd, options); 39 | 40 | return result; 41 | } 42 | 43 | tomcat() { 44 | let cmd = 'sh', options = ['tomcat.sh']; 45 | let result = this.cli(cmd, options); 46 | 47 | return result; 48 | } 49 | 50 | mysql() { 51 | let cmd = 'sh', options = ['mysql.sh']; 52 | let result = this.cli(cmd, options); 53 | 54 | return result; 55 | } 56 | 57 | /* 58 | * @param {string} command ����ؼ��� 59 | * @param {array} option ������� 60 | * @return {json} 61 | */ 62 | async cli(command, option) { 63 | const {spawn} = child_process; 64 | 65 | let cmd = process.platform === "win322" ? command + ".cmd" : command; 66 | let cli = spawn(cmd, option); 67 | 68 | cli.stdout.setEncoding('UTF-8'); 69 | cli.stdout.on('data', (data) => { 70 | return this.success(data); 71 | }); 72 | 73 | cli.stderr.setEncoding('UTF-8'); 74 | cli.stderr.on('data', (data) => { 75 | return this.error(6001, data); 76 | }); 77 | 78 | cli.on('close', () => { 79 | 80 | }); 81 | } 82 | 83 | /* 84 | * socket 85 | */ 86 | openAction(self) { 87 | const {socket} = self.http; 88 | 89 | this.broadcast("dCloud", "connected"); 90 | } 91 | 92 | launcherAction(self) { 93 | const {socket, data} = self.http; 94 | const {spawn} = child_process; 95 | const cwd = think.ROOT_PATH + '/bin'; 96 | 97 | let cmd = 'sh', options = [data.cmd + '.sh']; 98 | 99 | let cli = spawn(cmd, options, {cwd: cwd}); 100 | 101 | cli.stdout.setEncoding('UTF-8'); 102 | cli.stdout.on('data', (data) => { 103 | this.emit('launcher', data); 104 | }); 105 | 106 | cli.stderr.setEncoding('UTF-8'); 107 | cli.stderr.on('data', (data) => { 108 | this.emit('launcher', data); 109 | }); 110 | 111 | cli.on('close', () => { 112 | this.emit('launcher', 'complete dCloud'); 113 | }); 114 | } 115 | 116 | closeAction(self) { 117 | this.broadcast("dCloud", "disconnected"); 118 | } 119 | } -------------------------------------------------------------------------------- /src/admin/controller/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from './base.js'; 4 | 5 | 6 | export default class extends Base { 7 | __before() { 8 | this.navType = 'server'; 9 | } 10 | 11 | indexAction() { 12 | let server = this.model('server').select(); 13 | this.assign('server', server); 14 | 15 | return this.display(); 16 | } 17 | 18 | /** 19 | * add action 20 | * @return {Promise} [] 21 | */ 22 | addAction() { 23 | if (this.isPost()) { 24 | const _post = this.post(); 25 | 26 | //服务器身份标识 27 | _post.accessToken = think.uuid(); 28 | this.model('server').add(_post); 29 | 30 | this.redirect('/admin/server/index.html'); 31 | } 32 | 33 | return this.display(); 34 | } 35 | 36 | editAction() { 37 | const _get = this.get(); 38 | let {id} = _get; 39 | 40 | if (this.isPost()) { 41 | const _post = this.post(); 42 | 43 | if (!think.isEmpty(id)) { 44 | this.model('server').where({id}).update(_post); 45 | } 46 | 47 | this.redirect('/admin/server/index.html'); 48 | } 49 | 50 | const serverData = this.model('server').where({id}).find(); 51 | 52 | this.assign('server', serverData); 53 | 54 | return this.display(); 55 | } 56 | 57 | /** 58 | * del action 59 | * @return 60 | */ 61 | delAction() { 62 | let _get = this.get(); 63 | let {id} = _get; 64 | 65 | this.model('server').where({id}).delete(); 66 | this.redirect('/admin/server/index.html'); 67 | } 68 | 69 | /** 70 | * 检测客户端服务器运行状态 71 | * 72 | */ 73 | async checkremoteserverstateAction() { 74 | const _get = this.get(); 75 | let {id} = _get; 76 | const serverData = await this.model('server').where({id}).find(); 77 | const {ip, port} = serverData; 78 | 79 | let url = 'http://' + ip + ':' + port; 80 | let resultData = await global.request(url).catch((e)=> { 81 | return e 82 | }); 83 | 84 | if (resultData.code == 'ETIMEDOUT') { 85 | this.model('server').where({id}).update({status: 0}); 86 | return this.fail(this.locale('query_fail'), undefined); 87 | } else { 88 | this.model('server').where({id}).update({status: 1}); 89 | return this.success(undefined, this.locale('query_success')); 90 | } 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /src/admin/controller/system.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from './base.js'; 4 | 5 | export default class extends Base { 6 | __before() { 7 | this.navType = 'system'; 8 | } 9 | 10 | indexAction() { 11 | 12 | if (this.isPost()) { 13 | const data = this.post(); 14 | this.model('config').where({id: 1}).update(data); 15 | } 16 | 17 | const configData = this.model('config').find(); 18 | this.assign('config', configData); 19 | 20 | return this.display(); 21 | } 22 | } -------------------------------------------------------------------------------- /src/admin/controller/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from './base.js'; 4 | 5 | 6 | export default class extends Base { 7 | __before() { 8 | this.navType = 'user'; 9 | } 10 | 11 | indexAction() { 12 | let user = this.model('appusers').select(); 13 | 14 | this.assign('user', user); 15 | 16 | return this.display('index'); 17 | } 18 | 19 | delAction() { 20 | const _get = this.get(); 21 | this.model('appusers').where(_get).delete(); 22 | 23 | this.redirect('/admin/user'); 24 | } 25 | 26 | /** 27 | * 单台客户端服务器用户同步 28 | * @param ip 29 | * @return json 30 | */ 31 | async syncallremoteusersAction() { 32 | const {ip} = this.get(); 33 | const appusersData = await this.model('appusers').select(); 34 | 35 | const {remoteUserUrl} = this.config('api'); 36 | let url = remoteUserUrl.replace('${ip}', ip); 37 | 38 | appusersData.forEach((item)=> { 39 | // win2003 密码字符不能大于14 40 | // todo socket 发送数据, 将用户数据提交高agent查询处理 41 | let {UserId, RemotePassword} = item; 42 | let formData = {username: UserId, password: RemotePassword}; 43 | global.request(url, formData); 44 | }); 45 | 46 | // 最后同步时间 47 | this.model('server').where({ip: ip}).update({syncUserDate: think.datetime()}); 48 | 49 | 50 | return this.success(undefined, this.locale('query_success')); 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /src/admin/logic/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * logic 4 | * @param {} [] 5 | * @return {} [] 6 | */ 7 | export default class extends think.logic.base { 8 | /** 9 | * index action logic 10 | * @return {} [] 11 | */ 12 | indexAction(){ 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /src/admin/logic/install.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * logic 4 | * @param {} [] 5 | * @return {} [] 6 | */ 7 | export default class extends think.logic.base { 8 | /** 9 | * index action logic 10 | * @return {} [] 11 | */ 12 | indexAction(){ 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /src/admin/logic/system.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * logic 4 | * @param {} [] 5 | * @return {} [] 6 | */ 7 | export default class extends think.logic.base { 8 | /** 9 | * index action logic 10 | * @return {} [] 11 | */ 12 | indexAction() { 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /src/admin/model/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * model 4 | */ 5 | export default class extends think.model.base { 6 | 7 | } -------------------------------------------------------------------------------- /src/admin/model/program.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * model 4 | */ 5 | export default class extends think.model.base { 6 | 7 | getList() { 8 | //SELECT * FROM `program` LEFT JOIN server ON program.serverId=server.id 9 | //return this.join("server ON program.serverId=server.id").select(); 10 | 11 | return this.field(['program.*','server.name as serverName']).join({ 12 | table: 'server', 13 | join: 'left', 14 | on: ['serverId','id'] 15 | }).select(); 16 | 17 | } 18 | 19 | getSingleList(data) { 20 | return this.field(['program.*','server.id as serverId, server.ip as serverIp']).join({ 21 | table: 'server', 22 | join: 'left', 23 | on: ['serverId','id'] 24 | }).where(data).find(); 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /src/admin/model/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * relation model 4 | */ 5 | export default class extends think.model.relation { 6 | /** 7 | * init 8 | * @param {} args [] 9 | * @return {} [] 10 | */ 11 | init(...args) { 12 | super.init(...args); 13 | 14 | this.relation = { 15 | applogs: { 16 | type: think.model.HAS_MANY, 17 | field: 'UserId' 18 | } 19 | } 20 | 21 | } 22 | 23 | 24 | async getAppLogs() { 25 | let data = await this.model('applogs').join({ 26 | table: 'appusers', 27 | on: ['UserId', 'UserId'] 28 | }) 29 | } 30 | 31 | 32 | } -------------------------------------------------------------------------------- /src/admin/service/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default class extends think.service.base { 4 | /** 5 | * init 6 | * @return {} [] 7 | */ 8 | init(...args){ 9 | super.init(...args); 10 | } 11 | } -------------------------------------------------------------------------------- /src/common/bootstrap/global.js: -------------------------------------------------------------------------------- 1 | /** 2 | * this file will be loaded before server started 3 | * you can define global functions used in controllers, models, templates 4 | */ 5 | 6 | /** 7 | * use global.xxx to define global functions 8 | * 9 | * global.fn1 = function(){ 10 | * 11 | * } 12 | */ 13 | 14 | import request from 'request'; 15 | 16 | global.request = (url, formData, method) => { 17 | let fn = think.promisify(request); 18 | return fn({ 19 | method: method || 'POST', 20 | url: url, 21 | timeout:200, 22 | form: formData 23 | }); 24 | }; 25 | 26 | global.setUrlParam = (obj)=> { 27 | let str = []; 28 | 29 | for (var i in obj) { 30 | str.push(i + '=' + encodeURI(obj[i])); 31 | } 32 | 33 | return str.join('&'); 34 | }; 35 | 36 | global.setCorsHeader= (self) =>{ 37 | self.header("Access-Control-Allow-Origin", self.header("origin") || "*"); 38 | self.header("Access-Control-Allow-Headers", "x-requested-with"); 39 | self.header("Access-Control-Request-Method", "GET,POST,PUT,DELETE"); 40 | self.header("Access-Control-Allow-Credentials", "true"); 41 | }; -------------------------------------------------------------------------------- /src/common/bootstrap/middleware.js: -------------------------------------------------------------------------------- 1 | /** 2 | * this file will be loaded before server started 3 | * you can register middleware 4 | * https://thinkjs.org/doc/middleware.html 5 | */ 6 | 7 | /** 8 | * 9 | * think.middleware('xxx', http => { 10 | * 11 | * }) 12 | * 13 | */ 14 | -------------------------------------------------------------------------------- /src/common/config/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * config 4 | */ 5 | export default { 6 | //key: value 7 | //default_module: "admin" 8 | port: 8361, 9 | 10 | api: { 11 | remoteProgramUrl: 'http://${ip}:3000/app/add', 12 | remoteUserUrl: 'http://${ip}:3000/user/add', 13 | remoteProbePath:'${dir}/app/${name}.bat', 14 | } 15 | }; -------------------------------------------------------------------------------- /src/common/config/db.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * db config 4 | * @type {Object} 5 | */ 6 | export default { 7 | type: 'mysql', 8 | adapter: { 9 | mysql: { 10 | host: '127.0.0.1', 11 | port: '', 12 | database: 'dcloud', 13 | user: 'root', 14 | password: 'root', 15 | prefix: '', 16 | encoding: 'utf8' 17 | }, 18 | mongo: {} 19 | } 20 | }; -------------------------------------------------------------------------------- /src/common/config/env/development.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default { 4 | 5 | }; -------------------------------------------------------------------------------- /src/common/config/env/production.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default { 4 | 5 | }; -------------------------------------------------------------------------------- /src/common/config/env/testing.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default { 4 | 5 | }; -------------------------------------------------------------------------------- /src/common/config/error.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * err config 4 | */ 5 | export default { 6 | //key: value 7 | key: "resultCode", //error number 8 | msg: "restultMsg" //error message 9 | }; -------------------------------------------------------------------------------- /src/common/config/hook.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * hook config 5 | * https://thinkjs.org/doc/middleware.html#toc-df6 6 | */ 7 | export default { 8 | 9 | } -------------------------------------------------------------------------------- /src/common/config/locale/en.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default { 4 | 5 | }; -------------------------------------------------------------------------------- /src/common/config/session.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * session configs 5 | */ 6 | export default { 7 | name: 'dCloud', 8 | type: 'file', 9 | secret: 'OX@$WZI%', 10 | timeout: 24 * 3600, 11 | //timeout: 60, 12 | cookie: { // cookie options 13 | length: 32, 14 | httponly: true 15 | }, 16 | adapter: { 17 | file: { 18 | path: think.RUNTIME_PATH + '/session' 19 | } 20 | } 21 | }; -------------------------------------------------------------------------------- /src/common/config/view.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * template config 4 | */ 5 | export default { 6 | type: 'ejs', 7 | content_type: 'text/html', 8 | file_ext: '.html', 9 | file_depr: '_', 10 | root_path: think.ROOT_PATH + '/view', 11 | adapter: { 12 | ejs: {} 13 | } 14 | }; -------------------------------------------------------------------------------- /src/common/config/websocket.js: -------------------------------------------------------------------------------- 1 | export default { 2 | on: true, //是否开启 WebSocket 3 | type: "socket.io", 4 | allow_origin: "", 5 | sub_protocal: "", 6 | adapter: undefined, 7 | path: "", //url path for websocket 8 | messages: { 9 | open: 'admin/install/open', 10 | close: 'admin/install/close', 11 | launcher: 'admin/install/launcher' 12 | } 13 | }; -------------------------------------------------------------------------------- /src/common/controller/error.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * error controller 4 | */ 5 | export default class extends think.controller.base { 6 | /** 7 | * display error page 8 | * @param {Number} status [] 9 | * @return {Promise} [] 10 | */ 11 | displayError(status){ 12 | 13 | //hide error message on production env 14 | if(think.env === 'production'){ 15 | this.http.error = null; 16 | } 17 | 18 | let errorConfig = this.config('error'); 19 | let message = this.http.error && this.http.error.message || ''; 20 | if(this.isJsonp()){ 21 | return this.jsonp({ 22 | [errorConfig.key]: status, 23 | [errorConfig.msg]: message 24 | }) 25 | }else if(this.isAjax()){ 26 | return this.fail(status, message); 27 | } 28 | 29 | let module = 'common'; 30 | if(think.mode !== think.mode_module){ 31 | module = this.config('default_module'); 32 | } 33 | let file = `${module}/error/${status}.html`; 34 | let options = this.config('tpl'); 35 | options = think.extend({}, options, {type: 'base', file_depr: '_'}); 36 | this.fetch(file, {}, options).then(content => { 37 | content = content.replace('ERROR_MESSAGE', message); 38 | this.type(options.content_type); 39 | this.end(content); 40 | }); 41 | } 42 | /** 43 | * Bad Request 44 | * @return {Promise} [] 45 | */ 46 | _400Action(){ 47 | return this.displayError(400); 48 | } 49 | /** 50 | * Forbidden 51 | * @return {Promise} [] 52 | */ 53 | _403Action(){ 54 | return this.displayError(403); 55 | } 56 | /** 57 | * Not Found 58 | * @return {Promise} [] 59 | */ 60 | _404Action(){ 61 | return this.displayError(404); 62 | } 63 | /** 64 | * Internal Server Error 65 | * @return {Promise} [] 66 | */ 67 | _500Action(){ 68 | return this.displayError(500); 69 | } 70 | /** 71 | * Service Unavailable 72 | * @return {Promise} [] 73 | */ 74 | _503Action(){ 75 | return this.displayError(503); 76 | } 77 | } -------------------------------------------------------------------------------- /src/home/config/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * config 4 | */ 5 | export default { 6 | //key: value 7 | }; -------------------------------------------------------------------------------- /src/home/controller/api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from './base.js'; 4 | 5 | export default class extends Base { 6 | /** 7 | * index action 8 | * @return {Promise} [] 9 | */ 10 | async indexAction() { 11 | 12 | return this.display(); 13 | } 14 | 15 | async getappslistAction() { 16 | global.setCorsHeader(this); 17 | 18 | const program = await this.model('program').getList({'program.status': 1}); 19 | 20 | return this.success(program, this.locale('query_success')); 21 | } 22 | 23 | 24 | /* 25 | * 查询所有应用程序列表 26 | * @return {json} 27 | * */ 28 | async getallappAction() { 29 | const program = await this.model('program').select(); 30 | 31 | return this.success(program, this.locale('query_success')); 32 | } 33 | 34 | 35 | /* 36 | * 查询 37 | * @return {json} 38 | * */ 39 | getapikeyAction() { 40 | 41 | return this.success({apiKey: think.uuid()}); 42 | } 43 | 44 | 45 | pathlistAction() { 46 | return this.action('desktop', 'pathlist'); 47 | } 48 | } -------------------------------------------------------------------------------- /src/home/controller/api/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from '../base.js'; 4 | 5 | export default class extends think.controller.base { 6 | 7 | async indexAction() { 8 | global.setCorsHeader(this); 9 | 10 | const program = await this.model('program').getAllList(); 11 | return this.success(program, this.locale('query_success')); 12 | } 13 | } -------------------------------------------------------------------------------- /src/home/controller/api/applog.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from '../base.js'; 4 | 5 | export default class extends think.controller.base { 6 | 7 | async indexAction(){ 8 | return this.success(); 9 | } 10 | } -------------------------------------------------------------------------------- /src/home/controller/api/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from '../base.js'; 4 | 5 | export default class extends Base { 6 | 7 | async indexAction(){ 8 | const program = await this.model('config').where({id:1}).find(); 9 | 10 | delete program['id']; 11 | 12 | return this.success(program); 13 | } 14 | } -------------------------------------------------------------------------------- /src/home/controller/api/hosts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from '../base.js'; 4 | import fs from 'fs'; 5 | 6 | export default class extends Base { 7 | 8 | async indexAction() { 9 | global.setCorsHeader(this); 10 | 11 | let userInfo = await this.session('userInfo'); 12 | if (think.isEmpty(userInfo)) { 13 | return this.fail(this.locale('user_islogin')) 14 | } 15 | let {UserId} = userInfo; 16 | let result = await this.model('proxy').where({userId: UserId}).select(); 17 | 18 | return this.success(result, this.locale('query_success')); 19 | } 20 | 21 | async addAction() { 22 | global.setCorsHeader(this); 23 | const {target, source} = this.post(); 24 | const userInfo = await this.session('userInfo'); 25 | 26 | if (think.isEmpty(userInfo)) { 27 | return this.fail(this.locale('user_islogin')); 28 | } 29 | 30 | if (think.isEmpty(target) || think.isEmpty(source)) { 31 | return this.fail('参数错误'); 32 | } 33 | 34 | let data = { 35 | userId: userInfo.UserId, 36 | target, 37 | source 38 | } 39 | 40 | const result = await this.model('proxy').thenAdd(data, {target, source}); 41 | 42 | if (result.type == 'exist') { 43 | return this.fail(this.locale('user_isExist')); 44 | } 45 | 46 | this.success(data, this.locale('query_success')); 47 | } 48 | 49 | async delAction() { 50 | global.setCorsHeader(this); 51 | 52 | const {id} = this.get(); 53 | // 当前用户信息 54 | const userInfo = await this.session('userInfo'); 55 | 56 | if (think.isEmpty(userInfo)) { 57 | return this.fail(this.locale('user_islogin')) 58 | } 59 | 60 | let data = { 61 | userId: userInfo.UserId, 62 | id 63 | } 64 | 65 | const proxyData = await this.model('proxy').where(data).delete(); 66 | if(proxyData == 0) { 67 | return this.fail(this.locale('query_fail')) 68 | } 69 | 70 | return this.success(proxyData, this.locale('query_success')); 71 | } 72 | } -------------------------------------------------------------------------------- /src/home/controller/api/js.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from '../base.js'; 4 | 5 | export default class extends Base { 6 | 7 | indexAction() { 8 | return this.action('desktop','commonjs'); 9 | } 10 | } -------------------------------------------------------------------------------- /src/home/controller/api/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default class extends think.controller.base { 4 | 5 | async indexAction() { 6 | const _session = await this.session('userInfo'); 7 | 8 | return this.success(_session, this.locale('query_success')); 9 | } 10 | 11 | modifyAction() { 12 | 13 | if (this.isPost()) { 14 | const _post = this.post(); 15 | 16 | this.model('appusers').where(_post).update(_post); 17 | } 18 | 19 | return this.success(undefined, this.locale('query_success')); 20 | } 21 | 22 | 23 | async registerAction() { 24 | this.action('home/user', 'register'); 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /src/home/controller/base.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | 5 | export default class extends think.controller.base { 6 | /** 7 | * some base method in here 8 | */ 9 | 10 | async __before() { 11 | 12 | let is_login = await this.islogin(); 13 | 14 | if (!is_login) { 15 | return this.redirect('/signin.html'); 16 | } 17 | 18 | this.userInfo = await this.session('userInfo'); 19 | 20 | this.assign('username', this.userInfo.UserId); 21 | } 22 | 23 | __after() { 24 | let config = this.model('config').where({id: 1}).select(); 25 | this.assign('config', config); 26 | } 27 | 28 | async islogin() { 29 | let userInfo = await this.session('userInfo'); 30 | let result = think.isEmpty(userInfo) ? false : true; 31 | 32 | return result; 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/home/controller/desktop.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from './base.js'; 4 | 5 | export default class extends Base { 6 | /** 7 | * index action 8 | * @return {Promise} [] 9 | */ 10 | indexAction() { 11 | return this.display('apps/index'); 12 | } 13 | } -------------------------------------------------------------------------------- /src/home/controller/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from './base.js'; 4 | 5 | export default class extends Base { 6 | /** 7 | * index action 8 | * @return {Promise} [] 9 | */ 10 | indexAction(){ 11 | return this.redirect('/signin.html'); 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /src/home/controller/install.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from './base.js'; 4 | 5 | export default class extends Base { 6 | /** 7 | * index action 8 | * @return {Promise} [] 9 | */ 10 | indexAction(){ 11 | //auto render template file index_index.html 12 | return this.display(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/home/controller/openapp.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from './base.js'; 4 | import http from 'http'; 5 | import url from 'url'; 6 | 7 | export default class extends Base { 8 | /* 9 | * todo 从远程获取数据,并在当前地址展示 10 | * 11 | * */ 12 | async indexAction() { 13 | const _get = this.get(); 14 | const config = await this.model('config').find(); 15 | let programData = await this.model('program').getFindSingleServer(_get.id); 16 | 17 | let path = `${programData.serverProbePath}\\app\\${programData.id}.bat`; 18 | path = `${path} proxy "default" "about:blank" ${config.apiKey}`; 19 | 20 | let params = { 21 | id: 'c/' + programData.serverAccessToken, 22 | username: this.userInfo.UserId, 23 | password: this.userInfo.RemotePassword, 24 | title: programData.name, 25 | icon: 'http://' + this.http.host + programData.icon, 26 | program: path 27 | }; 28 | 29 | let url = config.guacamoleApi + '?' + global.setUrlParam(params); 30 | 31 | this.redirect(url); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/home/controller/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Base from './base.js'; 4 | 5 | export default class extends Base { 6 | /** 7 | * index action 8 | * @return {Promise} [] 9 | */ 10 | indexAction() { 11 | //auto render template file index_index.html 12 | return this.display(); 13 | } 14 | 15 | settingAction() { 16 | return this.display(); 17 | } 18 | } -------------------------------------------------------------------------------- /src/home/logic/api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * logic 4 | * @param {} [] 5 | * @return {} [] 6 | */ 7 | export default class extends think.logic.base { 8 | /** 9 | * index action logic 10 | * @return {} [] 11 | */ 12 | async indexAction(){ 13 | 14 | 15 | } 16 | } -------------------------------------------------------------------------------- /src/home/logic/api/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * logic 4 | * @param {} [] 5 | * @return {} [] 6 | */ 7 | export default class extends think.logic.base { 8 | /** 9 | * index action logic 10 | * @return {} [] 11 | */ 12 | indexAction(){ 13 | 14 | } 15 | 16 | addAction() { 17 | this.allowMethods = "post"; 18 | 19 | this.rules = { 20 | ip:"required|ip", 21 | //port:"maxLength:5" 22 | }; 23 | } 24 | } -------------------------------------------------------------------------------- /src/home/logic/api/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * logic 4 | * @param {} [] 5 | * @return {} [] 6 | */ 7 | export default class extends think.logic.base { 8 | /** 9 | * index action logic 10 | * @return {} [] 11 | */ 12 | indexAction(){ 13 | 14 | } 15 | 16 | registerAction() { 17 | this.allowMethods = "post"; 18 | 19 | this.rules = { 20 | UserId:"required", 21 | RemotePassword:"byteLength:6,32|required" 22 | }; 23 | 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/home/logic/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * logic 4 | * @param {} [] 5 | * @return {} [] 6 | */ 7 | export default class extends think.logic.base { 8 | /** 9 | * index action logic 10 | * @return {} [] 11 | */ 12 | indexAction(){ 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /src/home/logic/install.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * logic 4 | * @param {} [] 5 | * @return {} [] 6 | */ 7 | export default class extends think.logic.base { 8 | /** 9 | * index action logic 10 | * @return {} [] 11 | */ 12 | indexAction(){ 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /src/home/logic/openapp.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * logic 4 | * @param {} [] 5 | * @return {} [] 6 | */ 7 | export default class extends think.logic.base { 8 | /** 9 | * index action logic 10 | * @return {} [] 11 | */ 12 | indexAction(){ 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /src/home/logic/proxy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * logic 4 | * @param {} [] 5 | * @return {} [] 6 | */ 7 | export default class extends think.logic.base { 8 | /** 9 | * index action logic 10 | * @return {} [] 11 | */ 12 | indexAction() { 13 | 14 | } 15 | 16 | addAction() { 17 | this.allowMethods = "post"; 18 | 19 | this.rules = { 20 | hosts: "required" 21 | }; 22 | } 23 | 24 | delAction () { 25 | id: "required" 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /src/home/logic/signin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * logic 4 | * @param {} [] 5 | * @return {} [] 6 | */ 7 | export default class extends think.logic.base { 8 | /** 9 | * index action logic 10 | * @return {} [] 11 | */ 12 | indexAction() { 13 | 14 | } 15 | 16 | registerAction() { 17 | this.allowMethods = "post"; 18 | 19 | this.rules = { 20 | UserId: "required", 21 | RemotePassword: "byteLength:6,32|required" 22 | }; 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /src/home/model/appusers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * model 4 | */ 5 | export default class extends think.model.base { 6 | 7 | async autoLogin(){ 8 | const userInfo = await this.select(); 9 | 10 | let random = parseInt(Math.random() * userInfo.length); 11 | let user = userInfo[random]; 12 | 13 | return user; 14 | } 15 | 16 | async signin(data) { 17 | const user = await this.where(data).find(); 18 | 19 | return user; 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/home/model/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * model 4 | */ 5 | export default class extends think.model.base { 6 | 7 | } -------------------------------------------------------------------------------- /src/home/model/program.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * model 4 | */ 5 | export default class extends think.model.base { 6 | 7 | getList(data) { 8 | let table = this.getTableName() + '.*'; 9 | let status = this.getTableName() + '.status'; 10 | // 关联表重命名 11 | let serverTablePrefix = this.getTablePrefix() + 'server.'; 12 | let serverId = serverTablePrefix + 'id as serverId'; 13 | let serverName = serverTablePrefix + 'name as serverName'; 14 | let serverIp = serverTablePrefix + 'ip as serverIp'; 15 | let serverAccessToken = serverTablePrefix + 'accessToken as serverAccessToken'; 16 | let serverProbePath = serverTablePrefix + 'probePath as serverProbePath'; 17 | // 关联查询 18 | let res = this.field([table, serverId, serverName, serverIp, serverAccessToken, serverProbePath]).join({ 19 | table: 'server', 20 | join: 'left', 21 | on: ['serverId', 'id'] 22 | }) 23 | 24 | return res.where(data).select(); 25 | } 26 | 27 | getAllList() { 28 | let table = this.getTableName() + '.*'; 29 | let status = this.getTableName() + '.status'; 30 | // 关联表重命名 31 | let serverTablePrefix = this.getTablePrefix() + 'server.'; 32 | let serverId = serverTablePrefix + 'id as serverId'; 33 | let serverName = serverTablePrefix + 'name as serverName'; 34 | let serverIp = serverTablePrefix + 'ip as serverIp'; 35 | let serverAccessToken = serverTablePrefix + 'accessToken as serverAccessToken'; 36 | let serverProbePath = serverTablePrefix + 'probePath as serverProbePath'; 37 | // 关联查询 38 | let res = this.field([table, serverId, serverName, serverIp, serverAccessToken, serverProbePath]).join({ 39 | table: 'server', 40 | join: 'left', 41 | on: ['serverId', 'id'] 42 | }) 43 | 44 | return res.where({[status]: 1}).select(); 45 | } 46 | 47 | getStatus() { 48 | let status = this.getTableName() + '.status'; 49 | return this.where({[status]: 1}); 50 | } 51 | 52 | getFindSingleServer(programId) { 53 | let table = this.getTableName() + '.*'; 54 | let id = this.getTableName() + '.id'; 55 | let status = this.getTableName() + '.status'; 56 | // 关联表重命名 57 | let serverTablePrefix = this.getTablePrefix() + 'server.'; 58 | let serverId = serverTablePrefix + 'id as serverId'; 59 | let serverName = serverTablePrefix + 'name as serverName'; 60 | let serverIp = serverTablePrefix + 'ip as serverIp'; 61 | let serverAccessToken = serverTablePrefix + 'accessToken as serverAccessToken'; 62 | let serverProbePath = serverTablePrefix + 'probePath as serverProbePath'; 63 | // 关联查询 64 | let res = this.field([table, serverId, serverName, serverIp, serverAccessToken, serverProbePath]).join({ 65 | table: 'server', 66 | join: 'left', 67 | on: ['serverId', 'id'] 68 | }) 69 | 70 | return res.where({[id]: programId}).getStatus().find(); 71 | } 72 | 73 | getSingleList(data) { 74 | return this.field(['program.*', 'server.id as serverId,server.name as serverName,server.ip as serverIp, server.accessToken as serverAccessToken, server.probePath as serverProbePath']).join({ 75 | table: 'server', 76 | join: 'left', 77 | on: ['serverId', 'id'] 78 | }).where(data).find(); 79 | } 80 | 81 | getProxySingleList(programId) { 82 | let table = this.getTableName() + '.*'; 83 | let id = this.getTableName() + '.id'; 84 | let path = this.getTableName() + '.path'; 85 | // 关联表重命名 86 | let serverTablePrefix = this.getTablePrefix() + 'server.'; 87 | let serverId = serverTablePrefix + 'id as serverId'; 88 | let serverIp = serverTablePrefix + 'ip as serverIp'; 89 | let serverProbePath = serverTablePrefix + 'probePath as serverProbePath'; 90 | // 关联查询 91 | let res = this.field([id, serverId, serverIp, path, serverProbePath]).join({ 92 | table: 'server', 93 | join: 'left', 94 | on: ['serverId', 'id'] 95 | }); 96 | 97 | return res.where({[id]: programId}).find(); 98 | } 99 | 100 | } -------------------------------------------------------------------------------- /src/home/model/proxy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * model 4 | */ 5 | export default class extends think.model.base { 6 | 7 | getList(data) { 8 | let table = this.getTableName() + '.*'; 9 | let appusersTablePrefix = 'appusers.'; 10 | let userId = appusersTablePrefix + 'UserId as userId'; 11 | 12 | return this.field([table, userId]).join({ 13 | table: 'appusers', 14 | join: 'left', 15 | on: ['UserId', 'UserId'] 16 | }).where(data).select(); 17 | } 18 | 19 | getSingleList(data) { 20 | let table = this.getTableName() + '.*'; 21 | 22 | let appusersTablePrefix = 'appusers.'; 23 | let userId = appusersTablePrefix + 'UserId as userId'; 24 | 25 | return this.field([table, userId]).join({ 26 | table: 'appusers', 27 | join: 'left', 28 | on: ['UserId', 'UserId'] 29 | }).where(data).find(); 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /view/admin/app_add.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dCloud Admin 6 | 7 | 8 | <% include common/style.html %> 9 | 10 | 11 |
12 | <% include common/silder-menu.html %> 13 | 14 |
15 | 16 |
17 | 18 |
19 |
20 | 应用程序 21 | 22 | 返回 23 | 24 |
25 |
26 | 27 |
28 | 29 |
30 |
31 | 32 | 33 |
34 |
35 |
36 | 37 |
38 |
39 | 40 |
41 |
42 |
43 |
44 |
45 | 46 | 47 |
48 |
49 |
50 | 51 |
52 |
53 | 54 |
55 |
56 |
57 |
58 |
59 | 60 | 61 |
62 |
63 |
64 | 65 |
66 |
67 | 68 |
69 |
70 |
71 |
72 | 73 |
74 | 75 | 76 |
77 | 78 |
79 |
80 | 81 |
82 | 83 |
84 |
85 |
86 | 87 | 88 |
89 | 90 |
91 | 92 | <% include common/footer.html %> 93 |
94 | 95 | -------------------------------------------------------------------------------- /view/admin/app_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dCloud Admin 6 | 7 | 8 | <% include common/style.html %> 9 | 10 | 11 |
12 | <% include common/silder-menu.html %> 13 | 14 |
15 | 16 |
17 | 18 |
19 |
20 | 应用程序 21 | 22 | 部署应用 23 | 添加应用程序 24 | 25 |
26 |
27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | <% app.forEach(function(item){ %> 42 | 43 | 44 | 45 | 46 | 47 | 48 | 56 | 60 | 61 | <% }) %> 62 | 63 |
#图标应用名称所属服务程序路径显示操作
<%= item.id%><%= item.name%><%= item.serverName%><%= item.path%> 49 | <%if (item.status== 1) { %> 50 | 显示 52 | <% } else { %> 53 | 隐藏 54 | <% } %> 55 | 57 | 修改 58 | 删除 59 |
64 | 65 |
66 |
67 |
68 | 69 |
70 | 71 |
72 | 73 | <% include common/footer.html %> 74 |
75 | <% include common/script.html %> 76 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /view/admin/common/container.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/view/admin/common/container.html -------------------------------------------------------------------------------- /view/admin/common/footer.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | dCloud Admin 4 | 5 | 6 |

7 | © 2016 dCloud Admin. ALL Rights Reserved. 8 |

9 |
10 | -------------------------------------------------------------------------------- /view/admin/common/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dCloud Admin 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
-------------------------------------------------------------------------------- /view/admin/common/script.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /view/admin/common/silder-menu.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 14 | 15 |
16 |
17 | 18 | -------------------------------------------------------------------------------- /view/admin/common/style.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /view/admin/index_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dCloud Admin 6 | 7 | 8 | <% include common/style.html %> 9 | 10 | 11 |
12 | <% include common/silder-menu.html %> 13 |
14 |
15 | 服务端 16 | 25 |
26 | 客户端 27 |
28 | 32 |
33 | 34 |
35 | 36 |
37 | <% include common/footer.html %> 38 |
39 | <% include common/script.html %> 40 | 119 | 120 | -------------------------------------------------------------------------------- /view/admin/install_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dCloud Admin 6 | 7 | 8 | <% include common/style.html %> 9 | 10 | 11 |
12 | <% include common/silder-menu.html %> 13 | 14 | 15 | 16 | 17 |
18 | 19 | 29 | 30 |
31 | 32 | <% include common/footer.html %> 33 |
34 | <% include common/script.html %> 35 | 36 | 37 | 38 | 39 | 40 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /view/admin/user_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dCloud Admin 6 | 7 | 8 | <% include common/style.html %> 9 | 10 | 11 |
12 | <% include common/silder-menu.html %> 13 | 14 |
15 | 16 |
17 | 18 |
19 |
20 | 用户管理 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | <% user.forEach(function(item){ %> 45 | 46 | 47 | 48 | 57 | 64 | 65 | 66 | 68 | 69 | <% }) %> 70 | 71 | 72 | 73 |
UserId用户名部门/职位最后登录时间最后访问IP操作
<%=item.UserId%><%=item.NickName%> 49 | <% if(item.Job) { 50 | 51 | %> 52 | <%=item.Department%>/<%=item.Job%> 53 | <% } else { %> 54 | <%=item.Department %> 55 | <% } %> 56 | 58 | <% if(item.LastTime) { %> 59 | <%=think.datetime(item.LastTime, "YYYY-MM-DD HH:mm:ss") %> 60 | <% } else { %> 61 | 未登陆 62 | <% } %> 63 | <%= item.LastIp %>删除 67 |
74 | 75 |
76 |
77 |
78 | 79 |
80 | 81 |
82 | 83 | <% include common/footer.html %> 84 |
85 | 86 | -------------------------------------------------------------------------------- /view/common/error_400.html: -------------------------------------------------------------------------------- 1 | 400 2 | ERROR_MESSAGE -------------------------------------------------------------------------------- /view/common/error_403.html: -------------------------------------------------------------------------------- 1 | 403 -------------------------------------------------------------------------------- /view/common/error_404.html: -------------------------------------------------------------------------------- 1 | 404 -------------------------------------------------------------------------------- /view/common/error_500.html: -------------------------------------------------------------------------------- 1 | 500 2 | ERROR_MESSAGE -------------------------------------------------------------------------------- /view/common/error_503.html: -------------------------------------------------------------------------------- 1 | 503 -------------------------------------------------------------------------------- /view/home/api_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 接口地址: 9 | 10 | 客户端服务器生成bat文件并写有命令 11 | /proxy/remotegeneratecmdsingle?id=1 12 | 13 | 所有的客户端服务器生成bat文件并写有命令 14 | /proxy/remoteGenerateCmd 15 | 16 | 代理配置脚本 17 | /proxy/pac 18 | 19 | 20 | http://127.0.0.1:8361/api/server/config?type=write 21 | 读取配置文件信息 22 | 入参: 23 | type=write 24 | 写入配置文件信息 25 | 26 | 客户端服务器接口 27 | http://192.168.159.137/setuser.asp?shellName=test&shellPath=test 28 | 29 | 方法:get 30 | 入参: 31 | shellName 32 | shellPath 33 | 34 | 探针 35 | http://127.0.0.1:8361/admin/download/probe 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /view/home/apps_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | dcloud 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/home/common/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /view/home/common/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dCloud 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 |
23 | dCloud 24 | 桌面 25 | 26 | 27 |
28 |
29 | 42 |
43 |
44 |
45 |
46 | -------------------------------------------------------------------------------- /view/home/index_index.html: -------------------------------------------------------------------------------- 1 | <% include common/header.html %> 2 | 3 | 4 | <% include common/footer.html %> 5 | -------------------------------------------------------------------------------- /view/home/install_index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/view/home/install_index.html -------------------------------------------------------------------------------- /view/home/user_setting.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | KodExplorer - Powered by KodExplorer 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 23 |
个人中心
24 | 29 | 30 |
31 |
32 | 用户名 33 | 34 |
35 | 部门 36 | 37 |
38 | 职位 39 |
40 | 保存 41 |
42 | 43 | 44 |
45 |
46 | 47 | 57 |
58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | <% include common/footer.html %> 76 | 77 | -------------------------------------------------------------------------------- /www/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/README.md -------------------------------------------------------------------------------- /www/development.js: -------------------------------------------------------------------------------- 1 | var thinkjs = require('thinkjs'); 2 | var path = require('path'); 3 | 4 | var rootPath = path.dirname(__dirname); 5 | 6 | var instance = new thinkjs({ 7 | APP_PATH: rootPath + path.sep + 'app', 8 | RUNTIME_PATH: rootPath + path.sep + 'runtime', 9 | ROOT_PATH: rootPath, 10 | RESOURCE_PATH: __dirname, 11 | env: 'development' 12 | }); 13 | 14 | // Build code from src to app directory. 15 | instance.compile({ 16 | log: true 17 | }); 18 | 19 | instance.run(); 20 | -------------------------------------------------------------------------------- /www/production.js: -------------------------------------------------------------------------------- 1 | var thinkjs = require('thinkjs'); 2 | var path = require('path'); 3 | 4 | var rootPath = path.dirname(__dirname); 5 | 6 | var instance = new thinkjs({ 7 | APP_PATH: rootPath + path.sep + 'app', 8 | RUNTIME_PATH: rootPath + path.sep + 'runtime', 9 | ROOT_PATH: rootPath, 10 | RESOURCE_PATH: __dirname, 11 | env: 'production' 12 | }); 13 | 14 | instance.run(true); 15 | -------------------------------------------------------------------------------- /www/static/admin/css/datepicker.css: -------------------------------------------------------------------------------- 1 | div.datepicker { 2 | position: relative; 3 | font-family: Arial, Helvetica, sans-serif; 4 | font-size: 12px; 5 | width: 196px; 6 | height: 147px; 7 | position: absolute; 8 | cursor: default; 9 | top: 0; 10 | left: 0; 11 | display: none; 12 | } 13 | .datepickerContainer { 14 | background: #121212; 15 | position: absolute; 16 | top: 10px; 17 | left: 10px; 18 | } 19 | .datepickerBorderT { 20 | position: absolute; 21 | left: 10px; 22 | top: 0; 23 | right: 10px; 24 | height: 10px; 25 | /*background: url(../images/datepicker_t.png);*/ 26 | } 27 | .datepickerBorderB { 28 | position: absolute; 29 | left: 10px; 30 | bottom: 0; 31 | right: 10px; 32 | height: 10px; 33 | /*background: url(../images/datepicker_b.png);*/ 34 | } 35 | .datepickerBorderL { 36 | position: absolute; 37 | left: 0; 38 | bottom: 10px; 39 | top: 10px; 40 | width: 10px; 41 | /*background: url(../images/datepicker_l.png);*/ 42 | } 43 | .datepickerBorderR { 44 | position: absolute; 45 | right: 0; 46 | bottom: 10px; 47 | top: 10px; 48 | width: 10px; 49 | /*background: url(../images/datepicker_r.png);*/ 50 | } 51 | .datepickerBorderTL { 52 | position: absolute; 53 | top: 0; 54 | left: 0; 55 | width: 10px; 56 | height: 10px; 57 | /*background: url(../images/datepicker_tl.png);*/ 58 | } 59 | .datepickerBorderTR { 60 | position: absolute; 61 | top: 0; 62 | right: 0; 63 | width: 10px; 64 | height: 10px; 65 | /*background: url(../images/datepicker_tr.png);*/ 66 | } 67 | .datepickerBorderBL { 68 | position: absolute; 69 | bottom: 0; 70 | left: 0; 71 | width: 10px; 72 | height: 10px; 73 | /*background: url(../images/datepicker_bl.png);*/ 74 | } 75 | .datepickerBorderBR { 76 | position: absolute; 77 | bottom: 0; 78 | right: 0; 79 | width: 10px; 80 | height: 10px; 81 | /*background: url(../images/datepicker_br.png);*/ 82 | } 83 | .datepickerHidden { 84 | display: none; 85 | } 86 | div.datepicker table { 87 | border-collapse:collapse; 88 | } 89 | div.datepicker a { 90 | color: #eee; 91 | text-decoration: none; 92 | cursor: default; 93 | outline: none; 94 | } 95 | div.datepicker table td { 96 | text-align: right; 97 | padding: 0; 98 | margin: 0; 99 | } 100 | div.datepicker th { 101 | text-align: center; 102 | color: #999; 103 | font-weight: normal; 104 | } 105 | div.datepicker tbody th { 106 | text-align: left; 107 | } 108 | div.datepicker tbody a { 109 | display: block; 110 | } 111 | .datepickerDays a { 112 | width: 20px; 113 | line-height: 16px; 114 | height: 16px; 115 | padding-right: 2px; 116 | } 117 | .datepickerYears a, 118 | .datepickerMonths a{ 119 | width: 44px; 120 | line-height: 36px; 121 | height: 36px; 122 | text-align: center; 123 | } 124 | td.datepickerNotInMonth a { 125 | color: #666; 126 | } 127 | tbody.datepickerDays td.datepickerSelected{ 128 | background: #136A9F; 129 | } 130 | tbody.datepickerDays td.datepickerNotInMonth.datepickerSelected { 131 | background: #17384d; 132 | } 133 | tbody.datepickerYears td.datepickerSelected, 134 | tbody.datepickerMonths td.datepickerSelected{ 135 | background: #17384d; 136 | } 137 | div.datepicker a:hover, 138 | div.datepicker a:hover { 139 | color: #88c5eb; 140 | } 141 | div.datepicker td.datepickerNotInMonth a:hover { 142 | color: #999; 143 | } 144 | div.datepicker tbody th { 145 | text-align: left; 146 | } 147 | .datepickerSpace div { 148 | width: 20px; 149 | } 150 | .datepickerGoNext a, 151 | .datepickerGoPrev a, 152 | .datepickerMonth a { 153 | text-align: center; 154 | height: 20px; 155 | line-height: 20px; 156 | } 157 | .datepickerGoNext a { 158 | float: right; 159 | width: 20px; 160 | } 161 | .datepickerGoPrev a { 162 | float: left; 163 | width: 20px; 164 | } 165 | table.datepickerViewDays tbody.datepickerMonths, 166 | table.datepickerViewDays tbody.datepickerYears { 167 | display: none; 168 | } 169 | table.datepickerViewMonths tbody.datepickerDays, 170 | table.datepickerViewMonths tbody.datepickerYears, 171 | table.datepickerViewMonths tr.datepickerDoW { 172 | display: none; 173 | } 174 | table.datepickerViewYears tbody.datepickerDays, 175 | table.datepickerViewYears tbody.datepickerMonths, 176 | table.datepickerViewYears tr.datepickerDoW { 177 | display: none; 178 | } 179 | td.datepickerDisabled a, 180 | td.datepickerDisabled.datepickerNotInMonth a{ 181 | color: #333; 182 | } 183 | td.datepickerDisabled a:hover { 184 | color: #333; 185 | } 186 | td.datepickerSpecial a { 187 | background: #700; 188 | } 189 | td.datepickerSpecial.datepickerSelected a { 190 | background: #a00; 191 | } -------------------------------------------------------------------------------- /www/static/admin/css/jquery.tagsinput.css: -------------------------------------------------------------------------------- 1 | div.tagsinput { border:1px solid #CCC; background: #FFF; padding:5px; width:300px; height:100px; overflow-y: auto;} 2 | div.tagsinput span.tag { border: 1px solid #a5d24a; -moz-border-radius:2px; -webkit-border-radius:2px; display: block; float: left; padding: 5px; text-decoration:none; background: #cde69c; color: #638421; margin-right: 5px; margin-bottom:5px;font-family: helvetica; font-size:13px;} 3 | div.tagsinput span.tag a { font-weight: bold; color: #82ad2b; text-decoration:none; font-size: 11px; } 4 | div.tagsinput input { width:80px; margin:0px; font-family: helvetica; font-size: 13px; border:1px solid transparent; padding:5px; background: transparent; color: #000; outline:0px; margin-right:5px; margin-bottom:5px; } 5 | div.tagsinput div { display:block; float: left; } 6 | .tags_clear { clear: both; width: 100%; height: 0px; } 7 | .not_valid {background: #FBD8DB !important; color: #90111A !important;} 8 | -------------------------------------------------------------------------------- /www/static/admin/css/morris.css: -------------------------------------------------------------------------------- 1 | .morris-hover{position:absolute;z-index:1000;}.morris-hover.morris-default-style{border-radius:10px;padding:6px;color:#666;background:rgba(255, 255, 255, 0.8);border:solid 2px rgba(230, 230, 230, 0.8);font-family:sans-serif;font-size:12px;text-align:center;}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold;margin:0.25em 0;} 2 | .morris-hover.morris-default-style .morris-hover-point{white-space:nowrap;margin:0.1em 0;} 3 | -------------------------------------------------------------------------------- /www/static/admin/css/owl.carousel.min.css: -------------------------------------------------------------------------------- 1 | .owl-carousel .animated{-webkit-animation-duration:1000ms;animation-duration:1000ms;-webkit-animation-fill-mode:both;animation-fill-mode:both}.owl-carousel .owl-animated-in{z-index:0}.owl-carousel .owl-animated-out{z-index:1}.owl-carousel .fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}.owl-height{-webkit-transition:height 500ms ease-in-out;-moz-transition:height 500ms ease-in-out;-ms-transition:height 500ms ease-in-out;-o-transition:height 500ms ease-in-out;transition:height 500ms ease-in-out}.owl-carousel{display:none;width:100%;-webkit-tap-highlight-color:transparent;position:relative;z-index:1}.owl-carousel .owl-stage{position:relative;-ms-touch-action:pan-Y}.owl-carousel .owl-stage:after{content:".";display:block;clear:both;visibility:hidden;line-height:0;height:0}.owl-carousel .owl-stage-outer{position:relative;overflow:hidden;-webkit-transform:translate3d(0px,0,0)}.owl-carousel .owl-controls .owl-dot,.owl-carousel .owl-controls .owl-nav .owl-next,.owl-carousel .owl-controls .owl-nav .owl-prev{cursor:pointer;cursor:hand;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.owl-carousel.owl-loaded{display:block}.owl-carousel.owl-loading{opacity:0;display:block}.owl-carousel.owl-hidden{opacity:0}.owl-carousel .owl-refresh .owl-item{display:none}.owl-carousel .owl-item{position:relative;min-height:1px;float:left;-webkit-backface-visibility:hidden;-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.owl-carousel .owl-item img{display:block;width:100%;-webkit-transform-style:preserve-3d}.owl-carousel.owl-text-select-on .owl-item{-webkit-user-select:auto;-moz-user-select:auto;-ms-user-select:auto;user-select:auto}.owl-carousel .owl-grab{cursor:move;cursor:-webkit-grab;cursor:-o-grab;cursor:-ms-grab;cursor:grab}.owl-carousel .owl-rtl{direction:rtl}.owl-carousel .owl-rtl .owl-item{float:right}.no-js .owl-carousel{display:block}.owl-carousel .owl-item .owl-lazy{opacity:0;-webkit-transition:opacity 400ms ease;-moz-transition:opacity 400ms ease;-ms-transition:opacity 400ms ease;-o-transition:opacity 400ms ease;transition:opacity 400ms ease}.owl-carousel .owl-item img{transform-style:preserve-3d}.owl-carousel .owl-video-wrapper{position:relative;height:100%;background:#000}.owl-carousel .owl-video-play-icon{position:absolute;height:80px;width:80px;left:50%;top:50%;margin-left:-40px;margin-top:-40px;background:url(owl.video.play.png) no-repeat;cursor:pointer;z-index:1;-webkit-backface-visibility:hidden;-webkit-transition:scale 100ms ease;-moz-transition:scale 100ms ease;-ms-transition:scale 100ms ease;-o-transition:scale 100ms ease;transition:scale 100ms ease}.owl-carousel .owl-video-play-icon:hover{-webkit-transition:scale(1.3,1.3);-moz-transition:scale(1.3,1.3);-ms-transition:scale(1.3,1.3);-o-transition:scale(1.3,1.3);transition:scale(1.3,1.3)}.owl-carousel .owl-video-playing .owl-video-play-icon,.owl-carousel .owl-video-playing .owl-video-tn{display:none}.owl-carousel .owl-video-tn{opacity:0;height:100%;background-position:center center;background-repeat:no-repeat;-webkit-background-size:contain;-moz-background-size:contain;-o-background-size:contain;background-size:contain;-webkit-transition:opacity 400ms ease;-moz-transition:opacity 400ms ease;-ms-transition:opacity 400ms ease;-o-transition:opacity 400ms ease;transition:opacity 400ms ease}.owl-carousel .owl-video-frame{position:relative;z-index:1} -------------------------------------------------------------------------------- /www/static/admin/css/owl.theme.default.min.css: -------------------------------------------------------------------------------- 1 | .owl-theme .owl-controls{margin-top:10px;text-align:center;-webkit-tap-highlight-color:transparent}.owl-theme .owl-controls .owl-nav [class*=owl-]{color:#fff;font-size:14px;margin:5px;padding:4px 7px;background:#d6d6d6;display:inline-block;cursor:pointer;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.owl-theme .owl-controls .owl-nav [class*=owl-]:hover{background:#869791;color:#fff;text-decoration:none}.owl-theme .owl-controls .owl-nav .disabled{opacity:.5;cursor:default}.owl-theme .owl-dots .owl-dot{display:inline-block;zoom:1;*display:inline}.owl-theme .owl-dots .owl-dot span{width:10px;height:10px;margin:5px 7px;background:#d6d6d6;display:block;-webkit-backface-visibility:visible;-webkit-transition:opacity 200ms ease;-moz-transition:opacity 200ms ease;-ms-transition:opacity 200ms ease;-o-transition:opacity 200ms ease;transition:opacity 200ms ease;-webkit-border-radius:30px;-moz-border-radius:30px;border-radius:30px}.owl-theme .owl-dots .owl-dot.active span,.owl-theme .owl-dots .owl-dot:hover span{background:#869791} -------------------------------------------------------------------------------- /www/static/admin/js/modernizr.min.js: -------------------------------------------------------------------------------- 1 | /* Modernizr 2.7.1 (Custom Build) | MIT & BSD 2 | * Build: http://modernizr.com/download/#-mq-teststyles 3 | */ 4 | ;window.Modernizr=function(a,b,c){function v(a){i.cssText=a}function w(a,b){return v(prefixes.join(a+";")+(b||""))}function x(a,b){return typeof a===b}function y(a,b){return!!~(""+a).indexOf(b)}function z(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:x(f,"function")?f.bind(d||b):f}return!1}var d="2.7.1",e={},f=b.documentElement,g="modernizr",h=b.createElement(g),i=h.style,j,k={}.toString,l={},m={},n={},o=[],p=o.slice,q,r=function(a,c,d,e){var h,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:g+(d+1),l.appendChild(j);return h=["­",'"].join(""),l.id=g,(m?l:n).innerHTML+=h,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=f.style.overflow,f.style.overflow="hidden",f.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),f.style.overflow=k),!!i},s=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return r("@media "+b+" { #"+g+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},t={}.hasOwnProperty,u;!x(t,"undefined")&&!x(t.call,"undefined")?u=function(a,b){return t.call(a,b)}:u=function(a,b){return b in a&&x(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=p.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(p.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(p.call(arguments)))};return e});for(var A in l)u(l,A)&&(q=A.toLowerCase(),e[q]=l[A](),o.push((e[q]?"":"no-")+q));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)u(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof enableClasses!="undefined"&&enableClasses&&(f.className+=" "+(b?"":"no-")+a),e[a]=b}return e},v(""),h=j=null,e._version=d,e.mq=s,e.testStyles=r,e}(this,this.document); -------------------------------------------------------------------------------- /www/static/admin/js/simplify/simplify_dashboard.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | //Flot Chart (Total Sales) 4 | var d1 = []; 5 | for (var i = 0; i <= 10; i += 1) { 6 | //d1.push([i, parseInt(Math.random() * 30)]); 7 | d1 = [[0,700],[1,1200],[2,1100],[3,900],[4,500],[5,700],[6,500],[7,600],[8,1200],[9,1700],[10,1200]]; 8 | } 9 | 10 | function plotWithOptions() { 11 | $.plot("#placeholder", [d1], { 12 | series: { 13 | lines: { 14 | show: true, 15 | fill: true, 16 | fillColor: '#eee', 17 | steps: false, 18 | 19 | }, 20 | points: { 21 | show: true, 22 | fill: false 23 | } 24 | }, 25 | 26 | grid: { 27 | color: '#fff', 28 | hoverable: true, 29 | autoHighlight: true, 30 | }, 31 | colors: [ '#bbb'], 32 | }); 33 | } 34 | 35 | $("
").css({ 36 | position: "absolute", 37 | display: "none", 38 | border: "1px solid #222", 39 | padding: "4px", 40 | color: "#fff", 41 | "border-radius": "4px", 42 | "background-color": "rgb(0,0,0)", 43 | opacity: 0.90 44 | }).appendTo("body"); 45 | 46 | $("#placeholder").bind("plothover", function (event, pos, item) { 47 | 48 | var str = "(" + pos.x.toFixed(2) + ", " + pos.y.toFixed(2) + ")"; 49 | $("#hoverdata").text(str); 50 | 51 | if (item) { 52 | var x = item.datapoint[0], 53 | y = item.datapoint[1]; 54 | 55 | $("#tooltip").html("Total Sales : " + y) 56 | .css({top: item.pageY+5, left: item.pageX+5}) 57 | .fadeIn(200); 58 | } else { 59 | $("#tooltip").hide(); 60 | } 61 | }); 62 | 63 | plotWithOptions(); 64 | 65 | //Morris Chart (Total Visits) 66 | var totalVisitChart = Morris.Bar({ 67 | element: 'totalSalesChart', 68 | data: [ 69 | { y: '2008', a: 100, b: 90 }, 70 | { y: '2009', a: 75, b: 65 }, 71 | { y: '2010', a: 50, b: 40 }, 72 | { y: '2011', a: 75, b: 65 }, 73 | { y: '2012', a: 50, b: 40 }, 74 | { y: '2013', a: 75, b: 65 }, 75 | { y: '2014', a: 100, b: 90 } 76 | ], 77 | xkey: 'y', 78 | ykeys: ['a', 'b'], 79 | labels: ['Total Visits', 'Bounce Rate'], 80 | barColors: ['#999', '#eee'], 81 | grid: false, 82 | gridTextColor: '#777', 83 | }); 84 | 85 | 86 | //Datepicker 87 | $('#calendar').DatePicker({ 88 | flat: true, 89 | date: '2014-06-07', 90 | current: '2014-06-07', 91 | calendars: 1, 92 | starts: 1 93 | }); 94 | 95 | //Skycon 96 | var icons = new Skycons({"color": "white"}); 97 | icons.set("skycon1", "sleet"); 98 | icons.set("skycon2", "partly-cloudy-day"); 99 | icons.set("skycon3", "wind"); 100 | icons.set("skycon4", "clear-day"); 101 | icons.play(); 102 | 103 | //Scrollable Chat Widget 104 | $('#chatScroll').slimScroll({ 105 | height:'230px' 106 | }); 107 | 108 | //Chat notification 109 | setTimeout(function() { 110 | $('.chat-notification').find('.badge').addClass('active'); 111 | $('.chat-alert').addClass('active'); 112 | }, 3000); 113 | 114 | setTimeout(function() { 115 | $('.chat-alert').removeClass('active'); 116 | }, 8000); 117 | 118 | $(window).resize(function(e) { 119 | // Redraw All Chart 120 | setTimeout(function() { 121 | totalVisitChart.redraw(); 122 | plotWithOptions(); 123 | },500); 124 | }); 125 | 126 | $('#sidebarToggleLG').click(function() { 127 | // Redraw All Chart 128 | setTimeout(function() { 129 | totalVisitChart.redraw(); 130 | plotWithOptions(); 131 | },500); 132 | }); 133 | 134 | $('#sidebarToggleSM').click(function() { 135 | // Redraw All Chart 136 | setTimeout(function() { 137 | totalVisitChart.redraw(); 138 | plotWithOptions(); 139 | },500); 140 | }); 141 | }); 142 | -------------------------------------------------------------------------------- /www/static/admin/js/uncompressed/jquery.sortable.js: -------------------------------------------------------------------------------- 1 | /* 2 | * HTML5 Sortable jQuery Plugin 3 | * http://farhadi.ir/projects/html5sortable 4 | * 5 | * Copyright 2012, Ali Farhadi 6 | * Released under the MIT license. 7 | */ 8 | (function($) { 9 | var dragging, placeholders = $(); 10 | $.fn.sortable = function(options) { 11 | var method = String(options); 12 | options = $.extend({ 13 | connectWith: false 14 | }, options); 15 | return this.each(function() { 16 | if (/^enable|disable|destroy$/.test(method)) { 17 | var items = $(this).children($(this).data('items')).attr('draggable', method == 'enable'); 18 | if (method == 'destroy') { 19 | items.add(this).removeData('connectWith items') 20 | .off('dragstart.h5s dragend.h5s selectstart.h5s dragover.h5s dragenter.h5s drop.h5s'); 21 | } 22 | return; 23 | } 24 | var isHandle, index, items = $(this).children(options.items); 25 | var placeholder = $('<' + (/^ul|ol$/i.test(this.tagName) ? 'li' : 'div') + ' class="sortable-placeholder">'); 26 | items.find(options.handle).mousedown(function() { 27 | isHandle = true; 28 | }).mouseup(function() { 29 | isHandle = false; 30 | }); 31 | $(this).data('items', options.items) 32 | placeholders = placeholders.add(placeholder); 33 | if (options.connectWith) { 34 | $(options.connectWith).add(this).data('connectWith', options.connectWith); 35 | } 36 | items.attr('draggable', 'true').on('dragstart.h5s', function(e) { 37 | if (options.handle && !isHandle) { 38 | return false; 39 | } 40 | isHandle = false; 41 | var dt = e.originalEvent.dataTransfer; 42 | dt.effectAllowed = 'move'; 43 | dt.setData('Text', 'dummy'); 44 | index = (dragging = $(this)).addClass('sortable-dragging').index(); 45 | }).on('dragend.h5s', function() { 46 | if (!dragging) { 47 | return; 48 | } 49 | dragging.removeClass('sortable-dragging').show(); 50 | placeholders.detach(); 51 | if (index != dragging.index()) { 52 | dragging.parent().trigger('sortupdate', {item: dragging}); 53 | } 54 | dragging = null; 55 | }).not('a[href], img').on('selectstart.h5s', function() { 56 | this.dragDrop && this.dragDrop(); 57 | return false; 58 | }).end().add([this, placeholder]).on('dragover.h5s dragenter.h5s drop.h5s', function(e) { 59 | if (!items.is(dragging) && options.connectWith !== $(dragging).parent().data('connectWith')) { 60 | return true; 61 | } 62 | if (e.type == 'drop') { 63 | e.stopPropagation(); 64 | placeholders.filter(':visible').after(dragging); 65 | dragging.trigger('dragend.h5s'); 66 | return false; 67 | } 68 | e.preventDefault(); 69 | e.originalEvent.dataTransfer.dropEffect = 'move'; 70 | if (items.is(this)) { 71 | if (options.forcePlaceholderSize) { 72 | placeholder.height(dragging.outerHeight()); 73 | } 74 | dragging.hide(); 75 | $(this)[placeholder.index() < $(this).index() ? 'after' : 'before'](placeholder); 76 | placeholders.not(placeholder).detach(); 77 | } else if (!placeholders.is(this) && !$(this).children(options.items).length) { 78 | placeholders.detach(); 79 | $(this).append(placeholder); 80 | } 81 | return false; 82 | }); 83 | }); 84 | }; 85 | })(jQuery); 86 | -------------------------------------------------------------------------------- /www/static/css/bootstrap-slider.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Slider for Bootstrap 3 | * 4 | * Licensed under the Apache License v2.0 5 | * 6 | */ 7 | .slider { 8 | display: inline-block; 9 | vertical-align: middle; 10 | position: relative; 11 | } 12 | .slider.slider-horizontal { 13 | width: 210px; 14 | height: 20px; 15 | } 16 | .slider.slider-horizontal .slider-track { 17 | height: 10px; 18 | width: 100%; 19 | margin-top: -5px; 20 | top: 50%; 21 | left: 0; 22 | } 23 | .slider.slider-horizontal .slider-selection { 24 | height: 100%; 25 | top: 0; 26 | bottom: 0; 27 | } 28 | .slider.slider-horizontal .slider-handle { 29 | margin-left: -10px; 30 | margin-top: -5px; 31 | } 32 | .slider.slider-horizontal .slider-handle.triangle { 33 | border-width: 0 10px 10px 10px; 34 | width: 0; 35 | height: 0; 36 | border-bottom-color: #0480be; 37 | margin-top: 0; 38 | } 39 | .slider.slider-vertical { 40 | height: 210px; 41 | width: 20px; 42 | } 43 | .slider.slider-vertical .slider-track { 44 | width: 10px; 45 | height: 100%; 46 | margin-left: -5px; 47 | left: 50%; 48 | top: 0; 49 | } 50 | .slider.slider-vertical .slider-selection { 51 | width: 100%; 52 | left: 0; 53 | top: 0; 54 | bottom: 0; 55 | } 56 | .slider.slider-vertical .slider-handle { 57 | margin-left: -5px; 58 | margin-top: -10px; 59 | } 60 | .slider.slider-vertical .slider-handle.triangle { 61 | border-width: 10px 0 10px 10px; 62 | width: 1px; 63 | height: 1px; 64 | border-left-color: #0480be; 65 | margin-left: 0; 66 | } 67 | .slider.slider-disabled .slider-handle { 68 | background-image: -webkit-linear-gradient(top, #dfdfdf 0%, #bebebe 100%); 69 | background-image: linear-gradient(to bottom, #dfdfdf 0%, #bebebe 100%); 70 | background-repeat: repeat-x; 71 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdfdfdf', endColorstr='#ffbebebe', GradientType=0); 72 | } 73 | .slider.slider-disabled .slider-track { 74 | background-image: -webkit-linear-gradient(top, #e5e5e5 0%, #e9e9e9 100%); 75 | background-image: linear-gradient(to bottom, #e5e5e5 0%, #e9e9e9 100%); 76 | background-repeat: repeat-x; 77 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe5e5e5', endColorstr='#ffe9e9e9', GradientType=0); 78 | cursor: not-allowed; 79 | } 80 | .slider input { 81 | display: none; 82 | } 83 | .slider .tooltip.hide { 84 | display: none; 85 | } 86 | .slider .tooltip-inner { 87 | white-space: nowrap; 88 | } 89 | .slider-track { 90 | position: absolute; 91 | cursor: pointer; 92 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #f9f9f9 100%); 93 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #f9f9f9 100%); 94 | background-repeat: repeat-x; 95 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); 96 | -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); 97 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); 98 | border-radius: 4px; 99 | } 100 | .slider-selection { 101 | position: absolute; 102 | background-image: -webkit-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%); 103 | background-image: linear-gradient(to bottom, #f9f9f9 0%, #f5f5f5 100%); 104 | background-repeat: repeat-x; 105 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0); 106 | -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); 107 | box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); 108 | -webkit-box-sizing: border-box; 109 | -moz-box-sizing: border-box; 110 | box-sizing: border-box; 111 | border-radius: 4px; 112 | } 113 | .slider-handle { 114 | position: absolute; 115 | width: 20px; 116 | height: 20px; 117 | background-color: #3a94a5; 118 | background-image: -webkit-linear-gradient(top, #149bdf 0%, #0480be 100%); 119 | background-image: linear-gradient(to bottom, #149bdf 0%, #0480be 100%); 120 | background-repeat: repeat-x; 121 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); 122 | filter: none; 123 | -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); 124 | box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); 125 | opacity: 0.8; 126 | border: 0px solid transparent; 127 | } 128 | .slider-handle.round { 129 | border-radius: 50%; 130 | } 131 | .slider-handle.triangle { 132 | background: transparent none; 133 | } 134 | .slider-handle.custom { 135 | background: transparent none; 136 | } 137 | .slider-handle.custom::before { 138 | line-height: 20px; 139 | font-size: 20px; 140 | content: '\2605'; 141 | color: #726204; 142 | } 143 | -------------------------------------------------------------------------------- /www/static/css/datepicker.css: -------------------------------------------------------------------------------- 1 | div.datepicker { 2 | position: relative; 3 | font-family: Arial, Helvetica, sans-serif; 4 | font-size: 12px; 5 | width: 196px; 6 | height: 147px; 7 | position: absolute; 8 | cursor: default; 9 | top: 0; 10 | left: 0; 11 | display: none; 12 | } 13 | .datepickerContainer { 14 | background: #121212; 15 | position: absolute; 16 | top: 10px; 17 | left: 10px; 18 | } 19 | .datepickerBorderT { 20 | position: absolute; 21 | left: 10px; 22 | top: 0; 23 | right: 10px; 24 | height: 10px; 25 | /*background: url(../images/datepicker_t.png);*/ 26 | } 27 | .datepickerBorderB { 28 | position: absolute; 29 | left: 10px; 30 | bottom: 0; 31 | right: 10px; 32 | height: 10px; 33 | /*background: url(../images/datepicker_b.png);*/ 34 | } 35 | .datepickerBorderL { 36 | position: absolute; 37 | left: 0; 38 | bottom: 10px; 39 | top: 10px; 40 | width: 10px; 41 | /*background: url(../images/datepicker_l.png);*/ 42 | } 43 | .datepickerBorderR { 44 | position: absolute; 45 | right: 0; 46 | bottom: 10px; 47 | top: 10px; 48 | width: 10px; 49 | /*background: url(../images/datepicker_r.png);*/ 50 | } 51 | .datepickerBorderTL { 52 | position: absolute; 53 | top: 0; 54 | left: 0; 55 | width: 10px; 56 | height: 10px; 57 | /*background: url(../images/datepicker_tl.png);*/ 58 | } 59 | .datepickerBorderTR { 60 | position: absolute; 61 | top: 0; 62 | right: 0; 63 | width: 10px; 64 | height: 10px; 65 | /*background: url(../images/datepicker_tr.png);*/ 66 | } 67 | .datepickerBorderBL { 68 | position: absolute; 69 | bottom: 0; 70 | left: 0; 71 | width: 10px; 72 | height: 10px; 73 | /*background: url(../images/datepicker_bl.png);*/ 74 | } 75 | .datepickerBorderBR { 76 | position: absolute; 77 | bottom: 0; 78 | right: 0; 79 | width: 10px; 80 | height: 10px; 81 | /*background: url(../images/datepicker_br.png);*/ 82 | } 83 | .datepickerHidden { 84 | display: none; 85 | } 86 | div.datepicker table { 87 | border-collapse:collapse; 88 | } 89 | div.datepicker a { 90 | color: #eee; 91 | text-decoration: none; 92 | cursor: default; 93 | outline: none; 94 | } 95 | div.datepicker table td { 96 | text-align: right; 97 | padding: 0; 98 | margin: 0; 99 | } 100 | div.datepicker th { 101 | text-align: center; 102 | color: #999; 103 | font-weight: normal; 104 | } 105 | div.datepicker tbody th { 106 | text-align: left; 107 | } 108 | div.datepicker tbody a { 109 | display: block; 110 | } 111 | .datepickerDays a { 112 | width: 20px; 113 | line-height: 16px; 114 | height: 16px; 115 | padding-right: 2px; 116 | } 117 | .datepickerYears a, 118 | .datepickerMonths a{ 119 | width: 44px; 120 | line-height: 36px; 121 | height: 36px; 122 | text-align: center; 123 | } 124 | td.datepickerNotInMonth a { 125 | color: #666; 126 | } 127 | tbody.datepickerDays td.datepickerSelected{ 128 | background: #136A9F; 129 | } 130 | tbody.datepickerDays td.datepickerNotInMonth.datepickerSelected { 131 | background: #17384d; 132 | } 133 | tbody.datepickerYears td.datepickerSelected, 134 | tbody.datepickerMonths td.datepickerSelected{ 135 | background: #17384d; 136 | } 137 | div.datepicker a:hover, 138 | div.datepicker a:hover { 139 | color: #88c5eb; 140 | } 141 | div.datepicker td.datepickerNotInMonth a:hover { 142 | color: #999; 143 | } 144 | div.datepicker tbody th { 145 | text-align: left; 146 | } 147 | .datepickerSpace div { 148 | width: 20px; 149 | } 150 | .datepickerGoNext a, 151 | .datepickerGoPrev a, 152 | .datepickerMonth a { 153 | text-align: center; 154 | height: 20px; 155 | line-height: 20px; 156 | } 157 | .datepickerGoNext a { 158 | float: right; 159 | width: 20px; 160 | } 161 | .datepickerGoPrev a { 162 | float: left; 163 | width: 20px; 164 | } 165 | table.datepickerViewDays tbody.datepickerMonths, 166 | table.datepickerViewDays tbody.datepickerYears { 167 | display: none; 168 | } 169 | table.datepickerViewMonths tbody.datepickerDays, 170 | table.datepickerViewMonths tbody.datepickerYears, 171 | table.datepickerViewMonths tr.datepickerDoW { 172 | display: none; 173 | } 174 | table.datepickerViewYears tbody.datepickerDays, 175 | table.datepickerViewYears tbody.datepickerMonths, 176 | table.datepickerViewYears tr.datepickerDoW { 177 | display: none; 178 | } 179 | td.datepickerDisabled a, 180 | td.datepickerDisabled.datepickerNotInMonth a{ 181 | color: #333; 182 | } 183 | td.datepickerDisabled a:hover { 184 | color: #333; 185 | } 186 | td.datepickerSpecial a { 187 | background: #700; 188 | } 189 | td.datepickerSpecial.datepickerSelected a { 190 | background: #a00; 191 | } -------------------------------------------------------------------------------- /www/static/css/jquery.tagsinput.css: -------------------------------------------------------------------------------- 1 | div.tagsinput { border:1px solid #CCC; background: #FFF; padding:5px; width:300px; height:100px; overflow-y: auto;} 2 | div.tagsinput span.tag { border: 1px solid #a5d24a; -moz-border-radius:2px; -webkit-border-radius:2px; display: block; float: left; padding: 5px; text-decoration:none; background: #cde69c; color: #638421; margin-right: 5px; margin-bottom:5px;font-family: helvetica; font-size:13px;} 3 | div.tagsinput span.tag a { font-weight: bold; color: #82ad2b; text-decoration:none; font-size: 11px; } 4 | div.tagsinput input { width:80px; margin:0px; font-family: helvetica; font-size: 13px; border:1px solid transparent; padding:5px; background: transparent; color: #000; outline:0px; margin-right:5px; margin-bottom:5px; } 5 | div.tagsinput div { display:block; float: left; } 6 | .tags_clear { clear: both; width: 100%; height: 0px; } 7 | .not_valid {background: #FBD8DB !important; color: #90111A !important;} 8 | -------------------------------------------------------------------------------- /www/static/css/morris.css: -------------------------------------------------------------------------------- 1 | .morris-hover{position:absolute;z-index:1000;}.morris-hover.morris-default-style{border-radius:10px;padding:6px;color:#666;background:rgba(255, 255, 255, 0.8);border:solid 2px rgba(230, 230, 230, 0.8);font-family:sans-serif;font-size:12px;text-align:center;}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold;margin:0.25em 0;} 2 | .morris-hover.morris-default-style .morris-hover-point{white-space:nowrap;margin:0.1em 0;} 3 | -------------------------------------------------------------------------------- /www/static/css/owl.carousel.min.css: -------------------------------------------------------------------------------- 1 | .owl-carousel .animated{-webkit-animation-duration:1000ms;animation-duration:1000ms;-webkit-animation-fill-mode:both;animation-fill-mode:both}.owl-carousel .owl-animated-in{z-index:0}.owl-carousel .owl-animated-out{z-index:1}.owl-carousel .fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}.owl-height{-webkit-transition:height 500ms ease-in-out;-moz-transition:height 500ms ease-in-out;-ms-transition:height 500ms ease-in-out;-o-transition:height 500ms ease-in-out;transition:height 500ms ease-in-out}.owl-carousel{display:none;width:100%;-webkit-tap-highlight-color:transparent;position:relative;z-index:1}.owl-carousel .owl-stage{position:relative;-ms-touch-action:pan-Y}.owl-carousel .owl-stage:after{content:".";display:block;clear:both;visibility:hidden;line-height:0;height:0}.owl-carousel .owl-stage-outer{position:relative;overflow:hidden;-webkit-transform:translate3d(0px,0,0)}.owl-carousel .owl-controls .owl-dot,.owl-carousel .owl-controls .owl-nav .owl-next,.owl-carousel .owl-controls .owl-nav .owl-prev{cursor:pointer;cursor:hand;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.owl-carousel.owl-loaded{display:block}.owl-carousel.owl-loading{opacity:0;display:block}.owl-carousel.owl-hidden{opacity:0}.owl-carousel .owl-refresh .owl-item{display:none}.owl-carousel .owl-item{position:relative;min-height:1px;float:left;-webkit-backface-visibility:hidden;-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.owl-carousel .owl-item img{display:block;width:100%;-webkit-transform-style:preserve-3d}.owl-carousel.owl-text-select-on .owl-item{-webkit-user-select:auto;-moz-user-select:auto;-ms-user-select:auto;user-select:auto}.owl-carousel .owl-grab{cursor:move;cursor:-webkit-grab;cursor:-o-grab;cursor:-ms-grab;cursor:grab}.owl-carousel .owl-rtl{direction:rtl}.owl-carousel .owl-rtl .owl-item{float:right}.no-js .owl-carousel{display:block}.owl-carousel .owl-item .owl-lazy{opacity:0;-webkit-transition:opacity 400ms ease;-moz-transition:opacity 400ms ease;-ms-transition:opacity 400ms ease;-o-transition:opacity 400ms ease;transition:opacity 400ms ease}.owl-carousel .owl-item img{transform-style:preserve-3d}.owl-carousel .owl-video-wrapper{position:relative;height:100%;background:#000}.owl-carousel .owl-video-play-icon{position:absolute;height:80px;width:80px;left:50%;top:50%;margin-left:-40px;margin-top:-40px;background:url(owl.video.play.png) no-repeat;cursor:pointer;z-index:1;-webkit-backface-visibility:hidden;-webkit-transition:scale 100ms ease;-moz-transition:scale 100ms ease;-ms-transition:scale 100ms ease;-o-transition:scale 100ms ease;transition:scale 100ms ease}.owl-carousel .owl-video-play-icon:hover{-webkit-transition:scale(1.3,1.3);-moz-transition:scale(1.3,1.3);-ms-transition:scale(1.3,1.3);-o-transition:scale(1.3,1.3);transition:scale(1.3,1.3)}.owl-carousel .owl-video-playing .owl-video-play-icon,.owl-carousel .owl-video-playing .owl-video-tn{display:none}.owl-carousel .owl-video-tn{opacity:0;height:100%;background-position:center center;background-repeat:no-repeat;-webkit-background-size:contain;-moz-background-size:contain;-o-background-size:contain;background-size:contain;-webkit-transition:opacity 400ms ease;-moz-transition:opacity 400ms ease;-ms-transition:opacity 400ms ease;-o-transition:opacity 400ms ease;transition:opacity 400ms ease}.owl-carousel .owl-video-frame{position:relative;z-index:1} -------------------------------------------------------------------------------- /www/static/css/owl.theme.default.min.css: -------------------------------------------------------------------------------- 1 | .owl-theme .owl-controls{margin-top:10px;text-align:center;-webkit-tap-highlight-color:transparent}.owl-theme .owl-controls .owl-nav [class*=owl-]{color:#fff;font-size:14px;margin:5px;padding:4px 7px;background:#d6d6d6;display:inline-block;cursor:pointer;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.owl-theme .owl-controls .owl-nav [class*=owl-]:hover{background:#869791;color:#fff;text-decoration:none}.owl-theme .owl-controls .owl-nav .disabled{opacity:.5;cursor:default}.owl-theme .owl-dots .owl-dot{display:inline-block;zoom:1;*display:inline}.owl-theme .owl-dots .owl-dot span{width:10px;height:10px;margin:5px 7px;background:#d6d6d6;display:block;-webkit-backface-visibility:visible;-webkit-transition:opacity 200ms ease;-moz-transition:opacity 200ms ease;-ms-transition:opacity 200ms ease;-o-transition:opacity 200ms ease;transition:opacity 200ms ease;-webkit-border-radius:30px;-moz-border-radius:30px;border-radius:30px}.owl-theme .owl-dots .owl-dot.active span,.owl-theme .owl-dots .owl-dot:hover span{background:#869791} -------------------------------------------------------------------------------- /www/static/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /www/static/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /www/static/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /www/static/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /www/static/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /www/static/fonts/ionicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/fonts/ionicons.eot -------------------------------------------------------------------------------- /www/static/fonts/ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/fonts/ionicons.ttf -------------------------------------------------------------------------------- /www/static/fonts/ionicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/fonts/ionicons.woff -------------------------------------------------------------------------------- /www/static/home/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "rules": { 4 | "no-use-before-define": 0, 5 | "react/prefer-stateless-function": 0, 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /www/static/home/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /www/static/home/README.md: -------------------------------------------------------------------------------- 1 | # dcloud 2 | 3 | dcloud 前端静态页面,采用reactjs+redux+webpack 4 | 5 | 1. `npm install` 6 | 2. `npm run start` 7 | 3. `http://localhost:8080` 8 | 9 | 10 | ### 关联项目 11 | 12 | ``` 13 | https://github.com/stbui/dcloud 14 | ``` -------------------------------------------------------------------------------- /www/static/home/dist/img/1FQwjQx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/1FQwjQx.png -------------------------------------------------------------------------------- /www/static/home/dist/img/1N5sAtp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/1N5sAtp.png -------------------------------------------------------------------------------- /www/static/home/dist/img/1V4SDVd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/1V4SDVd.png -------------------------------------------------------------------------------- /www/static/home/dist/img/1Ybvq1U.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/1Ybvq1U.png -------------------------------------------------------------------------------- /www/static/home/dist/img/1i7E9zO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/1i7E9zO.png -------------------------------------------------------------------------------- /www/static/home/dist/img/1vM7g2p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/1vM7g2p.png -------------------------------------------------------------------------------- /www/static/home/dist/img/22evHO-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/22evHO-.png -------------------------------------------------------------------------------- /www/static/home/dist/img/299xJ8e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/299xJ8e.png -------------------------------------------------------------------------------- /www/static/home/dist/img/2CU4Idb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/2CU4Idb.png -------------------------------------------------------------------------------- /www/static/home/dist/img/2i2fdwz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/2i2fdwz.jpg -------------------------------------------------------------------------------- /www/static/home/dist/img/3Q_wqMC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/3Q_wqMC.png -------------------------------------------------------------------------------- /www/static/home/dist/img/3mCKcZU.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/3mCKcZU.png -------------------------------------------------------------------------------- /www/static/home/dist/img/3s71Wf2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/3s71Wf2.png -------------------------------------------------------------------------------- /www/static/home/dist/img/3zJXP6G.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/3zJXP6G.png -------------------------------------------------------------------------------- /www/static/home/dist/img/KxOzIex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/KxOzIex.png -------------------------------------------------------------------------------- /www/static/home/dist/img/LzfhU0_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/LzfhU0_.png -------------------------------------------------------------------------------- /www/static/home/dist/img/Xi_hJy_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/Xi_hJy_.png -------------------------------------------------------------------------------- /www/static/home/dist/img/ix3EeZk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/dist/img/ix3EeZk.png -------------------------------------------------------------------------------- /www/static/home/images/2345浏览器.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/2345浏览器.png -------------------------------------------------------------------------------- /www/static/home/images/360极速浏览器.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/360极速浏览器.png -------------------------------------------------------------------------------- /www/static/home/images/360浏览器.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/360浏览器.png -------------------------------------------------------------------------------- /www/static/home/images/IE 10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/IE 10.png -------------------------------------------------------------------------------- /www/static/home/images/IE 11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/IE 11.png -------------------------------------------------------------------------------- /www/static/home/images/IE 6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/IE 6.png -------------------------------------------------------------------------------- /www/static/home/images/IE 7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/IE 7.png -------------------------------------------------------------------------------- /www/static/home/images/IE 8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/IE 8.png -------------------------------------------------------------------------------- /www/static/home/images/IE 9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/IE 9.png -------------------------------------------------------------------------------- /www/static/home/images/QQ浏览器.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/QQ浏览器.png -------------------------------------------------------------------------------- /www/static/home/images/Safari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/Safari.png -------------------------------------------------------------------------------- /www/static/home/images/UC浏览器.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/UC浏览器.png -------------------------------------------------------------------------------- /www/static/home/images/chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/chrome.png -------------------------------------------------------------------------------- /www/static/home/images/computer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/computer.png -------------------------------------------------------------------------------- /www/static/home/images/desktop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/desktop.jpg -------------------------------------------------------------------------------- /www/static/home/images/download.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/download.jpg -------------------------------------------------------------------------------- /www/static/home/images/firefox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/firefox.png -------------------------------------------------------------------------------- /www/static/home/images/opera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/opera.png -------------------------------------------------------------------------------- /www/static/home/images/世界之窗.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/世界之窗.png -------------------------------------------------------------------------------- /www/static/home/images/傲游浏览器.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/傲游浏览器.png -------------------------------------------------------------------------------- /www/static/home/images/搜狗浏览器.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/搜狗浏览器.png -------------------------------------------------------------------------------- /www/static/home/images/猎豹浏览器.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stbui/dcloud/4fbb234fe0d2bfd2da1729f73b5f16140d719a75/www/static/home/images/猎豹浏览器.png -------------------------------------------------------------------------------- /www/static/home/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | dcloud 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /www/static/home/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dcloud", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "set NODE_ENV=production && webpack -p --config ./webpack.prod.config.js", 8 | "start": "webpack-dev-server --progress --colors --hot --content-base ./ --config ./webpack.config.js" 9 | }, 10 | "homepage": "https://github.com/stbui/dcloud", 11 | "devDependencies": { 12 | "autoprefixer-loader": "^3.1.0", 13 | "babel-core": "^6.7.2", 14 | "babel-eslint": "^6.0.4", 15 | "babel-loader": "^6.2.4", 16 | "babel-polyfill": "^6.7.2", 17 | "babel-preset-es2015": "^6.6.0", 18 | "babel-preset-react": "^6.5.0", 19 | "babel-preset-stage-0": "^6.5.0", 20 | "babel-register": "^6.7.2", 21 | "css-loader": "^0.23.1", 22 | "eslint": "^2.4.0", 23 | "eslint-config-airbnb": "^9.0.1", 24 | "eslint-plugin-import": "^1.8.1", 25 | "eslint-plugin-jsx-a11y": "^1.2.3", 26 | "eslint-plugin-react": "^5.1.1", 27 | "expect": "^1.12.2", 28 | "extract-text-webpack-plugin": "^1.0.1", 29 | "fastclick": "^1.0.6", 30 | "isomorphic-fetch": "^2.1.1", 31 | "js-cookie": "^2.0.4", 32 | "jsx-loader": "^0.13.2", 33 | "lodash": "^4.6.1", 34 | "mocha": "^2.3.3", 35 | "mock-local-storage": "^1.0.2", 36 | "moment": "^2.10.6", 37 | "nock": "^8.0.0", 38 | "node-sass": "^3.3.3", 39 | "normalizr": "^2.0.0", 40 | "react": "^15.1.0", 41 | "react-dom": "^15.1.0", 42 | "react-hot-loader": "^1.3.0", 43 | "react-motion": "^0.4.2", 44 | "react-redux": "^4.4.1", 45 | "redux": "^3.0.0", 46 | "redux-thunk": "^2.0.1", 47 | "sass-loader": "^3.2.0", 48 | "soundcloud": "^3.0.1", 49 | "style-loader": "^0.13.0", 50 | "webpack": "^1.12.2", 51 | "webpack-dev-server": "^1.11.0" 52 | }, 53 | "dependencies": { 54 | "file-loader": "^0.9.0", 55 | "rc-dialog": "^6.1.1", 56 | "rc-switch": "^1.4.2", 57 | "rc-trigger": "^1.6.3", 58 | "url-loader": "^0.5.7" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /www/static/home/scripts/actions/app.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/ActionTypes'; 2 | import Cookies from 'js-cookie'; 3 | 4 | import {constructDesktopAppListUrl, constructProxyListUrl} from '../utils/DesktopUtils.js'; 5 | 6 | 7 | const COOKIE_PATH = 'accessToken'; 8 | export function fetchApps() { 9 | 10 | return dispatch => 11 | fetch(constructDesktopAppListUrl()) 12 | .then(response => response.json()) 13 | .then(json => { 14 | dispatch(receiveAppList(json)); 15 | }) 16 | .catch(err => { 17 | throw err; 18 | }); 19 | 20 | } 21 | 22 | export function receiveAppList(entities) { 23 | return { 24 | type: types.RECEIVE_APP_LIST, 25 | entities, 26 | }; 27 | } 28 | 29 | 30 | export function fetchProxys() { 31 | return dispatch => 32 | fetch(constructProxyListUrl(), { 33 | credentials: 'include' 34 | }) 35 | .then(response => response.json()) 36 | .then(json => { 37 | dispatch(receiveProxysList(json)); 38 | }) 39 | .catch(err => { 40 | throw err; 41 | }); 42 | } 43 | 44 | export function receiveProxysList(entities) { 45 | return { 46 | type: types.RECEIVE_PROXY_LIST, 47 | entities, 48 | }; 49 | } 50 | 51 | 52 | export function fetchProxysAdd(data) { 53 | 54 | return dispatch => 55 | fetch(constructProxyListUrl() + '/add.html', { 56 | method: "POST", 57 | headers: {"Content-Type": "application/x-www-form-urlencoded"}, 58 | credentials: 'include', 59 | body: data 60 | }) 61 | .then(response => response.json()) 62 | .then(json => { 63 | dispatch(receiveProxysAdd(json)); 64 | }) 65 | .catch(err => { 66 | throw err; 67 | }); 68 | 69 | 70 | } 71 | 72 | export function receiveProxysAdd(entities) { 73 | return { 74 | type: types.RECEIVE_PROXY_ADD, 75 | entities, 76 | }; 77 | } 78 | 79 | 80 | export function fetchProxysDel(id) { 81 | 82 | return dispatch => 83 | fetch(constructProxyListUrl() + '/del?id=' + id, { 84 | credentials: 'include' 85 | }) 86 | .then(response => response.json()) 87 | .then(json => { 88 | dispatch(receiveProxysDel(json)); 89 | }) 90 | .catch(err => { 91 | throw err; 92 | }); 93 | 94 | 95 | } 96 | 97 | export function receiveProxysDel(entities) { 98 | return { 99 | type: types.RECEIVE_PROXY_DEL, 100 | entities 101 | }; 102 | } 103 | 104 | /* 105 | * 请求pac服务 106 | * */ 107 | export function fetchProxyStatus() { 108 | return dispath => fetch(constructProxyPac(), {credentials: 'include'}) 109 | .then(response=> response.json()) 110 | .then(json=> { 111 | dispatch(receiveProxyStatus(json)) 112 | }) 113 | .catch(err=> { 114 | throw err 115 | }); 116 | } 117 | 118 | export function receiveProxyStatus(entities) { 119 | return { 120 | type: types.RECEIVE_PROXY_STATUS, 121 | entities 122 | }; 123 | } 124 | -------------------------------------------------------------------------------- /www/static/home/scripts/actions/authed.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/ActionTypes'; 2 | import Cookies from 'js-cookie'; 3 | 4 | 5 | const COOKIE_PATH = 'accessToken'; 6 | 7 | export function loginUser() { 8 | //Cookies.remove(COOKIE_PATH) 9 | let accessToken = Cookies.get(COOKIE_PATH); 10 | 11 | return { 12 | type: types.RECEIVE_ACCESS_TOKEN, 13 | accessToken 14 | }; 15 | } -------------------------------------------------------------------------------- /www/static/home/scripts/actions/navigator.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/ActionTypes'; 2 | 3 | 4 | export function changePath(route) { 5 | return { 6 | type: types.CHANGE_PATH, 7 | route, 8 | }; 9 | } 10 | 11 | export function initNavigator() { 12 | return dispatch => { 13 | window.onpopstate = e => { 14 | dispatch(navigateBack(e)); 15 | }; 16 | 17 | if (window.location.hash !== '') { 18 | dispatch(navigateTo(parseUrl(window.location.hash))); 19 | } 20 | }; 21 | } 22 | 23 | export function navigateBack(e) { 24 | return dispatch => { 25 | if (e.state) { 26 | return dispatch(navigateTo(e.state.route, false)); 27 | } 28 | 29 | return null; 30 | }; 31 | } 32 | 33 | export function navigateTo(route, shouldPushState = true) { 34 | return (dispatch, getState) => { 35 | const { navigator } = getState(); 36 | 37 | 38 | if (shouldPushState) { 39 | pushState(route); 40 | } 41 | 42 | return dispatch(changePath(route)); 43 | }; 44 | } 45 | 46 | function pushState(route) { 47 | history.pushState({route}, '', `#/${constructUrl(route)}`); 48 | } 49 | 50 | function constructUrl(route) { 51 | const { path, query } = route; 52 | let result = path.join('/'); 53 | let queryArr = []; 54 | if (query && typeof query === 'object') { 55 | queryArr = Object.keys(query).sort() 56 | .filter(key => query[key] !== null) 57 | .map(key => `${key}=${query[key]}`); 58 | } 59 | 60 | if (queryArr.length > 0) { 61 | result += `?${queryArr.join('&')}`; 62 | } 63 | 64 | return result; 65 | } 66 | 67 | function parseUrl(windowHash) { 68 | let path = []; 69 | const query = {}; 70 | const hashArr = windowHash.replace('#/', '').split('?'); 71 | path = hashArr[0].split('/'); 72 | 73 | if (hashArr.length > 1) { 74 | hashArr[1].split('&').forEach(str => { 75 | const arr = str.split('='); 76 | const key = arr[0]; 77 | const value = arr[1]; 78 | if (isNaN(value)) { 79 | query[key] = value; 80 | } else { 81 | query[key] = Number(value); 82 | } 83 | }); 84 | } 85 | 86 | return {path, query}; 87 | } -------------------------------------------------------------------------------- /www/static/home/scripts/actions/startmenu.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/ActionTypes'; 2 | 3 | export function changeStartMenu(startmenu) { 4 | return { 5 | type: types.CHANGE_STARTMENU, 6 | startmenu 7 | }; 8 | } -------------------------------------------------------------------------------- /www/static/home/scripts/components/AppCard.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import ReactDom from 'react-dom'; 3 | 4 | import { constructDesktopAppDomainUrl } from '../utils/DesktopUtils.js'; 5 | 6 | const propTypes = { 7 | data: PropTypes.object.isRequired, 8 | left: PropTypes.number.isRequired, 9 | top: PropTypes.number.isRequired, 10 | id: PropTypes.number.isRequired, 11 | }; 12 | 13 | class AppCard extends Component { 14 | constructor(props) { 15 | super(props); 16 | 17 | 18 | this.handleMouseClick = this.handleMouseClick.bind(this); 19 | this.handleMouseDoubleClick = this.handleMouseDoubleClick.bind(this); 20 | } 21 | 22 | handleMouseClick(e) { 23 | } 24 | 25 | handleMouseDoubleClick(e) { 26 | const {id} = this.props; 27 | 28 | let url = `${constructDesktopAppDomainUrl()}openApp?id=${id}`; 29 | window.open(url) 30 | } 31 | 32 | render() { 33 | const { data, left, top} = this.props; 34 | let icon; 35 | 36 | try { 37 | icon = require(`../../images/${data.name}.png`); 38 | } catch (e) { 39 | icon = require(`../../images/computer.png`); 40 | } 41 | 42 | return ( 43 |
45 |
46 |
{data.name}
47 |
48 | ); 49 | } 50 | } 51 | 52 | AppCard.propTypes = propTypes; 53 | 54 | export default AppCard; 55 | 56 | -------------------------------------------------------------------------------- /www/static/home/scripts/components/Desktop.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | import AppCard from './AppCard.js'; 4 | 5 | 6 | const propTypes = {}; 7 | 8 | class Desktop extends Component { 9 | 10 | constructor(props) { 11 | super(props); 12 | 13 | this.onResize = this.onResize.bind(this); 14 | 15 | this.state = { 16 | adjust: false 17 | } 18 | } 19 | 20 | componentDidMount() { 21 | window.addEventListener('resize', this.onResize, false); 22 | } 23 | 24 | onResize() { 25 | // 触发事件,通知dom更新数据 26 | //adjust属性没有实际意义 27 | this.setState({ 28 | adjust: true 29 | }); 30 | } 31 | 32 | renderDesktop() { 33 | const {applists} = this.props; 34 | const datas = applists.items; 35 | 36 | return datas.map((data, key)=> { 37 | var e = 10, a = 10, t = 85, i = 100, n = 10, s = document.documentElement.clientHeight, o = Math.floor((s - e) / (i + n)), r = 0, l = 0, c = 0, d = 0; 38 | r = key % o, l = Math.floor(key / o), c = a + (t + n) * l, d = e + (i + n) * r 39 | 40 | let left = c; 41 | let top = d; 42 | 43 | return 44 | }); 45 | } 46 | 47 | render() { 48 | 49 | return ( 50 |
51 | {this.renderDesktop()} 52 |
53 | ); 54 | } 55 | } 56 | 57 | Desktop.propTypes = propTypes; 58 | 59 | export default Desktop; 60 | -------------------------------------------------------------------------------- /www/static/home/scripts/components/Proxy.js: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | 3 | import {fetchProxys, fetchProxysAdd, fetchProxysDel} from '../actions/app'; 4 | 5 | 6 | class Proxy extends Component { 7 | constructor(props) { 8 | super(props); 9 | 10 | // const {dispatch} = props; 11 | // dispatch(fetchProxys()); 12 | 13 | this.onHandleAddProxyClick = this.onHandleAddProxyClick.bind(this); 14 | this.onHandleDelProxyClick = this.onHandleDelProxyClick.bind(this); 15 | 16 | 17 | this.state = { 18 | isRer: false 19 | } 20 | } 21 | 22 | componentDidMount() { 23 | } 24 | 25 | componentWillMount() { 26 | const {dispatch} = this.props; 27 | dispatch(fetchProxys()); 28 | } 29 | 30 | componentWillReceiveProps() { 31 | } 32 | 33 | shouldComponentUpdate() { 34 | return true; 35 | } 36 | 37 | componentWillUpdate() { 38 | } 39 | 40 | onHandleAddProxyClick() { 41 | let ip = this.refs.ip.value; 42 | let domain = this.refs.domain.value; 43 | let hosts = `target=${ip}&source=${domain}` 44 | 45 | const {dispatch} = this.props; 46 | dispatch(fetchProxysAdd(hosts)); 47 | 48 | 49 | this.setState({isRe: true}) 50 | } 51 | 52 | onHandleDelProxyClick(e) { 53 | let id = e.target.getAttribute('id'); 54 | 55 | const {dispatch} = this.props; 56 | dispatch(fetchProxysDel(id)); 57 | 58 | 59 | this.setState({isRe: true}) 60 | } 61 | 62 | renderContent() { 63 | const {proxys} = this.props; 64 | const data = proxys.items; 65 | let result = []; 66 | 67 | data.map((value, key)=> { 68 | result.push( 69 | 70 | {value.target} 71 | {value.source} 72 | x 73 | 74 | ); 75 | }); 76 | 77 | return result; 78 | } 79 | 80 | render() { 81 | 82 | return ( 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | {this.renderContent()} 93 | 94 | 95 | 96 | 97 | 98 | 99 |
IP域名
100 | ); 101 | } 102 | } 103 | 104 | export default Proxy; 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /www/static/home/scripts/components/StartMenu.js: -------------------------------------------------------------------------------- 1 | import React, {Component, PropTypes} from 'react'; 2 | import Dialog from 'rc-dialog'; 3 | import Switch from 'rc-switch'; 4 | 5 | import * as types from '../constants/StartMenu'; 6 | import {fetchProxyStatus} from '../actions/app'; 7 | import Proxy from './Proxy'; 8 | 9 | 10 | class StartMenu extends Component { 11 | constructor(props) { 12 | super(props); 13 | 14 | 15 | this.onHandleProxyClick = this.onHandleProxyClick.bind(this); 16 | this.onHandleHelpClick = this.onHandleHelpClick.bind(this); 17 | this.onClose = this.onClose.bind(this); 18 | 19 | this.state = { 20 | visible: false, 21 | width: 600, 22 | disabled: false, 23 | title: '设置' 24 | } 25 | } 26 | 27 | onHandleProxyClick(e) { 28 | e.preventDefault(); 29 | 30 | this.setState({ 31 | visible: true, 32 | width: 600, 33 | title: e.target.innerHTML 34 | }) 35 | } 36 | 37 | onHandleHelpClick(e) { 38 | e.preventDefault(); 39 | 40 | this.setState({ 41 | visible: true, 42 | width: 800, 43 | title: e.target.innerHTML 44 | }) 45 | } 46 | 47 | onClose(e) { 48 | this.setState({ 49 | visible: false, 50 | }); 51 | } 52 | 53 | toggle() { 54 | this.setState({ 55 | disabled: !this.state.disabled, 56 | }); 57 | } 58 | 59 | onChange() { 60 | console.log(this.props) 61 | const {dispatch} = this.props; 62 | dispatch(fetchProxyStatus()); 63 | } 64 | 65 | 66 | renderDialogContent() { 67 | const {visible, width, title} = this.state; 68 | 69 | 70 | if (title == types.USER_PROFILES_PROXY) { 71 | return ( 72 | 73 | 78 | 79 | 80 | ); 81 | } else if (title == types.USER_PROFILES_HELP) { 82 | return ( 83 | 84 |

欢迎您使用dCloud

85 |
86 | ); 87 | } else { 88 | return ( 89 | 90 | 91 | 92 | ); 93 | } 94 | } 95 | 96 | renderLists() { 97 | const START_MENU_LISTS = types.START_MENU_LISTS; 98 | let data = []; 99 | 100 | START_MENU_LISTS.map((value, key)=> { 101 | data.push(
  • {value.name}
  • ); 102 | }); 103 | 104 | return data; 105 | } 106 | 107 | 108 | render() { 109 | const {authed} = this.props; 110 | const {accessToken, user} = authed; 111 | 112 | return ( 113 |
    114 |
      115 | {this.renderLists()} 116 |
    117 | 126 | 127 | {types.START_MENU_EXIT.name} 128 | 129 | {this.renderDialogContent()} 130 |
    131 | ); 132 | 133 | 134 | } 135 | 136 | } 137 | 138 | export default StartMenu; 139 | 140 | -------------------------------------------------------------------------------- /www/static/home/scripts/components/TaskBar.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import Trigger from 'rc-trigger'; 3 | 4 | import {changeStartMenu} from '../actions/startmenu'; 5 | import StartMenu from '../components/StartMenu'; 6 | 7 | 8 | class TaskBar extends Component { 9 | constructor(props) { 10 | super(props); 11 | 12 | 13 | this.state = { 14 | isOpen: false, 15 | timer: '' 16 | } 17 | } 18 | 19 | componentDidMount() { 20 | this.onTimer(); 21 | } 22 | 23 | componentDidUpdate() { 24 | } 25 | 26 | onClose() { 27 | 28 | } 29 | 30 | onTimer() { 31 | let now = new Date(); 32 | let f = { 33 | h: now.getHours(), 34 | m: now.getMinutes(), 35 | } 36 | 37 | this.onClose = this.onClose.bind(this); 38 | 39 | this.setState({timer: `${f.h}:${f.m}`}); 40 | } 41 | 42 | render() { 43 | const {timer} = this.state; 44 | 45 | return ( 46 |
    47 |
    48 | } 55 | > 56 | 开始 57 | 58 |
    59 |
    60 | {timer} 61 | 62 |
    63 |
    64 | ); 65 | } 66 | } 67 | 68 | 69 | export default TaskBar; 70 | 71 | -------------------------------------------------------------------------------- /www/static/home/scripts/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | export const RECEIVE_APP_LIST = 'RECEIVE_APP_LIST'; 2 | 3 | export const RECEIVE_PROXY_LIST = 'RECEIVE_PROXY_LIST'; 4 | export const RECEIVE_PROXY_ADD = 'RECEIVE_PROXY_ADD'; 5 | export const RECEIVE_PROXY_DEL = 'RECEIVE_PROXY_DEL'; 6 | export const RECEIVE_PROXY_EDIT = 'RECEIVE_PROXY_EDIT'; 7 | export const RECEIVE_PROXY_STATUS = 'RECEIVE_PROXY_STATUS'; 8 | 9 | export const CHANGE_STARTMENU = 'CHANGE_STARTMENU'; 10 | export const CHANGE_PATH = 'CHANGE_PATH'; 11 | export const RECEIVE_ACCESS_TOKEN = 'RECEIVE_ACCESS_TOKEN'; 12 | export const RECEIVE_AUTHED_USER = 'RECEIVE_AUTHED_USER'; -------------------------------------------------------------------------------- /www/static/home/scripts/constants/StartMenu.js: -------------------------------------------------------------------------------- 1 | export const USER_PROFILES_PROXY = '代理设置'; 2 | export const USER_PROFILES_SYSTEM = '系统设置'; 3 | export const USER_PROFILES_HELP = '使用帮助'; 4 | 5 | export const START_MENU_EXIT = {name: '退出', link: '/signin/logout'}; 6 | export const START_MENU_LISTS = [ 7 | { 8 | name: 'JSlint', link: 'javascript:;', 9 | }, 10 | { 11 | name: '自测云', link: 'javascript:;', 12 | }, 13 | { 14 | name: 'WDRiver', link: 'javascript:;' 15 | } 16 | ]; -------------------------------------------------------------------------------- /www/static/home/scripts/containers/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | import { initNavigator } from '../actions/navigator'; 5 | import { loginUser } from '../actions/authed'; 6 | 7 | import DesktopContainer from '../containers/DesktopContainer'; 8 | import TaskBarContainer from '../containers/TaskBarContainer'; 9 | //import StartMenuContainer from '../containers/StartMenuContainer'; 10 | 11 | 12 | class App extends Component { 13 | componentDidMount() { 14 | const { dispatch } = this.props; 15 | dispatch(initNavigator()); 16 | dispatch(loginUser()); 17 | } 18 | 19 | renderContent() { 20 | const { path } = this.props; 21 | //console.log(path) 22 | switch (path[0]) { 23 | case 'desktop': 24 | break; 25 | case 'signin': 26 | break; 27 | default: 28 | break 29 | } 30 | } 31 | 32 | render() { 33 | 34 | return ( 35 |
    36 | 37 | 38 | {this.renderContent()} 39 |
    40 | ); 41 | } 42 | } 43 | 44 | 45 | function mapStateToProps(state) { 46 | const { applists,startmenu,navigator,authed } = state; 47 | const {path} = navigator.route; 48 | 49 | return { 50 | applists, 51 | startmenu, 52 | path, 53 | authed, 54 | }; 55 | } 56 | 57 | 58 | export default connect(mapStateToProps)(App); 59 | -------------------------------------------------------------------------------- /www/static/home/scripts/containers/DesktopContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | import {fetchApps} from '../actions/app'; 5 | import Desktop from '../components/Desktop'; 6 | 7 | 8 | const propTypes = {}; 9 | 10 | class desktopContainer extends Component { 11 | componentDidMount() { 12 | const { dispatch } = this.props; 13 | dispatch(fetchApps()); 14 | } 15 | 16 | render() { 17 | return ; 18 | } 19 | } 20 | 21 | desktopContainer.propTypes = propTypes; 22 | 23 | function mapStateToProps(state) { 24 | const {dispatch, applists } = state; 25 | return { applists }; 26 | } 27 | 28 | export default connect(mapStateToProps)(desktopContainer); 29 | -------------------------------------------------------------------------------- /www/static/home/scripts/containers/StartMenuContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | import StartMenu from '../components/StartMenu'; 5 | 6 | 7 | class StartMenuContainer extends Component { 8 | render() { 9 | return ; 10 | } 11 | } 12 | 13 | 14 | function mapStateToProps(state) { 15 | const {applists, startmenu,authed} = state; 16 | 17 | return { 18 | applists, 19 | startmenu, 20 | authed 21 | } 22 | } 23 | 24 | export default connect(mapStateToProps)(StartMenuContainer); 25 | -------------------------------------------------------------------------------- /www/static/home/scripts/containers/TaskBarContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | import TaskBar from '../components/TaskBar'; 5 | 6 | 7 | const propTypes = {}; 8 | 9 | class TaskBarContainer extends Component { 10 | render() { 11 | return ; 12 | } 13 | } 14 | 15 | TaskBarContainer.propTypes = propTypes; 16 | 17 | function mapStateToProps(state) { 18 | const {applists, startmenu, authed,proxys} = state; 19 | 20 | return { 21 | applists, 22 | startmenu, 23 | authed, 24 | proxys 25 | } 26 | } 27 | 28 | export default connect(mapStateToProps)(TaskBarContainer); 29 | -------------------------------------------------------------------------------- /www/static/home/scripts/main.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill'; 2 | import 'fastclick'; 3 | import 'isomorphic-fetch'; 4 | 5 | import React from 'react'; 6 | import ReactDOM from 'react-dom'; 7 | import { Provider } from 'react-redux'; 8 | 9 | import '../styles/main.scss'; 10 | import App from './containers/App'; 11 | import configureStore from './store/configureStore'; 12 | 13 | const store = configureStore(); 14 | 15 | ReactDOM.render( 16 | 17 | 18 | , 19 | document.getElementById('main') 20 | ); 21 | -------------------------------------------------------------------------------- /www/static/home/scripts/reducers/applists.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/ActionTypes'; 2 | 3 | const initialState = { 4 | items: [] 5 | }; 6 | 7 | 8 | export default function applists(state = initialState, action) { 9 | 10 | switch (action.type) { 11 | case types.RECEIVE_APP_LIST: 12 | return Object.assign({}, state, { 13 | items: action.entities.data, 14 | }); 15 | 16 | default: 17 | return state; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /www/static/home/scripts/reducers/authed.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/ActionTypes'; 2 | 3 | const initialState = { 4 | accessToken: null, 5 | user: null, 6 | }; 7 | 8 | export default function authed(state = initialState, action) { 9 | switch (action.type) { 10 | case types.RECEIVE_ACCESS_TOKEN: 11 | return Object.assign({}, state, { 12 | accessToken: action.accessToken, 13 | }); 14 | 15 | case types.RECEIVE_AUTHED_USER: 16 | return Object.assign({}, state, { 17 | user: action.user, 18 | }); 19 | 20 | default: 21 | return state; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /www/static/home/scripts/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | import applists from '../reducers/applists'; 4 | import startmenu from '../reducers/startmenu'; 5 | import navigator from '../reducers/navigator'; 6 | import authed from '../reducers/authed'; 7 | import proxys from '../reducers/proxys'; 8 | 9 | 10 | const rootReducer = combineReducers({ 11 | applists, 12 | startmenu, 13 | navigator, 14 | authed, 15 | proxys 16 | }); 17 | 18 | export default rootReducer; 19 | -------------------------------------------------------------------------------- /www/static/home/scripts/reducers/navigator.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/ActionTypes'; 2 | 3 | const initialRoute = { path: ['desktop'], query: { q: 'index' } }; 4 | const initialState = { route: initialRoute }; 5 | 6 | export default function navigator(state = initialState, action) { 7 | switch (action.type) { 8 | case types.CHANGE_PATH: 9 | return Object.assign({}, state, { 10 | route: action.route, 11 | }); 12 | default: 13 | return state; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /www/static/home/scripts/reducers/proxys.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/ActionTypes'; 2 | 3 | const initialState = { 4 | items: [], 5 | add: [], 6 | edit: [], 7 | del: [] 8 | }; 9 | 10 | 11 | export default function proxys(state = initialState, action) { 12 | switch (action.type) { 13 | case types.RECEIVE_PROXY_LIST: 14 | return Object.assign({}, state, { 15 | items: action.entities.data, 16 | }); 17 | 18 | case types.RECEIVE_PROXY_ADD: 19 | return Object.assign({}, state, { 20 | add: action.entities.data, 21 | }); 22 | 23 | case types.RECEIVE_PROXY_EDIT: 24 | return Object.assign({}, state, { 25 | edit: action.entities.data, 26 | }); 27 | 28 | case types.RECEIVE_PROXY_DEL: 29 | return Object.assign({}, state, { 30 | del: action.entities.data, 31 | }); 32 | 33 | case types.RECEIVE_PROXY_STATUS: 34 | return Object.assign({}, state, {status: action.entities.data}); 35 | 36 | default: 37 | return state; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /www/static/home/scripts/reducers/startmenu.js: -------------------------------------------------------------------------------- 1 | import * as types from '../constants/ActionTypes'; 2 | 3 | 4 | export default function startmenu(state = null, action) { 5 | switch (action.type) { 6 | case types.CHANGE_STARTMENU: 7 | return action.startmenu; 8 | default: 9 | return state; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /www/static/home/scripts/store/configureStore.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | import thunkMiddleware from 'redux-thunk'; 3 | import rootReducer from '../reducers/index'; 4 | 5 | const createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore); 6 | 7 | export default function configureStore(initialState) { 8 | const store = createStoreWithMiddleware(rootReducer, initialState); 9 | 10 | return store; 11 | } 12 | -------------------------------------------------------------------------------- /www/static/home/scripts/utils/DesktopUtils.js: -------------------------------------------------------------------------------- 1 | 2 | export function constructDesktopAppDomainUrl() { 3 | return `//127.0.0.1:8361/`; 4 | } 5 | 6 | export function constructDesktopAppListUrl() { 7 | return `//127.0.0.1:8361/api/app`; 8 | } 9 | 10 | export function constructProxyListUrl() { 11 | return `//127.0.0.1:8361/api/hosts`; 12 | } 13 | 14 | export function constructProxyPac() { 15 | return `//127.0.0.1:8361/proxy/pac`; 16 | } -------------------------------------------------------------------------------- /www/static/home/styles/custom/components/desktop.scss: -------------------------------------------------------------------------------- 1 | .desktop { 2 | 3 | } 4 | 5 | .desktop-app { 6 | width: 80px; 7 | overflow: hidden; 8 | height: 100px; 9 | -webkit-transition: all 0.3s; 10 | -moz-transition: all 0.3s; 11 | -o-transition: all 0.3s; 12 | -ms-transition: all 0.3s; 13 | transition: all 0.3s; 14 | text-shadow: 0px 0px 0px #fff, 0px 0px 2px #000, 0px 0px 4px #000; 15 | } 16 | 17 | .desktop-app:hover { 18 | background-color: rgba(255, 255, 255, 0.2); 19 | } 20 | 21 | .desktop-app.active { 22 | background-color: rgba(255, 255, 255, 0.4); 23 | } 24 | 25 | .desktop-icon { 26 | width: 60px; 27 | height: 60px; 28 | background-repeat: no-repeat; 29 | background-position: center center; 30 | margin: 0 auto; 31 | margin-top: 4px; 32 | margin-bottom: 3px; 33 | display: block !important; 34 | border-radius: 5px; 35 | background-size: contain; 36 | } 37 | 38 | .desktop-name { 39 | width: 80px; 40 | text-align: center; 41 | word-break: break-all; 42 | font-size: 12px; 43 | margin: 0 auto; 44 | line-height: 1.4em; 45 | color: #fff; 46 | transition: all 0.3s; 47 | text-shadow: 0px 0px 0px #fff, 0px 0px 2px #000, 0px 0px 4px #000; 48 | } -------------------------------------------------------------------------------- /www/static/home/styles/custom/components/reset.scss: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | .container { 7 | width: 1170px; 8 | margin: 0 auto; 9 | } 10 | 11 | .clearfix:after { 12 | visibility: hidden; 13 | display: block; 14 | font-size: 0; 15 | content: " "; 16 | clear: both; 17 | height: 0; 18 | } 19 | 20 | .clearfix { 21 | display: inline-block; 22 | } 23 | 24 | * html .clearfix { 25 | height: 1%; 26 | } 27 | 28 | .clearfix { 29 | display: block; 30 | } 31 | 32 | *, *:after, *:before { 33 | -webkit-box-sizing: border-box; 34 | -moz-box-sizing: border-box; 35 | box-sizing: border-box; 36 | } 37 | -------------------------------------------------------------------------------- /www/static/home/styles/custom/components/switch.scss: -------------------------------------------------------------------------------- 1 | .rc-switch { 2 | position: relative; 3 | display: inline-block; 4 | box-sizing: border-box; 5 | width: 44px; 6 | height: 22px; 7 | line-height: 20px; 8 | vertical-align: middle; 9 | border-radius: 20px 20px; 10 | border: 1px solid #ccc; 11 | background-color: #ccc; 12 | cursor: pointer; 13 | transition: all 0.3s cubic-bezier(0.35, 0, 0.25, 1); 14 | } 15 | .rc-switch-inner { 16 | color: #fff; 17 | font-size: 12px; 18 | position: absolute; 19 | left: 24px; 20 | } 21 | .rc-switch:after { 22 | position: absolute; 23 | width: 18px; 24 | height: 18px; 25 | left: 2px; 26 | top: 1px; 27 | border-radius: 50% 50%; 28 | background-color: #ffffff; 29 | content: " "; 30 | cursor: pointer; 31 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.26); 32 | -webkit-transform: scale(1); 33 | transform: scale(1); 34 | transition: left 0.3s cubic-bezier(0.35, 0, 0.25, 1); 35 | -webkit-animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1); 36 | animation-timing-function: cubic-bezier(0.35, 0, 0.25, 1); 37 | -webkit-animation-duration: 0.3s; 38 | animation-duration: 0.3s; 39 | -webkit-animation-name: rcSwitchOff; 40 | animation-name: rcSwitchOff; 41 | } 42 | .rc-switch:hover:after { 43 | -webkit-transform: scale(1.1); 44 | transform: scale(1.1); 45 | -webkit-animation-name: rcSwitchOn; 46 | animation-name: rcSwitchOn; 47 | } 48 | .rc-switch:focus { 49 | box-shadow: 0 0 0 2px #d5f1fd; 50 | outline: none; 51 | } 52 | .rc-switch-checked { 53 | border: 1px solid #87d068; 54 | background-color: #87d068; 55 | } 56 | .rc-switch-checked .rc-switch-inner { 57 | left: 6px; 58 | } 59 | .rc-switch-checked:after { 60 | left: 22px; 61 | } 62 | .rc-switch-disabled { 63 | cursor: no-drop; 64 | background: #ccc; 65 | border-color: #ccc; 66 | } 67 | .rc-switch-disabled:after { 68 | background: #9e9e9e; 69 | -webkit-animation-name: none; 70 | animation-name: none; 71 | cursor: no-drop; 72 | } 73 | .rc-switch-disabled:hover:after { 74 | -webkit-transform: scale(1); 75 | transform: scale(1); 76 | -webkit-animation-name: none; 77 | animation-name: none; 78 | } 79 | .rc-switch-label { 80 | display: inline-block; 81 | line-height: 20px; 82 | font-size: 14px; 83 | padding-left: 10px; 84 | vertical-align: middle; 85 | white-space: normal; 86 | pointer-events: none; 87 | -webkit-user-select: text; 88 | -moz-user-select: text; 89 | -ms-user-select: text; 90 | user-select: text; 91 | } 92 | @-webkit-keyframes rcSwitchOn { 93 | 0% { 94 | -webkit-transform: scale(1); 95 | transform: scale(1); 96 | } 97 | 50% { 98 | -webkit-transform: scale(1.25); 99 | transform: scale(1.25); 100 | } 101 | 100% { 102 | -webkit-transform: scale(1.1); 103 | transform: scale(1.1); 104 | } 105 | } 106 | @keyframes rcSwitchOn { 107 | 0% { 108 | -webkit-transform: scale(1); 109 | transform: scale(1); 110 | } 111 | 50% { 112 | -webkit-transform: scale(1.25); 113 | transform: scale(1.25); 114 | } 115 | 100% { 116 | -webkit-transform: scale(1.1); 117 | transform: scale(1.1); 118 | } 119 | } 120 | @-webkit-keyframes rcSwitchOff { 121 | 0% { 122 | -webkit-transform: scale(1.1); 123 | transform: scale(1.1); 124 | } 125 | 100% { 126 | -webkit-transform: scale(1); 127 | transform: scale(1); 128 | } 129 | } 130 | @keyframes rcSwitchOff { 131 | 0% { 132 | -webkit-transform: scale(1.1); 133 | transform: scale(1.1); 134 | } 135 | 100% { 136 | -webkit-transform: scale(1); 137 | transform: scale(1); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /www/static/home/styles/custom/components/table.scss: -------------------------------------------------------------------------------- 1 | .table { 2 | width: 100%; 3 | } 4 | 5 | .table tr th { 6 | padding: 10px; 7 | text-align: center; 8 | } 9 | 10 | .table tr td { 11 | padding: 5px; 12 | text-align: center; 13 | } 14 | 15 | .form-control { 16 | display: block; 17 | width: 100%; 18 | height: 34px; 19 | padding: 6px 12px; 20 | font-size: 14px; 21 | line-height: 1.42857143; 22 | color: #555; 23 | background-color: #fff; 24 | background-image: none; 25 | border: 1px solid #ccc; 26 | border-radius: 4px; 27 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); 28 | box-shadow: inset 0 1px 1px rgba(0,0,0,.075); 29 | -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; 30 | -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; 31 | transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; 32 | 33 | border-radius: 4px; 34 | -moz-border-radius: 4px; 35 | -webkit-border-radius: 4px; 36 | font-size: 12px; 37 | 38 | font-size: 12px; 39 | border-radius: 4px; 40 | -moz-border-radius: 4px; 41 | -webkit-border-radius: 4px; 42 | margin: 0; 43 | background: #fdfdfd; 44 | border-color: #e4e4e4; 45 | } -------------------------------------------------------------------------------- /www/static/home/styles/custom/components/taskbar.scss: -------------------------------------------------------------------------------- 1 | 2 | .taskbar { 3 | position: fixed; 4 | width: 100%; 5 | left: 0; 6 | bottom: 0; 7 | height: 35px; 8 | 9 | background: rgba(255, 255, 255, 0.1); 10 | border-top: 1px solid #ACADAE; 11 | box-shadow: 0 -3px 6px rgba(35, 39, 48, 0.46); 12 | 13 | -webkit-touch-callout: none; 14 | -webkit-user-select: none; 15 | -khtml-user-select: none; 16 | -moz-user-select: none; 17 | -ms-user-select: none; 18 | user-select: none; 19 | } 20 | 21 | .taskbar-left { 22 | float: left; 23 | line-height: 35px; 24 | font-size: 14px; 25 | color: #fff; 26 | } 27 | 28 | .taskbar-right { 29 | float: right; 30 | line-height: 35px; 31 | font-size: 14px; 32 | color: #fff; 33 | } 34 | 35 | .taskbar-start { 36 | padding: 11px 15px; 37 | background: #077ED4; 38 | cursor: default; 39 | } 40 | 41 | .taskbar-start:hover { 42 | background: rgb(51, 159, 236); 43 | } 44 | 45 | .taskbar-time { 46 | margin-right: 5px; 47 | } 48 | 49 | .taskbar-desktop { 50 | width: 23px; 51 | height: 34px; 52 | border-radius: 4px; 53 | cursor: pointer; 54 | border: 1px solid rgba(255, 255, 255, 0.4); 55 | float: right; 56 | background: rgba(255, 255, 255, 0); 57 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='rgba(255,255,255,0.5)', endColorstr='rgba(255,255,255,0)'); 58 | background-image: -webkit-linear-gradient(top, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0)); 59 | background-image: -moz-linear-gradient(top, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0)); 60 | background-image: -o-linear-gradient(top, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0)); 61 | background-image: -ms-linear-gradient(top, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0)); 62 | background-image: linear-gradient(top, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0)); 63 | } -------------------------------------------------------------------------------- /www/static/home/styles/custom/components/type.scss: -------------------------------------------------------------------------------- 1 | body { 2 | color: #222; 3 | font-family: 'Open Sans'; 4 | font-size: 14px; 5 | background-color: #f4f4f4; 6 | -webkit-user-select: none; 7 | } 8 | -------------------------------------------------------------------------------- /www/static/home/styles/custom/custom.scss: -------------------------------------------------------------------------------- 1 | @import 'components/reset'; 2 | @import 'components/type'; 3 | @import 'components/desktop'; 4 | @import 'components/taskbar'; 5 | @import 'components/dialog'; 6 | @import 'components/table'; 7 | @import 'components/switch'; -------------------------------------------------------------------------------- /www/static/home/styles/main.scss: -------------------------------------------------------------------------------- 1 | @import 'custom/custom'; 2 | 3 | #main { 4 | background: url("../images/desktop.jpg"); 5 | position: absolute; 6 | width: 100%; 7 | height: 100%; 8 | right: 0px; 9 | top: 0px; 10 | bottom: 0px; 11 | left: 0px; 12 | overflow: hidden; 13 | background-size: 100% 100%; 14 | } 15 | 16 | .startmenu { 17 | position: fixed; 18 | width: 350px; 19 | left: 0; 20 | bottom: 35px; 21 | height: 300px; 22 | 23 | border: solid 1px #102a3e; 24 | height: 404px; 25 | width: 390px; 26 | opacity: 0.9; 27 | filter: alpha(opacity=90); 28 | -webkit-border-radius: 5px; 29 | position: absolute; 30 | box-shadow: inset 0 0 1px #fff; 31 | -webkit-box-shadow: inset 0 0 1px #fff; 32 | background-color: #619bb9; 33 | background: -webkit-gradient(linear, center top, center bottom, from(#327aa4), color-stop(45%, #2e4b5a), to(#5cb0dc)); 34 | background: rgba(0, 0, 0, 0.5); 35 | 36 | display: none; 37 | } 38 | 39 | .startmenu.active, 40 | .startmenu.open { 41 | display: block; 42 | } 43 | 44 | .programs { 45 | float: left; 46 | padding: 0; 47 | list-style: none; 48 | position: relative; 49 | 50 | background: #fff; 51 | border: solid 1px #365167; 52 | height: 385px; 53 | margin: 7px 0 7px 7px; 54 | box-shadow: 0 0 1px #fff; 55 | width: 250px; 56 | -webkit-border-radius: 3px; 57 | -moz-border-radius: 3px; 58 | border-radius: 3px; 59 | -webkit-box-shadow: 0 0 1px #fff; 60 | -moz-box-shadow: 0 0 1px #fff; 61 | } 62 | 63 | .programs a { 64 | border: solid 1px transparent; 65 | display: block; 66 | padding-left: 3px; 67 | height: 30px; 68 | line-height: 30px; 69 | margin: 3px 10px; 70 | color: #4b4b4b; 71 | text-decoration: none; 72 | min-width: 200px; 73 | } 74 | 75 | .programs a:hover { 76 | border: solid 1px #7da2ce; 77 | -webkit-border-radius: 3px; 78 | -moz-border-radius: 3px; 79 | border-radius: 3px; 80 | -webkit-box-shadow: inset 0 0 1px #fff; 81 | -moz-box-shadow: inset 0 0 1px #fff; 82 | box-shadow: inset 0 0 1px #fff; 83 | background-color: #cfe3fd; 84 | background: -webkit-gradient(linear, center top, center bottom, from(#dcebfd), to(#c2dcfd)); 85 | } 86 | 87 | .startmenu-quick { 88 | float: left; 89 | padding: 0; 90 | list-style: none; 91 | position: relative; 92 | 93 | margin: 5px; 94 | } 95 | 96 | .startmenu-quick a { 97 | border: solid 1px transparent; 98 | display: block; 99 | margin: 5px 0; 100 | position: relative; 101 | color: #fff; 102 | text-decoration: none; 103 | min-width: 120px; 104 | padding: 5px; 105 | padding-left: 10px; 106 | display: block; 107 | } 108 | 109 | .startmenu-quick a:hover, 110 | .startmenu-logout:hover { 111 | border: solid 1px #000; 112 | -webkit-border-radius: 3px; 113 | -moz-border-radius: 3px; 114 | border-radius: 3px; 115 | -webkit-box-shadow: 0 0 1px #fff; 116 | -moz-box-shadow: 0 0 1px #fff; 117 | box-shadow: 0 0 1px #fff; 118 | background-color: #658da0; 119 | background: -webkit-gradient(linear, 0% 100%, 100% 100%, from(#517384), color-stop(50%, #79a3b8), to(#517384)); 120 | } 121 | 122 | .startmenu-logout { 123 | position: absolute; 124 | bottom: 40px; 125 | right: 10px; 126 | padding: 5px; 127 | color: #fff; 128 | width: 110px; 129 | min-width: 110px; 130 | } 131 | 132 | .rc-trigger-popup .startmenu { 133 | display: block; 134 | } 135 | 136 | .rc-trigger-popup-hidden .startmenu { 137 | display: none; 138 | } 139 | 140 | .startmenu-profile { 141 | margin-bottom: 20px; 142 | overflow: hidden; 143 | } 144 | 145 | .startmenu-icon { 146 | height: 64px; 147 | width: 64px; 148 | margin: 10px auto; 149 | border: 1px solid #619bb9; 150 | border-radius: 4px; 151 | box-shadow: inset 0 0 1px #fff; 152 | } 153 | 154 | .startmenu-name { 155 | font-size: 18px; 156 | color: #fff; 157 | text-align: center; 158 | } -------------------------------------------------------------------------------- /www/static/home/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var ignore = new webpack.IgnorePlugin(/\.svg$/) 3 | 4 | module.exports = { 5 | devtool: 'source-map', 6 | entry: { 7 | main: [ 8 | './scripts/main.js', 9 | 'webpack-dev-server/client?http://localhost:8080', 10 | 'webpack/hot/only-dev-server' 11 | ] 12 | }, 13 | output: { 14 | publicPath: 'http://localhost:8080/', 15 | filename: '/js/[name].js' 16 | }, 17 | module: { 18 | loaders: [ 19 | { test: /\.js$/, loaders: ['react-hot', 'babel?' + JSON.stringify({presets: ['react', 'es2015', 'stage-0']})], exclude: /node_modules/ }, 20 | { test: /\.scss$/, loaders: ['style', 'css', 'autoprefixer', 'sass'] }, 21 | { test: /\.(png|jpg)$/, loader: 'url?limit=8192' } 22 | ] 23 | }, 24 | plugins: [ignore], 25 | devServer: { 26 | host: '0.0.0.0', 27 | proxy: { 28 | '/api/*': 'http://localhost:8081', 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /www/static/home/webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | var ignore = new webpack.IgnorePlugin(/\.svg$/); 4 | var nodeModulesDir = path.resolve(__dirname, 'node_modules'); 5 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 6 | 7 | module.exports = { 8 | entry: { 9 | main: './scripts/main.js', 10 | vendor: [ 11 | 'react', 12 | 'redux' 13 | ] 14 | }, 15 | output: { 16 | publicPath: '/static/home/', 17 | filename: './dist/js/[name].js' 18 | }, 19 | module: { 20 | loaders: [ 21 | { test: /\.js$/, loader: 'babel?' + JSON.stringify({presets: ['react', 'es2015']}), exclude: [nodeModulesDir] }, 22 | { test: /\.scss$/, loader: ExtractTextPlugin.extract('style', 'css!autoprefixer!sass') }, 23 | {test: /\.(png|jpg)$/, loader: 'file-loader?limit=50000&name=./dist/img/[sha512:hash:base64:7].[ext]'} 24 | ] 25 | }, 26 | plugins: [ 27 | ignore, 28 | new ExtractTextPlugin('./dist/css/main.css'), 29 | new webpack.optimize.CommonsChunkPlugin('vendor', './dist/js/vendor.js'), 30 | new webpack.DefinePlugin({ "process.env": { NODE_ENV: JSON.stringify("production") } }) 31 | ] 32 | }; 33 | -------------------------------------------------------------------------------- /www/static/lib/jquery.easypiechart.min.js: -------------------------------------------------------------------------------- 1 | /**! 2 | * easyPieChart 3 | * Lightweight plugin to render simple, animated and retina optimized pie charts 4 | * 5 | * @license 6 | * @author Robert Fleischmann (http://robert-fleischmann.de) 7 | * @version 2.1.5 8 | **/ 9 | !function(a,b){"object"==typeof exports?module.exports=b(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],b):b(a.jQuery)}(this,function(a){var b=function(a,b){var c,d=document.createElement("canvas");a.appendChild(d),"undefined"!=typeof G_vmlCanvasManager&&G_vmlCanvasManager.initElement(d);var e=d.getContext("2d");d.width=d.height=b.size;var f=1;window.devicePixelRatio>1&&(f=window.devicePixelRatio,d.style.width=d.style.height=[b.size,"px"].join(""),d.width=d.height=b.size*f,e.scale(f,f)),e.translate(b.size/2,b.size/2),e.rotate((-0.5+b.rotate/180)*Math.PI);var g=(b.size-b.lineWidth)/2;b.scaleColor&&b.scaleLength&&(g-=b.scaleLength+2),Date.now=Date.now||function(){return+new Date};var h=function(a,b,c){c=Math.min(Math.max(-1,c||0),1);var d=0>=c?!0:!1;e.beginPath(),e.arc(0,0,g,0,2*Math.PI*c,d),e.strokeStyle=a,e.lineWidth=b,e.stroke()},i=function(){var a,c;e.lineWidth=1,e.fillStyle=b.scaleColor,e.save();for(var d=24;d>0;--d)d%6===0?(c=b.scaleLength,a=0):(c=.6*b.scaleLength,a=b.scaleLength-c),e.fillRect(-b.size/2+a,0,c,1),e.rotate(Math.PI/12);e.restore()},j=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(a){window.setTimeout(a,1e3/60)}}(),k=function(){b.scaleColor&&i(),b.trackColor&&h(b.trackColor,b.lineWidth,1)};this.getCanvas=function(){return d},this.getCtx=function(){return e},this.clear=function(){e.clearRect(b.size/-2,b.size/-2,b.size,b.size)},this.draw=function(a){b.scaleColor||b.trackColor?e.getImageData&&e.putImageData?c?e.putImageData(c,0,0):(k(),c=e.getImageData(0,0,b.size*f,b.size*f)):(this.clear(),k()):this.clear(),e.lineCap=b.lineCap;var d;d="function"==typeof b.barColor?b.barColor(a):b.barColor,h(d,b.lineWidth,a/100)}.bind(this),this.animate=function(a,c){var d=Date.now();b.onStart(a,c);var e=function(){var f=Math.min(Date.now()-d,b.animate.duration),g=b.easing(this,f,a,c-a,b.animate.duration);this.draw(g),b.onStep(a,c,g),f>=b.animate.duration?b.onStop(a,c):j(e)}.bind(this);j(e)}.bind(this)},c=function(a,c){var d={barColor:"#ef1e25",trackColor:"#f9f9f9",scaleColor:"#dfe0e0",scaleLength:5,lineCap:"round",lineWidth:3,size:110,rotate:0,animate:{duration:1e3,enabled:!0},easing:function(a,b,c,d,e){return b/=e/2,1>b?d/2*b*b+c:-d/2*(--b*(b-2)-1)+c},onStart:function(){},onStep:function(){},onStop:function(){}};if("undefined"!=typeof b)d.renderer=b;else{if("undefined"==typeof SVGRenderer)throw new Error("Please load either the SVG- or the CanvasRenderer");d.renderer=SVGRenderer}var e={},f=0,g=function(){this.el=a,this.options=e;for(var b in d)d.hasOwnProperty(b)&&(e[b]=c&&"undefined"!=typeof c[b]?c[b]:d[b],"function"==typeof e[b]&&(e[b]=e[b].bind(this)));e.easing="string"==typeof e.easing&&"undefined"!=typeof jQuery&&jQuery.isFunction(jQuery.easing[e.easing])?jQuery.easing[e.easing]:d.easing,"number"==typeof e.animate&&(e.animate={duration:e.animate,enabled:!0}),"boolean"!=typeof e.animate||e.animate||(e.animate={duration:1e3,enabled:e.animate}),this.renderer=new e.renderer(a,e),this.renderer.draw(f),a.dataset&&a.dataset.percent?this.update(parseFloat(a.dataset.percent)):a.getAttribute&&a.getAttribute("data-percent")&&this.update(parseFloat(a.getAttribute("data-percent")))}.bind(this);this.update=function(a){return a=parseFloat(a),e.animate.enabled?this.renderer.animate(f,a):this.renderer.draw(a),f=a,this}.bind(this),this.disableAnimation=function(){return e.animate.enabled=!1,this},this.enableAnimation=function(){return e.animate.enabled=!0,this},g()};a.fn.easyPieChart=function(b){return this.each(function(){var d;a.data(this,"easyPieChart")||(d=a.extend({},b,a(this).data()),a.data(this,"easyPieChart",new c(this,d)))})}}); -------------------------------------------------------------------------------- /www/static/lib/jquery.localScroll.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2014 Ariel Flesler - afleslergmailcom | http://flesler.blogspot.com 3 | * Licensed under MIT 4 | * @author Ariel Flesler 5 | * @version 1.3.5 6 | */ 7 | ;(function(a){if(typeof define==='function'&&define.amd){define(['jquery'],a)}else{a(jQuery)}}(function($){var g=location.href.replace(/#.*/,'');var h=$.localScroll=function(a){$('body').localScroll(a)};h.defaults={duration:1000,axis:'y',event:'click',stop:true,target:window};$.fn.localScroll=function(a){a=$.extend({},h.defaults,a);if(a.hash&&location.hash){if(a.target)window.scrollTo(0,0);scroll(0,location,a)}return a.lazy?this.on(a.event,'a,area',function(e){if(filter.call(this)){scroll(e,this,a)}}):this.find('a,area').filter(filter).bind(a.event,function(e){scroll(e,this,a)}).end().end();function filter(){return!!this.href&&!!this.hash&&this.href.replace(this.hash,'')==g&&(!a.filter||$(this).is(a.filter))}};h.hash=function(){};function scroll(e,a,b){var c=a.hash.slice(1),elem=document.getElementById(c)||document.getElementsByName(c)[0];if(!elem)return;if(e)e.preventDefault();var d=$(b.target);if(b.lock&&d.is(':animated')||b.onBefore&&b.onBefore(e,elem,d)===false)return;if(b.stop)d._scrollable().stop(true);if(b.hash){var f=elem.id===c?'id':'name',$a=$(' ').attr(f,c).css({position:'absolute',top:$(window).scrollTop(),left:$(window).scrollLeft()});elem[f]='';$('body').prepend($a);location.hash=a.hash;$a.remove();elem[f]=c}d.scrollTo(elem,b).trigger('notify.serialScroll',[elem])};return h})); -------------------------------------------------------------------------------- /www/static/lib/jquery.scrollTo.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2007-2014 Ariel Flesler - afleslergmailcom | http://flesler.blogspot.com 3 | * Licensed under MIT 4 | * @author Ariel Flesler 5 | * @version 1.4.12 6 | */ 7 | ;(function(a){if(typeof define==='function'&&define.amd){define(['jquery'],a)}else{a(jQuery)}}(function($){var j=$.scrollTo=function(a,b,c){return $(window).scrollTo(a,b,c)};j.defaults={axis:'xy',duration:parseFloat($.fn.jquery)>=1.3?0:1,limit:true};j.window=function(a){return $(window)._scrollable()};$.fn._scrollable=function(){return this.map(function(){var a=this,isWin=!a.nodeName||$.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!isWin)return a;var b=(a.contentWindow||a).document||a.ownerDocument||a;return/webkit/i.test(navigator.userAgent)||b.compatMode=='BackCompat'?b.body:b.documentElement})};$.fn.scrollTo=function(f,g,h){if(typeof g=='object'){h=g;g=0}if(typeof h=='function')h={onAfter:h};if(f=='max')f=9e9;h=$.extend({},j.defaults,h);g=g||h.duration;h.queue=h.queue&&h.axis.length>1;if(h.queue)g/=2;h.offset=both(h.offset);h.over=both(h.over);return this._scrollable().each(function(){if(f==null)return;var d=this,$elem=$(d),targ=f,toff,attr={},win=$elem.is('html,body');switch(typeof targ){case'number':case'string':if(/^([+-]=?)?\d+(\.\d+)?(px|%)?$/.test(targ)){targ=both(targ);break}targ=win?$(targ):$(targ,this);if(!targ.length)return;case'object':if(targ.is||targ.style)toff=(targ=$(targ)).offset()}var e=$.isFunction(h.offset)&&h.offset(d,targ)||h.offset;$.each(h.axis.split(''),function(i,a){var b=a=='x'?'Left':'Top',pos=b.toLowerCase(),key='scroll'+b,old=d[key],max=j.max(d,a);if(toff){attr[key]=toff[pos]+(win?0:old-$elem.offset()[pos]);if(h.margin){attr[key]-=parseInt(targ.css('margin'+b))||0;attr[key]-=parseInt(targ.css('border'+b+'Width'))||0}attr[key]+=e[pos]||0;if(h.over[pos])attr[key]+=targ[a=='x'?'width':'height']()*h.over[pos]}else{var c=targ[pos];attr[key]=c.slice&&c.slice(-1)=='%'?parseFloat(c)/100*max:c}if(h.limit&&/^\d+$/.test(attr[key]))attr[key]=attr[key]<=0?0:Math.min(attr[key],max);if(!i&&h.queue){if(old!=attr[key])animate(h.onAfterFirst);delete attr[key]}});animate(h.onAfter);function animate(a){$elem.animate(attr,g,h.easing,a&&function(){a.call(this,targ,h)})}}).end()};j.max=function(a,b){var c=b=='x'?'Width':'Height',scroll='scroll'+c;if(!$(a).is('html,body'))return a[scroll]-$(a)[c.toLowerCase()]();var d='client'+c,html=a.ownerDocument.documentElement,body=a.ownerDocument.body;return Math.max(html[scroll],body[scroll])-Math.min(html[d],body[d])};function both(a){return $.isFunction(a)||typeof a=='object'?a:{top:a,left:a}};return j})); 8 | -------------------------------------------------------------------------------- /www/static/lib/jquery.slimscroll.min.js: -------------------------------------------------------------------------------- 1 | /*! Copyright (c) 2011 Piotr Rochala (http://rocha.la) 2 | * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 3 | * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. 4 | * 5 | * Version: 1.3.0 6 | * 7 | */ 8 | (function(f){jQuery.fn.extend({slimScroll:function(h){var a=f.extend({width:"auto",height:"250px",size:"7px",color:"#000",position:"right",distance:"1px",start:"top",opacity:0.4,alwaysVisible:!1,disableFadeOut:!1,railVisible:!1,railColor:"#333",railOpacity:0.2,railDraggable:!0,railClass:"slimScrollRail",barClass:"slimScrollBar",wrapperClass:"slimScrollDiv",allowPageScroll:!1,wheelStep:20,touchScrollStep:200,borderRadius:"7px",railBorderRadius:"7px"},h);this.each(function(){function r(d){if(s){d=d|| 9 | window.event;var c=0;d.wheelDelta&&(c=-d.wheelDelta/120);d.detail&&(c=d.detail/3);f(d.target||d.srcTarget||d.srcElement).closest("."+a.wrapperClass).is(b.parent())&&m(c,!0);d.preventDefault&&!k&&d.preventDefault();k||(d.returnValue=!1)}}function m(d,f,h){k=!1;var e=d,g=b.outerHeight()-c.outerHeight();f&&(e=parseInt(c.css("top"))+d*parseInt(a.wheelStep)/100*c.outerHeight(),e=Math.min(Math.max(e,0),g),e=0=b.outerHeight()?k=!0:(c.stop(!0,!0).fadeIn("fast"),a.railVisible&&g.stop(!0,!0).fadeIn("fast"))}function p(){a.alwaysVisible||(A=setTimeout(function(){a.disableFadeOut&&s||(x||y)||(c.fadeOut("slow"),g.fadeOut("slow"))},1E3))}var s,x,y,A,z,u,l,B,D=30,k=!1,b=f(this);if(b.parent().hasClass(a.wrapperClass)){var n=b.scrollTop(), 12 | c=b.parent().find("."+a.barClass),g=b.parent().find("."+a.railClass);w();if(f.isPlainObject(h)){if("height"in h&&"auto"==h.height){b.parent().css("height","auto");b.css("height","auto");var q=b.parent().parent().height();b.parent().css("height",q);b.css("height",q)}if("scrollTo"in h)n=parseInt(a.scrollTo);else if("scrollBy"in h)n+=parseInt(a.scrollBy);else if("destroy"in h){c.remove();g.remove();b.unwrap();return}m(n,!1,!0)}}else{a.height="auto"==a.height?b.parent().height():a.height;n=f("
    ").addClass(a.wrapperClass).css({position:"relative", 13 | overflow:"hidden",width:a.width,height:a.height});b.css({overflow:"hidden",width:a.width,height:a.height});var g=f("
    ").addClass(a.railClass).css({width:a.size,height:"100%",position:"absolute",top:0,display:a.alwaysVisible&&a.railVisible?"block":"none","border-radius":a.railBorderRadius,background:a.railColor,opacity:a.railOpacity,zIndex:90}),c=f("
    ").addClass(a.barClass).css({background:a.color,width:a.size,position:"absolute",top:0,opacity:a.opacity,display:a.alwaysVisible? 14 | "block":"none","border-radius":a.borderRadius,BorderRadius:a.borderRadius,MozBorderRadius:a.borderRadius,WebkitBorderRadius:a.borderRadius,zIndex:99}),q="right"==a.position?{right:a.distance}:{left:a.distance};g.css(q);c.css(q);b.wrap(n);b.parent().append(c);b.parent().append(g);a.railDraggable&&c.bind("mousedown",function(a){var b=f(document);y=!0;t=parseFloat(c.css("top"));pageY=a.pageY;b.bind("mousemove.slimscroll",function(a){currTop=t+a.pageY-pageY;c.css("top",currTop);m(0,c.position().top,!1)}); 15 | b.bind("mouseup.slimscroll",function(a){y=!1;p();b.unbind(".slimscroll")});return!1}).bind("selectstart.slimscroll",function(a){a.stopPropagation();a.preventDefault();return!1});g.hover(function(){v()},function(){p()});c.hover(function(){x=!0},function(){x=!1});b.hover(function(){s=!0;v();p()},function(){s=!1;p()});b.bind("touchstart",function(a,b){a.originalEvent.touches.length&&(z=a.originalEvent.touches[0].pageY)});b.bind("touchmove",function(b){k||b.originalEvent.preventDefault();b.originalEvent.touches.length&& 16 | (m((z-b.originalEvent.touches[0].pageY)/a.touchScrollStep,!0),z=b.originalEvent.touches[0].pageY)});w();"bottom"===a.start?(c.css({top:b.outerHeight()-c.outerHeight()}),m(0,!0)):"top"!==a.start&&(m(f(a.start).position().top,null,!0),a.alwaysVisible||c.hide());C()}});return this}});jQuery.fn.extend({slimscroll:jQuery.fn.slimScroll})})(jQuery); -------------------------------------------------------------------------------- /www/static/lib/js.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * JavaScript Cookie v2.1.2 3 | * https://github.com/js-cookie/js-cookie 4 | * 5 | * Copyright 2006, 2015 Klaus Hartl & Fagner Brack 6 | * Released under the MIT license 7 | */ 8 | ;(function (factory) { 9 | if (typeof define === 'function' && define.amd) { 10 | define(factory); 11 | } else if (typeof exports === 'object') { 12 | module.exports = factory(); 13 | } else { 14 | var OldCookies = window.Cookies; 15 | var api = window.Cookies = factory(); 16 | api.noConflict = function () { 17 | window.Cookies = OldCookies; 18 | return api; 19 | }; 20 | } 21 | }(function () { 22 | function extend () { 23 | var i = 0; 24 | var result = {}; 25 | for (; i < arguments.length; i++) { 26 | var attributes = arguments[ i ]; 27 | for (var key in attributes) { 28 | result[key] = attributes[key]; 29 | } 30 | } 31 | return result; 32 | } 33 | 34 | function init (converter) { 35 | function api (key, value, attributes) { 36 | var result; 37 | if (typeof document === 'undefined') { 38 | return; 39 | } 40 | 41 | // Write 42 | 43 | if (arguments.length > 1) { 44 | attributes = extend({ 45 | path: '/' 46 | }, api.defaults, attributes); 47 | 48 | if (typeof attributes.expires === 'number') { 49 | var expires = new Date(); 50 | expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5); 51 | attributes.expires = expires; 52 | } 53 | 54 | try { 55 | result = JSON.stringify(value); 56 | if (/^[\{\[]/.test(result)) { 57 | value = result; 58 | } 59 | } catch (e) {} 60 | 61 | if (!converter.write) { 62 | value = encodeURIComponent(String(value)) 63 | .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent); 64 | } else { 65 | value = converter.write(value, key); 66 | } 67 | 68 | key = encodeURIComponent(String(key)); 69 | key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent); 70 | key = key.replace(/[\(\)]/g, escape); 71 | 72 | return (document.cookie = [ 73 | key, '=', value, 74 | attributes.expires ? '; expires=' + attributes.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE 75 | attributes.path ? '; path=' + attributes.path : '', 76 | attributes.domain ? '; domain=' + attributes.domain : '', 77 | attributes.secure ? '; secure' : '' 78 | ].join('')); 79 | } 80 | 81 | // Read 82 | 83 | if (!key) { 84 | result = {}; 85 | } 86 | 87 | // To prevent the for loop in the first place assign an empty array 88 | // in case there are no cookies at all. Also prevents odd result when 89 | // calling "get()" 90 | var cookies = document.cookie ? document.cookie.split('; ') : []; 91 | var rdecode = /(%[0-9A-Z]{2})+/g; 92 | var i = 0; 93 | 94 | for (; i < cookies.length; i++) { 95 | var parts = cookies[i].split('='); 96 | var cookie = parts.slice(1).join('='); 97 | 98 | if (cookie.charAt(0) === '"') { 99 | cookie = cookie.slice(1, -1); 100 | } 101 | 102 | try { 103 | var name = parts[0].replace(rdecode, decodeURIComponent); 104 | cookie = converter.read ? 105 | converter.read(cookie, name) : converter(cookie, name) || 106 | cookie.replace(rdecode, decodeURIComponent); 107 | 108 | if (this.json) { 109 | try { 110 | cookie = JSON.parse(cookie); 111 | } catch (e) {} 112 | } 113 | 114 | if (key === name) { 115 | result = cookie; 116 | break; 117 | } 118 | 119 | if (!key) { 120 | result[name] = cookie; 121 | } 122 | } catch (e) {} 123 | } 124 | 125 | return result; 126 | } 127 | 128 | api.set = api; 129 | api.get = function (key) { 130 | return api(key); 131 | }; 132 | api.getJSON = function () { 133 | return api.apply({ 134 | json: true 135 | }, [].slice.call(arguments)); 136 | }; 137 | api.defaults = {}; 138 | 139 | api.remove = function (key, attributes) { 140 | api(key, '', extend(attributes, { 141 | expires: -1 142 | })); 143 | }; 144 | 145 | api.withConverter = init; 146 | 147 | return api; 148 | } 149 | 150 | return init(function () {}); 151 | })); 152 | -------------------------------------------------------------------------------- /www/static/lib/xterm/addons/attach/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 49 | 50 | 51 |
    52 | 53 |

    54 | xterm.js: socket attach 55 |

    56 |

    57 | Attach the terminal to a WebSocket terminal stream with ease. Perfect for attaching to your 58 | Docker containers. 59 |

    60 |

    61 | Socket information 62 |

    63 |
    64 | 68 | 71 |
    72 |
    73 | 74 |
    75 | 92 | 93 | -------------------------------------------------------------------------------- /www/static/lib/xterm/addons/attach/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xterm.attach", 3 | "main": "attach.js", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /www/static/lib/xterm/addons/fit/fit.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Fit terminal columns and rows to the dimensions of its 3 | * DOM element. 4 | * 5 | * Approach: 6 | * - Rows: Truncate the division of the terminal parent element height 7 | * by the terminal row height 8 | * 9 | * - Columns: Truncate the division of the terminal parent element width by 10 | * the terminal character width (apply display: inline at the 11 | * terminal row and truncate its width with the current number 12 | * of columns) 13 | */ 14 | (function (fit) { 15 | if (typeof exports === 'object' && typeof module === 'object') { 16 | /* 17 | * CommonJS environment 18 | */ 19 | module.exports = fit(require('../../src/xterm')); 20 | } else if (typeof define == 'function') { 21 | /* 22 | * Require.js is available 23 | */ 24 | define(['../../src/xterm'], fit); 25 | } else { 26 | /* 27 | * Plain browser environment 28 | */ 29 | fit(this.Xterm); 30 | } 31 | })(function (Xterm) { 32 | /** 33 | * This module provides methods for fitting a terminal's size to a parent container. 34 | * 35 | * @module xterm/addons/fit/fit 36 | */ 37 | var exports = {}; 38 | 39 | exports.proposeGeometry = function (term) { 40 | var parentElementStyle = window.getComputedStyle(term.element.parentElement), 41 | parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')), 42 | parentElementWidth = parseInt(parentElementStyle.getPropertyValue('width')), 43 | elementStyle = window.getComputedStyle(term.element), 44 | elementPaddingVer = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')), 45 | elementPaddingHor = parseInt(elementStyle.getPropertyValue('padding-right')) + parseInt(elementStyle.getPropertyValue('padding-left')), 46 | availableHeight = parentElementHeight - elementPaddingVer, 47 | availableWidth = parentElementWidth - elementPaddingHor, 48 | container = term.rowContainer, 49 | subjectRow = term.rowContainer.firstElementChild, 50 | contentBuffer = subjectRow.innerHTML, 51 | characterHeight, 52 | rows, 53 | characterWidth, 54 | cols, 55 | geometry; 56 | 57 | subjectRow.style.display = 'inline'; 58 | subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace 59 | characterWidth = subjectRow.getBoundingClientRect().width; 60 | subjectRow.style.display = ''; // Revert style before calculating height, since they differ. 61 | characterHeight = parseInt(subjectRow.offsetHeight); 62 | subjectRow.innerHTML = contentBuffer; 63 | 64 | rows = parseInt(availableHeight / characterHeight); 65 | cols = parseInt(availableWidth / characterWidth) - 1; 66 | 67 | geometry = {cols: cols, rows: rows}; 68 | return geometry; 69 | }; 70 | 71 | exports.fit = function (term) { 72 | var geometry = exports.proposeGeometry(term); 73 | 74 | term.resize(geometry.cols, geometry.rows); 75 | }; 76 | 77 | Xterm.prototype.proposeGeometry = function () { 78 | return exports.proposeGeometry(this); 79 | }; 80 | 81 | Xterm.prototype.fit = function () { 82 | return exports.fit(this); 83 | }; 84 | 85 | return exports; 86 | }); 87 | -------------------------------------------------------------------------------- /www/static/lib/xterm/addons/fit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xterm.fit", 3 | "main": "fit.js", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /www/static/lib/xterm/addons/fullscreen/fullscreen.css: -------------------------------------------------------------------------------- 1 | .xterm.fullscreen { 2 | position: fixed; 3 | top: 0; 4 | bottom: 0; 5 | left: 0; 6 | right: 0; 7 | width: auto; 8 | height: auto; 9 | z-index: 255; 10 | } 11 | -------------------------------------------------------------------------------- /www/static/lib/xterm/addons/fullscreen/fullscreen.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Fullscreen addon for xterm.js 3 | * 4 | * Implements the toggleFullscreen function. 5 | * 6 | * If the `fullscreen` argument has been supplied, then 7 | * if it is true, the fullscreen mode gets turned on, 8 | * if it is false or null, the fullscreen mode gets turned off. 9 | * 10 | * If the `fullscreen` argument has not been supplied, the 11 | * fullscreen mode is being toggled. 12 | */ 13 | (function (fullscreen) { 14 | if (typeof exports === 'object' && typeof module === 'object') { 15 | /* 16 | * CommonJS environment 17 | */ 18 | module.exports = fullscreen(require('../../src/xterm')); 19 | } else if (typeof define == 'function') { 20 | /* 21 | * Require.js is available 22 | */ 23 | define(['../../src/xterm'], fullscreen); 24 | } else { 25 | /* 26 | * Plain browser environment 27 | */ 28 | fullscreen(this.Xterm); 29 | } 30 | })(function (Xterm) { 31 | var exports = {}; 32 | 33 | exports.toggleFullScreen = function (term, fullscreen) { 34 | var fn; 35 | 36 | if (typeof fullscreen == 'undefined') { 37 | fn = (term.element.classList.contains('fullscreen')) ? 'remove' : 'add'; 38 | } else if (!fullscreen) { 39 | fn = 'remove'; 40 | } else { 41 | fn = 'add'; 42 | } 43 | 44 | term.element.classList[fn]('fullscreen'); 45 | }; 46 | 47 | Xterm.prototype.toggleFullscreen = function (fullscreen) { 48 | exports.toggleFullScreen(this, fullscreen); 49 | }; 50 | 51 | return exports; 52 | }); 53 | -------------------------------------------------------------------------------- /www/static/lib/xterm/addons/fullscreen/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xterm.fullscreen", 3 | "main": "fullscreen.js", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /www/static/lib/xterm/addons/linkify/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 22 | 23 | 24 |
    25 | 35 | 36 | -------------------------------------------------------------------------------- /www/static/lib/xterm/addons/linkify/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xterm.linkify", 3 | "main": "linkify.js", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /www/testing.js: -------------------------------------------------------------------------------- 1 | var thinkjs = require('thinkjs'); 2 | var path = require('path'); 3 | 4 | var rootPath = path.dirname(__dirname); 5 | 6 | var instance = new thinkjs({ 7 | APP_PATH: rootPath + path.sep + 'app', 8 | RUNTIME_PATH: rootPath + path.sep + 'runtime', 9 | ROOT_PATH: rootPath, 10 | RESOURCE_PATH: __dirname, 11 | env: 'testing' 12 | }); 13 | 14 | instance.run(); --------------------------------------------------------------------------------