├── electron ├── .gitignore ├── src │ ├── images │ │ ├── iconTemplate.png │ │ ├── iconTemplate@2x.png │ │ └── windows-icon@2x.png │ ├── app │ │ ├── public │ │ │ ├── footer.vue │ │ │ ├── left.vue │ │ │ ├── right.vue │ │ │ ├── css │ │ │ │ ├── container.css │ │ │ │ └── common.css │ │ │ ├── container │ │ │ │ ├── actionSelf.vue │ │ │ │ ├── header.vue │ │ │ │ ├── actionOther.vue │ │ │ │ └── compose.vue │ │ │ ├── store.js │ │ │ ├── setup │ │ │ │ ├── setup-left.vue │ │ │ │ └── setup-content.vue │ │ │ └── header.vue │ │ ├── setup.vue │ │ └── index.vue │ ├── package.json │ ├── core │ │ ├── error.js │ │ ├── docker.js │ │ ├── index.js │ │ ├── counter.js │ │ ├── renderSessionStorage.js │ │ ├── nodeStorage.js │ │ ├── controller.js │ │ ├── commonFunc.js │ │ ├── menu.js │ │ ├── setupContent.js │ │ ├── docker.node.js │ │ ├── event.js │ │ └── process.exec.js │ ├── config │ │ ├── nodeStorageJson.json │ │ ├── config.js │ │ ├── config.docker.js │ │ └── config.command.js │ ├── webpack │ │ ├── ddl.config.js │ │ ├── webpack.server.js │ │ └── webpack.config.js │ ├── package-lock.json │ ├── resources │ │ ├── terminal │ │ └── OPENSSH_LICENSE │ └── appMain.js ├── index.ejs ├── main.js └── package.json ├── docker ├── nginx │ ├── conf │ │ ├── 说明.txt │ │ ├── vhost │ │ │ └── www.baidu.dev.conf │ │ ├── scgi_params │ │ ├── scgi_params.default │ │ ├── uwsgi_params │ │ ├── uwsgi_params.default │ │ ├── fastcgi_params.default │ │ ├── fastcgi_params │ │ ├── fastcgi.conf.default │ │ ├── fastcgi.conf │ │ ├── koi-win │ │ ├── nginx.conf.default │ │ ├── koi-utf │ │ ├── nginx.conf │ │ ├── win-utf │ │ ├── mime.types │ │ └── mime.types.default │ └── Dockerfile ├── 注意.txt ├── docker-compose-common.yml ├── php │ ├── conf │ │ ├── etc │ │ │ ├── php-fpm.conf │ │ │ └── pear.conf │ │ └── conf │ │ │ └── fdfs.client.conf │ ├── src │ │ ├── install.json │ │ └── php-ext.sh │ └── Dockerfile ├── docker-compose-powershell.yml ├── docker-compose.yml ├── base │ ├── Dockerfile │ └── sources.list ├── .env └── docker-compose-full.yml ├── .gitignore ├── LICENSE └── README.md /electron/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docker/nginx/conf/说明.txt: -------------------------------------------------------------------------------- 1 | vhost为站点配置 -------------------------------------------------------------------------------- /docker/注意.txt: -------------------------------------------------------------------------------- 1 | 1、最好先下载到本地,docker pull ubuntu:14.04 2 | -------------------------------------------------------------------------------- /electron/src/images/iconTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aogg/docker_lnmp/HEAD/electron/src/images/iconTemplate.png -------------------------------------------------------------------------------- /electron/src/images/iconTemplate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aogg/docker_lnmp/HEAD/electron/src/images/iconTemplate@2x.png -------------------------------------------------------------------------------- /electron/src/images/windows-icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aogg/docker_lnmp/HEAD/electron/src/images/windows-icon@2x.png -------------------------------------------------------------------------------- /electron/src/app/public/footer.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | 8 | 9 | 10 | 13 | -------------------------------------------------------------------------------- /electron/src/app/public/left.vue: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /electron/src/app/public/right.vue: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /docker/docker-compose-common.yml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | services: 3 | common_nginx_aliases: 4 | # 当使用virtualBox的时候就需要在这里填写 5 | # 本地nginx项目填写在这 6 | networks: 7 | all: 8 | aliases: 9 | - nginx 10 | common_hosts: 11 | # php中公共的hosts修改 12 | extra_hosts: 13 | - "localhost:127.0.0.1" 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | # 不能用//node_modules/* 3 | electron/*/* 4 | !electron/src/* 5 | electron/src/node_modules/* 6 | .idea 7 | .DS_Store 8 | .vscode 9 | electron/src/temp/* 10 | electron/npm-debug.log 11 | electron/npm-debug.log.* 12 | docker/nginx/conf/vhost/* 13 | docker/nginx/conf/vhost/www.baidu.dev.conf 14 | electron/src/config/nodeStorageJson.json 15 | -------------------------------------------------------------------------------- /electron/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docker_lnmp", 3 | "version": "0.1.0", 4 | "description": "docker_lnmp gui on account of the electron", 5 | "author": "https://github.com/aogg", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/aogg/docker_lnmp.git" 9 | }, 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "dotenv": "^6.0.0", 13 | "iconv-lite": "^0.4.23" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /electron/src/core/error.js: -------------------------------------------------------------------------------- 1 | let msg = ''; 2 | 3 | 4 | 5 | class Error{ 6 | 7 | // static msg = ''; 8 | 9 | static hasError(){ 10 | return !!Error.msg; 11 | } 12 | 13 | static getError(){ 14 | return Error.msg; 15 | } 16 | 17 | static setError(msg){ 18 | Error.msg = msg; 19 | } 20 | } 21 | 22 | // es6只有静态方法,没有静态属性 23 | Error.msg = ''; 24 | 25 | module.exports = Error; 26 | 27 | -------------------------------------------------------------------------------- /docker/nginx/conf/vhost/www.baidu.dev.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name www.baidu.dev; 4 | root /www/baidu; 5 | index index.html index.htm index.php; 6 | 7 | location / { 8 | if (!-e $request_filename) { 9 | rewrite ^(.*)$ /index.php?s=/$1 last; 10 | break; 11 | } 12 | } 13 | 14 | location ~ \.php$ { 15 | fastcgi_pass php:9000; 16 | include fastcgi.conf; 17 | } 18 | } -------------------------------------------------------------------------------- /electron/src/config/nodeStorageJson.json: -------------------------------------------------------------------------------- 1 | { 2 | "config-exe": { 3 | "docker": "\"/usr/local/bin/docker\"", 4 | "docker-machine": "\"/usr/local/bin/docker-machine\"", 5 | "docker-compose": "\"/usr/local/bin/docker-compose\"" 6 | }, 7 | "containerConfig": { 8 | "DOCKER_TLS_VERIFY": 1, 9 | "DOCKER_HOST": "tcp://192.168.99.100:2376", 10 | "DOCKER_CERT_PATH": "/Users/code/.docker/machine/machines/default", 11 | "COMPOSE_CONVERT_WINDOWS_PATHS": true 12 | }, 13 | "firstStartEvents": 2 14 | } -------------------------------------------------------------------------------- /docker/php/conf/etc/php-fpm.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | daemonize = no 3 | ; 错误日志目录 4 | error_log = /proc/self/fd/2 5 | pid = run/php-fpm.pid 6 | 7 | 8 | 9 | 10 | [www] 11 | user = www-data 12 | group = www-data 13 | 14 | 15 | pm = dynamic 16 | pm.max_children = 5 17 | pm.start_servers = 2 18 | pm.min_spare_servers = 1 19 | pm.max_spare_servers = 3 20 | 21 | listen = [::]:9000 22 | ; 对应配置容器ip 23 | ;listen = 172.17.0.2:9000 24 | 25 | 26 | access.log = /proc/self/fd/2 27 | 28 | catch_workers_output = yes 29 | clear_env = no 30 | 31 | -------------------------------------------------------------------------------- /electron/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= htmlWebpackPlugin.options.title %> 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /docker/php/src/install.json: -------------------------------------------------------------------------------- 1 | [ 2 | // "all", 3 | "composer", 4 | "memcache-3.0.8", 5 | "memcached-2.2.0", 6 | "xdebug-2.4.0", 7 | "svn-1.0.2", 8 | "redis-2.2.8", 9 | "mongo-1.6.14", 10 | "mongodb-1.1.9", 11 | // "ioncube-5.5", 12 | "phalcon", 13 | "swoole-2.0.9", 14 | "xhprof-0.9.4", 15 | "php_zip", 16 | "amqp-1.4.0", 17 | "http-3.1.0", 18 | 19 | "curl", "pcntl", "mysqli", "mysql", "mbstring", "json", "sockets", "iconv", "hash", "gettext", "ftp", "exif", "posix", "tokenizer", "shmop", "bcmath", "phar", "pdo", "pdo_mysql", "zlib", 20 | "openssl", "mcrypt", "gd", "session" 21 | // "需要安装的扩展" 22 | // 留意最后的逗号 23 | // todo 安装顺序不对 24 | ] -------------------------------------------------------------------------------- /electron/src/webpack/ddl.config.js: -------------------------------------------------------------------------------- 1 | const config = require(__dirname + '/../config/config.js'); 2 | const webpack = require('webpack'); 3 | 4 | 5 | 6 | 7 | 8 | module.exports = { 9 | output:{ 10 | path: `${config.tempPath}/dll`, 11 | filename:'[name].js', 12 | library:'[name]', 13 | }, 14 | entry:{ 15 | "lib":[ 16 | "vue", 17 | ] 18 | }, 19 | plugins:[ 20 | new webpack.DllPlugin({ 21 | path: `${config.tempPath}/manifest.json`, 22 | name:'[name]', // 这里的name为entry的key 23 | context: __dirname, 24 | }), 25 | ] 26 | }; 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /electron/src/core/docker.js: -------------------------------------------------------------------------------- 1 | let onName = 'docker-command'; // ipc on的名称 2 | let ipcRenderName = 'ipcRenderCallback'; // ipcRender on 3 | 4 | module.exports = { 5 | ipcRenderName, 6 | onName, 7 | eventsName: 'docker-events', 8 | dockerParam (channel, type, arg, callbackName) { 9 | callbackName = callbackName ? callbackName : onName; 10 | return [onName, {type, name: channel, arg, callbackName}]; 11 | }, 12 | 13 | /** 14 | * 发回给ipcRenderer的参数 15 | */ 16 | sendCallBack(data, callbackName, code, errMsg = ''){ 17 | return {data, callbackName, code, errMsg}; 18 | }, 19 | 20 | }; 21 | 22 | 23 | // module.exports = 24 | 25 | 26 | -------------------------------------------------------------------------------- /docker/nginx/conf/scgi_params: -------------------------------------------------------------------------------- 1 | 2 | scgi_param REQUEST_METHOD $request_method; 3 | scgi_param REQUEST_URI $request_uri; 4 | scgi_param QUERY_STRING $query_string; 5 | scgi_param CONTENT_TYPE $content_type; 6 | 7 | scgi_param DOCUMENT_URI $document_uri; 8 | scgi_param DOCUMENT_ROOT $document_root; 9 | scgi_param SCGI 1; 10 | scgi_param SERVER_PROTOCOL $server_protocol; 11 | scgi_param REQUEST_SCHEME $scheme; 12 | scgi_param HTTPS $https if_not_empty; 13 | 14 | scgi_param REMOTE_ADDR $remote_addr; 15 | scgi_param REMOTE_PORT $remote_port; 16 | scgi_param SERVER_PORT $server_port; 17 | scgi_param SERVER_NAME $server_name; 18 | -------------------------------------------------------------------------------- /docker/nginx/conf/scgi_params.default: -------------------------------------------------------------------------------- 1 | 2 | scgi_param REQUEST_METHOD $request_method; 3 | scgi_param REQUEST_URI $request_uri; 4 | scgi_param QUERY_STRING $query_string; 5 | scgi_param CONTENT_TYPE $content_type; 6 | 7 | scgi_param DOCUMENT_URI $document_uri; 8 | scgi_param DOCUMENT_ROOT $document_root; 9 | scgi_param SCGI 1; 10 | scgi_param SERVER_PROTOCOL $server_protocol; 11 | scgi_param REQUEST_SCHEME $scheme; 12 | scgi_param HTTPS $https if_not_empty; 13 | 14 | scgi_param REMOTE_ADDR $remote_addr; 15 | scgi_param REMOTE_PORT $remote_port; 16 | scgi_param SERVER_PORT $server_port; 17 | scgi_param SERVER_NAME $server_name; 18 | -------------------------------------------------------------------------------- /docker/nginx/conf/uwsgi_params: -------------------------------------------------------------------------------- 1 | 2 | uwsgi_param QUERY_STRING $query_string; 3 | uwsgi_param REQUEST_METHOD $request_method; 4 | uwsgi_param CONTENT_TYPE $content_type; 5 | uwsgi_param CONTENT_LENGTH $content_length; 6 | 7 | uwsgi_param REQUEST_URI $request_uri; 8 | uwsgi_param PATH_INFO $document_uri; 9 | uwsgi_param DOCUMENT_ROOT $document_root; 10 | uwsgi_param SERVER_PROTOCOL $server_protocol; 11 | uwsgi_param REQUEST_SCHEME $scheme; 12 | uwsgi_param HTTPS $https if_not_empty; 13 | 14 | uwsgi_param REMOTE_ADDR $remote_addr; 15 | uwsgi_param REMOTE_PORT $remote_port; 16 | uwsgi_param SERVER_PORT $server_port; 17 | uwsgi_param SERVER_NAME $server_name; 18 | -------------------------------------------------------------------------------- /docker/nginx/conf/uwsgi_params.default: -------------------------------------------------------------------------------- 1 | 2 | uwsgi_param QUERY_STRING $query_string; 3 | uwsgi_param REQUEST_METHOD $request_method; 4 | uwsgi_param CONTENT_TYPE $content_type; 5 | uwsgi_param CONTENT_LENGTH $content_length; 6 | 7 | uwsgi_param REQUEST_URI $request_uri; 8 | uwsgi_param PATH_INFO $document_uri; 9 | uwsgi_param DOCUMENT_ROOT $document_root; 10 | uwsgi_param SERVER_PROTOCOL $server_protocol; 11 | uwsgi_param REQUEST_SCHEME $scheme; 12 | uwsgi_param HTTPS $https if_not_empty; 13 | 14 | uwsgi_param REMOTE_ADDR $remote_addr; 15 | uwsgi_param REMOTE_PORT $remote_port; 16 | uwsgi_param SERVER_PORT $server_port; 17 | uwsgi_param SERVER_NAME $server_name; 18 | -------------------------------------------------------------------------------- /electron/src/app/public/css/container.css: -------------------------------------------------------------------------------- 1 | .container{ 2 | /*width: 100%;*/ 3 | flex-wrap: wrap; 4 | padding-top: 1%; 5 | padding-left: 1%; 6 | align-items: center; 7 | } 8 | .container-name{ 9 | padding-left: 1%; 10 | flex-basis: 10%; 11 | flex-grow: 0; 12 | } 13 | .container-status{ 14 | width: 16px; 15 | height: 16px; 16 | flex-grow: 0; 17 | } 18 | .container-status-dot{ 19 | width: 100%; 20 | height: 100%; 21 | display: inline-block; 22 | border-radius: 50%; 23 | } 24 | .container-action{ 25 | flex-grow: 6; 26 | flex-wrap: wrap; 27 | } 28 | .container-action>div{ 29 | flex-grow: 1; 30 | flex-basis: 50%; 31 | } 32 | .container-action-self{ 33 | 34 | } 35 | .container-button{ 36 | padding: 2% 4%; 37 | } 38 | -------------------------------------------------------------------------------- /electron/src/app/public/container/actionSelf.vue: -------------------------------------------------------------------------------- 1 | 9 | 19 | -------------------------------------------------------------------------------- /docker/docker-compose-powershell.yml: -------------------------------------------------------------------------------- 1 | # 需要配置指定参数 2 | version: '2.1' 3 | services: 4 | "php": 5 | volumes: 6 | - "${compose_volumes_php_conf}" 7 | - "${compose_volumes_php_etc}" 8 | volumes_from: 9 | - base 10 | extends: 11 | file: "docker-compose-full.yml" 12 | service: "php" 13 | # 在virtualBox时出现无法连接外网的情况 14 | dns: '8.8.8.8' 15 | networks: 16 | all: 17 | aliases: 18 | - php 19 | 20 | "nginx": 21 | volumes: 22 | - "${compose_volumes_nginx}" 23 | volumes_from: 24 | - base 25 | # 在virtualBox时出现无法连接外网的情况 26 | dns: '8.8.8.8' 27 | extends: 28 | file: "docker-compose-full.yml" 29 | service: "nginx" 30 | 31 | base: 32 | volumes: 33 | - "${compose_volumes_base}" 34 | extends: 35 | file: "docker-compose-full.yml" 36 | service: "base" 37 | 38 | networks: 39 | all: {} -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | services: 3 | php: 4 | volumes: 5 | - "${compose_dir}/docker_lnmp/docker/php/conf/conf:/usr/src/php/conf/" 6 | - "${compose_dir}/docker_lnmp/docker/php/conf/etc/:/usr/src/php/etc/" 7 | volumes_from: 8 | - base 9 | extends: 10 | file: "docker-compose-full.yml" 11 | service: "php" 12 | networks: 13 | all: 14 | aliases: 15 | - php 16 | 17 | nginx: 18 | volumes: 19 | - "${compose_dir}/docker_lnmp/docker/nginx/conf/:/usr/src/nginx/conf/" 20 | volumes_from: 21 | - base 22 | extends: 23 | file: "docker-compose-full.yml" 24 | service: "nginx" 25 | 26 | base: 27 | volumes: 28 | - "${compose_volumes_base}" 29 | extends: 30 | file: "docker-compose-full.yml" 31 | service: "base" 32 | 33 | networks: 34 | all: -------------------------------------------------------------------------------- /electron/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const electron = require('electron'); 4 | const app = electron.app; 5 | const config = require('./src/config/config'); // 全局变量 6 | global.config = config; 7 | // global.mainWindow = {}; // todo 未知,是否需要在主文件定义 8 | 9 | 10 | const {LocalEvent} = require(`${config.srcPath}/core/`); 11 | 12 | 13 | 14 | // 初始化并准备创建浏览器窗口 15 | app.on('ready', function () { 16 | LocalEvent('app-ready') 17 | }); 18 | 19 | // 当 Electron 结束的时候,这个方法将会生效 20 | app.on('window-all-closed', function () { 21 | // 用于mac 22 | if (process.platform !== 'darwin') { 23 | app.quit(); 24 | } 25 | 26 | LocalEvent('app-window-all-closed'); 27 | }); 28 | 29 | // mac专用 30 | app.on('activate', function () { 31 | LocalEvent('app-activate'); 32 | }); 33 | 34 | 35 | app.on('before-quit', function () { 36 | LocalEvent('app-before-quit'); 37 | }); -------------------------------------------------------------------------------- /electron/src/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docker_lnmp", 3 | "version": "0.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "dotenv": { 8 | "version": "6.0.0", 9 | "resolved": "http://registry.npm.taobao.org/dotenv/download/dotenv-6.0.0.tgz", 10 | "integrity": "sha1-JON8BBdBxfSyUySVjrvDS8qWWTU=" 11 | }, 12 | "iconv-lite": { 13 | "version": "0.4.23", 14 | "resolved": "http://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.23.tgz", 15 | "integrity": "sha1-KXhx9jvlB63Pv8pxXQzQ7thOmmM=", 16 | "requires": { 17 | "safer-buffer": ">= 2.1.2 < 3" 18 | }, 19 | "dependencies": { 20 | "safer-buffer": { 21 | "version": "2.1.2", 22 | "resolved": "http://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz", 23 | "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docker/base/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:14.04 2 | # @see https://hub.docker.com/r/nickistre/ubuntu-lamp/~/dockerfile/ 3 | # 最好先下载到本地,docker pull ubuntu:14.04 4 | # 然后此images的tags为my-ubuntu-:14.04 5 | # docker run --name share_volume -v /Users/:/www/ -d my-ubuntu:14.04 6 | MAINTAINER aogg 7 | 8 | # 中文乱码,(无用) 9 | # ENV LANG zh_CN.UTF-8 10 | 11 | ARG BASE_ZONE='Asia/Shanghai' 12 | # 修改软件源 @see http://wiki.ubuntu.org.cn/%E6%BA%90%E5%88%97%E8%A1%A8 ,看好对应版本,并在本机ping过 13 | COPY ./sources.list /etc/apt/sources.list 14 | 15 | 16 | # rm是解决apt-get报错,如果还是运行失败就多运行几次 17 | RUN cp "/usr/share/zoneinfo/${BASE_ZONE}" /etc/localtime \ 18 | && sudo rm -rf /etc/apt/sources.list.d/ \ 19 | && sudo rm -rf /var/lib/apt/lists/partial/ \ 20 | && apt update \ 21 | && apt install -y --no-install-recommends \ 22 | gcc \ 23 | make \ 24 | g++ \ 25 | libssl-dev \ 26 | curl \ 27 | vim \ 28 | zip \ 29 | unzip \ 30 | libxml2 \ 31 | && rm -rf /var/lib/apt/lists/* 32 | -------------------------------------------------------------------------------- /electron/src/app/public/container/header.vue: -------------------------------------------------------------------------------- 1 | 11 | 16 | 33 | -------------------------------------------------------------------------------- /electron/src/app/public/container/actionOther.vue: -------------------------------------------------------------------------------- 1 | 9 | 25 | -------------------------------------------------------------------------------- /docker/base/sources.list: -------------------------------------------------------------------------------- 1 | deb http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse 2 | deb http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse 3 | deb http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse 4 | deb http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse 5 | ##测试版源 6 | deb http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse 7 | # 源码 8 | deb-src http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse 9 | deb-src http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse 10 | deb-src http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse 11 | deb-src http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse 12 | ##测试版源 13 | deb-src http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse 14 | # Canonical 合作伙伴和附加 15 | deb http://archive.canonical.com/ubuntu/ trusty partner 16 | #deb http://extras.ubuntu.com/ubuntu/ trusty main -------------------------------------------------------------------------------- /docker/nginx/conf/fastcgi_params.default: -------------------------------------------------------------------------------- 1 | 2 | fastcgi_param QUERY_STRING $query_string; 3 | fastcgi_param REQUEST_METHOD $request_method; 4 | fastcgi_param CONTENT_TYPE $content_type; 5 | fastcgi_param CONTENT_LENGTH $content_length; 6 | 7 | fastcgi_param SCRIPT_NAME $fastcgi_script_name; 8 | fastcgi_param REQUEST_URI $request_uri; 9 | fastcgi_param DOCUMENT_URI $document_uri; 10 | fastcgi_param DOCUMENT_ROOT $document_root; 11 | fastcgi_param SERVER_PROTOCOL $server_protocol; 12 | fastcgi_param REQUEST_SCHEME $scheme; 13 | fastcgi_param HTTPS $https if_not_empty; 14 | 15 | fastcgi_param GATEWAY_INTERFACE CGI/1.1; 16 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; 17 | 18 | fastcgi_param REMOTE_ADDR $remote_addr; 19 | fastcgi_param REMOTE_PORT $remote_port; 20 | fastcgi_param SERVER_ADDR $server_addr; 21 | fastcgi_param SERVER_PORT $server_port; 22 | fastcgi_param SERVER_NAME $server_name; 23 | 24 | # PHP only, required if PHP was built with --enable-force-cgi-redirect 25 | fastcgi_param REDIRECT_STATUS 200; 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 aogg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docker/nginx/conf/fastcgi_params: -------------------------------------------------------------------------------- 1 | 2 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 3 | fastcgi_param QUERY_STRING $query_string; 4 | fastcgi_param REQUEST_METHOD $request_method; 5 | fastcgi_param CONTENT_TYPE $content_type; 6 | fastcgi_param CONTENT_LENGTH $content_length; 7 | 8 | fastcgi_param SCRIPT_NAME $fastcgi_script_name; 9 | fastcgi_param REQUEST_URI $request_uri; 10 | fastcgi_param DOCUMENT_URI $document_uri; 11 | fastcgi_param DOCUMENT_ROOT $document_root; 12 | fastcgi_param SERVER_PROTOCOL $server_protocol; 13 | fastcgi_param REQUEST_SCHEME $scheme; 14 | fastcgi_param HTTPS $https if_not_empty; 15 | 16 | fastcgi_param GATEWAY_INTERFACE CGI/1.1; 17 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; 18 | 19 | fastcgi_param REMOTE_ADDR $remote_addr; 20 | fastcgi_param REMOTE_PORT $remote_port; 21 | fastcgi_param SERVER_ADDR $server_addr; 22 | fastcgi_param SERVER_PORT $server_port; 23 | fastcgi_param SERVER_NAME $server_name; 24 | 25 | # PHP only, required if PHP was built with --enable-force-cgi-redirect 26 | fastcgi_param REDIRECT_STATUS 200; 27 | -------------------------------------------------------------------------------- /docker/nginx/conf/fastcgi.conf.default: -------------------------------------------------------------------------------- 1 | 2 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 3 | fastcgi_param QUERY_STRING $query_string; 4 | fastcgi_param REQUEST_METHOD $request_method; 5 | fastcgi_param CONTENT_TYPE $content_type; 6 | fastcgi_param CONTENT_LENGTH $content_length; 7 | 8 | fastcgi_param SCRIPT_NAME $fastcgi_script_name; 9 | fastcgi_param REQUEST_URI $request_uri; 10 | fastcgi_param DOCUMENT_URI $document_uri; 11 | fastcgi_param DOCUMENT_ROOT $document_root; 12 | fastcgi_param SERVER_PROTOCOL $server_protocol; 13 | fastcgi_param REQUEST_SCHEME $scheme; 14 | fastcgi_param HTTPS $https if_not_empty; 15 | 16 | fastcgi_param GATEWAY_INTERFACE CGI/1.1; 17 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; 18 | 19 | fastcgi_param REMOTE_ADDR $remote_addr; 20 | fastcgi_param REMOTE_PORT $remote_port; 21 | fastcgi_param SERVER_ADDR $server_addr; 22 | fastcgi_param SERVER_PORT $server_port; 23 | fastcgi_param SERVER_NAME $server_name; 24 | 25 | # PHP only, required if PHP was built with --enable-force-cgi-redirect 26 | fastcgi_param REDIRECT_STATUS 200; 27 | -------------------------------------------------------------------------------- /docker/php/conf/etc/pear.conf: -------------------------------------------------------------------------------- 1 | #PEAR_Config 0.9 2 | a:33:{s:9:"cache_dir";s:15:"/tmp/pear/cache";s:15:"default_channel";s:12:"pear.php.net";s:16:"preferred_mirror";s:12:"pear.php.net";s:13:"remote_config";s:0:"";s:13:"auto_discover";i:0;s:13:"master_server";s:12:"pear.php.net";s:10:"http_proxy";s:0:"";s:7:"php_dir";s:20:"/usr/src/php/lib/php";s:7:"ext_dir";s:57:"/usr/src/php/lib/php/extensions/no-debug-non-zts-20121212";s:7:"doc_dir";s:24:"/usr/src/php/lib/php/doc";s:7:"bin_dir";s:16:"/usr/src/php/bin";s:8:"data_dir";s:25:"/usr/src/php/lib/php/data";s:7:"cfg_dir";s:24:"/usr/src/php/lib/php/cfg";s:7:"www_dir";s:27:"/usr/src/php/lib/php/htdocs";s:7:"man_dir";s:30:"/usr/src/php/lib/php/local/man";s:8:"test_dir";s:25:"/usr/src/php/lib/php/test";s:8:"temp_dir";s:14:"/tmp/pear/temp";s:12:"download_dir";s:18:"/tmp/pear/download";s:7:"php_bin";s:20:"/usr/src/php/bin/php";s:10:"php_prefix";s:0:"";s:10:"php_suffix";s:0:"";s:7:"php_ini";s:0:"";s:12:"metadata_dir";s:0:"";s:8:"username";s:0:"";s:8:"password";s:0:"";s:7:"verbose";i:1;s:15:"preferred_state";s:6:"stable";s:5:"umask";i:18;s:9:"cache_ttl";i:3600;s:8:"sig_type";s:3:"gpg";s:7:"sig_bin";s:12:"/usr/bin/gpg";s:9:"sig_keyid";s:0:"";s:10:"sig_keydir";s:25:"/usr/src/php/etc/pearkeys";} -------------------------------------------------------------------------------- /electron/src/app/public/store.js: -------------------------------------------------------------------------------- 1 | const Vux = require('vuex'); 2 | const commonFunc = require('../../core/commonFunc'); 3 | const electron = window.require('electron'); 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | module.exports = new Vux.Store({ 13 | state: { 14 | fontFamily: localStorage.getItem('fontFamily') || '', 15 | nodeStorage: {}, 16 | title: electron.remote.getGlobal('config').title, 17 | }, 18 | 19 | mutations:{ 20 | updateFontFamily(state){ 21 | state.fontFamily = localStorage.getItem('fontFamily') || ''; 22 | }, 23 | setLocalStorage(state, {key, value}){ 24 | localStorage.setItem(key, value); 25 | 26 | if (Reflect.has(state, key)){ 27 | state[key] = value; 28 | opener.postMessage(key, '*'); 29 | } 30 | }, 31 | setNodeStorage(state, {proto, value}){ 32 | state.nodeStorage = commonFunc.setObjectProto(state.nodeStorage, proto, value); 33 | }, 34 | setTitle(state, value){ 35 | state.title = value; 36 | } 37 | }, 38 | getters:{ 39 | }, 40 | }); 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /docker/nginx/conf/fastcgi.conf: -------------------------------------------------------------------------------- 1 | 2 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 3 | fastcgi_param QUERY_STRING $query_string; 4 | fastcgi_param REQUEST_METHOD $request_method; 5 | fastcgi_param CONTENT_TYPE $content_type; 6 | fastcgi_param CONTENT_LENGTH $content_length; 7 | 8 | fastcgi_param SCRIPT_NAME $fastcgi_script_name; 9 | fastcgi_param REQUEST_URI $request_uri; 10 | fastcgi_param DOCUMENT_URI $document_uri; 11 | fastcgi_param DOCUMENT_ROOT $document_root; 12 | fastcgi_param SERVER_PROTOCOL $server_protocol; 13 | fastcgi_param REQUEST_SCHEME $scheme; 14 | fastcgi_param HTTPS $https if_not_empty; 15 | 16 | fastcgi_param GATEWAY_INTERFACE CGI/1.1; 17 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; 18 | 19 | fastcgi_param REMOTE_ADDR $remote_addr; 20 | fastcgi_param REMOTE_PORT $remote_port; 21 | fastcgi_param SERVER_ADDR $server_addr; 22 | fastcgi_param SERVER_PORT $server_port; 23 | fastcgi_param SERVER_NAME $server_name; 24 | 25 | # PHP only, required if PHP was built with --enable-force-cgi-redirect 26 | fastcgi_param REDIRECT_STATUS 200; 27 | 28 | fastcgi_index index.php; 29 | 30 | -------------------------------------------------------------------------------- /electron/src/core/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // 加载docker类 3 | const IpcMain = require('electron').ipcMain; 4 | const NodeDocker = require('./docker.node.js'); 5 | const {onName} = require('./docker'); 6 | const controllerFunc = require('./controller'); 7 | const LocalEvent = require('./event'); 8 | 9 | exports.LocalEvent = LocalEvent; 10 | 11 | // export * from './docker.node.js' 12 | 13 | 14 | 15 | 16 | IpcMain.on(onName, function (event, arg) { // todo 待改为 function (event, {name:name, arg} = {}) 17 | /** 18 | * arg.name 事件动作名称 19 | * arg.type 事件行为 sync | async 20 | * arg.arg 事件参数 21 | */ 22 | let nd = new NodeDocker(); 23 | nd.event = event; 24 | nd.execDocker(arg.type, arg.name, arg); 25 | // nd = null; 26 | }); 27 | 28 | 29 | IpcMain.on('ipcRenderer', function(event, arg){ 30 | if (arg.name){ 31 | LocalEvent(`ipcRenderer-${arg.name}`, arg, event) 32 | } 33 | }); 34 | 35 | 36 | 37 | 38 | 39 | IpcMain.on('controller', function(event, args){ 40 | if (!args.action){ 41 | return; 42 | } 43 | 44 | event.sender.send('controller-callback', {uniqid: args.uniqid, data: controllerFunc(args.action, args.args, event)}); 45 | }); 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /electron/src/core/counter.js: -------------------------------------------------------------------------------- 1 | // 计数器,可用于判断对象是否更新 2 | 3 | let data = {}; 4 | let emptySymbol = Symbol('Counter'); // 第一次获取数据时 5 | 6 | class Counter { 7 | constructor(name) { 8 | this.name = name; 9 | data[name] || (data[name] = {}); 10 | } 11 | 12 | symbol(key = ''){ // 不能是数组 13 | return Symbol(this.name); 14 | // return Symbol(key?{key, [this.name]:data[this.name][key]}:{[this.name]:data[this.name]}); 15 | } 16 | 17 | // 获取symbolValue 18 | getUpdate(key = ''){ 19 | return (key ? data[this.name][key] : data[this.name]) || emptySymbol; 20 | } 21 | 22 | // 执行更新 23 | update(key = '') { 24 | if (key) { 25 | data[this.name][key] = this.symbol(key); 26 | } else { 27 | data[this.name] = this.symbol(); 28 | } 29 | 30 | return this; 31 | } 32 | 33 | // 判断是否更新,true为已更新 34 | isUpdate(symbolValue, key = ''){ 35 | if (!symbolValue || typeof symbolValue !== 'symbol'){ 36 | return true; 37 | } 38 | 39 | 40 | return key ? ( 41 | data[this.name][key] && data[this.name][key] !== symbolValue 42 | ) : ( 43 | data[this.name] && data[this.name] !== symbolValue 44 | ); 45 | } 46 | } 47 | 48 | module.exports = Counter; -------------------------------------------------------------------------------- /docker/.env: -------------------------------------------------------------------------------- 1 | # docker-compose.yml中的变量 2 | # 根目录的上一级目录 3 | compose_dir=/Users/github 4 | # www目录共享 5 | compose_volumes_base=/Users/:/www/ 6 | 7 | # php的--build-arg 8 | # php版本号 9 | compose_build_php_version=5.5.36 10 | # php编译目录 11 | compose_build_php_configure_dir=/usr/local/php 12 | # php安装目录 13 | compose_build_php_dir=/usr/src/php 14 | # php验证用 15 | compose_build_php_sha256=e1bbe33d6b4da66b15c483131520a9fc505eeb6629fa70c5cfba79590a1d0801 16 | compose_build_php_gpg_keys=0B96609E270F565C13292B24C13C70B87267B52D 0BD78B5F97500D450838F95DFE857D9A90D90EC1 F38252826ACD957EF380D39F2F7956BC5DA04B5D 17 | # php的编译参数 18 | compose_build_php_configure_args=--enable-fpm 19 | # 并发安装扩展的数量 20 | compose_build_php_processes_num=15 21 | # php的apt install 22 | compose_build_php_apt_install=libxml2-dev xz-utils libpng-dev libjpeg-dev 23 | 24 | 25 | # nginx的--build-arg 26 | # nginx版本号 27 | compose_build_nginx_version=1.10.1 28 | # 安装nginx的目录 29 | compose_build_nginx_dir=/usr/src/nginx 30 | # nginx的编译参数 31 | compose_build_nginx_args= --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_geoip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_perl_module --with-http_realip_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_xslt_module --with-ipv6 --with-mail --with-mail_ssl_module 32 | 33 | 34 | 35 | # base的--build-arg 36 | compose_build_base_zone=PRC -------------------------------------------------------------------------------- /electron/src/core/renderSessionStorage.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | // 如果改成Proxy应该可以保证onStorage事件触发 5 | class renderSessionStorage{ 6 | 7 | 8 | constructor() { 9 | let tempData = {}; 10 | this.key = 'renderSession'; 11 | this.startTime = window.appStartTime; 12 | try{ 13 | tempData = JSON.parse(localStorage.getItem(this.key)); 14 | }catch (e){ 15 | } 16 | 17 | this.data = Object.assign({ 18 | data: {}, 19 | time: this.startTime, 20 | }, tempData || {}); 21 | 22 | if (this.data.time !== this.startTime){ // 不是一次打开 23 | this.clear(); 24 | } 25 | } 26 | 27 | getItem(key){ 28 | return this.data.data[key]; 29 | } 30 | 31 | setItem(key, value){ 32 | if (key === null){ // 清空 33 | this.data.data = (value || {}); 34 | }else{ // 赋值 35 | this.data.data[key] = value; 36 | } 37 | 38 | localStorage.setItem(this.key, JSON.stringify(this.data)); 39 | 40 | return this; 41 | } 42 | 43 | clear(){ 44 | return this.data = { 45 | data: {}, 46 | time: this.startTime, 47 | }, 48 | this.setItem(null, {}), this; 49 | } 50 | 51 | } 52 | 53 | 54 | 55 | 56 | // export default new renderSessionStorage(); 57 | module.exports = new renderSessionStorage(); 58 | -------------------------------------------------------------------------------- /electron/src/app/setup.vue: -------------------------------------------------------------------------------- 1 | 7 | 12 | 50 | -------------------------------------------------------------------------------- /electron/src/webpack/webpack.server.js: -------------------------------------------------------------------------------- 1 | const config = require('../config/config'); 2 | const webpack = require('webpack'); 3 | const webpackConfig = require('./webpack.config'); 4 | const webpackDevServer = require('webpack-dev-server'); 5 | let webpackDevServerConfig = config.webpackDevServer; 6 | 7 | 8 | webpackConfig.plugins.unshift(new webpack.HotModuleReplacementPlugin()); // 加入热替换 9 | webpackConfig.output.publicPath = webpackDevServerConfig.publicPath; 10 | 11 | new webpackDevServer(webpack(webpackConfig), { 12 | publicPath: webpackDevServerConfig.publicPath, // 和webpackConfig.output.publicPath一样 13 | hot: true, 14 | inline: true, 15 | // quiet: true, // lets WebpackDashboard do its thing 静默状态 16 | stats: {colors: true}, 17 | }).listen(webpackDevServerConfig.port, '127.0.0.1', function (error, result) { 18 | if (error) { 19 | console.error(error); 20 | } 21 | 22 | console.log('监听 ' + webpackDevServerConfig.url); 23 | }); 24 | 25 | 26 | 27 | 28 | 29 | 30 | // const exec = require('child_process'); 31 | // exec.execSync('webpack-dev-server --inline --config ./webpack/webpack.config.js --port 7777 --hot --colors --content-base src/temp '); 32 | 33 | 34 | // let command = `webpack --config ./src/webpack/webpack.config.js 35 | // --display-error-details --watch`; 36 | // 37 | // 38 | // exec('webpack-dev-server --help', {}, function (error, stdout, stderr) { 39 | // // console.log(error, stdout, stderr,1); 40 | // }); 41 | // 42 | // console.log(config.color.colorFormat(command)); 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /docker/php/conf/conf/fdfs.client.conf: -------------------------------------------------------------------------------- 1 | # connect timeout in seconds 2 | # default value is 30s 3 | connect_timeout=30 4 | 5 | # network timeout in seconds 6 | # default value is 30s 7 | network_timeout=60 8 | 9 | # the base path to store log files 10 | base_path=/home/yuqing/fastdfs 11 | 12 | # tracker_server can ocur more than once, and tracker_server format is 13 | # "host:port", host can be hostname or ip address 14 | tracker_server=192.168.0.197:22122 15 | 16 | #standard log level as syslog, case insensitive, value list: 17 | ### emerg for emergency 18 | ### alert 19 | ### crit for critical 20 | ### error 21 | ### warn for warning 22 | ### notice 23 | ### info 24 | ### debug 25 | log_level=info 26 | 27 | # if use connection pool 28 | # default value is false 29 | # since V4.05 30 | use_connection_pool = false 31 | 32 | # connections whose the idle time exceeds this time will be closed 33 | # unit: second 34 | # default value is 3600 35 | # since V4.05 36 | connection_pool_max_idle_time = 3600 37 | 38 | # if load FastDFS parameters from tracker server 39 | # since V4.05 40 | # default value is false 41 | load_fdfs_parameters_from_tracker=false 42 | 43 | # if use storage ID instead of IP address 44 | # same as tracker.conf 45 | # valid only when load_fdfs_parameters_from_tracker is false 46 | # default value is false 47 | # since V4.05 48 | use_storage_id = false 49 | 50 | # specify storage ids filename, can use relative or absolute path 51 | # same as tracker.conf 52 | # valid only when load_fdfs_parameters_from_tracker is false 53 | # since V4.05 54 | storage_ids_filename = storage_ids.conf 55 | 56 | 57 | #HTTP settings 58 | http.tracker_server_port=80 59 | 60 | #use "#include" directive to include HTTP other settiongs 61 | ##include http.conf 62 | 63 | -------------------------------------------------------------------------------- /electron/src/core/nodeStorage.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const commonFunc = require('./commonFunc'); 3 | const Counter = require('./counter'); 4 | let CounterClass = new Counter('nodeStorage'); 5 | 6 | let options = { 7 | encoding: 'utf8', 8 | flag: 'r', 9 | }, 10 | queue = commonFunc.queueAsync(), 11 | data = {}; 12 | 13 | 14 | 15 | 16 | try{ 17 | data = fs.readFileSync(config.nodeStoragePath, options); 18 | data = (data && JSON.parse(data)) || {}; 19 | }catch(error){ 20 | throw `${config.nodeStoragePath}文件不是正确的json文件或没有读取权限`; 21 | } 22 | 23 | options.flag = 'w+'; // 前面不需要写入 24 | 25 | 26 | module.exports = { 27 | getItem(proto, defaultData = ''){ 28 | return commonFunc.getObjectProto(data, proto, defaultData); 29 | }, 30 | setItem(proto, value){ 31 | proto = (typeof proto === 'string'? proto.split('.') : proto) || []; 32 | if (proto[0]){ 33 | CounterClass.update(proto[0]); // 只处理一层 34 | data = commonFunc.setObjectProto(data, proto, value); 35 | 36 | queue(function (func) { 37 | fs.writeFile( 38 | config.nodeStoragePath, 39 | 1 ? JSON.stringify(data, null, '\t') : JSON.stringify(data), // 是否美化json 40 | options, 41 | function () { 42 | func(); 43 | func = null; 44 | } 45 | ); 46 | }, true); 47 | } 48 | }, 49 | // 判断返回值是否相等 50 | isUpdate(symbolValue, key){ 51 | return CounterClass.isUpdate(symbolValue, key); 52 | }, 53 | getUpdate(key){ 54 | return CounterClass.getUpdate(key); 55 | } 56 | }; -------------------------------------------------------------------------------- /electron/src/resources/terminal: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | CMD="clear && $*" 5 | 6 | ITERM_EXISTS=`osascript < /dev/null < /dev/null < /dev/null < 2 |
3 | 11 |
12 | 13 | 36 | 69 | -------------------------------------------------------------------------------- /electron/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docker_lnmp", 3 | "version": "0.1.0", 4 | "description": "docker_lnmp gui on account of the electron", 5 | "author": "https://github.com/aogg", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/aogg/docker_lnmp.git" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/aogg/docker_lnmp/issues" 12 | }, 13 | "license": "Apache-2.0", 14 | "main": "main.js", 15 | "scripts": { 16 | "start": "electron .", 17 | "debug": "electron . --debug -无效", 18 | "server": "node ./src/webpack/webpack.server.js", 19 | "build": "webpack --config ./src/webpack/webpack.config.js", 20 | "watch": "webpack --config ./src/webpack/webpack.config.js -w", 21 | "build-ddl": "webpack --progress --colors --config ./src/webpack/ddl.config.js", 22 | "pack:help": "build --help", 23 | "pack": "build --dir", 24 | "dist": "build", 25 | "postinstall": "cd src && npm install" 26 | }, 27 | "build": { 28 | "appId": "com.electron.aogg.docker_lnmp", 29 | "asar": [ 30 | "./src/*", 31 | "asar相关无效" 32 | ], 33 | "asarUnpack": [ 34 | "!./src/*" 35 | ], 36 | "directories": { 37 | "output": "app/dist" 38 | }, 39 | "files": [ 40 | "!.vscode/*", 41 | "!node_modules/*" 42 | ], 43 | "extraResources": [ 44 | { 45 | "from": "../docker", 46 | "to": "docker" 47 | } 48 | ], 49 | "win": { 50 | "icon": "", 51 | "target": "7z" 52 | }, 53 | "nsis": {}, 54 | "mac": { 55 | "category": "public.app.category.utilities", 56 | "icon": "" 57 | }, 58 | "linux": { 59 | "icon": "" 60 | } 61 | }, 62 | "devDependencies": { 63 | "babel-cli": "^6.26.0", 64 | "babel-core": "^6.26.3", 65 | "babel-loader": "^7.1.5", 66 | "babel-plugin-transform-runtime": "^6.23.0", 67 | "babel-preset-env": "^1.7.0", 68 | "babel-preset-stage-0": "^6.24.1", 69 | "colors": "^1.3.1", 70 | "css-loader": "^1.0.0", 71 | "electron": "^2.0.8", 72 | "electron-builder": "^20.28.1", 73 | "electron-packager": "^12.1.0", 74 | "html-webpack-plugin": "^3.2.0", 75 | "mini-css-extract-plugin": "^0.4.2", 76 | "style-loader": "^0.22.1", 77 | "vue": "^2.5.17", 78 | "vue-devtools": "^4.1.4", 79 | "vue-hot-reload-api": "^2.3.0", 80 | "vue-html-loader": "^1.2.4", 81 | "vue-loader": "^15.4.0", 82 | "vue-router": "^3.0.1", 83 | "vue-style-loader": "^4.1.2", 84 | "vue-template-compiler": "^2.5.17", 85 | "vuex": "^3.0.1", 86 | "webpack": "^4.16.5", 87 | "webpack-command": "^0.4.1", 88 | "webpack-dev-server": "^3.1.5" 89 | }, 90 | "__npminstall_done": false 91 | } 92 | -------------------------------------------------------------------------------- /docker/nginx/conf/koi-win: -------------------------------------------------------------------------------- 1 | 2 | charset_map koi8-r windows-1251 { 3 | 4 | 80 88 ; # euro 5 | 6 | 95 95 ; # bullet 7 | 8 | 9A A0 ; #   9 | 10 | 9E B7 ; # · 11 | 12 | A3 B8 ; # small yo 13 | A4 BA ; # small Ukrainian ye 14 | 15 | A6 B3 ; # small Ukrainian i 16 | A7 BF ; # small Ukrainian yi 17 | 18 | AD B4 ; # small Ukrainian soft g 19 | AE A2 ; # small Byelorussian short u 20 | 21 | B0 B0 ; # ° 22 | 23 | B3 A8 ; # capital YO 24 | B4 AA ; # capital Ukrainian YE 25 | 26 | B6 B2 ; # capital Ukrainian I 27 | B7 AF ; # capital Ukrainian YI 28 | 29 | B9 B9 ; # numero sign 30 | 31 | BD A5 ; # capital Ukrainian soft G 32 | BE A1 ; # capital Byelorussian short U 33 | 34 | BF A9 ; # (C) 35 | 36 | C0 FE ; # small yu 37 | C1 E0 ; # small a 38 | C2 E1 ; # small b 39 | C3 F6 ; # small ts 40 | C4 E4 ; # small d 41 | C5 E5 ; # small ye 42 | C6 F4 ; # small f 43 | C7 E3 ; # small g 44 | C8 F5 ; # small kh 45 | C9 E8 ; # small i 46 | CA E9 ; # small j 47 | CB EA ; # small k 48 | CC EB ; # small l 49 | CD EC ; # small m 50 | CE ED ; # small n 51 | CF EE ; # small o 52 | 53 | D0 EF ; # small p 54 | D1 FF ; # small ya 55 | D2 F0 ; # small r 56 | D3 F1 ; # small s 57 | D4 F2 ; # small t 58 | D5 F3 ; # small u 59 | D6 E6 ; # small zh 60 | D7 E2 ; # small v 61 | D8 FC ; # small soft sign 62 | D9 FB ; # small y 63 | DA E7 ; # small z 64 | DB F8 ; # small sh 65 | DC FD ; # small e 66 | DD F9 ; # small shch 67 | DE F7 ; # small ch 68 | DF FA ; # small hard sign 69 | 70 | E0 DE ; # capital YU 71 | E1 C0 ; # capital A 72 | E2 C1 ; # capital B 73 | E3 D6 ; # capital TS 74 | E4 C4 ; # capital D 75 | E5 C5 ; # capital YE 76 | E6 D4 ; # capital F 77 | E7 C3 ; # capital G 78 | E8 D5 ; # capital KH 79 | E9 C8 ; # capital I 80 | EA C9 ; # capital J 81 | EB CA ; # capital K 82 | EC CB ; # capital L 83 | ED CC ; # capital M 84 | EE CD ; # capital N 85 | EF CE ; # capital O 86 | 87 | F0 CF ; # capital P 88 | F1 DF ; # capital YA 89 | F2 D0 ; # capital R 90 | F3 D1 ; # capital S 91 | F4 D2 ; # capital T 92 | F5 D3 ; # capital U 93 | F6 C6 ; # capital ZH 94 | F7 C2 ; # capital V 95 | F8 DC ; # capital soft sign 96 | F9 DB ; # capital Y 97 | FA C7 ; # capital Z 98 | FB D8 ; # capital SH 99 | FC DD ; # capital E 100 | FD D9 ; # capital SHCH 101 | FE D7 ; # capital CH 102 | FF DA ; # capital hard sign 103 | } 104 | -------------------------------------------------------------------------------- /electron/src/app/public/header.vue: -------------------------------------------------------------------------------- 1 | 23 | 52 | 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 基于docker的LNMP环境 2 | [![npm](https://img.shields.io/badge/npm-passing-brightgreen.svg)](https://www.npmjs.com/) [![npm](https://img.shields.io/badge/electron-passing-brightgreen.svg)](https://electron.atom.io/) [![npm](https://img.shields.io/badge/docker-passing-brightgreen.svg)](https://www.docker.com/) [![npm](https://img.shields.io/badge/vue-passing-brightgreen.svg)](https://cn.vuejs.org/) [![npm](https://img.shields.io/badge/node-passing-brightgreen.svg)]() 3 | [![npm](https://img.shields.io/badge/webpack-passing-brightgreen.svg)]() [![npm](https://img.shields.io/badge/babel-passing-brightgreen.svg)]() 4 | 5 | 6 | ## 说明 7 | 一个基于[docker](https://www.docker.com/)的LNMP环境,并利用[electron](https://electron.atom.io/) + [vue](https://cn.vuejs.org/) 提供gui管理(目前只处理了window环境和mac环境,后续支持linux) 8 | 9 |
其中gui界面通过[electron](https://electron.atom.io/)+[webpack](http://webpack.github.io/)+[vue](https://cn.vuejs.org/)+[babel](http://babeljs.cn/)实现。 10 | 11 | 12 | ## 使用 13 | - npm打开 14 | ```bash 15 | git clone https://github.com/aogg/docker_lnmp.git 16 | cd electron 17 | npm install --registry=https://registry.npm.taobao.org 18 | npm start 19 | ``` 20 | 21 | - 首次点击构建,构建docker容器 22 | ![github](https://raw.githubusercontent.com/aogg/image_repository/master/docker_lnmp/首次点击构建.png "首次打开点击构建") 23 | 24 | - 再点击启动按钮,启动php环境 25 | - 其中xdebug需要额外配置127.0.0.1 host_localhost的hosts配置 26 | 27 | ## 容器配置 28 | 各容器放在[docker](docker)文件夹内,对应配置也在容器文件夹的conf文件夹 29 | 其中php、nginx的conf文件夹实现共享目录,可本地修改并在容器内及时体现出 30 | 如: 31 | 1、[docker/php/conf/conf/php.ini](docker/php/conf/conf/php.ini) 32 | 2、[docker/php/conf/etc/php-fpm.conf](docker/php/conf/etc/php-fpm.conf) 33 | 3、[docker/nginx/conf/nginx.conf](docker/nginx/conf/nginx.conf) 34 | 35 | 其中sources.list是通过COPY过去,所以如要修改必须重新构建所有容器 36 | 37 | 38 |
39 | 40 | ## 多进程安装扩展 41 | - 1、PHP扩展安装相关目录为[docker/php/src/](docker/php/src),对应容器内路径为/usr/local/php-ext/。 42 | 43 | - 2、[config.json](docker/php/src/config.json)为所有扩展的配置文件 44 | 可配置参数: 45 | ```json 46 | { 47 | "EXT_INSTALL" : "是否安装", 48 | "EXT_NAME" : "扩展名称", 49 | "EXT_URL" : "扩展下载地址", 50 | "EXT_TGZ_DIR" : "不下载直接用本地目录", 51 | "EXT_DEPEND" : "扩展对应依赖", 52 | "EXT_ARG" : "扩展编译时参数", 53 | "EXT_EVAL" : "扩展下载完成后执行的代码", 54 | "EXT_DESC" : "扩展描述" 55 | }, 56 | ``` 57 | - 3、[install.json](docker/php/src/install.json)为本次构建(build)时需要安装的扩展 58 | - 4、[php-ext.sh](docker/php/src/php-ext.sh)为安装PHP扩展的核心多线程shell脚本。平时在容器内可通过下面方式直接安装指定的多个扩展
59 | ```shell 60 | bash /usr/local/php-ext.sh memcached-2.2.0 xdebug-2.4.0 61 | ``` 62 | - 5、最大并发安装PHP扩展的数量,默认值为15。 63 | 64 | 65 | 66 | 67 |
68 | 69 | ## 打包 70 | 放在electron/app/dist目录下 71 | ``` 72 | npm run pack 73 | ``` 74 | 75 | 76 |
77 | 78 | ## 各截图 79 | 80 | > 主界面截图 81 | ![主界面截图](https://cdn.jsdelivr.net/gh/aogg/image_repository@master/docker_lnmp/%E9%A6%96%E6%AC%A1%E7%82%B9%E5%87%BB%E6%9E%84%E5%BB%BA.png "主界面截图") 82 | 83 | 84 | > mini安装[多个扩展](https://github.com/aogg/docker_lnmp/blob/a716e496d59bf408804cda1e10b970af387a62bf/docker/php/src/install.json)时间 85 | ![github](https://raw.githubusercontent.com/aogg/image_repository/master/docker_lnmp/mini%E5%AE%89%E8%A3%85%E6%89%A9%E5%B1%95%E6%97%B6%E9%97%B4.png "mini安装扩展时间") 86 | 87 | 88 | -------------------------------------------------------------------------------- /docker/nginx/conf/nginx.conf.default: -------------------------------------------------------------------------------- 1 | 2 | #user nobody; 3 | worker_processes 1; 4 | 5 | #error_log logs/error.log; 6 | #error_log logs/error.log notice; 7 | #error_log logs/error.log info; 8 | 9 | #pid logs/nginx.pid; 10 | 11 | 12 | events { 13 | worker_connections 1024; 14 | } 15 | 16 | 17 | http { 18 | include mime.types; 19 | default_type application/octet-stream; 20 | 21 | #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 22 | # '$status $body_bytes_sent "$http_referer" ' 23 | # '"$http_user_agent" "$http_x_forwarded_for"'; 24 | 25 | #access_log logs/access.log main; 26 | 27 | sendfile on; 28 | #tcp_nopush on; 29 | 30 | #keepalive_timeout 0; 31 | keepalive_timeout 65; 32 | 33 | #gzip on; 34 | 35 | server { 36 | listen 80; 37 | server_name localhost; 38 | 39 | #charset koi8-r; 40 | 41 | #access_log logs/host.access.log main; 42 | 43 | location / { 44 | root html; 45 | index index.html index.htm; 46 | } 47 | 48 | #error_page 404 /404.html; 49 | 50 | # redirect server error pages to the static page /50x.html 51 | # 52 | error_page 500 502 503 504 /50x.html; 53 | location = /50x.html { 54 | root html; 55 | } 56 | 57 | # proxy the PHP scripts to Apache listening on 127.0.0.1:80 58 | # 59 | #location ~ \.php$ { 60 | # proxy_pass http://127.0.0.1; 61 | #} 62 | 63 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 64 | # 65 | #location ~ \.php$ { 66 | # root html; 67 | # fastcgi_pass 127.0.0.1:9000; 68 | # fastcgi_index index.php; 69 | # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; 70 | # include fastcgi_params; 71 | #} 72 | 73 | # deny access to .htaccess files, if Apache's document root 74 | # concurs with nginx's one 75 | # 76 | #location ~ /\.ht { 77 | # deny all; 78 | #} 79 | } 80 | 81 | 82 | # another virtual host using mix of IP-, name-, and port-based configuration 83 | # 84 | #server { 85 | # listen 8000; 86 | # listen somename:8080; 87 | # server_name somename alias another.alias; 88 | 89 | # location / { 90 | # root html; 91 | # index index.html index.htm; 92 | # } 93 | #} 94 | 95 | 96 | # HTTPS server 97 | # 98 | #server { 99 | # listen 443 ssl; 100 | # server_name localhost; 101 | 102 | # ssl_certificate cert.pem; 103 | # ssl_certificate_key cert.key; 104 | 105 | # ssl_session_cache shared:SSL:1m; 106 | # ssl_session_timeout 5m; 107 | 108 | # ssl_ciphers HIGH:!aNULL:!MD5; 109 | # ssl_prefer_server_ciphers on; 110 | 111 | # location / { 112 | # root html; 113 | # index index.html index.htm; 114 | # } 115 | #} 116 | 117 | } 118 | -------------------------------------------------------------------------------- /docker/nginx/conf/koi-utf: -------------------------------------------------------------------------------- 1 | 2 | # This map is not a full koi8-r <> utf8 map: it does not contain 3 | # box-drawing and some other characters. Besides this map contains 4 | # several koi8-u and Byelorussian letters which are not in koi8-r. 5 | # If you need a full and standard map, use contrib/unicode2nginx/koi-utf 6 | # map instead. 7 | 8 | charset_map koi8-r utf-8 { 9 | 10 | 80 E282AC ; # euro 11 | 12 | 95 E280A2 ; # bullet 13 | 14 | 9A C2A0 ; #   15 | 16 | 9E C2B7 ; # · 17 | 18 | A3 D191 ; # small yo 19 | A4 D194 ; # small Ukrainian ye 20 | 21 | A6 D196 ; # small Ukrainian i 22 | A7 D197 ; # small Ukrainian yi 23 | 24 | AD D291 ; # small Ukrainian soft g 25 | AE D19E ; # small Byelorussian short u 26 | 27 | B0 C2B0 ; # ° 28 | 29 | B3 D081 ; # capital YO 30 | B4 D084 ; # capital Ukrainian YE 31 | 32 | B6 D086 ; # capital Ukrainian I 33 | B7 D087 ; # capital Ukrainian YI 34 | 35 | B9 E28496 ; # numero sign 36 | 37 | BD D290 ; # capital Ukrainian soft G 38 | BE D18E ; # capital Byelorussian short U 39 | 40 | BF C2A9 ; # (C) 41 | 42 | C0 D18E ; # small yu 43 | C1 D0B0 ; # small a 44 | C2 D0B1 ; # small b 45 | C3 D186 ; # small ts 46 | C4 D0B4 ; # small d 47 | C5 D0B5 ; # small ye 48 | C6 D184 ; # small f 49 | C7 D0B3 ; # small g 50 | C8 D185 ; # small kh 51 | C9 D0B8 ; # small i 52 | CA D0B9 ; # small j 53 | CB D0BA ; # small k 54 | CC D0BB ; # small l 55 | CD D0BC ; # small m 56 | CE D0BD ; # small n 57 | CF D0BE ; # small o 58 | 59 | D0 D0BF ; # small p 60 | D1 D18F ; # small ya 61 | D2 D180 ; # small r 62 | D3 D181 ; # small s 63 | D4 D182 ; # small t 64 | D5 D183 ; # small u 65 | D6 D0B6 ; # small zh 66 | D7 D0B2 ; # small v 67 | D8 D18C ; # small soft sign 68 | D9 D18B ; # small y 69 | DA D0B7 ; # small z 70 | DB D188 ; # small sh 71 | DC D18D ; # small e 72 | DD D189 ; # small shch 73 | DE D187 ; # small ch 74 | DF D18A ; # small hard sign 75 | 76 | E0 D0AE ; # capital YU 77 | E1 D090 ; # capital A 78 | E2 D091 ; # capital B 79 | E3 D0A6 ; # capital TS 80 | E4 D094 ; # capital D 81 | E5 D095 ; # capital YE 82 | E6 D0A4 ; # capital F 83 | E7 D093 ; # capital G 84 | E8 D0A5 ; # capital KH 85 | E9 D098 ; # capital I 86 | EA D099 ; # capital J 87 | EB D09A ; # capital K 88 | EC D09B ; # capital L 89 | ED D09C ; # capital M 90 | EE D09D ; # capital N 91 | EF D09E ; # capital O 92 | 93 | F0 D09F ; # capital P 94 | F1 D0AF ; # capital YA 95 | F2 D0A0 ; # capital R 96 | F3 D0A1 ; # capital S 97 | F4 D0A2 ; # capital T 98 | F5 D0A3 ; # capital U 99 | F6 D096 ; # capital ZH 100 | F7 D092 ; # capital V 101 | F8 D0AC ; # capital soft sign 102 | F9 D0AB ; # capital Y 103 | FA D097 ; # capital Z 104 | FB D0A8 ; # capital SH 105 | FC D0AD ; # capital E 106 | FD D0A9 ; # capital SHCH 107 | FE D0A7 ; # capital CH 108 | FF D0AA ; # capital hard sign 109 | } 110 | -------------------------------------------------------------------------------- /docker/nginx/conf/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | #user nobody; 3 | worker_processes 1; 4 | 5 | #error_log logs/error.log; 6 | #error_log logs/error.log notice; 7 | #error_log logs/error.log info; 8 | 9 | #pid logs/nginx.pid; 10 | 11 | events { 12 | use epoll; 13 | worker_connections 1024; 14 | } 15 | 16 | 17 | http { 18 | charset utf-8; 19 | default_type application/octet-stream; 20 | 21 | # 日志文件记录内容 22 | # log_format www_access '$http_x_forwarded_for - $remote_user [$time_local] "$request" ' 23 | # '$status $body_bytes_sent "$http_referer" ' 24 | # '"$http_user_agent" $remote_addr'; 25 | 26 | 27 | #access_log logs/access.log main; 28 | 29 | sendfile on; 30 | #tcp_nopush on; 31 | 32 | keepalive_timeout 0; 33 | # keepalive_timeout 65; 34 | 35 | #gzip on; 36 | 37 | 38 | 39 | autoindex on;# 显示目录 40 | autoindex_exact_size on;# 显示文件大小 41 | autoindex_localtime on;# 显示文件时间 42 | 43 | 44 | 45 | 46 | 47 | include mime.types; 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | server { 62 | listen 80; 63 | server_name localhost; 64 | root /www; 65 | 66 | #charset koi8-r; 67 | 68 | #access_log logs/host.access.log main; 69 | 70 | location / { 71 | index index.html index.htm index.php; 72 | } 73 | 74 | #error_page 404 /404.html; 75 | 76 | # redirect server error pages to the static page /50x.html 77 | # 78 | # error_page 500 502 503 504 /50x.html; 79 | # location = /50x.html { 80 | # root /www; 81 | # } 82 | 83 | # proxy the PHP scripts to Apache listening on 127.0.0.1:80 84 | # 85 | #location ~ \.php$ { 86 | # proxy_pass http://127.0.0.1; 87 | #} 88 | 89 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 90 | # 91 | location ~ \.php$ { 92 | fastcgi_pass php:9000; 93 | fastcgi_index index.php; 94 | fastcgi_param SCRIPT_FILENAME /www$fastcgi_script_name; 95 | include fastcgi_params; 96 | } 97 | 98 | 99 | # deny access to .htaccess files, if Apache's document root 100 | # concurs with nginx's one 101 | # 102 | #location ~ /\.ht { 103 | # deny all; 104 | #} 105 | } 106 | 107 | 108 | 109 | # 站点配置在更目录后面 110 | include vhost/*; 111 | 112 | 113 | # another virtual host using mix of IP-, name-, and port-based configuration 114 | # 115 | #server { 116 | # listen 8000; 117 | # listen somename:8080; 118 | # server_name somename alias another.alias; 119 | 120 | # location / { 121 | # root html; 122 | # index index.html index.htm; 123 | # } 124 | #} 125 | 126 | 127 | # HTTPS server 128 | # 129 | #server { 130 | # listen 443 ssl; 131 | # server_name localhost; 132 | 133 | # ssl_certificate cert.pem; 134 | # ssl_certificate_key cert.key; 135 | 136 | # ssl_session_cache shared:SSL:1m; 137 | # ssl_session_timeout 5m; 138 | 139 | # ssl_ciphers HIGH:!aNULL:!MD5; 140 | # ssl_prefer_server_ciphers on; 141 | 142 | # location / { 143 | # root html; 144 | # index index.html index.htm; 145 | # } 146 | #} 147 | 148 | } 149 | -------------------------------------------------------------------------------- /electron/src/app/public/container/compose.vue: -------------------------------------------------------------------------------- 1 | 11 | 19 | 96 | -------------------------------------------------------------------------------- /docker/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM my-ubuntu:14.04 2 | #此images的tags为my-nginx 3 | #安装在/usr/src/nginx 4 | # see:https://github.com/docker-library/nginx/blob/master/1.7/Dockerfile 5 | # 原nginx的www:/usr/src/nginx/html 6 | # docker run --name nginx -p 80:80 --volumes-from share_volume -v /Users/github/docker_lnmp/nginx/conf/:/usr/src/nginx/conf/ --link php-5.5:php -d my-nginx 7 | 8 | MAINTAINER aogg 9 | 10 | # -y为如果提示yes/no,默认yes 11 | RUN apt update && apt install -y --no-install-recommends \ 12 | libgpg-error0 \ 13 | perl \ 14 | perl-modules \ 15 | xml-core \ 16 | # zlib \ 17 | # zlib-devel \ 18 | libssl1.0.0 \ 19 | # openssl \ 20 | # openssl-devel \ 21 | # prce \ 22 | # prce-devel \ 23 | # nginx必备依赖 24 | libpcre3 \ 25 | libpcre3-dev \ 26 | 27 | libxslt-dev libgd2-dev libgeoip-dev geoip-database libperl-dev \ 28 | && rm -rf /var/lib/apt/lists/* 29 | 30 | ARG NGINX_VERSION 31 | ARG NGINX_DIR 32 | ARG NGINX_ARGS 33 | 34 | ENV NGINX_VERSION ${NGINX_VERSION:-'1.10.1'} 35 | # 注意最后不要有斜线/ 36 | ENV NGINX_DIR ${NGINX_DIR:-'/usr/src/nginx'} 37 | ENV NGINX_ARGS ${NGINX_ARGS:-''} 38 | 39 | 40 | 41 | ENV NGINX_CONF_DIR $NGINX_DIR/conf 42 | 43 | 44 | # see http://nginx.org/en/pgp_keys.html 45 | # WARNING: options in `/root/.gnupg/gpg.conf' are not yet active during this run 46 | # RUN gpg --keyserver pgp.mit.edu --recv-key \ 47 | # A09CD539B8BB8CBE96E82BDFABD4D3B3F5806B4D \ 48 | # 4C2C85E705DC730833990C38A9376139A524C53E \ 49 | # B0F4253373F8F6F510D42178520A9993A1C052F8 \ 50 | # 65506C02EFC250F1B7A3D694ECF0E90B2C172083 \ 51 | # 7338973069ED3F443F4D37DFA64FD5B17ADB39A8 \ 52 | # 6E067260B83DCF2CA93C566F518509686C7E5E82 \ 53 | # 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 54 | 55 | 56 | 57 | # All our runtime and build dependencies, in alphabetical order (to ease maintenance) 原: 58 | # 分开,上面可使用缓存 59 | RUN set -x \ 60 | # nginx编译文件路径 61 | && nginx_configure_dir='/usr/local/nginx' \ 62 | && curl -SL "http://nginx.org/download/nginx-$NGINX_VERSION.tar.gz" -o nginx.tar.gz \ 63 | # && curl -SL "http://nginx.org/download/nginx-$NGINX_VERSION.tar.gz.asc" -o nginx.tar.gz.asc \ 64 | # 校验 65 | # && gpg --verify nginx.tar.gz.asc \ 66 | 67 | # 递归创建目录 68 | && mkdir -p $NGINX_DIR $nginx_configure_dir \ 69 | # 解压在/usr/local/nginx 70 | && tar -xvf nginx.tar.gz -C /usr/local/nginx/ --strip-components=1 \ 71 | && rm nginx.tar.gz* \ 72 | # 进入容器后仍然会保持在当前目录 73 | && cd $nginx_configure_dir \ 74 | # 编译nginx参数,各模块的依赖可看官网 @see http://nginx.org/en/docs/ 75 | && ./configure \ 76 | --prefix=$NGINX_DIR \ 77 | # --sbin-path=/usr/sbin/nginx 78 | # 配置文件路径 79 | --conf-path=$NGINX_CONF_DIR/nginx.conf \ 80 | # 访问成功日志 81 | --http-log-path=/proc/self/fd/1 \ 82 | # 失败日志 83 | --error-log-path=/proc/self/fd/2 \ 84 | # 可以不用,其中此路径是openssl的压缩包解压文件夹 85 | # --with-openssl=/usr/src/nginx/openssl/ \ 86 | ${NGINX_ARGS} \ 87 | 88 | 89 | # --with-http_addition_module \ 90 | # --with-http_auth_request_module \ 91 | # --with-http_dav_module \ 92 | # # 依赖:GeoIP(libgeoip-dev,geoip-database) 93 | # --with-http_geoip_module \ 94 | # --with-http_gzip_static_module \ 95 | # # 实时缩放图片,旋转图片,验证图片等,需要gd库,网站访问量小最好 96 | # --with-http_image_filter_module \ 97 | # --with-http_perl_module \ 98 | # --with-http_realip_module \ 99 | # --with-http_ssl_module \ 100 | # --with-http_stub_status_module \ 101 | # --with-http_sub_module \ 102 | # --with-http_xslt_module \ 103 | # --with-ipv6 \ 104 | # --with-mail \ 105 | # --with-mail_ssl_module \ 106 | # --with-pcre-jit \ 107 | && make -j"$(nproc)" \ 108 | && make install 109 | 110 | 111 | ENV PATH $NGINX_DIR/sbin:$PATH 112 | 113 | 114 | WORKDIR $NGINX_DIR 115 | 116 | 117 | # TODO USER www-data 118 | 119 | EXPOSE 80 443 120 | 121 | # 保证nginx运行在共享目录后 122 | VOLUME ["${NGINX_CONF_DIR}"] 123 | 124 | # 让nginx在前台运行 125 | ENTRYPOINT ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /docker/nginx/conf/win-utf: -------------------------------------------------------------------------------- 1 | 2 | # This map is not a full windows-1251 <> utf8 map: it does not 3 | # contain Serbian and Macedonian letters. If you need a full map, 4 | # use contrib/unicode2nginx/win-utf map instead. 5 | 6 | charset_map windows-1251 utf-8 { 7 | 8 | 82 E2809A ; # single low-9 quotation mark 9 | 10 | 84 E2809E ; # double low-9 quotation mark 11 | 85 E280A6 ; # ellipsis 12 | 86 E280A0 ; # dagger 13 | 87 E280A1 ; # double dagger 14 | 88 E282AC ; # euro 15 | 89 E280B0 ; # per mille 16 | 17 | 91 E28098 ; # left single quotation mark 18 | 92 E28099 ; # right single quotation mark 19 | 93 E2809C ; # left double quotation mark 20 | 94 E2809D ; # right double quotation mark 21 | 95 E280A2 ; # bullet 22 | 96 E28093 ; # en dash 23 | 97 E28094 ; # em dash 24 | 25 | 99 E284A2 ; # trade mark sign 26 | 27 | A0 C2A0 ; #   28 | A1 D18E ; # capital Byelorussian short U 29 | A2 D19E ; # small Byelorussian short u 30 | 31 | A4 C2A4 ; # currency sign 32 | A5 D290 ; # capital Ukrainian soft G 33 | A6 C2A6 ; # borken bar 34 | A7 C2A7 ; # section sign 35 | A8 D081 ; # capital YO 36 | A9 C2A9 ; # (C) 37 | AA D084 ; # capital Ukrainian YE 38 | AB C2AB ; # left-pointing double angle quotation mark 39 | AC C2AC ; # not sign 40 | AD C2AD ; # soft hypen 41 | AE C2AE ; # (R) 42 | AF D087 ; # capital Ukrainian YI 43 | 44 | B0 C2B0 ; # ° 45 | B1 C2B1 ; # plus-minus sign 46 | B2 D086 ; # capital Ukrainian I 47 | B3 D196 ; # small Ukrainian i 48 | B4 D291 ; # small Ukrainian soft g 49 | B5 C2B5 ; # micro sign 50 | B6 C2B6 ; # pilcrow sign 51 | B7 C2B7 ; # · 52 | B8 D191 ; # small yo 53 | B9 E28496 ; # numero sign 54 | BA D194 ; # small Ukrainian ye 55 | BB C2BB ; # right-pointing double angle quotation mark 56 | 57 | BF D197 ; # small Ukrainian yi 58 | 59 | C0 D090 ; # capital A 60 | C1 D091 ; # capital B 61 | C2 D092 ; # capital V 62 | C3 D093 ; # capital G 63 | C4 D094 ; # capital D 64 | C5 D095 ; # capital YE 65 | C6 D096 ; # capital ZH 66 | C7 D097 ; # capital Z 67 | C8 D098 ; # capital I 68 | C9 D099 ; # capital J 69 | CA D09A ; # capital K 70 | CB D09B ; # capital L 71 | CC D09C ; # capital M 72 | CD D09D ; # capital N 73 | CE D09E ; # capital O 74 | CF D09F ; # capital P 75 | 76 | D0 D0A0 ; # capital R 77 | D1 D0A1 ; # capital S 78 | D2 D0A2 ; # capital T 79 | D3 D0A3 ; # capital U 80 | D4 D0A4 ; # capital F 81 | D5 D0A5 ; # capital KH 82 | D6 D0A6 ; # capital TS 83 | D7 D0A7 ; # capital CH 84 | D8 D0A8 ; # capital SH 85 | D9 D0A9 ; # capital SHCH 86 | DA D0AA ; # capital hard sign 87 | DB D0AB ; # capital Y 88 | DC D0AC ; # capital soft sign 89 | DD D0AD ; # capital E 90 | DE D0AE ; # capital YU 91 | DF D0AF ; # capital YA 92 | 93 | E0 D0B0 ; # small a 94 | E1 D0B1 ; # small b 95 | E2 D0B2 ; # small v 96 | E3 D0B3 ; # small g 97 | E4 D0B4 ; # small d 98 | E5 D0B5 ; # small ye 99 | E6 D0B6 ; # small zh 100 | E7 D0B7 ; # small z 101 | E8 D0B8 ; # small i 102 | E9 D0B9 ; # small j 103 | EA D0BA ; # small k 104 | EB D0BB ; # small l 105 | EC D0BC ; # small m 106 | ED D0BD ; # small n 107 | EE D0BE ; # small o 108 | EF D0BF ; # small p 109 | 110 | F0 D180 ; # small r 111 | F1 D181 ; # small s 112 | F2 D182 ; # small t 113 | F3 D183 ; # small u 114 | F4 D184 ; # small f 115 | F5 D185 ; # small kh 116 | F6 D186 ; # small ts 117 | F7 D187 ; # small ch 118 | F8 D188 ; # small sh 119 | F9 D189 ; # small shch 120 | FA D18A ; # small hard sign 121 | FB D18B ; # small y 122 | FC D18C ; # small soft sign 123 | FD D18D ; # small e 124 | FE D18E ; # small yu 125 | FF D18F ; # small ya 126 | } 127 | -------------------------------------------------------------------------------- /docker/nginx/conf/mime.types: -------------------------------------------------------------------------------- 1 | 2 | types { 3 | text/html html htm shtml; 4 | text/css css; 5 | text/xml xml; 6 | image/gif gif; 7 | image/jpeg jpeg jpg; 8 | application/javascript js; 9 | application/atom+xml atom; 10 | application/rss+xml rss; 11 | 12 | text/mathml mml; 13 | text/plain txt; 14 | text/vnd.sun.j2me.app-descriptor jad; 15 | text/vnd.wap.wml wml; 16 | text/x-component htc; 17 | 18 | image/png png; 19 | image/tiff tif tiff; 20 | image/vnd.wap.wbmp wbmp; 21 | image/x-icon ico; 22 | image/x-jng jng; 23 | image/x-ms-bmp bmp; 24 | image/svg+xml svg svgz; 25 | image/webp webp; 26 | 27 | application/font-woff woff; 28 | application/java-archive jar war ear; 29 | application/json json; 30 | application/mac-binhex40 hqx; 31 | application/msword doc; 32 | application/pdf pdf; 33 | application/postscript ps eps ai; 34 | application/rtf rtf; 35 | application/vnd.apple.mpegurl m3u8; 36 | application/vnd.ms-excel xls; 37 | application/vnd.ms-fontobject eot; 38 | application/vnd.ms-powerpoint ppt; 39 | application/vnd.wap.wmlc wmlc; 40 | application/vnd.google-earth.kml+xml kml; 41 | application/vnd.google-earth.kmz kmz; 42 | application/x-7z-compressed 7z; 43 | application/x-cocoa cco; 44 | application/x-java-archive-diff jardiff; 45 | application/x-java-jnlp-file jnlp; 46 | application/x-makeself run; 47 | application/x-perl pl pm; 48 | application/x-pilot prc pdb; 49 | application/x-rar-compressed rar; 50 | application/x-redhat-package-manager rpm; 51 | application/x-sea sea; 52 | application/x-shockwave-flash swf; 53 | application/x-stuffit sit; 54 | application/x-tcl tcl tk; 55 | application/x-x509-ca-cert der pem crt; 56 | application/x-xpinstall xpi; 57 | application/xhtml+xml xhtml; 58 | application/xspf+xml xspf; 59 | application/zip zip; 60 | 61 | application/octet-stream bin exe dll; 62 | application/octet-stream deb; 63 | application/octet-stream dmg; 64 | application/octet-stream iso img; 65 | application/octet-stream msi msp msm; 66 | 67 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; 68 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; 69 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; 70 | 71 | audio/midi mid midi kar; 72 | audio/mpeg mp3; 73 | audio/ogg ogg; 74 | audio/x-m4a m4a; 75 | audio/x-realaudio ra; 76 | 77 | video/3gpp 3gpp 3gp; 78 | video/mp2t ts; 79 | video/mp4 mp4; 80 | video/mpeg mpeg mpg; 81 | video/quicktime mov; 82 | video/webm webm; 83 | video/x-flv flv; 84 | video/x-m4v m4v; 85 | video/x-mng mng; 86 | video/x-ms-asf asx asf; 87 | video/x-ms-wmv wmv; 88 | video/x-msvideo avi; 89 | } 90 | -------------------------------------------------------------------------------- /docker/nginx/conf/mime.types.default: -------------------------------------------------------------------------------- 1 | 2 | types { 3 | text/html html htm shtml; 4 | text/css css; 5 | text/xml xml; 6 | image/gif gif; 7 | image/jpeg jpeg jpg; 8 | application/javascript js; 9 | application/atom+xml atom; 10 | application/rss+xml rss; 11 | 12 | text/mathml mml; 13 | text/plain txt; 14 | text/vnd.sun.j2me.app-descriptor jad; 15 | text/vnd.wap.wml wml; 16 | text/x-component htc; 17 | 18 | image/png png; 19 | image/tiff tif tiff; 20 | image/vnd.wap.wbmp wbmp; 21 | image/x-icon ico; 22 | image/x-jng jng; 23 | image/x-ms-bmp bmp; 24 | image/svg+xml svg svgz; 25 | image/webp webp; 26 | 27 | application/font-woff woff; 28 | application/java-archive jar war ear; 29 | application/json json; 30 | application/mac-binhex40 hqx; 31 | application/msword doc; 32 | application/pdf pdf; 33 | application/postscript ps eps ai; 34 | application/rtf rtf; 35 | application/vnd.apple.mpegurl m3u8; 36 | application/vnd.ms-excel xls; 37 | application/vnd.ms-fontobject eot; 38 | application/vnd.ms-powerpoint ppt; 39 | application/vnd.wap.wmlc wmlc; 40 | application/vnd.google-earth.kml+xml kml; 41 | application/vnd.google-earth.kmz kmz; 42 | application/x-7z-compressed 7z; 43 | application/x-cocoa cco; 44 | application/x-java-archive-diff jardiff; 45 | application/x-java-jnlp-file jnlp; 46 | application/x-makeself run; 47 | application/x-perl pl pm; 48 | application/x-pilot prc pdb; 49 | application/x-rar-compressed rar; 50 | application/x-redhat-package-manager rpm; 51 | application/x-sea sea; 52 | application/x-shockwave-flash swf; 53 | application/x-stuffit sit; 54 | application/x-tcl tcl tk; 55 | application/x-x509-ca-cert der pem crt; 56 | application/x-xpinstall xpi; 57 | application/xhtml+xml xhtml; 58 | application/xspf+xml xspf; 59 | application/zip zip; 60 | 61 | application/octet-stream bin exe dll; 62 | application/octet-stream deb; 63 | application/octet-stream dmg; 64 | application/octet-stream iso img; 65 | application/octet-stream msi msp msm; 66 | 67 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; 68 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; 69 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; 70 | 71 | audio/midi mid midi kar; 72 | audio/mpeg mp3; 73 | audio/ogg ogg; 74 | audio/x-m4a m4a; 75 | audio/x-realaudio ra; 76 | 77 | video/3gpp 3gpp 3gp; 78 | video/mp2t ts; 79 | video/mp4 mp4; 80 | video/mpeg mpeg mpg; 81 | video/quicktime mov; 82 | video/webm webm; 83 | video/x-flv flv; 84 | video/x-m4v m4v; 85 | video/x-mng mng; 86 | video/x-ms-asf asx asf; 87 | video/x-ms-wmv wmv; 88 | video/x-msvideo avi; 89 | } 90 | -------------------------------------------------------------------------------- /docker/php/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM my-ubuntu:14.04 2 | #此images的tags为my-php:5.5 3 | #安装在/usr/src/php 4 | # see:https://github.com/docker-library/php/blob/master/5.5/Dockerfile 5 | # 如果是php -a运行则run时必须加-t参数 6 | # docker run --name php-5.5 --volumes-from share_volume -v /Users/github/docker_lnmp/php5.5/conf/conf:/usr/src/php/conf/ -v /Users/github/docker_lnmp/php5.5/conf/ext/:/usr/src/php/ext/ -v /Users/github/docker_lnmp/php5.5/conf/etc/:/usr/src/php/etc/ -v /Users/github/docker_lnmp/php5.5/src/:/usr/src/php/php-ext/ -p 9000:9000 -d my-php:5.5 7 | 8 | 9 | MAINTAINER aogg 10 | 11 | # 可自定义的配置 12 | ARG PHP_APT_INSTALL='libxml2-dev xz-utils' 13 | # 安装文件目录 14 | ARG PHP_CONFIGURE_DIR='/usr/local/php' 15 | ARG PHP_DIR='/usr/src/php' 16 | ARG PHP_VERSION='5.5.36' 17 | ARG PHP_CONFIGURE_ARGS='--enable-fpm' 18 | ARG PHP_SHA256='e1bbe33d6b4da66b15c483131520a9fc505eeb6629fa70c5cfba79590a1d0801' 19 | ARG PHP_GPG_KEYS='0B96609E270F565C13292B24C13C70B87267B52D 0BD78B5F97500D450838F95DFE857D9A90D90EC1 F38252826ACD957EF380D39F2F7956BC5DA04B5D' 20 | ARG PHP_PROCESSES_NUM=15 21 | 22 | # 可被删除的依赖 23 | # DEBIAN_FRONTEND解决apt对话框问题 24 | RUN export DEBIAN_FRONTEND=noninteractive \ 25 | && apt update && apt install -y \ 26 | git \ 27 | # persistent / runtime deps 28 | ca-certificates librecode0 libsqlite3-0 jq \ 29 | # phpize deps 30 | autoconf file libc-dev pkg-config re2c \ 31 | ${PHP_APT_INSTALL} \ 32 | --no-install-recommends \ 33 | 34 | && rm -r /var/lib/apt/lists/* \ 35 | 36 | 37 | # 创建目录 38 | # 各国地址会不一样 39 | # http://php.net/get/php-5.5.36.tar.gz/from/a/mirror 40 | # http://cn2.php.net/get/php-5.5.36.tar.gz/from/this/mirror 41 | # http://cn2.php.net/get/php-5.5.36.tar.gz.asc/from/this/mirror 42 | && echo '开始PHP安装' \ 43 | && PHP_INI_DIR="${PHP_DIR}/conf" \ 44 | 45 | # && PHP_FILENAME="php-${PHP_VERSION}.tar.xz" \ 46 | # && php_down_url='http://php.net/get' \ 47 | # && curl -fSL "${php_down_url}/${PHP_FILENAME}/from/this/mirror" -o "$PHP_FILENAME" \ 48 | 49 | # 改地址为:https://github.com/php/php-src/archive/php-7.2.18.tar.gz 50 | && PHP_FILENAME="php-${PHP_VERSION}.tar.gz" \ 51 | && php_down_url='https://github.com/php/php-src/archive' \ 52 | && curl -fSL "${php_down_url}/${PHP_FILENAME}" -o "$PHP_FILENAME" \ 53 | # && echo "$PHP_SHA256 *${PHP_FILENAME}" | sha256sum -c - \ 54 | # && curl -fSL "${php_down_url}/$PHP_FILENAME.asc/from/this/mirror" -o "${PHP_FILENAME}.asc" \ 55 | # 认证 56 | # && gpg --verify "$PHP_FILENAME.asc" \ 57 | # && export GNUPGHOME="$(mktemp -d)" \ 58 | # && for key in $PHP_GPG_KEYS; do \ 59 | # gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \ 60 | # done \ 61 | # && gpg --batch --verify "$PHP_FILENAME.asc" "$PHP_FILENAME" \ 62 | # && rm -r "$GNUPGHOME" "$PHP_FILENAME.asc" \ 63 | 64 | 65 | && mkdir -p $PHP_CONFIGURE_DIR $PHP_DIR $PHP_INI_DIR/conf.d \ 66 | && tar -xf "$PHP_FILENAME" -C $PHP_CONFIGURE_DIR --strip-components=1 \ 67 | # 删除压缩包 68 | && rm -rf ${PHP_FILENAME}* \ 69 | && cd $PHP_CONFIGURE_DIR \ 70 | # github包编译需要 71 | && ./buildconf --force \ 72 | && chmod 755 /usr/bin/gcc \ 73 | # 安装php 74 | && ./configure \ 75 | --prefix=$PHP_DIR \ 76 | # 指定php.ini位置 77 | --with-config-file-path="$PHP_INI_DIR" \ 78 | --with-config-file-scan-dir="$PHP_INI_DIR/conf.d" \ 79 | # --disable-cgi \ 80 | # 开启fpm 81 | # --enable-fpm \ 82 | # 开启mysqlnd驱动 83 | --enable-mysqlnd \ 84 | ${PHP_CONFIGURE_ARGS} \ 85 | && chmod 777 /usr/bin/gcc \ 86 | && make -j"$(nproc)" \ 87 | && make install \ 88 | # 未知 89 | # && { find /usr/local/bin /usr/local/sbin -type f -executable -exec strip --strip-all '{}' + || true; } \ 90 | # 删除 91 | # && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false -o APT::AutoRemove::SuggestsImportant=false $BUILDDEPS \ 92 | && make clean 93 | 94 | # 添加环境变量 95 | ENV PATH $PHP_DIR/bin:$PHP_DIR/sbin:$PATH 96 | # PHPStorm环境的console的xdebug调试配置 97 | ENV PHP_IDE_CONFIG="serverName=PHPStorm" 98 | 99 | # 安装php扩展 100 | COPY ./src/* ${PHP_CONFIGURE_DIR}/../php-ext/ 101 | 102 | ENV PHP_PROCESSES_NUM $PHP_PROCESSES_NUM 103 | ENV PHP_CONFIGURE_DIR $PHP_CONFIGURE_DIR 104 | # php-ext.sh不是一个可执行文件 105 | # 添加执行权限,并添加到/bin/中 106 | RUN php_ext_sh=${PHP_CONFIGURE_DIR}/../php-ext/php-ext.sh \ 107 | && chmod +x $php_ext_sh \ 108 | && ln -s $php_ext_sh /usr/local/bin/php-ext.sh \ 109 | && /bin/bash $php_ext_sh 110 | 111 | 112 | 113 | WORKDIR $PHP_DIR 114 | 115 | EXPOSE 9000 116 | 117 | VOLUME ["${PHP_DIR}/conf", "${PHP_DIR}/etc"] 118 | 119 | #### 120 | #fpm用这个即可,-R运行root,解决mac远程windows的共享目录 121 | CMD ["php-fpm", "-R"] 122 | # 此cmd需要在run时加-t参数 123 | # CMD ["php", "-a"] 124 | #### 125 | 126 | -------------------------------------------------------------------------------- /electron/src/webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | // webpack.config.js 2 | const config = require(__dirname + '/../config/config.js'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const VueLoaderPlugin = require('vue-loader/lib/plugin'); 5 | const webpack = require('webpack'); 6 | const Path = require('path'); 7 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 8 | 9 | global.config = config; // 解决nodeStorage 10 | 11 | let webpackConfig = {}; 12 | webpackConfig = { 13 | mode: 'development', // production, development 14 | devtool: '#cheap-module-eval-source-map', // 用生成环境(production supported)为no的可以不显示黄色警告 15 | // devtool: '#source-map', // 最好的错误提示 16 | target: 'electron-renderer', // 可以直接require('electron'),而不必window.require('electron') 17 | // entry point of our application 18 | entry: { 19 | appMain: [config.srcPath + '/appMain.js'], 20 | }, 21 | // where to place the compiled bundle 22 | output: { 23 | path: Path.dirname(config.indexHtmlPath), // 和config.mainLoadUrl关联 24 | filename: '[name].js' 25 | }, 26 | module: { 27 | // `loaders` is an array of loaders to use. 28 | // here we are only configuring vue-loader 29 | rules: [ 30 | { 31 | test: /\.vue$/, // a regex for matching all files that end in `.vue` 32 | use: 'vue-loader', // loader to use for matched files 33 | }, 34 | { 35 | "test": /\.css$/, 36 | "use": [ 37 | MiniCssExtractPlugin.loader, 38 | // "vue-style-loader", 39 | "css-loader" 40 | ], 41 | }, 42 | { // 不知为何<% %>解析不了 43 | "test": /\.html$/, 44 | "use": "vue-html-loader", 45 | }, 46 | { 47 | test: /\.js$/, 48 | use: { 49 | loader: 'babel-loader', 50 | options: { // 使用babel,省去.babelrc 51 | // enable stage 0 babel transforms. 52 | presets: ['env', 'stage-0'], 53 | plugins: ['transform-runtime'] 54 | } 55 | }, 56 | exclude: /(node_modules|bower_components)/, 57 | } 58 | ] 59 | }, 60 | resolve:{ 61 | modules: [ 62 | config.rootPath + '/node_modules', 63 | config.srcPath + '/node_modules' 64 | ], 65 | extensions: ['.js', '.vue', '.json', '.css'], 66 | alias : { 67 | appMain:"/temp/appMain.js", 68 | 'vue$': 'vue/dist/vue.min.js', // electron/node_modules/vue/dist/README.md 69 | 'vue-router$': 'vue-router/dist/vue-router.min.js', 70 | appDir: config.srcPath + '/app', // unused 71 | } 72 | }, 73 | // resolveLoader: { 74 | // modules: [ 75 | // Path.join(config.rootPath, 'node_modules') 76 | // ], 77 | // extensions: ['.js', '.vue', '.json', '.css'], 78 | // }, 79 | devServer:{ // 木有用,直接在server.js中另写了 80 | hot: true, 81 | inline: true, 82 | }, 83 | plugins:[ 84 | new MiniCssExtractPlugin({ // 将css都放到appMain.css中 85 | // Options similar to the same options in webpackOptions.output 86 | // both options are optional 87 | filename: "[name].css", 88 | chunkFilename: "[id].css" 89 | }), 90 | new webpack.DefinePlugin({ 91 | 'process.env': { 92 | NODE_ENV: '"'+webpackConfig.mode+'"' 93 | } 94 | }), 95 | new VueLoaderPlugin(), 96 | new HtmlWebpackPlugin({ // 让index.html内可自动处理编译后的appMain.js 97 | filename: config.indexHtmlName, 98 | template: config.indexHtmlPathSource, 99 | inject: 'body', 100 | title: config.title, 101 | }), 102 | // new webpack.DllReferencePlugin({ // 会报错 103 | // name: `${config.tempPath}/dll/lib.js`, // 优先于manifest.name 104 | // context: __dirname, 105 | // manifest: require(`${config.tempPath}/manifest.json`), 106 | // }), 107 | ] 108 | }; 109 | 110 | module.exports = webpackConfig; 111 | 112 | 113 | if (process.env.NODE_ENV === 'production') { 114 | module.exports.devtool = '#source-map'; 115 | // http://vue-loader.vuejs.org/en/workflow/production.html 116 | module.exports.plugins = (module.exports.plugins || []).concat([ 117 | // new webpack.optimize.UglifyJsPlugin({ 118 | // compress: { 119 | // warnings: false 120 | // } 121 | // }) 122 | ]) 123 | } -------------------------------------------------------------------------------- /electron/src/config/config.js: -------------------------------------------------------------------------------- 1 | const Path = require('path'); 2 | 3 | // 已配置为全局对象 4 | let config = { 5 | appDev: 0, // 是否开发模式webpack-dev-server 6 | electorn_devtron: 1, // 开启devtron工具 7 | openDevTools: 0, // 是否开启开发者工具 8 | dockerNodeCommandLog: false, // 执行命令之 后 的console.log是否显示在命令行中 9 | processExecCommandLog: false, // 执行命令之 前 的console.log是否显示在命令行中 10 | 11 | 12 | 13 | nodeStorage(){ 14 | return require('../core/nodeStorage'); 15 | }, 16 | 17 | 18 | get title(){ // 软件标题 19 | return this.nodeStorage().getItem('app-title') || 'docker_lnmp'; 20 | }, 21 | 22 | 23 | BrowserWindow:{ 24 | width: 500, height: 195, 25 | get title(){ 26 | return config.title; 27 | }, 28 | backgroundColor: '#3C3F41', // 和body颜色要保持一致 29 | useContentSize: true, 30 | webPreferences:{ 31 | devTools: true // 是否允许使用F12 32 | }, 33 | maximizable: false, // 不能最大化 34 | // resizable: false, // 是否可调整大小 35 | fullscreen:false, // 是否允许全屏 36 | // 是否显示菜单栏,无边框 37 | // transparent: true, // 透明 38 | frame: false, 39 | // titleBarStyle: 'hidden', // mac替代方案 40 | show: true, // 是否显示 41 | }, 42 | electronMacAboutPanel: { // 设置 "关于" 面板选项。 43 | applicationName: 'docker_lnmp', 44 | applicationVersion: '1.0.0', 45 | copyright: null, 46 | credits: null, 47 | version: null, 48 | }, 49 | get nodeStoragePath(){ 50 | return Path.join(this.srcPath, './config/nodeStorageJson.json'); 51 | }, 52 | 53 | 54 | 55 | 56 | rootPath: Path.join(__dirname, '../..'), 57 | indexHtmlSuf:'.html', 58 | get indexHtmlName(){ 59 | return 'index' + this.indexHtmlSuf; 60 | }, 61 | get macTerminalExecPath(){ // mac打开命令行窗口执行命令的工具 62 | return this.srcPath + '/resources/terminal'; 63 | }, 64 | get srcPath(){ 65 | return this.rootPath + '/src'; 66 | }, 67 | get tempPath(){ 68 | return this.srcPath + '/temp'; 69 | }, 70 | get dockerPath(){ 71 | return Path.join(this.rootPath, '../docker') 72 | }, 73 | get indexHtmlPath(){ // 编译后index.html放在哪 74 | return Path.join(this.tempPath, this.indexHtmlName); 75 | }, 76 | get indexHtmlPathSource(){ 77 | let fullPath; 78 | [this.indexHtmlSuf, suf] = ['.ejs', this.indexHtmlSuf]; // ejs模板文件可用于HtmlWebpackPlugin的模板传参 79 | fullPath = Path.join(this.rootPath, this.indexHtmlName); 80 | this.indexHtmlSuf = suf; 81 | 82 | return fullPath; 83 | }, 84 | /** 85 | * 根据是否开发模式使用webpack-dev-server 86 | * @param fileBool 是否返回file协议 87 | * @returns {string} 88 | */ 89 | mainLoadUrl(fileBool = true){ 90 | let url; 91 | if (this.appDev) { 92 | url = this.webpackDevServer.url + this.webpackDevServer.publicPath + this.indexHtmlName; 93 | } else { 94 | url = fileBool ? `file://${this.indexHtmlPath}` : this.indexHtmlPath; 95 | } 96 | 97 | return url; 98 | }, 99 | 100 | 101 | // webpack-dev-server 102 | webpackDevServer:{ 103 | publicPath: '/', // 在url的根目录 104 | port: 7777, 105 | host:'127.0.0.1', 106 | get url(){ 107 | return 'http://' + this.host + ':' + this.port + '/'; 108 | } 109 | }, 110 | 111 | 112 | // console颜色 113 | color:{ 114 | dockerColor: '\x1b[34m', 115 | webpackColor: '\x1b[33m', 116 | electronColor: '\x1b[32m', 117 | endColor: '\x1b[0m', 118 | commandAfterSpace:' ', 119 | /** 120 | * 121 | * @param command 122 | * @param type 返回命令名称 123 | * @param local 自定义颜色 124 | * @returns {string} 125 | */ 126 | colorFormat (command, type = 0, local = '') { 127 | command = command.toString().trim(); 128 | let startCommand = command.split(/\s+/, 1), 129 | color = '', 130 | colorFunc = (command, color) => color?`${color}${command}${this.endColor}`:command, 131 | space = this.commandAfterSpace; 132 | 133 | if (local) { // todo 可添加去除前缀功能 134 | color = local 135 | } else { 136 | color = this.hasOwnProperty(startCommand + 'Color') ? this[startCommand + 'Color'] : ''; 137 | } 138 | 139 | if (type == 1) { // 全部命令都是改变颜色 140 | return colorFunc(command, color) + '\n'; 141 | } else { // 提取命令名称并改变颜色 142 | return colorFunc(startCommand, color) + space + command.replace(/\n/g, '\n' + ' '.repeat(startCommand.length + space.length)) + '\n'; 143 | } 144 | 145 | } 146 | } 147 | }; 148 | 149 | 150 | module.exports = config; -------------------------------------------------------------------------------- /electron/src/app/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 21 | 22 | 139 | -------------------------------------------------------------------------------- /electron/src/core/commonFunc.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | module.exports = { 5 | 6 | 7 | /** 8 | * 异步任务的队列 9 | * 10 | * @returns {Function} 11 | */ 12 | queueAsync(){ 13 | let i = 0; 14 | let tempCallback = null; 15 | 16 | return function (callback, once = false) { 17 | once && (tempCallback = callback); 18 | let call = function (func, bool = false) { 19 | if (i > 0 && (i < 2 || bool)) { 20 | once ? tempCallback(func) : callback(func); 21 | } 22 | }, func = function () { 23 | if (once && i > 1) { 24 | i = 1; 25 | } else { 26 | --i; 27 | } 28 | call(func, true); 29 | }; 30 | ++i; 31 | call(func); 32 | }; 33 | }, 34 | 35 | 36 | /** 37 | * 获取data的属性proto 38 | */ 39 | getObjectProto(data, proto, defaultData = ''){ 40 | proto = (typeof proto === 'string'? proto.split('.') :proto) || []; 41 | if (data && typeof data === 'object' && proto.length){ 42 | let first = proto.shift(); 43 | if (Reflect.has(data, first)){ 44 | return this.getObjectProto(data[first], proto); 45 | }else{ 46 | return defaultData; 47 | } 48 | } 49 | 50 | return data; 51 | }, 52 | setObjectProto(data, proto, value){ 53 | let bool = this.isObject(data); 54 | if (!proto && bool && this.isObject(value)){ // 直接合并 55 | return Object.assign(data, value); 56 | } 57 | 58 | proto = (typeof proto === 'string'? proto.split('.') :proto) || []; 59 | if (bool && proto.length){ 60 | let first = proto.shift(); 61 | 62 | if (proto.length){ // 最后 63 | data[first] = this.setObjectProto(data[first] || {}, proto, value); 64 | }else{ 65 | data[first] = value; 66 | } 67 | } 68 | 69 | return data; 70 | }, 71 | 72 | 73 | /** 74 | * 递归何必对象 75 | */ 76 | objectMergeRecursive(source, target, options = {}){ 77 | if (options.recursive || this.isObject(source) && this.isObject(target)){ 78 | options = Object.assign({recursive: true}, options); 79 | for (let key in target){ 80 | if (!options.objectCover && this.isObject(source[key]) && this.isObject(target[key])){ // 递归对象 81 | source[key] = this.objectMergeRecursive(source[key], target[key], options); 82 | }else if ( 83 | (options.arrayEmptyCover || options.arrayCover) && 84 | Array.isArray(source[key]) && Array.isArray(target[key]) 85 | ){ // 追加数组 86 | source[key] = source[key].concat(target[key]); 87 | }else{ 88 | source[key] = target[key]; 89 | } 90 | 91 | } 92 | } 93 | 94 | return source; 95 | }, 96 | 97 | isObject(data){ 98 | return data && typeof data === 'object'; 99 | }, 100 | 101 | /** 102 | * 判断对象是否为空,true为空 103 | * 104 | * @param data 105 | * @returns {boolean} 106 | */ 107 | emptyObject(data){ 108 | return this.isObject(data) && Object.getOwnPropertyNames(data).length !== 0; 109 | }, 110 | 111 | 112 | /** 113 | * 递归判断对象是否存在指定的多个属性 114 | * 115 | * @param args 116 | * @returns {*} 117 | */ 118 | isRePropertyObject(...args){ 119 | let length = args.length, 120 | first = args.shift(); 121 | if (length < 2) { 122 | return first != undefined; 123 | } 124 | 125 | if (typeof first !== 'object') { 126 | return false; 127 | } 128 | 129 | let key = args.shift(); 130 | 131 | if (!key || !first.hasOwnProperty(key)) { 132 | return false; 133 | } 134 | 135 | return this.isRePropertyObject(first[key], ...args); 136 | }, 137 | 138 | 139 | pointString(str){ 140 | let arr = []; 141 | for (let i=0; i < str.length; i++){ 142 | arr[i] = str.codePointAt(i); 143 | } 144 | 145 | return "\\u"+arr.join("\\u"); 146 | }, 147 | 148 | atString(str) { 149 | return String.fromCodePoint(...str.split("\\u")); 150 | }, 151 | 152 | // 转译为实体 153 | html2Escape(str){ 154 | let map = { 155 | '<':'<', 156 | '>':'>', 157 | // '&':'&', 158 | '"':'"', 159 | "'": "\'", // '无效 160 | }; 161 | return (str && String(str).replace( 162 | /[<>&"']/g, 163 | (m) => { 164 | return map[m] 165 | } 166 | )) || ''; 167 | }, 168 | 169 | 170 | /** 171 | * 预定义一个Map对象 172 | * @returns {Function} 173 | */ 174 | mapData(){ 175 | let data = new Map(); 176 | // proxy,的key不能是一个对象 177 | return { 178 | getData(key){ 179 | return data.get(key); 180 | }, 181 | setData(key, value){ 182 | return data.set(key, value); 183 | }, 184 | deleteData(key){ 185 | return data.delete(key); 186 | } 187 | } 188 | }, 189 | }; 190 | 191 | -------------------------------------------------------------------------------- /electron/src/app/public/setup/setup-content.vue: -------------------------------------------------------------------------------- 1 | 8 | 48 | 157 | -------------------------------------------------------------------------------- /electron/src/config/config.docker.js: -------------------------------------------------------------------------------- 1 | // docker容器配置 2 | const Path = require('path'); 3 | const commonFunc = require('../core/commonFunc'); 4 | const nodeStorage = require('../core/nodeStorage'); 5 | const configCommand = require('../config/config.command'); 6 | let symbolTemp = {}; 7 | // const configCommand = require('./config.command'); 8 | 9 | let dockerRoot = config.dockerPath; 10 | 11 | 12 | let dockerConfig = { 13 | execCwd: Path.normalize(Path.join(dockerRoot, '..')), // 执行命令的目录 14 | dockerRoot : dockerRoot, // docker目录 15 | bashCommandPath: 'bash', // git的bash路径 16 | hostName:'default', 17 | get composeYml(){ 18 | return Path.join(dockerRoot, 'docker-compose-powershell.yml'); 19 | }, 20 | get dockerExe(){ 21 | return configCommand.whereCommand('docker', 'config-exe.docker', null, true); 22 | }, 23 | get dockerComposeExe(){ 24 | return configCommand.whereCommand('docker-compose', 'config-exe.docker-compose', null, true); 25 | }, 26 | get dockerMachineExe(){ 27 | return configCommand.whereCommand('docker-machine', 'config-exe.docker-machine', null, true); 28 | }, 29 | machineArgs: { 30 | driver: 'virtualbox', 31 | cap: '80', // cpu运行峰值 32 | memory: '666', // 内存大小 33 | noShare: true, // 不分享桌面 34 | addBridged: false, // 添加一个桥接网络 35 | bridgeadapter: 'Realtek PCIe GBE 系列控制器', // 界面名称 36 | sharedFolder: { // 共享目录,key为共享名,value为共享本地的路径 37 | 'Users':'F:/code/www', 38 | }, 39 | other: '', // 其他更多参数 40 | }, 41 | get execOption(){ // 执行命令时的参数,todo 待整理 42 | return { 43 | cwd: this.execCwd, 44 | env: this.execEnv, 45 | }; 46 | }, 47 | get execEnv(){ // 执行exec时的env 48 | let temp = {}; 49 | // 转多层数据为一层 50 | for (let k in this.env){ 51 | Object.assign(temp, typeof this.env[k] !== 'object'?{[k]: this.env[k]}:this.env[k]); 52 | } 53 | 54 | return Object.assign(temp, process.env); 55 | }, 56 | get configEnv(){ // 不获取process.env 57 | // 如出现getter的问题,可通过JSON.stringify 58 | return this.env; 59 | }, 60 | get env(){ // 用于环境变量设置 61 | let key = 'containerConfig'; 62 | symbolTemp[key] || (symbolTemp[key] = {}); 63 | if (!nodeStorage.isUpdate(symbolTemp[key]['symbol'], key)){ // 数据是否更新,否则直接返回结果 64 | return symbolTemp[key]['data']; 65 | } 66 | symbolTemp[key]['symbol'] = nodeStorage.getUpdate(key); 67 | let envP = process.env; 68 | 69 | 70 | let e = { 71 | // php的--build-arg 72 | php:{ 73 | compose_build_php_version: envP.compose_build_php_version, // php版本号 74 | compose_build_php_configure_dir: envP.compose_build_php_configure_dir, // php编译目录 75 | compose_build_php_dir: envP.compose_build_php_dir, // php安装目录 76 | // php验证用 77 | compose_build_php_sha256: envP.compose_build_php_sha256, 78 | compose_build_php_gpg_keys: envP.compose_build_php_gpg_keys, 79 | compose_build_php_configure_args: envP.compose_build_php_configure_args, // php的编译参数 80 | compose_build_php_processes_num: envP.compose_build_php_processes_num, // 并发安装扩展的数量 81 | compose_build_php_apt_install: envP.compose_build_php_apt_install, // php的apt install 82 | // php的共享目录 83 | get compose_volumes_php_conf(){ 84 | return `${e.compose_dir}/php/conf/conf:/usr/src/php/conf/`; 85 | }, 86 | get compose_volumes_php_etc(){ 87 | return `${e.compose_dir}/php/conf/etc/:/usr/src/php/etc/`; 88 | }, 89 | }, 90 | 91 | 92 | // nginx的--build-arg 93 | nginx: { 94 | compose_build_nginx_version: envP.compose_build_nginx_version, 95 | compose_build_nginx_dir: envP.compose_build_nginx_dir, 96 | compose_build_nginx_args: envP.compose_build_nginx_args, 97 | 98 | get compose_volumes_nginx(){ // nginx conf的共享目录 99 | return `${e.compose_dir}/nginx/conf/:/usr/src/nginx/conf/`; 100 | }, 101 | }, 102 | 103 | // bash的--build-arg 104 | base:{ 105 | compose_build_base_zone: envP.compose_build_base_zone, 106 | }, 107 | 108 | 109 | // 特殊处理 110 | // vmOrVirtualBox:'', // todo 111 | 112 | 113 | // 其他 114 | // host_volumes_www:'E:/code/www/', // 宿主机与容器的共享目录 115 | get compose_volumes_base(){ // 必须要匿名函数才能用this 116 | return `${e.host_volumes_www}:/www/`; 117 | }, 118 | get compose_dir(){ // docker_lnmp下的docker在虚拟机的路径 119 | return `${e.host_volumes_www}github/docker_lnmp/docker`; 120 | }, 121 | 122 | 123 | 124 | // todo docker-machine,待处理为自动识别 125 | host_volumes_www:'/www/', // 宿主机与容器的共享目录 126 | // DOCKER_TLS_VERIFY: "1", 127 | // DOCKER_HOST: "tcp://192.168.99.100:2376", 128 | // DOCKER_CERT_PATH: "C:/Users/code/.docker/machine/machines/default", 129 | // COMPOSE_CONVERT_WINDOWS_PATHS: 'true', // 会导致在window中执行会将composer.yml的volumes里本地路径改为\,这样就无法启动了 130 | DOCKER_MACHINE_NAME: 'default', 131 | }; 132 | 133 | return symbolTemp[key]['data'] = commonFunc.objectMergeRecursive(e, nodeStorage.getItem(key)); 134 | }, 135 | 136 | 'dockerProcessName': 'nodeDocker', 137 | }; 138 | 139 | 140 | module.exports = new Proxy(dockerConfig, { 141 | get:(target, key) => { 142 | return Reflect.has(target, key) ? target[key] : false; 143 | }, 144 | 145 | // set: () => { 146 | // // 147 | // }, 148 | }); 149 | 150 | -------------------------------------------------------------------------------- /electron/src/core/menu.js: -------------------------------------------------------------------------------- 1 | const NodeDocker = require('./docker.node'); 2 | const {app} = require('electron'); 3 | 4 | let nd = new NodeDocker(); 5 | 6 | function dockerExec(command, arg = null){ 7 | // IpcMain.send(onName, dockerParam(command, 'sync', arg)); 8 | nd.execDocker('async', command, arg); 9 | } 10 | 11 | let menuConfig = {}; 12 | menuConfig = { 13 | appMenuName: '操作', 14 | get defaultMenu(){ 15 | let template = [ 16 | { 17 | label: menuConfig.appMenuName, 18 | submenu: [ 19 | { 20 | label: 'Toggle Developer Tools', 21 | get accelerator(){ 22 | if (process.platform === 'darwin') { 23 | return 'Alt+Command+I' 24 | } else { 25 | return 'Ctrl+Shift+I' 26 | } 27 | }, 28 | click: function (item, focusedWindow) { 29 | if (focusedWindow){ 30 | if (focusedWindow.isDevToolsOpened()) { 31 | focusedWindow.closeDevTools(); 32 | }else{ 33 | // 必须固定新建窗口打开,否则如果点击“响应设计模式”会出现bug 34 | focusedWindow.openDevTools({mode: 'detach'}); 35 | } 36 | } 37 | } 38 | }, 39 | { 40 | label:'启动/重启', 41 | accelerator:'Ctrl+E', 42 | click: function(){ 43 | dockerExec('docker-compose/restart'); 44 | }, 45 | }, 46 | ] 47 | }, 48 | { 49 | label: 'Edit', 50 | submenu: [ 51 | {role: 'undo'}, 52 | {role: 'redo'}, 53 | {type: 'separator'}, 54 | {role: 'cut'}, 55 | {role: 'copy'}, 56 | {role: 'paste'}, 57 | {role: 'pasteandmatchstyle'}, 58 | {role: 'delete'}, 59 | {role: 'selectall'} 60 | ] 61 | }, 62 | { 63 | label: 'View', 64 | submenu: [ 65 | {role: 'reload'}, 66 | {role: 'forcereload'}, 67 | {role: 'toggledevtools'}, 68 | {type: 'separator'}, 69 | {role: 'resetzoom'}, 70 | {role: 'zoomin'}, 71 | {role: 'zoomout'}, 72 | {type: 'separator'}, 73 | {role: 'togglefullscreen'} 74 | ] 75 | }, 76 | { 77 | role: 'window', 78 | submenu: [ 79 | {role: 'minimize'}, 80 | {role: 'close'} 81 | ] 82 | }, 83 | { 84 | role: 'help', 85 | submenu: [ 86 | { 87 | label: 'Learn More', 88 | click () { require('electron').shell.openExternal('https://electronjs.org') } 89 | } 90 | ] 91 | } 92 | ]; 93 | 94 | if (process.platform === 'darwin') { 95 | template.unshift({ 96 | label: app.getName(), 97 | submenu: [ 98 | {role: 'about'}, 99 | {type: 'separator'}, 100 | {role: 'services', submenu: []}, 101 | {type: 'separator'}, 102 | {role: 'hide'}, 103 | {role: 'hideothers'}, 104 | {role: 'unhide'}, 105 | {type: 'separator'}, 106 | {role: 'quit'} 107 | ] 108 | }); 109 | 110 | // Edit menu 111 | template[2].submenu.push( 112 | {type: 'separator'}, 113 | { 114 | label: 'Speech', 115 | submenu: [ 116 | {role: 'startspeaking'}, 117 | {role: 'stopspeaking'} 118 | ] 119 | } 120 | ); 121 | 122 | // Window menu 123 | template[4].submenu = [ 124 | {role: 'close'}, 125 | {role: 'minimize'}, 126 | {role: 'zoom'}, 127 | {type: 'separator'}, 128 | {role: 'front'} 129 | ]; 130 | } 131 | 132 | 133 | return template; 134 | }, 135 | trayMenu: [ 136 | { 137 | label:'启动/重启', 138 | accelerator:'Ctrl+E', 139 | click: function(){ 140 | dockerExec('docker-compose/restart'); 141 | }, 142 | }, 143 | { 144 | label:'停止', 145 | click:function(){ 146 | dockerExec('docker-compose/stop') 147 | }, 148 | }, 149 | { 150 | label:'构建', 151 | click: function () { 152 | dockerExec('docker-compose/build'); 153 | }, 154 | }, 155 | { 156 | type: 'separator', 157 | }, 158 | { 159 | label: '退出', 160 | click: function(){ 161 | require('./event')('ipcRenderer-window-close', {close: 'close'}); 162 | } 163 | }, 164 | ], 165 | get dockMenu(){ 166 | return [ 167 | { 168 | label: menuConfig.appMenuName, 169 | submenu: menuConfig.trayMenu, 170 | }, 171 | ]; 172 | }, 173 | }; 174 | 175 | module.exports = menuConfig; 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /electron/src/appMain.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // 加载公共css,必须import否则会被放到最后 4 | import './app/public/css/common.css'; 5 | const Vue = require('vue'); 6 | const VueX = require('vuex'); 7 | const electron = window.require('electron'); 8 | const VueRouter = require('vue-router'); 9 | // var config = require('./config/config.js'); // 不能require config 10 | // 必须要有.开头 11 | import indexComponent from './app/index.vue'; 12 | import setupComponent from './app/setup.vue'; 13 | import windowHeader from './app/public/header.vue'; 14 | 15 | let {dockerParam, ipcRenderName} = require('./core/docker'), 16 | dataCallback = {}, // 存放sendDocker的func 17 | uniqidCallback = {}, // 同上,待转移 18 | uniqid = 1; 19 | 20 | 21 | // vue-router 22 | Vue.use(VueRouter); 23 | let router = new VueRouter({ 24 | routes: [ 25 | {path: '/index', components: { app: indexComponent }}, 26 | {path: '/setup', components: { app: setupComponent }}, 27 | {path: '*', components: {app: location.hash.startsWith('#/setup') ? setupComponent : indexComponent}}, 28 | ] 29 | }); 30 | 31 | /* 32 | 33 | */ 34 | 35 | window.appStartTime = electron.remote.getGlobal('appStartTime'); // 在vue加载会导致无法获取electron 36 | const renderSessionStorage = require('./core/renderSessionStorage'); 37 | const commonFunc = require('./core/commonFunc'); 38 | // Vue.config.devtools = true; 39 | 40 | Vue.use(VueX); 41 | let store = require('./app/public/store'); 42 | 43 | window['mainVue'] = new Vue({ 44 | el:'#body', 45 | // routes, 46 | data(){ 47 | return { 48 | containerHeaderName: 'ALL', 49 | closeButton: localStorage.getItem('windowClose') == 1, // 关闭按钮是否关闭,true为关闭,false为隐藏 50 | containers: [], 51 | }; 52 | }, 53 | router, 54 | store, 55 | beforeCreate(){ 56 | window.addEventListener('message', (event) => { 57 | if (event.data === 'fontFamily'){ 58 | this.$store.commit('updateFontFamily'); 59 | } 60 | 61 | if (commonFunc.isObject(event.data) && event.data.source && !event.data.source.startsWith('vue-devtools')) { 62 | console.log(event.data); 63 | } 64 | }, false); 65 | }, 66 | created(){ 67 | this.controller('getAllNodeStorage', (val) => { 68 | this.$store.commit('setNodeStorage', {proto: '', value: val}); 69 | }); 70 | 71 | this.getContainerList(); 72 | }, 73 | computed: VueX.mapState({ 74 | fontFamily: 'fontFamily', 75 | }), 76 | components: { 77 | windowHeader, 78 | }, 79 | methods: { 80 | sendIpcMain(name, type, arg = {}){ 81 | let data = true; 82 | type = type.toLowerCase(); 83 | arg = this.disArg(arg, 'sendIpcMain-' + name); 84 | if (type === 'async') { // 异步 85 | console.log(`ipc异步请求:${name}的${arg.name}`); 86 | 87 | electron.ipcRenderer.send(name, arg); 88 | }else if (type === 'sync'){ // 同步 89 | console.log('ipc同步请求:' + name); 90 | 91 | data = electron.ipcRenderer.sendSync(...sendParam); 92 | console.log([channel + 'ipc请求结果:', data]); 93 | } 94 | 95 | return data; 96 | }, 97 | sendDocker: function (channel, type, arg = {}, func =null) { 98 | // 为函数时ipc回调默认sendParam.onName 99 | let callbackName = ''; 100 | arg = this.disArg(arg, 'sendDocker-' + channel); 101 | if (typeof func === 'function') { 102 | dataCallback[channel] = {}; 103 | dataCallback[channel]['func'] = func; 104 | callbackName = ipcRenderName; 105 | } else { 106 | callbackName = `${channel}Callback`; 107 | } 108 | type = type.toLowerCase(); 109 | let sendParam = dockerParam(channel, type, arg, arg.callbackName || callbackName); 110 | 111 | return this.sendIpcMain(sendParam[0], type, sendParam[1]); 112 | 113 | // if (type === 'async') { // 异步 114 | // console.log(`将回调${callbackName}`); 115 | // } 116 | }, 117 | 118 | 119 | ipcRendererCallback(event, args){ 120 | console.log(`${ipcRenderName}:回调执行 ${args.callbackName || ''}`); 121 | if (Reflect.has(dataCallback, args['callbackName']) && typeof dataCallback[args['callbackName']]['func'] === 'function'){ 122 | // args会自动排序 123 | dataCallback[args['callbackName']]['func'](args['data'], args['callbackName'], args['code'], args['errMsg']); 124 | Reflect.deleteProperty(dataCallback, args['callbackName']) 125 | } 126 | // console.log([event, args]); // 输出返回的内容 127 | }, 128 | 129 | getContainerList(){ 130 | let key = 'docker-compose/config-services'; 131 | 132 | this.setContainers(renderSessionStorage.getItem(key)); 133 | 134 | if (!this.containers.length){ 135 | this.sendDocker(key, 'async', {}, (data) => { 136 | // data.map((val) => { 137 | // if (val) { 138 | // vm.containers.push(val); 139 | // } 140 | // }); 141 | 142 | renderSessionStorage.setItem(key, data); 143 | this.setContainers(data); 144 | }); 145 | } 146 | }, 147 | 148 | setContainers(data){ 149 | if (data && typeof data === 'string'){ 150 | let tempData = []; 151 | data.split(/\s+/).map((val) => { 152 | if (val){ 153 | tempData.push(commonFunc.html2Escape(val)); 154 | return true; 155 | } 156 | }); 157 | // data = data.slice(0, 1); 158 | this.$set(this, 'containers', tempData); 159 | } 160 | }, 161 | controller(action, callback = null, args = {}){ 162 | let data = this.disArg({action, args}, 'controller-' + action); 163 | 164 | this.uniqidCallback(data.uniqid, callback); 165 | electron.ipcRenderer.send('controller', data); 166 | }, 167 | uniqidCallback(key, callback = null, args = []){ 168 | if (callback === null && typeof uniqidCallback[key] === 'function'){ // 调用 169 | uniqidCallback[key](...args); 170 | Reflect.deleteProperty(uniqidCallback, key); 171 | }else if (typeof callback === 'function'){ // 存储 172 | uniqidCallback[key] = callback; 173 | } 174 | }, 175 | uniqid(){ // unique 176 | if (uniqid > 100000){ 177 | uniqid = 1; 178 | } 179 | return ++uniqid; 180 | }, 181 | disArg(arg, uniqidPrefix = ''){ 182 | arg = typeof arg !== 'object' || Array.isArray(arg)?{arg}:arg; 183 | return Object.assign({uniqid: uniqidPrefix + '_' + this.uniqid()}, arg); 184 | }, 185 | }, 186 | 187 | 188 | }); 189 | mainVue.$mount('#body'); 190 | 191 | 192 | electron.ipcRenderer.on(ipcRenderName, mainVue.ipcRendererCallback); 193 | 194 | electron.ipcRenderer.on('controller-callback', function(event, args){ 195 | if (!args.uniqid){ 196 | return false; 197 | } 198 | mainVue.uniqidCallback(args.uniqid, null, [args.data, event]); 199 | }); 200 | 201 | 202 | if(module.hot) { // 触发更新 203 | module.hot.accept(); 204 | } -------------------------------------------------------------------------------- /electron/src/core/setupContent.js: -------------------------------------------------------------------------------- 1 | 2 | const commonFunc = require('./commonFunc'); 3 | const electron = window.require('electron'); 4 | 5 | let containerList = Symbol('containerList'); 6 | 7 | 8 | 9 | let setupConfig = { 10 | 'default': [ 11 | { 12 | type: 'containerConfig', 13 | }, 14 | {type: 'space',}, 15 | { 16 | name: '点击主窗口关闭时', 17 | type: 'select', 18 | data: ['隐藏', '关闭'], 19 | selected: localStorage.getItem('windowClose'), 20 | // json-data 21 | change: { 22 | action: 'localStorage', 23 | name: 'windowClose', 24 | }, 25 | }, 26 | { 27 | name: '标题', 28 | type: 'text', 29 | get data(){ 30 | return electron.remote.getGlobal('config').title; 31 | }, 32 | change:{ 33 | action: 'nodeStorage', 34 | name: 'app-title', 35 | proto: '.', 36 | }, 37 | }, 38 | { 39 | name: 'font-family', 40 | type: 'text', 41 | get data(){ 42 | return localStorage.getItem('fontFamily') || window.getComputedStyle(document.getElementById('body')).fontFamily.slice(1, -2); 43 | }, 44 | change:{ 45 | name: 'fontFamily', 46 | action: 'localStorage' 47 | }, 48 | }, 49 | ], 50 | [containerList]: [ 51 | { 52 | type: 'containerConfig', 53 | childConfig:{ 54 | 'php':{ 55 | 'compose_build_php_processes_num':{ 56 | inputType: 'number', 57 | } 58 | }, 59 | 'nginx': { 60 | 'compose_build_nginx_args': { 61 | type: 'textarea', 62 | } 63 | } 64 | } 65 | }, 66 | ] 67 | }; 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | function renderHtml(config, containerData, name) { 77 | if (typeof config !== 'object') { 78 | return ''; 79 | } 80 | 81 | let html = ''; 82 | config.map(function (val, index) { 83 | if (typeof val === 'object') { 84 | 85 | html += renderTypeHtml(val, containerData, name); 86 | 87 | } 88 | }); 89 | 90 | return html; 91 | } 92 | 93 | 94 | function renderContainerHtml(config, containerData, name) { 95 | let html = ''; 96 | config.map(function(val, index){ 97 | if (typeof val === 'object') { 98 | 99 | html += renderTypeHtml(val, containerData, name); 100 | 101 | } 102 | }); 103 | 104 | return html; 105 | } 106 | 107 | 108 | /** 109 | * 110 | * @param data 111 | * @param {object} containerData 112 | * @param {string} name 113 | * @returns {string} 114 | */ 115 | function renderTypeHtml(data, containerData, name) { 116 | if (!data.type) { 117 | return ''; 118 | } 119 | let templateFunc = function (content, attrClass = '', attrHtml = '') { 120 | return ` 121 |
122 |
${data.name}
123 |
${content}
124 |
125 | `; 126 | }; 127 | let html = ''; 128 | if (data.change){ 129 | data.change.name = data.change.name || data.name; 130 | data.change = JSON.stringify(data.change) 131 | }else{ 132 | data.change = ''; 133 | } 134 | 135 | switch (data.type) { 136 | case 'containerConfig': 137 | let childConfig = commonFunc.getObjectProto(data, `childConfig.${name}`, {}); 138 | for (let key in containerData) { 139 | if (typeof containerData[key] !== 'object'){ 140 | html += renderTypeHtml(Object.assign({ 141 | type: 'text', 142 | name: key, 143 | data: containerData[key], 144 | change: { 145 | name: key, 146 | containerConfig: true, 147 | }, 148 | }, childConfig[key] || {})); 149 | } 150 | } 151 | childConfig = null; 152 | break; 153 | case 'select': 154 | if (Array.isArray(data.data)) { 155 | html += `'; 161 | html = templateFunc(html, 'render-select', ``); 162 | } 163 | 164 | break; 165 | 166 | case 'space': 167 | html = '
'; 168 | break; 169 | case 'textarea': 170 | html = templateFunc(``, 171 | 'render-textarea'); 172 | break; 173 | case 'text': 174 | default: // text 175 | let inputType = data.inputType ? data.inputType : 'text'; 176 | html = templateFunc(` 177 | 178 | `, 'render-text'); 179 | break; 180 | } 181 | 182 | return html; 183 | } 184 | 185 | 186 | function common(containerData) { 187 | let name = this.name; 188 | 189 | if (!name) { 190 | return; 191 | } 192 | 193 | if (this.componentsBool[name]){ 194 | this.setContentHtml(); 195 | return; 196 | } 197 | 198 | let template = ''; 199 | 200 | 201 | if (this.ifDefault) { // 集合 default 202 | this.$root.containers.map((val) => Reflect.deleteProperty(containerData, val)); // 删除其他容器配置 203 | template = renderHtml(setupConfig[name], containerData, name); 204 | } else if (this.$root.containers.includes(name)) { // containerList 205 | template = renderContainerHtml(setupConfig[containerList], containerData[name], name); 206 | } else { // $root.containers还未赋值 207 | this.$root.$watch('containers', () => { 208 | template = renderContainerHtml(setupConfig[containerList], containerData[name], name); 209 | 210 | template = '
' + template + '
'; 211 | this.setContentHtml(template); 212 | }); 213 | return; 214 | } 215 | 216 | template = '
' + template + '
'; 217 | this.setContentHtml(template); 218 | } 219 | 220 | 221 | module.exports = function (vue, containerData) { 222 | Reflect.apply(common, vue, [containerData]); 223 | }; 224 | 225 | 226 | 227 | 228 | // module.exports = function (vue, name, containerMap = []) { 229 | // if (Reflect.has(setupConfig, name)) { // 存在 230 | // renderHtml(setupConfig[name]); 231 | // } else if (Array.isArray(containerMap)) { // 容器列表 232 | // containerList(); 233 | // } else { // 查询 234 | 235 | // } 236 | // }; 237 | 238 | 239 | -------------------------------------------------------------------------------- /electron/src/config/config.command.js: -------------------------------------------------------------------------------- 1 | // 命令相关的配置 2 | const os = require('os'); 3 | const nodeStorage = require('../core/nodeStorage'); 4 | const emptySymbol = Symbol(); 5 | 6 | let currentOs = os.type(), 7 | isWin = /windows/i.test(currentOs), 8 | isMac = /Darwin/i.test(currentOs), 9 | isLinux = /Linux/i.test(currentOs), 10 | currentShellName = '', 11 | proxyConfig = {}; 12 | 13 | 14 | function processExec() { 15 | const processExec = require('../core/process.exec'); // 循环调用 16 | return new processExec('config.commnad'); 17 | } 18 | 19 | 20 | 21 | let commandConfig = { 22 | windows:{ 23 | whereCmd(command){ 24 | return `where ${command}`; 25 | }, 26 | 27 | execShellNameSync(command){ // 同步执行前缀 28 | if (this.shellName === 'powershell') { 29 | return `powershell -Command "${command.replace(/([^`])"/g, "$1\"\"\"\"")}"`; 30 | } 31 | 32 | return command; 33 | }, 34 | get execShellName (){ // child_process.exec 35 | if (this.shellName === 'powershell') { // 在powershell中不输出目录 36 | return 'powershell -NoLogo -Command -'; 37 | } 38 | 39 | return 'cmd'; 40 | }, 41 | checkPowerShell(){ 42 | return proxyConfig.whereCommand('powershell', 'powerShellBool', str => !!str, false, false) !== 'powershell'; 43 | }, 44 | get shellName(){ // 当前执行的命令的环境 45 | if (!currentShellName){ 46 | 47 | currentShellName = this.checkPowerShell() ? 'powershell' : 'cmd'; 48 | } 49 | 50 | return currentShellName; 51 | }, 52 | get headArr(){ 53 | let data = []; 54 | if (currentShellName === 'cmd') { 55 | data = [ 56 | '@echo off \n', 57 | ]; 58 | }else if (currentShellName === 'powershell'){ 59 | data = [ 60 | 61 | ]; 62 | } 63 | 64 | return data; 65 | }, 66 | openShell(command){ 67 | let environment = proxyConfig.shellName; 68 | 69 | if (environment === 'cmd'){ 70 | return `start cmd /k ${command}`; 71 | }else if(environment === 'git-bash'){ 72 | // todo 待改为git的bash,记得有部分功能有用 73 | return `start ${dockerConfig.bashCommandPath} -s -c '${command} ;read'`; 74 | }else if(environment === 'powershell'){ 75 | command = `echo '${command.replace(/(^`)'/g, "$1''")}';${command}`; // 输出执行命令 76 | command = command.replace(/([^`])"/g, "$1\"\"\"\"\"\""); // 转双引号需要三对双引号 77 | // return `start powershell -NoExit ${docker_compose} logs ${logsArgs} ${getContainerName()}`; // cmd下打开powershell 78 | return `start powershell -ArgumentList "-NoExit ${command}"`; // powershell打开powershell 79 | } 80 | }, 81 | 82 | 83 | }, 84 | 85 | 86 | 87 | 88 | linux:{ 89 | whereCmd(command){ 90 | return `which ${command}`; 91 | } 92 | }, 93 | mac:{ 94 | whereCmd(command){ 95 | return `which ${command}`; 96 | }, 97 | openShell(command){ 98 | return config.macTerminalExecPath + ' ' + command; 99 | }, 100 | }, 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | get headArr(){ 109 | return []; 110 | }, 111 | 112 | 113 | clearRow: '', // 在cmd中用于清除当前行并输入后面内容,即命令行的进度条。未知正确字符串,目前只能以乱码形式显示在ide中 114 | endRow: '', 115 | 116 | 117 | replaceEnter(str, replace, num = null){ 118 | num = (num === null ? this.enter.length : num) - 1; 119 | 120 | return this.enter[num] ? this.replaceEnter(str.replace(this.enter[num], replace), replace, num) : str; 121 | }, 122 | 123 | /** 124 | * 命令是否执行结束处理 125 | * 126 | * @return {string} 127 | */ 128 | ShellEOFCommand(eof){ 129 | return `echo "${eof}" \n`; 130 | }, 131 | 132 | 133 | /** 134 | * 不是回车符 135 | * 136 | * @param str 137 | * @returns {boolean} 138 | */ 139 | ifNotEnter(str){ 140 | return !(str.length === 2 && this.enter.includes(str)) 141 | }, 142 | 143 | /** 144 | * 换行切换数组 145 | * 146 | * @param str 147 | * @returns {Array} 148 | */ 149 | lineArr(str){ 150 | return str.replace("\r\n\r\n", "").split(/\r?\n/) || []; 151 | }, 152 | 153 | getNotEnter(str){ 154 | return str.trim() 155 | }, 156 | 157 | 158 | get shellName(){ 159 | return 'bash'; 160 | }, 161 | 162 | get execShellName(){ 163 | return 'bash'; 164 | }, 165 | enter: [ // 留意替换顺序 166 | "\n", // todo 换行 167 | "\r\n", // todo \r\n\r\n命令结束 168 | ], 169 | 170 | get headArr(){ 171 | return []; 172 | }, 173 | openShell(command){ 174 | 175 | }, 176 | 177 | 178 | 179 | 180 | 181 | /** 182 | * 查看command命令所在路径 183 | * 184 | * @param {string} command 命令名 185 | * @param {string} cacheKey 缓存 186 | * @param {callback} cacheFunc 缓存结果特殊处理 187 | * @param {bool} async 是否异步保存缓存 188 | * @param {bool} checkPowerShell 是否特殊处理执行绝对路径命令 189 | * @returns {string} 190 | */ 191 | whereCommand(command, cacheKey = '', cacheFunc = null, async = false, checkPowerShell = true){ // where npm 192 | let str, check; 193 | if (cacheKey && (str = nodeStorage.getItem(cacheKey, emptySymbol)) !== emptySymbol){ 194 | return str; 195 | } 196 | function disFunc(str) { // 处理返回的str 197 | if (str){ 198 | str = proxyConfig.lineArr(str).shift(); 199 | if (checkPowerShell && (check = proxyConfig.checkPowerShell) && check()) { // 是powershell,需&"C:\Program Files\Docker\Docker\Resources\bin\docker-compose.exe" 200 | str = '&"' + str + '"'; 201 | }else{ // mac和linux也可以用引号包裹路径 202 | str = '"' + str + '"'; 203 | } 204 | } 205 | 206 | if (cacheKey){ // 不考虑str 207 | str = typeof cacheFunc === 'function' ? cacheFunc(str) : str; 208 | nodeStorage.setItem(cacheKey, str); 209 | } 210 | 211 | return str; 212 | } 213 | 214 | if (async) { // 异步 215 | setTimeout(function () { 216 | processExec().exec(proxyConfig.whereCmd(command), {encoding : 'utf8'}, function (error, stdout, stderr) { 217 | if (error){ 218 | console.error(`whereCmd ${command} error: ${stderr}`); 219 | return; 220 | } 221 | 222 | return disFunc(stdout); 223 | }); 224 | }, 0); 225 | 226 | return async === true ? command : async; 227 | } 228 | 229 | return disFunc(processExec().execSync(proxyConfig.whereCmd(command), {encoding : 'utf8'})); 230 | }, 231 | }; 232 | 233 | 234 | 235 | proxyConfig = new Proxy(commandConfig, { 236 | get:(target, key) => { 237 | let data; 238 | if (isWin) { // todo 可能要改为win 239 | data = target['windows']; 240 | } else if(isLinux){ 241 | data = target['linux']; 242 | } else if (isMac){ 243 | data = target['mac']; 244 | } 245 | 246 | 247 | if (Reflect.has(data, key)){ // 系统配置存在 248 | target = data; 249 | } 250 | 251 | return Reflect.has(target, key) ? target[key] : false; 252 | } 253 | }); 254 | 255 | 256 | module.exports = proxyConfig; 257 | -------------------------------------------------------------------------------- /electron/src/core/docker.node.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const configCommand = require('../config/config.command'); 4 | const dockerConfig = require('../config/config.docker'); 5 | const commonFunc = require('./commonFunc'); 6 | const EventEmitter = require('events'); 7 | const event = new EventEmitter(); 8 | const Error = require('./error.js'); 9 | const fs = require('fs'); // 调试写文件 10 | const os = require('os'); 11 | const util = require('util'); 12 | const {sendCallBack} = require('./docker'); 13 | const processExec = require('./process.exec'); 14 | let commandMapFunc = require('./docker.commandMap'); 15 | let cmd = Symbol('cmd'), 16 | clearRow = '', 17 | endRow = '', 18 | closing = '', // 结束时发送 19 | dockerCmdStartBool = false, // 直接启用命令 20 | syncInit = Symbol('syncInit'); 21 | 22 | 23 | 24 | 25 | 26 | class NodeDocker { 27 | constructor(shellName = '') { 28 | this.event = null; // IpcMain的回调event 29 | // 初始化 30 | this.commandData = []; // 当前执行的所有数据 31 | this.currentCommand = ''; // 当前需要执行的命令 32 | this.currentExecArgs = {}; // 当前执行命令的参数 33 | this.localSend = null; // 自定义消息输出 34 | 35 | this.headArr = configCommand.headArr; 36 | this.pe = new processExec(shellName || dockerConfig.dockerProcessName); // 是要每次都new 37 | this.syncInitBool = false; 38 | 39 | 40 | this[syncInit](); 41 | } 42 | 43 | 44 | [syncInit](){ 45 | if(!this.syncInitBool){ // 头部只执行一次 46 | this.headArr.map( 47 | (value) => this.pe.shellExec(value) 48 | ); // todo 这要保证等待执行 49 | this.syncInitBool = true; 50 | } 51 | 52 | 53 | this.pe.setCallBack('stdout_data', (allMsg, errMsg) => { 54 | // console.log(this); // todo 会出现this一直是同样的值的问题 55 | // console.log('out_data'); 56 | 57 | // asyncFunc(allMsg); 58 | this.execDockerAsync(0, allMsg, errMsg); 59 | }); 60 | // this.pe.setCallBack('stderr_data', (msg) => { // docker命令会在stderr中输出 61 | // // fs.appendFile('F:/code/www/electron/a.txt', msg); 62 | // // console.log(msg.toString().includes('')); 63 | // // console.log('err_data'); 64 | // console.log([222, msg]); 65 | // this.execDockerAsync(0, '', msg); 66 | // }); 67 | this.pe.setCallBack('on_close', (code) => { // electron关闭后,貌似不会运行此 68 | // console.log('close'); 69 | 70 | this.execDockerAsync(code, '', ''); 71 | }); 72 | } 73 | 74 | 75 | execDockerSwitch(startSwitch){ 76 | dockerCmdStartBool = startSwitch; 77 | 78 | if (startSwitch){ // 开始 79 | // console.log(EventEmitter.listenerCount(event, 'execDockerSwitch')); 80 | event.emit('execDockerSwitch'); 81 | } 82 | } 83 | 84 | 85 | 86 | /** 87 | * 执行docker命令,会解析,入口 88 | * 89 | * @param type 90 | * @param name 执行的命令name 91 | * @param args 92 | * @returns {boolean} 93 | */ 94 | execDocker(type, name, args) { 95 | let switchBool = false; 96 | if (type === 'async-switch'){ 97 | type = 'async'; 98 | this.pe.setProcessEnd(true); 99 | switchBool = true; 100 | } 101 | 102 | if (!dockerCmdStartBool && !switchBool){ // 等待执行 103 | event.addListener('execDockerSwitch', () => { 104 | this.execDocker(type, name, args); 105 | }); 106 | return false; 107 | } 108 | 109 | this.currentExecArgs = args; 110 | 111 | if (type !== 'sync') { // 异步 async 112 | this.exec(name, args, this.execDockerAsync); 113 | } else { // 同步 sync 114 | let value = this.exec(name, args, null); 115 | 116 | if (this.event) this.event.returnValue = value; 117 | } 118 | 119 | return true; 120 | } 121 | 122 | 123 | 124 | execDockerAsync(error, allMsg, errMsg){ 125 | 126 | if (this){ // todo 出现this不存在的情况 127 | if(util.isFunction(this.getCommandData().filterMsg)){ // 过滤,要绑定当前this 128 | // todo 暂无考虑process.exec的tailBool为true时 129 | allMsg = Reflect.apply(this.getCommandData().filterMsg, this, [allMsg, this]); 130 | 131 | if (util.isNull(allMsg)) {return;} // 返回null,则不发送 132 | } 133 | 134 | this.execDockerAsyncSend(error, allMsg, errMsg); 135 | } 136 | 137 | 138 | config.dockerNodeCommandLog && allMsg && console.log('output : ' + os.EOL + allMsg); // 命令输出 139 | } 140 | 141 | 142 | /** 143 | * 发送命令的返回 144 | * 145 | * @param error 146 | * @param allMsg 147 | * @param errMsg 148 | */ 149 | execDockerAsyncSend(error, allMsg, errMsg){ 150 | if (this.event && this.currentExecArgs['callbackName']) { 151 | config.dockerNodeCommandLog && console.log(`event.sender.send : ${this.currentExecArgs['callbackName']}`); 152 | this.event.sender.send( 153 | this.currentExecArgs['callbackName'], 154 | sendCallBack(allMsg, this.currentExecArgs['name'], parseInt(error), errMsg) 155 | ); // todo 待添加错误处理 156 | }else if(typeof this.localSend === 'function'){ 157 | this.localSend(allMsg, this.currentExecArgs['name'], parseInt(error), errMsg); 158 | } 159 | } 160 | 161 | 162 | 163 | /** 164 | * 通过cmd执行命令 165 | * 166 | * @param command 167 | * @param asyncFunc 这个参数在异步中应该没有用,同步未知 168 | */ 169 | cmdExec(command, asyncFunc){ // 木有cwd 170 | // console.log('cmdExec'); 171 | this.pe.shellExec(command || this.currentCommand, null, asyncFunc || function(){ 172 | // 发送完毕执行,无参数 173 | // console.log('exec command end'); 174 | }); 175 | 176 | 177 | } 178 | 179 | 180 | /** 181 | * 执行系统命令 182 | * 183 | * @param name 184 | * @param arg 185 | * @param asyncFunc 186 | */ 187 | exec(name, arg, asyncFunc = null) { // todo 可试下spawn的args来解决command.arg 188 | this.commandData = commandMapFunc(name, arg); 189 | let commandData = this.getCommandData(), 190 | command = commandData.command; 191 | if (!command) { 192 | return false; 193 | } 194 | this.currentCommand = command; 195 | // console.log(this.commandData); 196 | 197 | // 是否持续输出 198 | commandData.setTailBool && this.setTailBool(commandData.setTailBool); 199 | commandData.restart && (this.pe.restart = commandData.restart); 200 | 201 | if (asyncFunc && typeof asyncFunc === 'function') { // 异步 202 | this.cmdExec(command, asyncFunc); 203 | // this.cmdExec(command, commandData.execOption, asyncFunc); 204 | 205 | // return true; 206 | } else { // 同步执行会影响整个electron主进程 207 | command = configCommand.execShellNameSync(command); 208 | console.log('sync exec command: ' + command); 209 | 210 | let tempOption = Object.assign({}, this.getCommandData().execOption || {}), // 取消一层引用 211 | option = dockerConfig.execOption; 212 | if (!commonFunc.emptyObject(tempOption.env)) { // 处理env对象 213 | option.env = Object.assign(option.env, tempOption.env); 214 | Reflect.deleteProperty(tempOption, 'env'); 215 | } 216 | 217 | let data = this.pe.execSync( 218 | command, 219 | Object.assign(option, tempOption) 220 | ); 221 | if (this.getCommandData().hasOwnProperty('returnData') && typeof this.getCommandData().returnData === 'function') { // 自定义处理函数 222 | return this.getCommandData().execData(data); 223 | } else { 224 | // const iconv = require('iconv-lite'); 225 | // data= data ? iconv.decode(data, 'GBK') : ''; // 未测试 226 | return data; 227 | } 228 | } 229 | } 230 | 231 | 232 | 233 | getCommandData(){ 234 | return this.commandData || {}; 235 | } 236 | 237 | 238 | setTailBool(bool){ 239 | this.pe.setTailBool(bool); 240 | } 241 | 242 | } 243 | 244 | 245 | 246 | 247 | module.exports = NodeDocker; 248 | 249 | 250 | 251 | 252 | // 253 | // 254 | // function getDir() { 255 | // let msg = ''; 256 | // // let bat = spawn('powershell.exe', ['dir']); 257 | // // exec('powershell.exe dir', [], function (error, stdout, stderr) { 258 | // // 259 | // // msg = 'ppppp'; 260 | // // // console.log(msg); 261 | // // }); 262 | // 263 | // msg = iconv.decode(execSync('dir /B'), 'GBK'); 264 | // 265 | // return msg; 266 | // 267 | // // bat.stdout.on('data', (data) => { 268 | // // return iconv.decode(data, 'GBK'); 269 | // // }); 270 | // // bat.stderr.on('data', (data) => { 271 | // // return iconv.decode(data, 'GBK'); 272 | // // }); 273 | // } 274 | 275 | 276 | // let test_cmd = exec('cmd',{cwd:'F:/code/www/github/docker_lnmp/docker'}); 277 | // 278 | // test_cmd.stdin.on('data', function () { 279 | // console.log('1111111111111111') 280 | // }); 281 | // test_cmd.stdout.on('data', function (data) { 282 | // // console.log('hhhhhhhhhhhhhhhhhhhhhhhhh:' + data); 283 | // }); 284 | // 285 | // 286 | // test_cmd.stdin.write('@echo off \n'); 287 | // test_cmd.stdin.write('cd ./nginx \n'); 288 | // test_cmd.stdin.write('dir\n', null, function (...arg) { 289 | // console.log(arg) 290 | // }); 291 | // test_cmd.stdin.end(); 292 | // // console.log(test_cmd.stdin) 293 | // 294 | // 295 | // // 296 | // test_cmd.on('exit', function (code) { 297 | // console.log('code : ' + code); 298 | // }); -------------------------------------------------------------------------------- /electron/src/resources/OPENSSH_LICENSE: -------------------------------------------------------------------------------- 1 | Kitematic includes OpenSSH ssh.exe from the msysgit distribution version Git-1.9.5-preview20150319, available online at https://github.com/msysgit/git/tree/v1.9.5.msysgit.1 2 | 3 | This file is part of the OpenSSH software. 4 | 5 | The licences which components of this software fall under are as 6 | follows. First, we will summarize and say that all components 7 | are under a BSD licence, or a licence more free than that. 8 | 9 | OpenSSH contains no GPL code. 10 | 11 | 1) 12 | * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland 13 | * All rights reserved 14 | * 15 | * As far as I am concerned, the code I have written for this software 16 | * can be used freely for any purpose. Any derived versions of this 17 | * software must be clearly marked as such, and if the derived work is 18 | * incompatible with the protocol description in the RFC file, it must be 19 | * called by a name other than "ssh" or "Secure Shell". 20 | 21 | [Tatu continues] 22 | * However, I am not implying to give any licenses to any patents or 23 | * copyrights held by third parties, and the software includes parts that 24 | * are not under my direct control. As far as I know, all included 25 | * source code is used in accordance with the relevant license agreements 26 | * and can be used freely for any purpose (the GNU license being the most 27 | * restrictive); see below for details. 28 | 29 | [However, none of that term is relevant at this point in time. All of 30 | these restrictively licenced software components which he talks about 31 | have been removed from OpenSSH, i.e., 32 | 33 | - RSA is no longer included, found in the OpenSSL library 34 | - IDEA is no longer included, its use is deprecated 35 | - DES is now external, in the OpenSSL library 36 | - GMP is no longer used, and instead we call BN code from OpenSSL 37 | - Zlib is now external, in a library 38 | - The make-ssh-known-hosts script is no longer included 39 | - TSS has been removed 40 | - MD5 is now external, in the OpenSSL library 41 | - RC4 support has been replaced with ARC4 support from OpenSSL 42 | - Blowfish is now external, in the OpenSSL library 43 | 44 | [The licence continues] 45 | 46 | Note that any information and cryptographic algorithms used in this 47 | software are publicly available on the Internet and at any major 48 | bookstore, scientific library, and patent office worldwide. More 49 | information can be found e.g. at "http://www.cs.hut.fi/crypto". 50 | 51 | The legal status of this program is some combination of all these 52 | permissions and restrictions. Use only at your own responsibility. 53 | You will be responsible for any legal consequences yourself; I am not 54 | making any claims whether possessing or using this is legal or not in 55 | your country, and I am not taking any responsibility on your behalf. 56 | 57 | 58 | NO WARRANTY 59 | 60 | BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 61 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 62 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 63 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 64 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 65 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 66 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 67 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 68 | REPAIR OR CORRECTION. 69 | 70 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 71 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 72 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 73 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 74 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 75 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 76 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 77 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 78 | POSSIBILITY OF SUCH DAMAGES. 79 | 80 | 2) 81 | The 32-bit CRC compensation attack detector in deattack.c was 82 | contributed by CORE SDI S.A. under a BSD-style license. 83 | 84 | * Cryptographic attack detector for ssh - source code 85 | * 86 | * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. 87 | * 88 | * All rights reserved. Redistribution and use in source and binary 89 | * forms, with or without modification, are permitted provided that 90 | * this copyright notice is retained. 91 | * 92 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 93 | * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE 94 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR 95 | * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS 96 | * SOFTWARE. 97 | * 98 | * Ariel Futoransky 99 | * 100 | 101 | 3) 102 | ssh-keyscan was contributed by David Mazieres under a BSD-style 103 | license. 104 | 105 | * Copyright 1995, 1996 by David Mazieres . 106 | * 107 | * Modification and redistribution in source and binary forms is 108 | * permitted provided that due credit is given to the author and the 109 | * OpenBSD project by leaving this copyright notice intact. 110 | 111 | 4) 112 | The Rijndael implementation by Vincent Rijmen, Antoon Bosselaers 113 | and Paulo Barreto is in the public domain and distributed 114 | with the following license: 115 | 116 | * @version 3.0 (December 2000) 117 | * 118 | * Optimised ANSI C code for the Rijndael cipher (now AES) 119 | * 120 | * @author Vincent Rijmen 121 | * @author Antoon Bosselaers 122 | * @author Paulo Barreto 123 | * 124 | * This code is hereby placed in the public domain. 125 | * 126 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS 127 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 128 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 129 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE 130 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 131 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 132 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 133 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 134 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 135 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 136 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 137 | 138 | 5) 139 | One component of the ssh source code is under a 3-clause BSD license, 140 | held by the University of California, since we pulled these parts from 141 | original Berkeley code. 142 | 143 | * Copyright (c) 1983, 1990, 1992, 1993, 1995 144 | * The Regents of the University of California. All rights reserved. 145 | * 146 | * Redistribution and use in source and binary forms, with or without 147 | * modification, are permitted provided that the following conditions 148 | * are met: 149 | * 1. Redistributions of source code must retain the above copyright 150 | * notice, this list of conditions and the following disclaimer. 151 | * 2. Redistributions in binary form must reproduce the above copyright 152 | * notice, this list of conditions and the following disclaimer in the 153 | * documentation and/or other materials provided with the distribution. 154 | * 3. Neither the name of the University nor the names of its contributors 155 | * may be used to endorse or promote products derived from this software 156 | * without specific prior written permission. 157 | * 158 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 159 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 160 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 161 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 162 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 163 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 164 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 165 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 166 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 167 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 168 | * SUCH DAMAGE. 169 | 170 | 6) 171 | Remaining components of the software are provided under a standard 172 | 2-term BSD licence with the following names as copyright holders: 173 | 174 | Markus Friedl 175 | Theo de Raadt 176 | Niels Provos 177 | Dug Song 178 | Aaron Campbell 179 | Damien Miller 180 | Kevin Steves 181 | Daniel Kouril 182 | Wesley Griffin 183 | Per Allansson 184 | Nils Nordman 185 | Simon Wilkinson 186 | 187 | * Redistribution and use in source and binary forms, with or without 188 | * modification, are permitted provided that the following conditions 189 | * are met: 190 | * 1. Redistributions of source code must retain the above copyright 191 | * notice, this list of conditions and the following disclaimer. 192 | * 2. Redistributions in binary form must reproduce the above copyright 193 | * notice, this list of conditions and the following disclaimer in the 194 | * documentation and/or other materials provided with the distribution. 195 | * 196 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 197 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 198 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 199 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 200 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 201 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 202 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 203 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 204 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 205 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 206 | -------------------------------------------------------------------------------- /electron/src/core/event.js: -------------------------------------------------------------------------------- 1 | const {Tray, Menu, BrowserWindow, nativeImage, app} = require('electron'); 2 | const dotEnv = require('dotenv'); 3 | const fs = require('fs'); 4 | 5 | let appIcon, 6 | eventConfig, 7 | mainWindow = {}; 8 | global.mainWindow = mainWindow; 9 | global.appStartTime = (new Date()).getTime(); 10 | 11 | function createWindow(){ 12 | //创建一个 800x600 的浏览器窗口 13 | mainWindow = new BrowserWindow(config.BrowserWindow); 14 | 15 | 16 | // 使用devtron 17 | disDevtron(); 18 | 19 | 20 | 21 | //加载应用的界面文件 22 | let url = config.mainLoadUrl(); 23 | mainWindow.loadURL(url); 24 | console.log('mainWindow loadURL : ' + url); 25 | 26 | 27 | //打开开发者工具,方便调试 28 | config.openDevTools && mainWindow.webContents.openDevTools(); 29 | 30 | // 窗口关闭时触发 31 | mainWindow.on('closed', function () { 32 | // 想要取消窗口对象的引用,如果你的应用支持多窗口, 33 | // 通常你需要将所有的窗口对象存储到一个数组中, 34 | // 在这个时候你应该删除相应的元素 35 | mainWindow = null; 36 | }); 37 | 38 | mainWindow.on('page-title-updated', function () { 39 | appIcon.setToolTip(config.title); 40 | appIcon.setTitle(config.title); 41 | }) 42 | 43 | } 44 | 45 | 46 | function disDevtron() { // 安装vue-devtools 47 | const devtron = require('vue-devtools'); 48 | 49 | if (config.electorn_devtron) { 50 | // 必须要在new BrowserWindow后执行 51 | 52 | // 不要每次都输出 53 | !BrowserWindow.getDevToolsExtensions().hasOwnProperty('Vue.js devtools') && devtron.install(); 54 | }else if (!config.electorn_devtron){ 55 | devtron.uninstall(); 56 | } 57 | } 58 | 59 | 60 | eventConfig = { // todo 待,通过get()将处理逻辑放入对象内 61 | 62 | 'ipcRenderer-window-mini': function(arg, event){ // 渲染端调用最小化 63 | let window = event && event.sender ? BrowserWindow.fromWebContents(event.sender) : mainWindow; 64 | if (!window.isMinimized()) { 65 | window.minimize(); 66 | } 67 | }, 68 | 'ipcRenderer-window-close': function(arg, event){ // 渲染端调用关闭窗口 69 | // todo 可迁移到remote中 70 | arg = arg || {}; 71 | let window = event && event.sender ? BrowserWindow.fromWebContents(event.sender) : mainWindow; 72 | if (arg.close) { 73 | window.close(); 74 | } else { // 后台运行 75 | window.hide(); 76 | } 77 | }, 78 | 79 | 'app-ready': { 80 | checkOnlyWindow(){ // 窗口唯一性 81 | let bool = app.makeSingleInstance((commandLine, workingDirectory) => { 82 | if (mainWindow){ 83 | mainWindow.show(); 84 | } 85 | }); 86 | 87 | if (bool){ 88 | app.quit(); 89 | return 'exit'; 90 | } 91 | }, 92 | loadEnv(){ // 加载env 93 | let envConfig = dotEnv.parse(fs.readFileSync(config.dockerPath + '/.env')); 94 | for (let k in envConfig) { 95 | if (!Reflect.has(process.env, k)) { 96 | process.env[k] = envConfig[k] 97 | } 98 | } 99 | }, 100 | mac(){ // mac处理 101 | if (process.platform !== 'darwin'){ 102 | return; 103 | } 104 | 105 | app.setAboutPanelOptions(config.electronMacAboutPanel); 106 | // app.dock.setBadge(config.title) 107 | }, 108 | firstStart(){ // 首次运行 109 | const NodeDocker = require('./docker.node.js'); 110 | const nodeStorage = require('./nodeStorage'); 111 | let containerConfigKey = 'containerConfig', 112 | dockerNode = new NodeDocker(), 113 | machineInspectFunc = function (){ 114 | dockerNode.localSend = function (msg, name, error){ 115 | const configCommand = require('../config/config.command'); 116 | if (error || !msg){ 117 | return; 118 | } 119 | 120 | let inspectData = {}; 121 | try{ 122 | msg = configCommand.getNotEnter(msg); 123 | inspectData = JSON.parse(msg); 124 | }catch (e){ 125 | console.error('json解析错误' + msg); 126 | } 127 | 128 | if (Reflect.has(inspectData, 'Name')){ 129 | nodeStorage.setItem(containerConfigKey + '.DOCKER_TLS_VERIFY', 1); 130 | nodeStorage.setItem( 131 | containerConfigKey + '.DOCKER_HOST', 132 | 'tcp://' + inspectData['Driver']['IPAddress'] + ':2376' // 这里url是拼接的,正确应该是通过docker-machine url获取的 133 | ); 134 | nodeStorage.setItem( 135 | containerConfigKey + '.DOCKER_CERT_PATH', 136 | inspectData['HostOptions']['AuthOptions']['StorePath'] 137 | ); 138 | nodeStorage.setItem(containerConfigKey + '.COMPOSE_CONVERT_WINDOWS_PATHS', true); 139 | 140 | nodeStorage.setItem('firstStartEvents', 2); // docker-machine管理,未配置 141 | dockerNode.execDockerSwitch(true); 142 | } 143 | }; 144 | 145 | // todo 看下是否必传{} 146 | dockerNode.execDocker('async-switch', 'docker-machine/inspect-json', {}); 147 | }; 148 | 149 | 150 | let firstStartValue = nodeStorage.getItem('firstStartEvents'), 151 | containerConfigBool = nodeStorage.getItem(containerConfigKey + '.DOCKER_TLS_VERIFY'); 152 | 153 | if (!firstStartValue && !containerConfigBool) { 154 | // 检查vmOrVirtualBox 155 | dockerNode.localSend = function (msg, name, error, errMsg){ 156 | 157 | // 可以找到但是virtualbox环境 158 | if (msg.match('"provider=virtualbox"')){ // docker-machine管理,并已成功配置 159 | nodeStorage.setItem('firstStartEvents', 1); 160 | dockerNode.execDockerSwitch(true); 161 | return; 162 | }else if ( 163 | msg.match('error during connect:') || 164 | msg.match('Is the docker daemon running?') 165 | ){ // 连接错误 166 | // docker-machine inspect 167 | machineInspectFunc(); 168 | }else{ // 没有报错,也没有labels,应该是直连 169 | dockerNode.execDockerSwitch(true); 170 | } 171 | 172 | }; 173 | 174 | dockerNode.execDocker('async-switch', 'docker/info-labels', {}); 175 | }else if (firstStartValue === 2){ // docker-machine 176 | // 随时可能改变ip 177 | machineInspectFunc(); 178 | // dockerNode.execDockerSwitch(true); 179 | }else{ 180 | dockerNode.execDockerSwitch(true); 181 | } 182 | }, 183 | createWindow, 184 | menu () { 185 | const { defaultMenu, trayMenu, dockMenu } = require('./menu'); // 放在创建中 186 | let iconName = process.platform === 'win32' ? 'windows-icon.png' : 'iconTemplate.png'; 187 | let iconNative = nativeImage.createFromPath(`${config.srcPath}/images/${iconName}`); 188 | // icon菜单 189 | appIcon = new Tray(iconNative); 190 | 191 | appIcon.setContextMenu(Menu.buildFromTemplate(trayMenu)); 192 | appIcon.on('double-click', function (event , bounds) { // 双击 193 | if (event.altKey){ // 重启 194 | 195 | }else{ // 激活主页面 196 | mainWindow.show(); 197 | } 198 | }); 199 | // 在上面(createWindow)的on中 200 | // appIcon.setToolTip(config.title); 201 | // appIcon.setTitle(config.title); 202 | 203 | 204 | // 系统菜单 205 | Menu.setApplicationMenu(Menu.buildFromTemplate(defaultMenu)); 206 | 207 | 208 | if (process.platform === 'darwin'){ // mac的dock右键菜单 209 | app.dock.setMenu(Menu.buildFromTemplate(dockMenu)); 210 | } 211 | }, 212 | 'docker-events': function () { // docker-compose events --json 213 | const NodeDocker = require('./docker.node'); 214 | const configCommand = require('../config/config.command'); 215 | let {eventsName} = require('./docker'); 216 | 217 | let eventsData = new NodeDocker('docker-events'); 218 | eventsData.localSend = function(msg){ 219 | if (!msg) return; 220 | 221 | try{ 222 | // 有可能一次输出多个json数据 223 | msg = JSON.parse(configCommand.lineArr(msg).filter(val => !!val).pop()); 224 | }catch (error){ 225 | console.log('docker events error msg : ' + msg); 226 | return; 227 | } 228 | 229 | mainWindow.webContents.send(eventsName, msg); 230 | }; 231 | eventsData.execDocker('async', 'docker-compose/ps-events', {'callbackName' : eventsName}); 232 | }, 233 | }, 234 | 235 | 236 | 'app-window-all-closed':{ 237 | tray() { 238 | appIcon.destroy(); 239 | } 240 | }, 241 | 242 | 'app-activate':{ 243 | createWindow(){ 244 | if (mainWindow === null){ 245 | createWindow(); 246 | } 247 | } 248 | }, 249 | 250 | 'app-page-title-updated':{ 251 | 252 | } 253 | }; 254 | 255 | 256 | 257 | 258 | 259 | module.exports = function (name, arg, event = null){ 260 | if (!name) { 261 | return false; 262 | } 263 | 264 | if (eventConfig.hasOwnProperty(name)) { 265 | let eventData = eventConfig[name]; 266 | switch (typeof eventData){ 267 | case 'function': 268 | eventData(arg, event); 269 | break; 270 | case 'object': 271 | for (let key in eventData){ // es7 Object.entries 272 | if (eventData.hasOwnProperty(key)){ 273 | let eventDataType = typeof eventData[key]; 274 | if (eventDataType === 'function') { // 多个需要处理的事件函数 275 | 276 | let eventReturn = eventData[key](arg, event); 277 | if (eventReturn === 'exit') { // 返回 278 | break; 279 | } 280 | 281 | }else if(eventDataType === 'object'){ // 未定 282 | 283 | } 284 | } 285 | } 286 | break; 287 | } 288 | } 289 | 290 | return true; 291 | }; 292 | 293 | 294 | -------------------------------------------------------------------------------- /docker/php/src/php-ext.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # sudo rm -rf /etc/apt/sources.list.d/ /var/lib/apt/lists/partial/ && apt update 4 | # set -e 5 | 6 | 7 | 8 | 9 | 10 | # 安装函数 11 | function install_so(){ 12 | { 13 | local tag_dir='' 14 | 15 | if ext_config_value_exists $EXT_DEPEND; then 16 | # 等待apt 17 | apt_get_fifo 18 | fi 19 | 20 | 21 | 22 | processes_get_fifo 23 | 24 | # 检测依赖扩展 25 | if [[ $install_json_bool > 0 ]];then 26 | check_not_installed "$EXT_SORT" 27 | fi 28 | 29 | echo -e "\e[1;1m ==========================${EXT_NAME}开始===================== \e[0m" 1>&2 30 | 31 | if [[ -d $EXT_TGZ_DIR ]]; then 32 | tag_dir=$EXT_TGZ_DIR 33 | elif ext_config_value_exists $EXT_URL; then 34 | tag_dir=$temp_dir/$EXT_NAME 35 | tar_file_path=${tag_dir}/${EXT_NAME}'.tgz' 36 | 37 | mkdir -p $tag_dir 38 | # 定义下载回来默认文件名 39 | curl_s "$EXT_URL" "${tar_file_path}" 40 | 41 | untar $EXT_URL_TYPE ${tar_file_path} $tag_dir 42 | 43 | if [[ $install_json_bool > 0 ]];then 44 | # 删除压缩包 45 | rm -rf "${tag_dir}.tgz" 46 | fi 47 | fi 48 | 49 | 50 | ext_no_make=0 51 | if ext_config_value_exists $EXT_EVAL; then 52 | # 可通过ext_no_make跳过make阶段 53 | eval $EXT_EVAL 54 | fi 55 | 56 | 57 | if [[ $ext_no_make < 1 ]]; then 58 | cd $tag_dir 59 | # 进入编译目录,如果没有找到会跑到用户目录去 60 | cd `find ./ \( -iname 'config.m4' -o -iname 'config0.m4' \) -exec dirname {} \;` 61 | # config0.m4改为config.m4 62 | if [[ -f config0.m4 ]]; then 63 | mv config0.m4 config.m4 64 | fi 65 | 66 | phpize 67 | ./configure $EXT_ARG 68 | make && make install 69 | 70 | # 删除网络包,install.json安装时才删除 71 | if [[ (! -d $EXT_TGZ_DIR) && $install_json_bool > 0 ]]; then 72 | rm -fr $tag_dir 73 | else 74 | echo '==============================='$tag_dir'============================' 1>&2 75 | fi 76 | fi 77 | 78 | processes_write_fifo 79 | } 1> ${log_fd} 80 | # } 81 | 82 | # 生成的so文件复制到php.ini指定的目录下 83 | # cp $(php-config --extension-dir)/* $(php -r 'echo ini_get("extension_dir");') 84 | 85 | echo -e "\e[1;33m ==========================${EXT_NAME}完成===================== \e[0m" 86 | 87 | echo "$EXT_NAME" >> $installed_path 88 | } 89 | 90 | 91 | 92 | # 主方法 93 | php_ext_main(){ 94 | echo -e "\e[30;46m ==========================开始安装PHP扩展===================== \e[0m" 95 | # 保持目录 96 | cd $root_dir; 97 | config_path=$root_dir/config.json 98 | install_json_path=$root_dir/install.json 99 | # docker的共享目录无法执行编译操作和fifo 100 | temp_dir=/tmp/php-ext/temp 101 | fifo_tmp_dir='/tmp/php-ext/docker_fifo' 102 | fifo_path=$fifo_tmp_dir/fifo_apt_update 103 | installed_path=$fifo_tmp_dir/file_installed 104 | fifo_lock='ext_apt_update_lock' 105 | # 同步安装数量,使用环境变量 106 | processes_num=${PHP_PROCESSES_NUM:-15} 107 | processes_path=$fifo_tmp_dir/fifo_processes 108 | install_json_bool=1 # 是否通过install.json安装,默认是,否为通过指定扩展安装 109 | 110 | 111 | mkdir -p ${temp_dir} ${fifo_tmp_dir} 112 | touch $installed_path 113 | echo '' > $installed_path; # 清空 114 | 115 | # 判断文件是否存在 116 | if [[ -f ${config_path} ]]; then 117 | # config.json内容 118 | # @todo 待优化为(?&2 231 | apt update 232 | 233 | # 不支持并发执行 234 | export DEBIAN_FRONTEND=noninteractive 235 | apt install -y --no-install-recommends $ext_depend 236 | 237 | echo -e "\e[1;33m ==========================apt完成===================== \e[0m" 1>&2 238 | 239 | # @todo 可改用信号 240 | for (( i = 0; i < $ext_depend_num; i++ )); do 241 | apt_write_fifo 242 | done 243 | 244 | } 1> ${log_fd} & 245 | # } & 246 | # 等待查找 247 | # sleep 2 248 | fi 249 | } 250 | 251 | # 检查是否存在值 252 | ext_config_value_exists(){ 253 | if [[ $1 > 0 && $1 != null ]]; then 254 | # 成功 255 | return 0 256 | else 257 | return 1 258 | fi 259 | } 260 | 261 | # $1 写入的内容 262 | apt_write_fifo(){ 263 | echo "$1">&33 264 | } 265 | 266 | 267 | apt_get_fifo(){ 268 | local row='' 269 | read -u33 row 270 | echo $row 271 | } 272 | 273 | # $1 fifo地址 274 | apt_exec_fifo(){ 275 | exec 33<>$1 276 | rm -fr $1 277 | } 278 | 279 | apt_end_fifo(){ 280 | rm -fr $1; 281 | exec 33>&-; 282 | exec 33<&-; 283 | } 284 | 285 | processes_end_fifo(){ 286 | rm -fr $1; 287 | exec 32>&-; 288 | exec 32<&-; 289 | } 290 | 291 | 292 | # $1 写入的内容 293 | processes_write_fifo(){ 294 | echo "$1" >&32 295 | } 296 | 297 | 298 | processes_get_fifo(){ 299 | local row='' 300 | read -u32 row 301 | echo $row 302 | } 303 | 304 | # $1 fifo地址 305 | processes_exec_fifo(){ 306 | exec 32<>$1 307 | rm -fr $1 308 | } 309 | 310 | 311 | # 控制curl是否显示进度条 312 | curl_s(){ 313 | # 多任务需要下载时有可能失败 314 | if [[ $log_fd = '/dev/null' ]]; then 315 | curl -L --retry 3 -fs "$1" -o "$2" 316 | else 317 | curl -L --retry 3 -f "$1" -o "$2" 318 | fi 319 | } 320 | 321 | # 多种解压方式 322 | untar(){ 323 | if [[ $1 == 'zip' ]]; then 324 | unzip -o $2 -d $3 325 | else 326 | tar -zxf "$2" -C $3 327 | fi 328 | } 329 | 330 | 331 | 332 | function check_not_installed(){ 333 | local val; 334 | 335 | if ! ext_config_value_exists "$1"; then 336 | # 可以安装 337 | return 1 338 | fi 339 | 340 | local check_arr=$(echo $1|jq -r ".[]") 341 | 342 | for val in $check_arr; do 343 | if [[ `grep "${val}" ${installed_path} | wc -l` < 1 ]]; then 344 | cat $installed_path 1>&2; 345 | echo $val 1>&2; 346 | processes_reset_fifo "$EXT_NAME" 347 | check_not_installed "$1" 348 | return; 349 | fi 350 | echo 'success install'; 1>&2; 351 | echo $val 1>&2; 352 | done 353 | 354 | return 1 355 | } 356 | 357 | 358 | 359 | function processes_reset_fifo(){ 360 | processes_write_fifo 361 | echo '重新构建'$1 1>&2; 362 | sleep 1 363 | processes_get_fifo 364 | } 365 | 366 | 367 | 368 | 369 | # 执行 370 | start_time=`date "+%s"` 371 | root_dir=$(cd $(dirname $(readlink -f $0));pwd) 372 | log_fd='/dev/null' 373 | # 支持 ./php-ext.sh curl 374 | if [[ $# > 0 ]]; then 375 | # 直接调用时显示输出 376 | log_fd='/proc/self/fd/1' 377 | 378 | opt_json_text='' 379 | for i in $*; do 380 | opt_json_text+='"'${i}'",' 381 | done 382 | opt_json_text='['${opt_json_text:0:-1}']' 383 | 384 | # 传参安装 385 | php_ext_main $opt_json_text 386 | else 387 | # 用install安装 388 | php_ext_main; 389 | fi 390 | 391 | 392 | let end_time=`date "+%s"`-start_time 393 | 394 | echo -e "\e[30;46m ---------------------------------Time : "${end_time}"--------------------------------------- \e[0m" 395 | echo -------------------------------------------------------------------------------- /electron/src/core/process.exec.js: -------------------------------------------------------------------------------- 1 | const {exec : cp_exec, execSync: cp_execSync, spawn:cp_spawn} = require('child_process'); 2 | const dockerConfig = require('../config/config.docker'); 3 | const configCommand = require('../config/config.command'); 4 | const commonFunc = require('./commonFunc'); 5 | 6 | let cmd = commonFunc.mapData(), 7 | allData = commonFunc.mapData(), // 各种需要的数据数据 8 | dataInitSymbol = Symbol('dataInit'), // data的init 9 | shellNew = Symbol('shell'), // 一条命令没执行完,则必须创建新进程执行命令 10 | shellNewNum = 1, // 生成不同的shellName 11 | shellEOF = 'nodeShellEOF-----------------------nodeShellEOF'; 12 | 13 | class pExec{ 14 | 15 | constructor(shellName = '') { 16 | this.shellName = shellName ? shellName : 'defaultShellName'; 17 | this.processData = {}; // 存储callback,应该放在this中 18 | this.processEndBool = false; // 执行一条命令后就关闭进程 19 | this.newLog = false; // 这是一条新命令,不需重复输出log 20 | this.msg = { // 命令的输出 21 | outMsg:'', 22 | errMsg:'', 23 | allMsg:'', 24 | }; 25 | this['callback'] = {}; 26 | this.tailBool = false; 27 | this.restart = false; 28 | this.args = []; 29 | } 30 | 31 | 32 | shellInit(){ 33 | let shellName = this.shellName; 34 | if (!cmd.getData(shellName)){ 35 | config.processExecCommandLog && console.log(`open process : ${shellName}`); 36 | this[dataInitSymbol](); 37 | // 系统环境变量没有了 38 | cmd.setData(shellName, this.exec(configCommand.execShellName, { 39 | cwd: dockerConfig.execCwd, 40 | env: dockerConfig.execEnv, 41 | })); 42 | 43 | // virtualBox下的docker-compose必须是chcp 936 44 | // cp_exec('docker-machine ls \n', function (){ 45 | // console.log(arguments); 46 | // }); 47 | // cp_execSync('start powershell \n'); 48 | // cmd.stdin.write('start cmd \n'); 49 | } 50 | } 51 | 52 | 53 | shell(){ 54 | // console.log(Object.keys(cmd)); 55 | let shellName = this.shellName; 56 | this.shellInit(); 57 | 58 | // process callback 59 | this['callback'] = { 60 | stdout : (msg) => { 61 | let errMsg = ''; 62 | // console.log('stdout_data'); 63 | // console.log(['stdout_data', msg, this]); 64 | this.setMsg(msg, 'out'); 65 | if (this.ifCommandEnd()) { // 已结束 66 | errMsg = this.getMsg('err'); 67 | msg = this.msgCommandEnd('all'); // 同时清空 68 | this.msgCommandEndFunc(); 69 | } else if (!this.getTailBool()) { // 需一次输出,等待满足上面条件 70 | return; 71 | } 72 | 73 | this.getCallBack('stdout_data')(msg, errMsg); 74 | }, 75 | stderr: (msg) => { 76 | // console.log('stderr_data'); 77 | this.setMsg(msg, 'err'); 78 | if (this.ifCommandEnd()) { // 已结束 79 | // msg = this.msgCommandEnd('err'); 80 | // this.msgCommandEndFunc(); 81 | } else if (!this.getTailBool()) { // 需一次输出,等待满足上面条件 82 | return; 83 | } 84 | 85 | // 结束符必定是成功的 86 | // this.getCallBack('stderr_data')(msg); 87 | // this.noSaveMsg && this.setMsg('', '', true); 88 | }, 89 | close: (code) => { // 未验证 90 | // console.log('on_close'); 91 | this.setTailBool(); 92 | 93 | this.getCallBack('on_close')(code); 94 | 95 | if (this.restart) this.shellExec(...this.args); // 报错重启 96 | }, 97 | }; 98 | 99 | 100 | this.onListener(); 101 | 102 | 103 | return cmd.getData(shellName); 104 | 105 | 106 | // old 107 | // 每次新进程,必须每次都覆盖this 108 | 109 | } 110 | 111 | 112 | [shellNew](...args){ 113 | // let shellName = this.shellName + '_' + shellNewNum; 114 | let shellName = {[this.shellName]: Symbol(Math.random())}; 115 | // let shellName = `${this.shellName}-${Math.random()}`; 116 | 117 | ++shellNewNum; 118 | allData.setData(shellName, Object.assign({}, allData.getData(this.shellName))); 119 | 120 | let tempPE = new pExec(shellName); 121 | tempPE.processData = Object.assign({}, this.processData); 122 | tempPE.newLog = true; 123 | tempPE.restart = this.restart; 124 | tempPE.setTailBool(this.tailBool); 125 | tempPE.setProcessEnd(true); 126 | tempPE.setIsCommandEnd(true); 127 | tempPE.shellExec(...args); 128 | 129 | return true; 130 | } 131 | 132 | 133 | /** 134 | * 执行命令,尽量唯一入口 135 | * 136 | * @param args 137 | * @returns {*} 138 | */ 139 | shellExec(...args){ 140 | this.args = args; 141 | if (!args[0]){ 142 | return false; 143 | } 144 | 145 | if (!(/\n\s*$/.test(args[0]))) { // 处理回车 146 | args[0] = args[0] + '\n'; 147 | } 148 | 149 | 150 | // console.log([shellName, args]); 151 | config.processExecCommandLog && !this.newLog && console.log(`exec command: '${args[0].toString().trim()}'`); 152 | 153 | if (!this.getIsCommandEnd()){ // 一条命令还没有执行结束 154 | return this[shellNew](...args); 155 | } 156 | this.setIsCommandEnd(false); 157 | 158 | 159 | // 执行命令 160 | let shellStdIn = this.shell().stdin, 161 | data = shellStdIn.write(...args); 162 | // 多执行一条命令,用于是否执行结束 163 | shellStdIn.write(configCommand.ShellEOFCommand(shellEOF)); 164 | 165 | return data; 166 | } 167 | 168 | /** 169 | * 处理消息结束时的消息 170 | * 171 | * @param msg 172 | * @param type 173 | * @returns {void|*|XML|string} 174 | */ 175 | msgCommandEnd(type){ 176 | let msg = this.getMsg(type).replace(new RegExp(`\s*${shellEOF}\s*`), ''); // 只去除默认的shellEOF 177 | 178 | // 获取消息并删除 179 | this.setMsg('', '', true); // 清空 180 | this.setIsCommandEnd(true); 181 | 182 | return msg; 183 | } 184 | 185 | /** 186 | * 一条命令结束时执行 187 | */ 188 | msgCommandEndFunc() { 189 | this.removeListener(); 190 | 191 | if (this.restart) { // 重新执行 192 | this.shellExec(...this.args); 193 | }else{ 194 | this.setTailBool(); 195 | this.ProcessEnd(); 196 | } 197 | 198 | return this; 199 | } 200 | 201 | 202 | setTailBool(bool = false){ 203 | return this.tailBool = bool; 204 | } 205 | 206 | 207 | /** 208 | * 为true则下条命令会持续输出,等待其结束 209 | * 210 | * @returns {bool} 211 | */ 212 | getTailBool(){ 213 | return this.tailBool 214 | } 215 | 216 | 217 | 218 | exec(...args){ 219 | if (false && this){ // 貌似webStorm对于类的方法如果不是调用当前类的则会有警告信息 220 | return; 221 | } 222 | 223 | return cp_exec(...args); 224 | } 225 | 226 | execSync(...args){ // 同步 227 | if (false && this){ // 貌似webStorm对于类的方法如果不是调用当前类的则会有警告信息 228 | return; 229 | } 230 | 231 | return cp_execSync(...args); 232 | } 233 | 234 | 235 | /** 236 | * 命令是否输出完毕,true为完成 237 | * 238 | * @returns {boolean|*} 239 | */ 240 | ifCommandEnd(){ 241 | return this.getMsg('all').includes(shellEOF); 242 | } 243 | 244 | 245 | /** 246 | * 数据初始化 247 | */ 248 | [dataInitSymbol](){ 249 | let shellName = this.shellName; 250 | let dataDefault = { 251 | [dataInitSymbol]:true, // 已初始化,临时变量 252 | // 'processData' : {}, // callback数据 253 | 'isCommandEnd': true, // 是否执行完一条命令 254 | // processEndBool:false, // 是否执行完命令后退出 255 | // msg: { 256 | // outMsg:'', 257 | // errMsg:'', 258 | // allMsg:'', 259 | // }, 260 | tailBool : false, // 是否设置下条命令为持续输出,false为一次输出 261 | }; 262 | 263 | let shellData = allData.getData(shellName); 264 | if (!shellData || !shellData[dataInitSymbol]) { 265 | allData.setData(shellName, dataDefault); 266 | }else{ 267 | allData.setData(shellName, Object.assign(dataDefault, shellData)); 268 | } 269 | 270 | } 271 | 272 | 273 | /** 274 | * 设置child_process的回调 275 | * 276 | * @param eventName 277 | * @param callback 278 | */ 279 | setCallBack(eventName, callback){ 280 | this['processData'][eventName] = callback; 281 | } 282 | 283 | 284 | /** 285 | * 获取事件函数的引用,返回匿名函数 286 | * 287 | * @param eventName 288 | * @returns {callback} 289 | */ 290 | getCallBack(eventName){ 291 | let defaultFunc = function () {}; 292 | 293 | // if (commonFunc.isRePropertyObject(this, 'processData', eventName)) { 294 | if (this['processData'][eventName]) { 295 | let func = this['processData'][eventName]; 296 | return typeof func === 'function' ? func : defaultFunc; 297 | } 298 | 299 | return defaultFunc; 300 | } 301 | 302 | 303 | 304 | setProcessEnd(bool){ 305 | this['processEndBool'] = bool; 306 | } 307 | 308 | /** 309 | * 如果设置结束当前开启的进程则结束 310 | * 311 | * @constructor 312 | */ 313 | ProcessEnd(){ 314 | if (this['processEndBool']) { 315 | config.processExecCommandLog && console.log(`delete ${this.shellName}`); // todo 未知delete在open process之前 316 | this.shell().stdin.end(); 317 | cmd.deleteData(this.shellName); 318 | allData.deleteData(this.shellName); 319 | } 320 | } 321 | 322 | 323 | /** 324 | * 设置是否执行完一条命令 325 | * 326 | * @param bool 327 | * @returns {pExec} 328 | */ 329 | setIsCommandEnd(bool){ 330 | this[dataInitSymbol](); 331 | allData.getData(this.shellName)['isCommandEnd'] = bool; 332 | 333 | return this; 334 | } 335 | 336 | 337 | getIsCommandEnd(){ 338 | this[dataInitSymbol](); 339 | 340 | return allData.getData(this.shellName)['isCommandEnd']; 341 | } 342 | 343 | 344 | setMsg(msg, type = 'out', clear = false){ 345 | this[dataInitSymbol](); 346 | 347 | if (clear) { // 清空 348 | this['msg'] = {}; 349 | return; 350 | } 351 | 352 | if (!this['msg'][type + 'Msg']){ 353 | this['msg'][type + 'Msg'] = ''; 354 | } 355 | 356 | if (!this['msg']['allMsg']) { 357 | this['msg']['allMsg'] = ''; 358 | } 359 | 360 | this['msg'][type + 'Msg'] += msg; 361 | this['msg']['allMsg'] += msg; 362 | } 363 | 364 | 365 | getMsg(type = 'out'){ 366 | return this['msg'][type + 'Msg'] || ''; 367 | } 368 | 369 | 370 | /** 371 | * 每次命令执行完都要remove,防止多次on 与 刷新箭头函数的this 372 | */ 373 | removeListener(){ 374 | // @see https://nodejs.org/dist/latest-v7.x/docs/api/events.html#events_emitter_removelistener_eventname_listener 375 | cmd.getData(this.shellName).stdout.removeListener('data', this.callback.stdout); 376 | cmd.getData(this.shellName).stderr.removeListener('data', this.callback.stderr); 377 | cmd.getData(this.shellName).removeListener('close', this.callback.close); 378 | } 379 | 380 | onListener(){ 381 | // 命令执行完后就remove 382 | // todo 可尝试用Reflect来解决上面this问题 383 | cmd.getData(this.shellName).stdout.on('data', this.callback.stdout); 384 | cmd.getData(this.shellName).stderr.on('data', this.callback.stderr); 385 | cmd.getData(this.shellName).on('close', this.callback.close); 386 | } 387 | 388 | } 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | module.exports = pExec; 397 | 398 | --------------------------------------------------------------------------------