├── .gitignore ├── LICENSE ├── README.md ├── docs ├── imgs │ └── example.gif └── start-guide.md ├── gulpfile.js ├── icon ├── windtalker.icns └── windtalker.ico ├── package.json ├── src ├── font │ ├── fontello.eot │ ├── fontello.svg │ ├── fontello.ttf │ └── fontello.woff ├── img │ └── favicon.png ├── index.html ├── js │ ├── features │ │ ├── auth │ │ │ ├── Routes.js │ │ │ ├── controller │ │ │ │ ├── ForgetController.js │ │ │ │ ├── LoginController.js │ │ │ │ ├── SetDBController.js │ │ │ │ └── SignupController.js │ │ │ ├── directive │ │ │ │ └── ShakeIcon.js │ │ │ ├── main.js │ │ │ └── partials │ │ │ │ ├── Login.html │ │ │ │ ├── forget.html │ │ │ │ ├── login.html │ │ │ │ ├── setdb.html │ │ │ │ └── signup.html │ │ ├── common │ │ │ ├── listener │ │ │ │ ├── BootVerification.js │ │ │ │ ├── ManagerMenu.js │ │ │ │ ├── MenuListener.js │ │ │ │ ├── NonManagerMenu.js │ │ │ │ ├── RouteIndicator.js │ │ │ │ ├── RouteListener.js │ │ │ │ └── main.js │ │ │ ├── main.js │ │ │ ├── service │ │ │ │ ├── AuthService.js │ │ │ │ ├── DB.js │ │ │ │ ├── StorageService.js │ │ │ │ └── main.js │ │ │ └── ui │ │ │ │ ├── Autofocus.js │ │ │ │ ├── BottomSheet.js │ │ │ │ ├── Confirm.js │ │ │ │ ├── Dialog.js │ │ │ │ ├── Info.js │ │ │ │ ├── Sidebar.js │ │ │ │ ├── Toast.js │ │ │ │ └── main.js │ │ ├── main.js │ │ └── manager │ │ │ ├── Routes.js │ │ │ ├── controller │ │ │ ├── CreateController.js │ │ │ ├── ManagerController.js │ │ │ └── SettingController.js │ │ │ ├── directive │ │ │ ├── ItemEdit.js │ │ │ └── ItemInput.js │ │ │ ├── filter │ │ │ └── SearchFilter.js │ │ │ ├── main.js │ │ │ ├── partials │ │ │ ├── create.html │ │ │ ├── manager.html │ │ │ └── setting.html │ │ │ └── service │ │ │ └── ManagerService.js │ ├── fw │ │ ├── config │ │ │ ├── MaterialConfig.js │ │ │ ├── RouterConfig.js │ │ │ └── main.js │ │ ├── ext │ │ │ └── main.js │ │ ├── init │ │ │ ├── Validator.js │ │ │ └── main.js │ │ ├── lib │ │ │ ├── ConfiguratorBase.js │ │ │ ├── Debounce.js │ │ │ ├── FeatureBase.js │ │ │ ├── Flatten.js │ │ │ ├── InitBase.js │ │ │ ├── Omit.js │ │ │ ├── Pluck.js │ │ │ └── ServiceBase.js │ │ └── service │ │ │ ├── Events.js │ │ │ ├── Utils.js │ │ │ ├── events.js │ │ │ └── main.js │ ├── index.js │ ├── main.js │ └── process │ │ ├── ExitHandler.js │ │ ├── Launcher.js │ │ ├── SecretService.js │ │ └── index.js └── less │ ├── auth │ ├── auth.less │ ├── forget.less │ ├── login.less │ ├── setdb.less │ └── signup.less │ ├── fontello.less │ ├── main.less │ ├── manager │ └── manager.less │ └── toast.less ├── webpack.config.dev.js └── webpack.config.prod.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled binary 2 | dist/ 3 | cache/ 4 | 5 | # Dependency directory 6 | node_modules/ 7 | 8 | .DS_Store 9 | npm-debug.log 10 | 11 | src/build/ 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Howard.Zuo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 保密局 ![](http://img.shields.io/badge/version-v2.2.2-green.svg) # 2 | =============== 3 | 4 | 在信息极速膨胀的时代,我们都面临着大量的敏感信息,最重要的莫过于自己各种网站、卡的账号和密码了。因为众所周知的一些云存储泄漏丑闻,以及人类本能的自我保护意识,相信大家都不会放心把自己的众多银行卡密码保存在一个Online的“云”里,随时有可能被盗哦! 5 | 6 | `保密局`就是这样一个工具,她将你的“秘密”加密后以文件形式存储在你指定的位置(可以是本地磁盘、移动硬盘、U盘,当然也可以是网络硬盘)。因为本质上,`保密局`仅仅提供一了一个"秘密"信息的聚合管理功能,至于机密存在哪里,由你决定。你甚至可以把“机密”文件拷贝在U盘里随身携带。 7 | 8 | 新版`保密局`基于[electron](http://electron.atom.io/)开发,提供了一些简单常用的管理功能,例如:“秘密”太多时,可以帮你快速查找到自己“去哪儿”网的账号密码。但“秘密”信息,完全由自我管理,不与任何网络环境发生交互。且“秘密”信息都经由OpenSSL加密,且加密混淆`password`仅我本人在生成安装包时指定。也就是说,只要你的“秘密”文件不会意外的发给我,那么被破解的可能性会低到没朋友。 9 | 10 | ![](./docs/imgs/example.gif) 11 | 12 | ## 下载 ## 13 | 14 | * **v2.2.2** (2016-02-5) 15 | 16 | * Windows: [64bit](https://github.com/leftstick/windtalker/releases/download/2.2.2/windtalker-2.2.2-win32-x64.zip) 17 | * Mac 10.11+: [64bit](https://github.com/leftstick/windtalker/releases/download/2.2.2/windtalker-2.2.2-darwin-x64.zip) 18 | 19 | 20 | ## 想要贡献代码? ## 21 | 22 | 看这里:[开发文档](./docs/start-guide.md) 23 | 24 | ## LICENSE ## 25 | 26 | [MIT License](https://raw.githubusercontent.com/leftstick/windtalker/master/LICENSE) 27 | -------------------------------------------------------------------------------- /docs/imgs/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leftstick/windtalker/99a177518297504fd28fea616f41a9f8104afc87/docs/imgs/example.gif -------------------------------------------------------------------------------- /docs/start-guide.md: -------------------------------------------------------------------------------- 1 | ## 开发需求 ## 2 | 3 | 1. [node](https://nodejs.org/en/) 4 | 2. [gulp](https://github.com/gulpjs/gulp) 5 | 6 | > `gulp`需要全局安装(即:`npm install -g gulp`) 7 | 8 | ## 本地调试 ## 9 | 10 | ### 安装依赖 ### 11 | 12 | ```bash 13 | npm install 14 | ``` 15 | 16 | ### 启动持续编译 ### 17 | 18 | 在命令行中输入: 19 | 20 | ```bash 21 | gulp watch -p 22 | ``` 23 | 24 | > 持续编译的目的是为了在代码修改后,自动编译,无需频繁手动编译,提高开发效率 25 | > `password`即为加密时的私有密钥,之所以在编译时传入就是为了避免他人得知,增加安全性 26 | 27 | ### 打开本地调试应用 ### 28 | 29 | 在命令行中输入: 30 | 31 | ```bash 32 | gulp dev 33 | ``` 34 | 35 | > `gulp dev`之后就打开了应用,如果想开调试工具,只要`ctrl + shift + i`(for windows),或者`cmd + option + i`(for mac)即可 36 | 37 | 每次修改万源码后,只要`ctrl + r`(for windows)或者`cmd + r`(for mac)就能刷新界面看到新效果了 38 | 39 | ## 生成独立应用 ## 40 | 41 | 在命令行中输入: 42 | 43 | ```bash 44 | gulp release -p 45 | ``` 46 | 47 | `windows-x64`和`osx-x64`两个平台的应用,会生成在`dist/`目录下 48 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var del = require('del'); 5 | var resolve = require('path').resolve; 6 | 7 | var logger = console.log; 8 | 9 | var argv = require('minimist')(process.argv.slice(2)); 10 | 11 | var password = argv.p; 12 | 13 | var getTime = function() { 14 | var d = new Date(); 15 | return d.getHours() + ':' + d.getMinutes() + ' ' + d.getSeconds() + '-' + d.getMilliseconds(); 16 | }; 17 | 18 | var handleStatsError = function(stats) { 19 | var info = stats.toJson(); 20 | if (info.errors.length > 0) { 21 | logger('[webpack]', stats.toString({colors: true})); 22 | logger('\n [ ' + getTime() + ' ] webpack: bundle is now INVALID.'); 23 | return; 24 | } 25 | logger('\n [ ' + getTime() + ' ] webpack: bundle is now VALID!'); 26 | }; 27 | 28 | var compile = function(isDev, cb) { 29 | var webpack = require('webpack'); 30 | var config; 31 | 32 | if (isDev) { 33 | config = require(resolve(__dirname, 'webpack.config.dev')); 34 | } else { 35 | config = require(resolve(__dirname, 'webpack.config.prod')); 36 | } 37 | var passwordOpts = JSON.stringify({password: password}); 38 | 39 | config.module.loaders[0].loader = config.module.loaders[0].loader + new Buffer(passwordOpts).toString('base64'); 40 | 41 | var compiler = webpack(config); 42 | 43 | if (isDev) { 44 | compiler.watch({aggregateTimeout: 500, poll: true}, function(err, stats) { 45 | if (err) { 46 | logger('[ERROR]: ', err); 47 | return; 48 | } 49 | handleStatsError(stats); 50 | }); 51 | return; 52 | } 53 | 54 | compiler.run(function(err, stats) { 55 | if (err) { 56 | cb(err); 57 | return; 58 | } 59 | cb(); 60 | }); 61 | }; 62 | 63 | gulp.task('clean-dist', function() { 64 | return del(['./dist/**/*']); 65 | }); 66 | 67 | gulp.task('clean-build', function() { 68 | return del(['./src/build/**/*']); 69 | }); 70 | 71 | gulp.task('copy-index', ['clean-build', 'clean-dist'], function() { 72 | return gulp 73 | .src(['./src/index.html']) 74 | .pipe(gulp.dest('./src/build')); 75 | }); 76 | 77 | gulp.task('copy-package.json', ['clean-build', 'clean-dist'], function() { 78 | return gulp 79 | .src(['./package.json']) 80 | .pipe(gulp.dest('./src/build')); 81 | }); 82 | 83 | gulp.task('watch', ['copy-index', 'copy-package.json'], function(cb) { 84 | compile(true, cb); 85 | }); 86 | 87 | gulp.task('compile-release', ['copy-index', 'copy-package.json'], function(cb) { 88 | if (!password) { 89 | logger('WARNING: you have to specify encryption password by -p'); 90 | process.exit(0); 91 | } 92 | compile(false, cb); 93 | }); 94 | 95 | gulp.task('release', ['compile-release'], function() { 96 | 97 | var electron = require('gulp-electron'); 98 | var packageJson = require('./package.json'); 99 | return gulp.src('') 100 | .pipe(electron({ 101 | src: './src/build/', 102 | packageJson: packageJson, 103 | release: './dist', 104 | cache: './cache', 105 | version: 'v0.36.4', 106 | packaging: true, 107 | asar: true, 108 | platforms: [ 109 | 'darwin-x64', 110 | 'win32-x64' 111 | ], 112 | platformResources: { 113 | darwin: { 114 | CFBundleDisplayName: packageJson.name, 115 | CFBundleIdentifier: packageJson.name, 116 | CFBundleName: packageJson.name, 117 | CFBundleVersion: packageJson.version, 118 | icon: 'icon/windtalker.icns' 119 | }, 120 | win: { 121 | 'version-string': packageJson.version, 122 | 'file-version': packageJson.version, 123 | 'product-version': packageJson.version, 124 | 'icon': 'icon/windtalker.ico' 125 | } 126 | } 127 | })) 128 | .pipe(gulp.dest('')); 129 | }); 130 | 131 | gulp.task('dev', function(cb) { 132 | require('child_process') 133 | .exec('node ./node_modules/.bin/electron --debug=5858 ./src/build/', { 134 | cwd: __dirname, 135 | env: { 136 | NODE_ENV: 'dev' 137 | } 138 | }, cb); 139 | }); 140 | -------------------------------------------------------------------------------- /icon/windtalker.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leftstick/windtalker/99a177518297504fd28fea616f41a9f8104afc87/icon/windtalker.icns -------------------------------------------------------------------------------- /icon/windtalker.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leftstick/windtalker/99a177518297504fd28fea616f41a9f8104afc87/icon/windtalker.ico -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "windtalker", 3 | "version": "2.2.2", 4 | "description": "windtalker helps people managing security cared information", 5 | "main": "./js/main.bundle.js", 6 | "scripts": { 7 | "dev": "node ./node_modules/.bin/gulp dev", 8 | "watch": "node ./node_modules/.bin/gulp watch -p abcdefg" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/leftstick/windtalker.git" 13 | }, 14 | "keywords": [ 15 | "password", 16 | "keeper", 17 | "electron" 18 | ], 19 | "author": "Howard.Zuo", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/leftstick/windtalker/issues" 23 | }, 24 | "homepage": "https://github.com/leftstick/windtalker", 25 | "dependencies": { 26 | "angular": "^1.4.9", 27 | "angular-animate": "^1.4.9", 28 | "angular-aria": "^1.4.9", 29 | "angular-material": "^1.0.5", 30 | "angular-messages": "^1.4.9", 31 | "angular-promiseify": "^1.0.0", 32 | "angular-route": "^1.4.9", 33 | "animate.css": "^3.5.1", 34 | "co": "^4.6.0", 35 | "nedb": "^1.7.4" 36 | }, 37 | "devDependencies": { 38 | "autoprefixer": "^6.3.1", 39 | "babel-core": "^6.4.5", 40 | "babel-loader": "^6.2.2", 41 | "babel-plugin-transform-runtime": "^6.4.3", 42 | "babel-preset-es2015": "^6.3.13", 43 | "css-loader": "^0.23.1", 44 | "del": "^2.2.0", 45 | "electron-prebuilt": "^0.36.7", 46 | "file-loader": "^0.8.5", 47 | "gulp": "^3.9.0", 48 | "gulp-electron": "0.0.10", 49 | "less": "^2.6.0", 50 | "less-loader": "^2.2.2", 51 | "minimist": "^1.2.0", 52 | "modify-loader": "^2.0.0", 53 | "postcss-loader": "^0.8.0", 54 | "raw-loader": "^0.5.1", 55 | "style-loader": "^0.13.0", 56 | "webpack": "^1.12.13" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/font/fontello.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leftstick/windtalker/99a177518297504fd28fea616f41a9f8104afc87/src/font/fontello.eot -------------------------------------------------------------------------------- /src/font/fontello.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright (C) 2015 by original authors @ fontello.com 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/font/fontello.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leftstick/windtalker/99a177518297504fd28fea616f41a9f8104afc87/src/font/fontello.ttf -------------------------------------------------------------------------------- /src/font/fontello.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leftstick/windtalker/99a177518297504fd28fea616f41a9f8104afc87/src/font/fontello.woff -------------------------------------------------------------------------------- /src/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leftstick/windtalker/99a177518297504fd28fea616f41a9f8104afc87/src/img/favicon.png -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Windtalker 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/js/features/auth/Routes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Routes module expose route information used in auth feature 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 20, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | 11 | var tpl = require('./partials/login.html'); 12 | var dbTpl = require('./partials/setdb.html'); 13 | var signupTpl = require('./partials/signup.html'); 14 | var forgetTpl = require('./partials/forget.html'); 15 | 16 | module.exports = [ 17 | { 18 | id: 'login', 19 | isDefault: true, 20 | when: '/login', 21 | controller: 'LoginController', 22 | template: tpl, 23 | size: { 24 | width: 500, 25 | height: 400 26 | } 27 | }, 28 | { 29 | id: 'setdb', 30 | isDefault: false, 31 | when: '/setdb', 32 | controller: 'SetDBController', 33 | template: dbTpl, 34 | size: { 35 | width: 500, 36 | height: 300 37 | } 38 | }, 39 | { 40 | id: 'signup', 41 | isDefault: false, 42 | when: '/signup', 43 | controller: 'SignupController', 44 | template: signupTpl, 45 | size: { 46 | width: 500, 47 | height: 560 48 | } 49 | }, 50 | { 51 | id: 'forget', 52 | isDefault: false, 53 | when: '/forget', 54 | controller: 'ForgetController', 55 | template: forgetTpl, 56 | size: { 57 | width: 500, 58 | height: 400 59 | } 60 | } 61 | ]; 62 | -------------------------------------------------------------------------------- /src/js/features/auth/controller/ForgetController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the ForgetController controller 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 30, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var debounce = require('lib/Debounce'); 11 | var clipboard = require('electron').clipboard; 12 | var co = require('co'); 13 | 14 | var ForgetController = function($scope, AuthService, DbService, $q, events) { 15 | $scope.hasDbSet = DbService.checkDbAddress(); 16 | $scope.user = {}; 17 | $scope.state = {incorrectusername: false}; 18 | 19 | $scope.getPassword = function() { 20 | co(function*() { 21 | var user = yield AuthService.getUserByName($scope.user.name); 22 | if (user.answer === $scope.user.answer) { 23 | events.emit('toast-info', '密码已保存至剪贴板,按"Ctrl + v"即可粘贴'); 24 | clipboard.writeText(user.password); 25 | return; 26 | } 27 | events.emit('toast-warning', '密码提示答案输入错误'); 28 | }) 29 | .catch(function(err) { 30 | events.emit('toast-warning', err); 31 | }); 32 | 33 | }; 34 | 35 | var checkUsername = debounce(function(username) { 36 | 37 | co(function*() { 38 | var data = yield AuthService.getUsers(); 39 | var users = data.filter(function(user) { 40 | return user.name === username; 41 | }); 42 | 43 | if (!users.length) { 44 | $scope.user.question = ''; 45 | $scope.state.incorrectusername = true; 46 | $scope.$apply(); 47 | return; 48 | } 49 | 50 | var questions = yield AuthService.questions(); 51 | $scope.user.question = questions.filter(function(qu) { 52 | return qu.value === users[0].question; 53 | })[0].label; 54 | $scope.state.incorrectusername = false; 55 | }); 56 | }, 300); 57 | 58 | var unwatch = $scope.$watch('user.name', function(newValue) { 59 | checkUsername(newValue); 60 | }); 61 | 62 | $scope.$on('$destroy', function() { 63 | unwatch(); 64 | }); 65 | }; 66 | 67 | module.exports = [ 68 | '$scope', 69 | 'AuthService', 70 | 'DbService', 71 | '$q', 72 | 'events', 73 | ForgetController 74 | ]; 75 | -------------------------------------------------------------------------------- /src/js/features/auth/controller/LoginController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the LoginController controller 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 26, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | var co = require('co'); 10 | 11 | var LoginController = function($scope, events, utils, AuthService, DbService) { 12 | $scope.hasDbSet = DbService.checkDbAddress(); 13 | $scope.user = {}; 14 | 15 | var errorHandler = function(type) { 16 | return function(msg) { 17 | events.emit('toast', {type: type, content: msg}); 18 | }; 19 | }; 20 | 21 | $scope.login = function() { 22 | co(function*() { 23 | var users = yield AuthService.getUsers(); 24 | var founds = users.filter(function(user) { 25 | return user.name === $scope.user.name && user.password === $scope.user.password; 26 | }); 27 | if (founds.length) { 28 | utils.redirect('/manager/' + founds[0].id); 29 | $scope.$apply(); 30 | return; 31 | } 32 | errorHandler('error')('用户名或密码输入错误'); 33 | }) 34 | .catch(errorHandler('warn'));; 35 | }; 36 | 37 | $scope.$on('$destroy', function() {}); 38 | }; 39 | 40 | module.exports = [ 41 | '$scope', 42 | 'events', 43 | 'utils', 44 | 'AuthService', 45 | 'DbService', 46 | LoginController 47 | ]; 48 | -------------------------------------------------------------------------------- /src/js/features/auth/controller/SetDBController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the SetDBController controller 3 | * 4 | * @author Howard.Zuo 5 | * @date Dec 21, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | var debounce = require('lib/Debounce'); 10 | 11 | var mainWindow = require('electron').remote.getCurrentWindow(); 12 | var dialog = require('electron').remote.require('dialog'); 13 | var fs = require('fs'); 14 | 15 | var SetDBController = function($scope, events, utils, DbService) { 16 | 17 | $scope.db = {address: DbService.address()}; 18 | 19 | $scope.state = {invalidAddress: false}; 20 | 21 | $scope.openDialog = function($event) { 22 | utils.stopEvent($event); 23 | dialog.showOpenDialog(mainWindow, { 24 | title: '位置选择', 25 | properties: [ 26 | 'openDirectory' 27 | ] 28 | }, function(files) { 29 | if (files && files.length) { 30 | $scope.db.address = files[0]; 31 | $scope.$apply(); 32 | } 33 | }); 34 | }; 35 | 36 | $scope.saveDB = function() { 37 | DbService.address($scope.db.address); 38 | events.emit('toast-success', '数据库目录锁定成功!'); 39 | }; 40 | 41 | var checkAddress = debounce(function(value) { 42 | var stat; 43 | try { 44 | stat = fs.statSync(value); 45 | $scope.state.invalidAddress = !stat.isDirectory(); 46 | } catch (e) { 47 | $scope.state.invalidAddress = true; 48 | } 49 | $scope.$apply(); 50 | }, 300); 51 | 52 | var unwatch = $scope.$watch('db.address', function(newValue) { 53 | if (!newValue) { 54 | return; 55 | } 56 | checkAddress(newValue); 57 | }); 58 | 59 | $scope.$on('$destroy', function() { 60 | unwatch(); 61 | }); 62 | }; 63 | 64 | module.exports = [ 65 | '$scope', 66 | 'events', 67 | 'utils', 68 | 'DbService', 69 | SetDBController 70 | ]; 71 | -------------------------------------------------------------------------------- /src/js/features/auth/controller/SignupController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the SignupController controller 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 30, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | var co = require('co'); 10 | 11 | var DB_ADDRESS_KEY = 'windtaler.dbaddress'; 12 | 13 | var SignupController = function($scope, events, AuthService, utils, DbService) { 14 | $scope.hasDbSet = DbService.checkDbAddress(); 15 | 16 | $scope.user = {}; 17 | $scope.state = {notsame: false}; 18 | 19 | AuthService.questions() 20 | .success(function(data) { 21 | $scope.questions = data; 22 | }); 23 | 24 | var unwatch1 = $scope.$watch('user.repassword', function(newValue) { 25 | $scope.state.notsame = newValue !== $scope.user.password; 26 | }); 27 | 28 | var unwatch2 = $scope.$watch('user.password', function(newValue) { 29 | $scope.state.notsame = newValue !== $scope.user.repassword; 30 | }); 31 | 32 | $scope.saveUser = function() { 33 | co(function*() { 34 | var user = yield AuthService.addUser({ 35 | name: $scope.user.name, 36 | password: $scope.user.password, 37 | question: $scope.user.question, 38 | answer: $scope.user.answer 39 | }); 40 | events.emit('toast-success', '用户创建成功,请登录'); 41 | utils.redirect('login'); 42 | $scope.$apply(); 43 | }) 44 | .catch(function(err) { 45 | events.emit('toast-error', err); 46 | }); 47 | }; 48 | 49 | $scope.$on('$destroy', function() { 50 | unwatch1(); 51 | unwatch2(); 52 | }); 53 | }; 54 | 55 | module.exports = [ 56 | '$scope', 57 | 'events', 58 | 'AuthService', 59 | 'utils', 60 | 'DbService', 61 | SignupController 62 | ]; 63 | -------------------------------------------------------------------------------- /src/js/features/auth/directive/ShakeIcon.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the ShakeIcon directive 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 20, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var ShakeIcon = function($timeout) { 11 | 12 | return { 13 | restrict: 'A', 14 | scope: {}, 15 | link: function(scope, element, attrs) { 16 | 17 | var promise1, promise2; 18 | 19 | var repeat = function() { 20 | promise1 = $timeout(function() { 21 | element.addClass('bounce'); 22 | promise2 = $timeout(function() { 23 | element.removeClass('bounce'); 24 | repeat(); 25 | }, 1000); 26 | }, 5000); 27 | }; 28 | 29 | repeat(); 30 | 31 | scope.$on('$destroy', function() { 32 | $timeout.cancel(promise1); 33 | $timeout.cancel(promise2); 34 | }); 35 | } 36 | }; 37 | }; 38 | 39 | module.exports = ['$timeout', ShakeIcon]; 40 | -------------------------------------------------------------------------------- /src/js/features/auth/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ****************************************************************************************************** 3 | * 4 | * Defines a auth feature 5 | * 6 | * @author Howard.Zuo 7 | * @date Nov 24, 2015 8 | * 9 | * ****************************************************************************************************** 10 | */ 11 | 'use strict'; 12 | var FeatureBase = require('lib/FeatureBase'); 13 | var Routes = require('./Routes'); 14 | var LoginController = require('./controller/LoginController'); 15 | var SetDBController = require('./controller/SetDBController'); 16 | var SignupController = require('./controller/SignupController'); 17 | var ForgetController = require('./controller/ForgetController'); 18 | var ShakeIcon = require('./directive/ShakeIcon'); 19 | 20 | class Feature extends FeatureBase { 21 | 22 | constructor() { 23 | super('auth'); 24 | this.routes = Routes; 25 | } 26 | 27 | execute() { 28 | this.controller('LoginController', LoginController); 29 | this.controller('SetDBController', SetDBController); 30 | this.controller('SignupController', SignupController); 31 | this.controller('ForgetController', ForgetController); 32 | this.directive('shakeIcon', ShakeIcon); 33 | } 34 | } 35 | 36 | module.exports = Feature; 37 | -------------------------------------------------------------------------------- /src/js/features/auth/partials/Login.html: -------------------------------------------------------------------------------- 1 |
2 | 更改数据库 3 | 4 |
5 | 欢迎进入保密局 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 忘记密码? 22 | 登录 23 | 24 | 25 | 26 | 没有账号?创建一个 27 | 28 | 29 | 30 |
31 |
32 | -------------------------------------------------------------------------------- /src/js/features/auth/partials/forget.html: -------------------------------------------------------------------------------- 1 |
2 | 返回登录 3 | 4 |
5 | 找回密码 6 |
7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
请输入正确的用户名
20 |
21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 确认找回 30 | 31 | 32 |
33 |
34 | 35 |
36 | -------------------------------------------------------------------------------- /src/js/features/auth/partials/login.html: -------------------------------------------------------------------------------- 1 |
2 | 更改数据库 3 | 4 |
5 | 欢迎进入保密局 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 忘记密码? 22 | 登录 23 | 24 | 25 | 26 | 没有账号?创建一个 27 | 28 | 29 | 30 |
31 |
32 | -------------------------------------------------------------------------------- /src/js/features/auth/partials/setdb.html: -------------------------------------------------------------------------------- 1 |
2 | 返回登录 3 | 4 |
5 | 选择数据文件存放位置 6 |
7 | 8 |
9 | 10 | 11 | 12 | 13 |
14 |
路径输入错误!请选择正确的文件路径
15 |
16 |
17 | 18 | 19 | 确认 20 | 21 | 22 |
23 |
24 | 25 |
26 | -------------------------------------------------------------------------------- /src/js/features/auth/partials/signup.html: -------------------------------------------------------------------------------- 1 |
2 | 返回登录 3 | 4 |
5 | 开始注册 6 |
7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
确认密码不一致
25 |
26 |
27 | 28 | 29 | 30 | 31 | {{ question.label }} 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 确认 42 | 43 | 44 |
45 |
46 | 47 |
48 | -------------------------------------------------------------------------------- /src/js/features/common/listener/BootVerification.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Defines BootVerification 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 26, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | var FeatureBase = require('lib/FeatureBase'); 11 | 12 | var DB_ADDRESS_KEY = 'windtaler.dbaddress'; 13 | 14 | class Feature extends FeatureBase { 15 | 16 | constructor() { 17 | super('BootVerificationModule'); 18 | } 19 | 20 | execute() { 21 | this.run([ 22 | 'StorageService', 23 | '$location', 24 | 'DbService', 25 | function(StorageService, $location, DbService) { 26 | var address = StorageService.get(DB_ADDRESS_KEY); 27 | if (!address) { 28 | $location.url('setdb'); 29 | return; 30 | } 31 | DbService.init(); 32 | } 33 | ]); 34 | } 35 | } 36 | 37 | module.exports = Feature; 38 | -------------------------------------------------------------------------------- /src/js/features/common/listener/ManagerMenu.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = [ 4 | { 5 | label: 'Application', 6 | submenu: [ 7 | { 8 | label: 'About Application', 9 | visible: process.platform == 'darwin', 10 | selector: 'orderFrontStandardAboutPanel:' 11 | }, 12 | { 13 | type: 'separator', 14 | visible: process.platform == 'darwin', 15 | }, 16 | { 17 | label: 'Quit', 18 | accelerator: 'CmdOrCtrl+Q', 19 | click: function() { 20 | require('electron').remote.app.quit(); 21 | } 22 | } 23 | ] 24 | }, 25 | { 26 | label: 'Edit', 27 | submenu: [ 28 | { 29 | label: 'Undo', 30 | accelerator: 'CmdOrCtrl+Z', 31 | selector: 'undo:' 32 | }, 33 | { 34 | label: 'Redo', 35 | accelerator: 'Shift+CmdOrCtrl+Z', 36 | selector: 'redo:' 37 | }, 38 | { 39 | type: 'separator' 40 | }, 41 | { 42 | label: 'Cut', 43 | accelerator: 'CmdOrCtrl+X', 44 | selector: 'cut:' 45 | }, 46 | { 47 | label: 'Copy', 48 | accelerator: 'CmdOrCtrl+C', 49 | selector: 'copy:' 50 | }, 51 | { 52 | label: 'Paste', 53 | accelerator: 'CmdOrCtrl+V', 54 | selector: 'paste:' 55 | }, 56 | { 57 | label: 'Select All', 58 | accelerator: 'CmdOrCtrl+A', 59 | selector: 'selectAll:' 60 | } 61 | ] 62 | }, 63 | { 64 | label: 'View', 65 | submenu: [ 66 | { 67 | label: 'Reload', 68 | accelerator: 'CmdOrCtrl+R', 69 | visible: process.env.NODE_ENV === 'dev', 70 | click: function(item, focusedWindow) { 71 | if (focusedWindow) { 72 | focusedWindow.reload(); 73 | } 74 | } 75 | }, 76 | { 77 | label: 'Toggle Developer Tools', 78 | visible: process.env.NODE_ENV === 'dev', 79 | accelerator: (function() { 80 | if (process.platform == 'darwin') { 81 | return 'Alt+Command+I'; 82 | } else { 83 | return 'Ctrl+Shift+I'; 84 | } 85 | })(), 86 | click: function(item, focusedWindow) { 87 | if (focusedWindow) { 88 | focusedWindow.toggleDevTools(); 89 | } 90 | } 91 | }, 92 | { 93 | label: 'Close Overlay', 94 | accelerator: 'CmdOrCtrl+W', 95 | click: function(item, focusedWindow) { 96 | if (window.closeOverlay) { 97 | window.closeOverlay(); 98 | } 99 | } 100 | } 101 | ] 102 | } 103 | ]; 104 | -------------------------------------------------------------------------------- /src/js/features/common/listener/MenuListener.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Defines MenuListener service 4 | * 5 | * @author Howard.Zuo 6 | * @date Dec 22, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | var FeatureBase = require('lib/FeatureBase'); 11 | var NonManagerMenu = require('./NonManagerMenu'); 12 | var ManagerMenu = require('./ManagerMenu'); 13 | 14 | var remote = require('electron').remote; 15 | var Menu = remote.Menu; 16 | var MenuItem = remote.MenuItem; 17 | 18 | class Feature extends FeatureBase { 19 | 20 | constructor() { 21 | super('MenuListenerModule'); 22 | } 23 | 24 | listener($rootScope, $mdDialog, $mdBottomSheet, $mdSidenav) { 25 | $rootScope.$on('$routeChangeSuccess', function(e, route) { 26 | if (!route || !route.$$route || !route.$$route.id) { 27 | return; 28 | } 29 | if (route.$$route.id === 'manager') { 30 | window.closeOverlay = function() { 31 | $mdDialog.cancel(); 32 | $mdBottomSheet.cancel(); 33 | $mdSidenav('left').close(); 34 | }; 35 | Menu.setApplicationMenu(Menu.buildFromTemplate(ManagerMenu)); 36 | return; 37 | } 38 | var menus = NonManagerMenu.slice(0, NonManagerMenu.length); 39 | if (process.env.NODE_ENV !== 'dev') { 40 | menus = NonManagerMenu.slice(0, NonManagerMenu.length - 1); 41 | } 42 | Menu.setApplicationMenu(Menu.buildFromTemplate(menus)); 43 | }); 44 | } 45 | 46 | execute() { 47 | this.listener.$inject = [ 48 | '$rootScope', 49 | '$mdDialog', 50 | '$mdBottomSheet', 51 | '$mdSidenav' 52 | ]; 53 | this.run(this.listener); 54 | } 55 | } 56 | 57 | module.exports = Feature; 58 | -------------------------------------------------------------------------------- /src/js/features/common/listener/NonManagerMenu.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = [ 4 | { 5 | label: 'Application', 6 | submenu: [ 7 | { 8 | label: 'About Application', 9 | visible: process.platform == 'darwin', 10 | selector: 'orderFrontStandardAboutPanel:' 11 | }, 12 | { 13 | type: 'separator', 14 | visible: process.platform == 'darwin', 15 | }, 16 | { 17 | label: 'Quit', 18 | accelerator: 'CmdOrCtrl+Q', 19 | click: function() { 20 | require('electron').remote.app.quit(); 21 | } 22 | } 23 | ] 24 | }, 25 | { 26 | label: 'Edit', 27 | submenu: [ 28 | { 29 | label: 'Undo', 30 | accelerator: 'CmdOrCtrl+Z', 31 | selector: 'undo:', 32 | role: 'undo' 33 | }, 34 | { 35 | label: 'Redo', 36 | accelerator: 'Shift+CmdOrCtrl+Z', 37 | selector: 'redo:', 38 | role: 'redo' 39 | }, 40 | { 41 | type: 'separator' 42 | }, 43 | { 44 | label: 'Cut', 45 | accelerator: 'CmdOrCtrl+X', 46 | selector: 'cut:', 47 | role: 'cut' 48 | }, 49 | { 50 | label: 'Copy', 51 | accelerator: 'CmdOrCtrl+C', 52 | selector: 'copy:', 53 | role: 'copy' 54 | }, 55 | { 56 | label: 'Paste', 57 | accelerator: 'CmdOrCtrl+V', 58 | selector: 'paste:', 59 | role: 'paste' 60 | }, 61 | { 62 | label: 'Select All', 63 | accelerator: 'CmdOrCtrl+A', 64 | selector: 'selectAll:', 65 | role: 'selectall' 66 | } 67 | ] 68 | }, 69 | { 70 | label: 'View', 71 | submenu: [ 72 | { 73 | label: 'Reload', 74 | visible: process.env.NODE_ENV === 'dev', 75 | accelerator: 'CmdOrCtrl+R', 76 | click: function(item, focusedWindow) { 77 | if (focusedWindow) { 78 | focusedWindow.reload(); 79 | } 80 | } 81 | }, 82 | { 83 | label: 'Toggle Developer Tools', 84 | visible: process.env.NODE_ENV === 'dev', 85 | accelerator: (function() { 86 | if (process.platform == 'darwin') { 87 | return 'Alt+Command+I'; 88 | } else { 89 | return 'Ctrl+Shift+I'; 90 | } 91 | })(), 92 | click: function(item, focusedWindow) { 93 | if (focusedWindow) { 94 | focusedWindow.toggleDevTools(); 95 | } 96 | } 97 | }, 98 | ] 99 | } 100 | ]; 101 | -------------------------------------------------------------------------------- /src/js/features/common/listener/RouteIndicator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Defines RouteIndicator service 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 20, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | var pluck = require('lib/Pluck'); 11 | var angular = require('angular'); 12 | var FeatureBase = require('lib/FeatureBase'); 13 | 14 | class Feature extends FeatureBase { 15 | 16 | constructor() { 17 | super('RouteIndicator'); 18 | this.$body = angular.element(document.body); 19 | } 20 | 21 | execute() { 22 | var self = this; 23 | this.run([ 24 | '$rootScope', 25 | 'Routes', 26 | function($rootScope, Routes) { 27 | var classes = pluck(Routes, 'id').join(' '); 28 | $rootScope.$on('$routeChangeSuccess', function(e, route) { 29 | self.$body.removeClass(classes); 30 | if (route && route.$$route && route.$$route.id) { 31 | self.$body.addClass(route.$$route.id); 32 | self.$body.attr('id', route.$$route.id); 33 | } 34 | }); 35 | } 36 | ]); 37 | } 38 | } 39 | 40 | module.exports = Feature; 41 | -------------------------------------------------------------------------------- /src/js/features/common/listener/RouteListener.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the RouteListener Module. 3 | * This module used to emit events while route changed 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 26, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | var FeatureBase = require('lib/FeatureBase'); 11 | var mainWindow = require('electron').remote.getCurrentWindow(); 12 | 13 | class Feature extends FeatureBase { 14 | 15 | constructor() { 16 | super('RouteListenerModule'); 17 | } 18 | 19 | execute() { 20 | this.run([ 21 | '$rootScope', 22 | 'Routes', 23 | '$document', 24 | function($rootScope, Routes, $document) { 25 | var isFirst = true; 26 | $rootScope.$on('$viewContentLoaded', function() { 27 | var route = Routes.filter(function(route) { 28 | return route.id === $document[0].body.id; 29 | })[0]; 30 | if (!route.size) { 31 | return; 32 | } 33 | var size = route.size; 34 | mainWindow.setContentSize(size.width, size.height); 35 | mainWindow.setMinimumSize(size.minWidth || size.width, size.minHeight || size.height); 36 | mainWindow.setResizable(!!size.resizable); 37 | if (isFirst) { 38 | isFirst = false; 39 | mainWindow.show(); 40 | } 41 | mainWindow.center(); 42 | }); 43 | } 44 | ]); 45 | } 46 | } 47 | 48 | module.exports = Feature; 49 | -------------------------------------------------------------------------------- /src/js/features/common/listener/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrance of common listener 3 | * 4 | * @author Howard.Zuo 5 | * @date Dec 22, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var boot = require('./BootVerification'); 11 | var menuListener = require('./MenuListener'); 12 | var indicator = require('./RouteIndicator'); 13 | var routeListener = require('./RouteListener'); 14 | 15 | module.exports = [ 16 | boot, 17 | menuListener, 18 | indicator, 19 | routeListener 20 | ]; 21 | -------------------------------------------------------------------------------- /src/js/features/common/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrance of common service 3 | * 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 20, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | 11 | var ui = require('./ui/main'); 12 | var service = require('./service/main'); 13 | var listener = require('./listener/main'); 14 | 15 | module.exports = [...ui, ...service, ...listener]; 16 | -------------------------------------------------------------------------------- /src/js/features/common/service/AuthService.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the Auth Module. 3 | * This module used to manage user info 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 30, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | var FeatureBase = require('lib/FeatureBase'); 11 | 12 | class Feature extends FeatureBase { 13 | 14 | constructor() { 15 | super('AuthModule'); 16 | } 17 | 18 | AuthService(utils, DbService) { 19 | 20 | var questions; 21 | this.questions = function() { 22 | if (questions) { 23 | return questions; 24 | } 25 | questions = utils.resolve([ 26 | { 27 | 'label': '父亲的名字', 28 | 'value': 'FATHER_NAME' 29 | }, 30 | { 31 | 'label': '母亲的名字', 32 | 'value': 'MOTHER_NAME' 33 | }, 34 | { 35 | 'label': '配偶的名字', 36 | 'value': 'SPOUSE_NAME' 37 | }, 38 | { 39 | 'label': '小学的名字', 40 | 'value': 'PRIMARY_SCHOOL_NAME' 41 | }, 42 | { 43 | 'label': '第一只宠物的名字', 44 | 'value': 'FIRST_PET_NAME' 45 | } 46 | ]); 47 | return questions; 48 | }; 49 | 50 | this.getUsers = function*() { 51 | if (!DbService.getUserDb()) { 52 | return utils.reject('您还没有设置数据库,清先设置数据库位置'); 53 | } 54 | 55 | var userDB = DbService.getUserDb(); 56 | 57 | var finder = utils.promisify(userDB.find).bind(userDB); 58 | 59 | var docs = yield finder({}); 60 | var users = docs.map(function(doc) { 61 | return { 62 | id: utils.decryptTxt(doc.id), 63 | name: utils.decryptTxt(doc.name), 64 | password: utils.decryptTxt(doc.password), 65 | question: utils.decryptTxt(doc.question), 66 | answer: utils.decryptTxt(doc.answer) 67 | }; 68 | }); 69 | return users; 70 | }; 71 | 72 | this.addUser = function*(user) { 73 | var encryptUser = { 74 | id: utils.encryptTxt(utils.ID()), 75 | name: utils.encryptTxt(user.name), 76 | password: utils.encryptTxt(user.password), 77 | question: utils.encryptTxt(user.question), 78 | answer: utils.encryptTxt(user.answer) 79 | }; 80 | 81 | var userDB = DbService.getUserDb(); 82 | var finder = utils.promisify(userDB.count).bind(userDB); 83 | var inserter = utils.promisify(userDB.insert).bind(userDB); 84 | 85 | var count = yield finder({ 86 | name: utils.encryptTxt(user.name) 87 | }); 88 | if (count > 0) { 89 | return utils.reject('该用户名已被注册,请重试'); 90 | } 91 | var doc = yield inserter(encryptUser); 92 | return { 93 | id: utils.decryptTxt(doc.id), 94 | name: utils.decryptTxt(doc.name), 95 | password: utils.decryptTxt(doc.password), 96 | question: utils.decryptTxt(doc.question), 97 | answer: utils.decryptTxt(doc.answer) 98 | }; 99 | }; 100 | 101 | this.updateUser = function*(user) { 102 | 103 | var encryptUser = { 104 | id: utils.encryptTxt(user.id), 105 | name: utils.encryptTxt(user.name), 106 | password: utils.encryptTxt(user.password), 107 | question: utils.encryptTxt(user.question), 108 | answer: utils.encryptTxt(user.answer) 109 | }; 110 | var userDB = DbService.getUserDb(); 111 | var updateer = utils.promisify(userDB.update).bind(userDB); 112 | yield updateer({id: encryptUser.id}, encryptUser, {}); 113 | return user; 114 | }; 115 | 116 | this.getUserByName = function*(name) { 117 | 118 | var userDB = DbService.getUserDb(); 119 | var finder = utils.promisify(userDB.findOne).bind(userDB); 120 | var doc = yield finder({name: utils.encryptTxt(name)}); 121 | if (!doc) { 122 | return utils.reject('该用户不存在'); 123 | } 124 | return { 125 | id: utils.decryptTxt(doc.id), 126 | name: utils.decryptTxt(doc.name), 127 | password: utils.decryptTxt(doc.password), 128 | question: utils.decryptTxt(doc.question), 129 | answer: utils.decryptTxt(doc.answer) 130 | }; 131 | }; 132 | 133 | this.getUserById = function*(userId) { 134 | var userDB = DbService.getUserDb(); 135 | var finder = utils.promisify(userDB.findOne).bind(userDB); 136 | 137 | var doc = yield finder({id: utils.encryptTxt(userId)}); 138 | 139 | if (!doc) { 140 | return utils.reject('该用户不存在'); 141 | } 142 | return { 143 | id: utils.decryptTxt(doc.id), 144 | name: utils.decryptTxt(doc.name), 145 | password: utils.decryptTxt(doc.password), 146 | question: utils.decryptTxt(doc.question), 147 | answer: utils.decryptTxt(doc.answer) 148 | }; 149 | }; 150 | } 151 | 152 | execute() { 153 | this.AuthService.$inject = ['utils', 'DbService']; 154 | this.service('AuthService', this.AuthService); 155 | } 156 | } 157 | 158 | module.exports = Feature; 159 | -------------------------------------------------------------------------------- /src/js/features/common/service/DB.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the DB Module. 3 | * This module used to connect to nedb 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 26, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | var FeatureBase = require('lib/FeatureBase'); 11 | var merge = require('angular').merge; 12 | 13 | var Datastore = require('nedb'); 14 | var path = require('path'); 15 | 16 | var USERS_DB_NAME = 'users.db'; 17 | var SECRETS_DB_NAME = 'secrets.db'; 18 | 19 | var DB_ADDRESS_KEY = 'windtaler.dbaddress'; 20 | 21 | class Feature extends FeatureBase { 22 | 23 | constructor() { 24 | super('DBModule'); 25 | } 26 | 27 | DbService(StorageService, events, utils) { 28 | var userDb, secretDb; 29 | 30 | this.init = function() { 31 | var address = StorageService.get(DB_ADDRESS_KEY); 32 | 33 | var userDbPath = path.join(address, USERS_DB_NAME); 34 | var secretDbPath = path.join(address, SECRETS_DB_NAME); 35 | userDb = new Datastore({ 36 | filename: userDbPath, 37 | autoload: true 38 | }); 39 | secretDb = new Datastore({ 40 | filename: secretDbPath, 41 | autoload: true 42 | }); 43 | }; 44 | 45 | this.getUserDb = function() { 46 | return userDb; 47 | }; 48 | 49 | this.getSecretDb = function() { 50 | return secretDb; 51 | }; 52 | 53 | this.checkDbAddress = function() { 54 | var hasSet = StorageService.get(DB_ADDRESS_KEY); 55 | if (!hasSet) { 56 | events.emit('toast', { 57 | type: 'warning', 58 | content: '您还没有设置数据库,请先完成该设置' 59 | }); 60 | } 61 | return !!hasSet; 62 | }; 63 | 64 | this.address = function(addr) { 65 | if (!addr) { 66 | return StorageService.get(DB_ADDRESS_KEY); 67 | } 68 | 69 | StorageService.set(DB_ADDRESS_KEY, addr); 70 | this.init(); 71 | }; 72 | } 73 | 74 | execute() { 75 | this.DbService.$inject = [ 76 | 'StorageService', 77 | 'events', 78 | 'utils' 79 | ]; 80 | this.service('DbService', this.DbService); 81 | 82 | } 83 | } 84 | 85 | module.exports = Feature; 86 | -------------------------------------------------------------------------------- /src/js/features/common/service/StorageService.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the StorageService Module. 3 | * This module used to control data in LocalStorage 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 30, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | var FeatureBase = require('lib/FeatureBase'); 11 | 12 | class Feature extends FeatureBase { 13 | 14 | constructor() { 15 | super('StorageModule'); 16 | } 17 | 18 | StorageService($window) { 19 | var storage = $window.localStorage; 20 | 21 | this.get = function(key) { 22 | return storage.getItem(key) || ''; 23 | }; 24 | 25 | this.indexOf = function(i) { 26 | if (!storage.key(i)) { 27 | return ''; 28 | } 29 | return storage.getItem(storage.key(i)); 30 | }; 31 | 32 | this.set = function(key, value) { 33 | storage.setItem(key, value); 34 | }; 35 | 36 | this.remove = function(key) { 37 | storage.removeItem(key); 38 | }; 39 | 40 | this.removeAll = function() { 41 | storage.clear(); 42 | }; 43 | } 44 | 45 | execute() { 46 | this.StorageService.$inject = ['$window']; 47 | this.service('StorageService', this.StorageService); 48 | } 49 | } 50 | 51 | module.exports = Feature; 52 | -------------------------------------------------------------------------------- /src/js/features/common/service/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrance of common service 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 24, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var authService = require('./AuthService'); 11 | var db = require('./DB'); 12 | var storageService = require('./StorageService'); 13 | 14 | module.exports = [authService, db, storageService]; 15 | -------------------------------------------------------------------------------- /src/js/features/common/ui/Autofocus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the Autofocus Module. 3 | * This module used to override the original `autofocus` attribute since it doesn't work properly with ngRoute 4 | * 5 | * @author Howard.Zuo 6 | * @date Dec 1, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | var FeatureBase = require('lib/FeatureBase'); 11 | 12 | class Feature extends FeatureBase { 13 | 14 | constructor() { 15 | super('AutofocusModule'); 16 | } 17 | 18 | autofocus() { 19 | return { 20 | restrict: 'A', 21 | link: function($scope, element) { 22 | element[0].focus(); 23 | } 24 | }; 25 | } 26 | 27 | execute() { 28 | this.directive('autofocus', this.autofocus); 29 | } 30 | } 31 | 32 | module.exports = Feature; 33 | -------------------------------------------------------------------------------- /src/js/features/common/ui/BottomSheet.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the BottomSheet 3 | * 4 | * @author Howard.Zuo 5 | * @date Dec 1, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var FeatureBase = require('lib/FeatureBase'); 11 | var omit = require('lib/Omit'); 12 | var merge = require('angular').merge; 13 | 14 | class Feature extends FeatureBase { 15 | constructor() { 16 | super('BottomSheetModule'); 17 | } 18 | 19 | beforeStart() {}; 20 | 21 | bottomsheetListener(events, $mdBottomSheet) { 22 | var defaults = {template: '', event: null}; 23 | 24 | events.on('bottomsheet', function(data) { 25 | var opts = merge({}, defaults, omit(data, [ 26 | 'scope', 27 | 'event' 28 | ])); 29 | 30 | $mdBottomSheet.show({ 31 | template: opts.template, 32 | targetEvent: data.event, 33 | controller: opts.controller, 34 | locals: opts.locals, 35 | resolve: opts.resolve 36 | }); 37 | }); 38 | 39 | events.on('bottomsheet-hide', function(data) { 40 | $mdBottomSheet.hide(); 41 | }); 42 | } 43 | 44 | execute() { 45 | this.bottomsheetListener.$inject = [ 46 | 'events', 47 | '$mdBottomSheet' 48 | ]; 49 | this.run(this.bottomsheetListener); 50 | } 51 | } 52 | 53 | module.exports = Feature; 54 | -------------------------------------------------------------------------------- /src/js/features/common/ui/Confirm.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the Confirm 3 | * 4 | * @author Howard.Zuo 5 | * @date Dec 22, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var FeatureBase = require('lib/FeatureBase'); 11 | var extend = require('angular').extend; 12 | 13 | class Feature extends FeatureBase { 14 | constructor() { 15 | super('ConfirmModule'); 16 | } 17 | 18 | beforeStart() { 19 | }; 20 | 21 | confirmListener(events, $mdDialog) { 22 | var defaults = { 23 | title: '确认', 24 | content: '', 25 | okTxt: '确定', 26 | cancelTxt: '取消', 27 | onComplete: function() {}, 28 | onCancel: function() {} 29 | }; 30 | 31 | events.on('confirm', function(data) { 32 | var opts = extend({}, defaults, data); 33 | 34 | var confirm = $mdDialog.confirm() 35 | .title(opts.title) 36 | .textContent(opts.content) 37 | .targetEvent(opts.event) 38 | .ok(opts.okTxt) 39 | .cancel(opts.cancelTxt); 40 | 41 | $mdDialog.show(confirm).then(opts.onComplete, opts.onCancel); 42 | }); 43 | } 44 | 45 | execute() { 46 | this.confirmListener.$inject = ['events', '$mdDialog']; 47 | this.run(this.confirmListener); 48 | } 49 | } 50 | 51 | module.exports = Feature; 52 | -------------------------------------------------------------------------------- /src/js/features/common/ui/Dialog.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the Dialog 3 | * 4 | * @author Howard.Zuo 5 | * @date Dec 22, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var FeatureBase = require('lib/FeatureBase'); 11 | var extend = require('angular').extend; 12 | 13 | class Feature extends FeatureBase { 14 | constructor() { 15 | super('DialogModule'); 16 | } 17 | 18 | beforeStart() { 19 | }; 20 | 21 | dialogListener(events, $mdDialog) { 22 | var defaults = { 23 | template: '', 24 | onComplete: function() {} 25 | }; 26 | 27 | events.on('dialog', function(data) { 28 | var opts = extend({}, defaults, data); 29 | $mdDialog.show({ 30 | scope: opts.scope, 31 | targetEvent: opts.event, 32 | template: opts.template, 33 | onComplete: opts.onComplete 34 | }); 35 | }); 36 | } 37 | 38 | execute() { 39 | this.dialogListener.$inject = ['events', '$mdDialog']; 40 | this.run(this.dialogListener); 41 | } 42 | } 43 | 44 | module.exports = Feature; 45 | -------------------------------------------------------------------------------- /src/js/features/common/ui/Info.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the Info Dialog 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 24, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var FeatureBase = require('lib/FeatureBase'); 11 | var extend = require('angular').extend; 12 | 13 | class Feature extends FeatureBase { 14 | constructor() { 15 | super('InfoModule'); 16 | } 17 | 18 | beforeStart() { 19 | }; 20 | 21 | infoListener(events, $mdDialog) { 22 | var defaults = { 23 | title: '确认', 24 | content: '', 25 | event: null, 26 | okTxt: '确定', 27 | onComplete: function() {} 28 | }; 29 | 30 | events.on('info', function(data) { 31 | var opts = extend({}, defaults, data); 32 | 33 | $mdDialog.show( 34 | $mdDialog.alert() 35 | .clickOutsideToClose(false) 36 | .title(opts.title) 37 | .content(opts.content) 38 | .ok(opts.okTxt) 39 | .targetEvent(opts.event) 40 | ) 41 | .then(opts.onComplete); 42 | }); 43 | } 44 | 45 | execute() { 46 | this.infoListener.$inject = ['events', '$mdDialog']; 47 | this.run(this.infoListener); 48 | } 49 | } 50 | 51 | module.exports = Feature; 52 | -------------------------------------------------------------------------------- /src/js/features/common/ui/Sidebar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the Sidebar 3 | * 4 | * @author Howard.Zuo 5 | * @date Dec 1, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var FeatureBase = require('lib/FeatureBase'); 11 | 12 | class Feature extends FeatureBase { 13 | constructor() { 14 | super('SidebarModule'); 15 | } 16 | 17 | beforeStart() {}; 18 | 19 | sidebarListener(events, $mdSidenav) { 20 | events.on('sidebar', function(data) { 21 | $mdSidenav(data.id).open(); 22 | }); 23 | 24 | events.on('sidebar-hide', function(data) { 25 | $mdSidenav(data.id).close(); 26 | }); 27 | } 28 | 29 | execute() { 30 | this.sidebarListener.$inject = ['events', '$mdSidenav']; 31 | this.run(this.sidebarListener); 32 | } 33 | } 34 | 35 | module.exports = Feature; 36 | -------------------------------------------------------------------------------- /src/js/features/common/ui/Toast.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the Toast 3 | * 4 | * @author Howard.Zuo 5 | * @date Dec 22, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var FeatureBase = require('lib/FeatureBase'); 11 | var {extend} = require('angular'); 12 | 13 | class Feature extends FeatureBase { 14 | constructor() { 15 | super('ToastModule'); 16 | } 17 | 18 | beforeStart() { 19 | }; 20 | 21 | toastListener(events, $mdToast) { 22 | var defaultDelay = { 23 | info: 2000, 24 | error: 3000, 25 | success: 700, 26 | warning: 1500 27 | }; 28 | 29 | var defaults = { 30 | content: '', 31 | position: 'top right', 32 | type: 'info' 33 | }; 34 | 35 | events.on('toast', function(data) { 36 | var opts = extend({}, defaults, data); 37 | $mdToast.show({ 38 | template: '
' + opts.content + '
', 39 | hideDelay: data.delay || defaultDelay[opts.type], 40 | position: opts.position 41 | }); 42 | }); 43 | 44 | events.on('toast-warning', function(content) { 45 | events.emit('toast', { 46 | type: 'warning', 47 | content: content 48 | }); 49 | }); 50 | 51 | events.on('toast-error', function(content) { 52 | events.emit('toast', { 53 | type: 'error', 54 | content: content 55 | }); 56 | }); 57 | 58 | events.on('toast-success', function(content) { 59 | events.emit('toast', { 60 | type: 'success', 61 | content: content 62 | }); 63 | }); 64 | 65 | events.on('toast-info', function(content) { 66 | events.emit('toast', { 67 | type: 'info', 68 | content: content 69 | }); 70 | }); 71 | } 72 | 73 | execute() { 74 | this.toastListener.$inject = ['events', '$mdToast']; 75 | this.run(this.toastListener); 76 | } 77 | } 78 | 79 | module.exports = Feature; 80 | -------------------------------------------------------------------------------- /src/js/features/common/ui/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrance of common ui 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 26, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var autofocus = require('./Autofocus'); 11 | var bottomSheet = require('./BottomSheet'); 12 | var confirm = require('./Confirm'); 13 | var dialog = require('./Dialog'); 14 | var info = require('./Info'); 15 | var sidebar = require('./Sidebar'); 16 | var toast = require('./Toast'); 17 | 18 | module.exports = [ 19 | autofocus, 20 | bottomSheet, 21 | confirm, 22 | dialog, 23 | info, 24 | sidebar, 25 | toast 26 | ]; 27 | -------------------------------------------------------------------------------- /src/js/features/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrance of features 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 20, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | var flatten = require('lib/Flatten'); 10 | 11 | var auth = require('./auth/main'); 12 | var common = require('./common/main'); 13 | var manager = require('./manager/main'); 14 | 15 | module.exports = flatten([common, auth, manager]); 16 | -------------------------------------------------------------------------------- /src/js/features/manager/Routes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Routes module expose route information used in auth feature 4 | * 5 | * @author Howard.Zuo 6 | * @date Dec 22, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | 11 | var tpl = require('./partials/manager.html'); 12 | 13 | module.exports = [ 14 | { 15 | id: 'manager', 16 | isDefault: false, 17 | when: '/manager/:userId', 18 | controller: 'ManagerController', 19 | template: tpl, 20 | size: { 21 | width: 750, 22 | height: 700, 23 | resizable: true, 24 | minWidth: 500, 25 | minHeight: 300 26 | } 27 | } 28 | ]; 29 | -------------------------------------------------------------------------------- /src/js/features/manager/controller/CreateController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the CreateController controller 3 | * 4 | * @author Howard.Zuo 5 | * @date Dec 22, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var noop = require('angular').noop; 11 | var co = require('co'); 12 | 13 | var CreateController = function($scope, $routeParams, events, ManagerService, AuthService, utils) { 14 | $scope.state = {canAdd: false}; 15 | 16 | $scope.secret = {}; 17 | $scope.info = {}; 18 | $scope.items = []; 19 | 20 | $scope.addItem = function() { 21 | $scope.items.push({ 22 | key: $scope.info.key, 23 | value: $scope.info.value 24 | }); 25 | }; 26 | 27 | $scope.removeItem = function(item) { 28 | $scope.items.splice($scope.items.indexOf(item), 1); 29 | }; 30 | 31 | $scope.addSecret = function() { 32 | co(function*() { 33 | var user = yield AuthService.getUserById($routeParams.userId); 34 | var info = yield ManagerService.addInfo({ 35 | userId: user.id, 36 | name: $scope.secret.name, 37 | desc: $scope.secret.desc, 38 | items: $scope.items 39 | }); 40 | events.emit('toast', { 41 | type: 'success', 42 | content: '新机密创建成功!', 43 | delay: 100 44 | }); 45 | yield utils.delay(noop, 20); 46 | events.emit('bottomsheet-hide'); 47 | yield utils.delay(noop, 550); 48 | events.emit('secrets-updated'); 49 | }) 50 | .catch(function(err) { 51 | events.emit('toast-error', err); 52 | }); 53 | }; 54 | 55 | var nameWatcher = $scope.$watch('secret.name', function(newValue) { 56 | $scope.state.canAdd = newValue && $scope.secret.desc; 57 | }); 58 | 59 | var descWatcher = $scope.$watch('secret.desc', function(newValue) { 60 | $scope.state.canAdd = newValue && $scope.secret.name; 61 | }); 62 | 63 | $scope.$on('$destroy', function() { 64 | nameWatcher(); 65 | descWatcher(); 66 | }); 67 | }; 68 | 69 | module.exports = [ 70 | '$scope', 71 | '$routeParams', 72 | 'events', 73 | 'ManagerService', 74 | 'AuthService', 75 | 'utils', 76 | CreateController 77 | ]; 78 | -------------------------------------------------------------------------------- /src/js/features/manager/controller/ManagerController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the ManagerController controller 3 | * 4 | * @author Howard.Zuo 5 | * @date Dec 22, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var debounce = require('lib/Debounce'); 11 | var settingTpl = require('../partials/setting.html'); 12 | var createTpl = require('../partials/create.html'); 13 | var merge = require('angular').merge; 14 | var co = require('co'); 15 | 16 | var ManagerController = function($scope, $routeParams, events, utils, ManagerService, AuthService) { 17 | 18 | $scope.search = {txt: ''}; 19 | $scope.info = {}; 20 | $scope.state = {canSave: true, secrets: [], loading: true}; 21 | 22 | co(function*() { 23 | $scope.user = yield AuthService.getUserById($routeParams.userId); 24 | }); 25 | 26 | var commonErrorHandler = function(err) { 27 | events.emit('toast', {type: 'error', content: err}); 28 | }; 29 | 30 | var secretsUpdate = function() { 31 | utils.delay(function() { 32 | $scope.state.loading = true; 33 | co(function*() { 34 | var user = yield AuthService.getUserById($routeParams.userId); 35 | var data = yield ManagerService.getInfos(user.id); 36 | $scope.$apply(function() { 37 | $scope.state.secrets = data; 38 | $scope.state.loading = false; 39 | }); 40 | }); 41 | }, 30); 42 | }; 43 | 44 | secretsUpdate(); 45 | 46 | $scope.create = function($event) { 47 | events.emit('bottomsheet', { 48 | event: $event, 49 | controller: 'CreateController', 50 | template: createTpl 51 | }); 52 | }; 53 | 54 | $scope.setting = function($event) { 55 | events.emit('bottomsheet', { 56 | event: $event, 57 | controller: 'SettingController', 58 | template: settingTpl 59 | }); 60 | }; 61 | 62 | $scope.logout = function($event) { 63 | events.emit('confirm', { 64 | content: '您确定要登出么?', 65 | event: $event, 66 | onComplete: function() { 67 | utils.delay(function() { 68 | utils.redirect('/login'); 69 | }, 400); 70 | } 71 | }); 72 | return; 73 | }; 74 | 75 | $scope.deleteSecret = function(secret, $event) { 76 | events.emit('confirm', { 77 | content: '您确定要删除这个秘密么?', 78 | event: $event, 79 | onComplete: function() { 80 | co(function*() { 81 | var removed = yield ManagerService.removeInfo(secret); 82 | events.emit('toast', { 83 | type: 'success', 84 | content: '秘密已删除!' 85 | }); 86 | utils.delay(secretsUpdate, 100); 87 | }) 88 | .catch(commonErrorHandler); 89 | } 90 | }); 91 | }; 92 | 93 | $scope.viewSecret = function(secret) { 94 | $scope.state.currentSecret = merge({}, secret); 95 | events.emit('sidebar', {id: 'left'}); 96 | }; 97 | 98 | $scope.removeItem = function(item) { 99 | $scope.state.currentSecret.items.splice($scope.state.currentSecret.items.indexOf(item), 1); 100 | }; 101 | 102 | $scope.saveSecret = function() { 103 | co(function*() { 104 | var info = yield ManagerService.updateInfo($scope.state.currentSecret); 105 | events.emit('toast', { 106 | type: 'success', 107 | content: '秘密已修改!' 108 | }); 109 | events.emit('sidebar-hide', {id: 'left'}); 110 | utils.delay(secretsUpdate, 300); 111 | }) 112 | .catch(commonErrorHandler); 113 | }; 114 | 115 | $scope.addItem = function() { 116 | $scope.state.currentSecret.items.push({ 117 | key: $scope.info.key, 118 | value: $scope.info.value 119 | }); 120 | }; 121 | 122 | var updateSearchTxt = debounce(function(newValue) { 123 | $scope.search.searchTxt = newValue; 124 | $scope.$apply(); 125 | }, 300); 126 | 127 | var searchWatch = $scope.$watch('search.txt', function(newValue) { 128 | updateSearchTxt(newValue); 129 | }); 130 | 131 | var searchWatch = $scope.$watch('state.currentSecret.desc', function(newValue) { 132 | $scope.state.canSave = !!newValue; 133 | }); 134 | 135 | events.on('secrets-updated', secretsUpdate); 136 | 137 | $scope.$on('$destroy', function() { 138 | updateSearchTxt(); 139 | events.off('secrets-updated', secretsUpdate); 140 | }); 141 | }; 142 | 143 | module.exports = [ 144 | '$scope', 145 | '$routeParams', 146 | 'events', 147 | 'utils', 148 | 'ManagerService', 149 | 'AuthService', 150 | ManagerController 151 | ]; 152 | -------------------------------------------------------------------------------- /src/js/features/manager/controller/SettingController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the SettingController controller 3 | * 4 | * @author Howard.Zuo 5 | * @date Dec 22, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var mainWindow = require('electron').remote.getCurrentWindow(); 11 | var dialog = require('electron').remote.require('dialog'); 12 | 13 | var merge = require('angular').merge; 14 | var debounce = require('lib/Debounce'); 15 | var fs = require('fs'); 16 | var co = require('co'); 17 | 18 | var SettingController = function($scope, $routeParams, events, AuthService, DbService, utils) { 19 | $scope.user = {}; 20 | $scope.db = {address: DbService.address()}; 21 | $scope.passwordstate = {oldpasswordIncorrect: false}; 22 | $scope.dbstate = {invalidAddress: false}; 23 | 24 | var user = {}; 25 | 26 | co(function*() { 27 | var userInfo = yield AuthService.getUserById($routeParams.userId); 28 | $scope.user.question = userInfo.question; 29 | merge(user, userInfo); 30 | }); 31 | 32 | var updateUser = function*(newUser, message, needlogout) { 33 | var userInfo; 34 | try { 35 | userInfo = yield AuthService.updateUser(newUser); 36 | events.emit('info', { 37 | content: message, 38 | onComplete: function() { 39 | events.emit('bottomsheet-hide'); 40 | if (!needlogout) { 41 | return; 42 | } 43 | utils.delay(function() { 44 | utils.redirect('login'); 45 | }, 600); 46 | } 47 | }); 48 | } catch (e) { 49 | events.emit('toast-error', e.message); 50 | } 51 | }; 52 | 53 | $scope.updatePassword = function(form) { 54 | var newUser = merge({}, user); 55 | newUser.password = $scope.user.password; 56 | co(updateUser(newUser, '密码重置成功,需重新登录', true)); 57 | }; 58 | 59 | $scope.saveNewQuestion = function() { 60 | var newUser = merge({}, user); 61 | newUser.question = $scope.user.question; 62 | newUser.answer = $scope.user.answer; 63 | $scope.user = {question: newUser.answer}; 64 | co(updateUser(newUser, '提示问题重置成功')); 65 | }; 66 | 67 | $scope.updatedb = function() { 68 | DbService.address($scope.db.address); 69 | events.emit('info', { 70 | content: '数据库目录锁定成功,需重新登录', 71 | onComplete: function() { 72 | events.emit('bottomsheet-hide'); 73 | utils.delay(function() { 74 | utils.redirect('login'); 75 | }, 600); 76 | } 77 | }); 78 | }; 79 | 80 | $scope.openDialog = function($event) { 81 | utils.stopEvent($event); 82 | dialog.showOpenDialog(mainWindow, { 83 | title: '位置选择', 84 | properties: [ 85 | 'openDirectory' 86 | ] 87 | }, function(files) { 88 | if (files && files.length) { 89 | $scope.db.address = files[0]; 90 | $scope.$apply(); 91 | } 92 | }); 93 | }; 94 | 95 | AuthService.questions() 96 | .success(function(data) { 97 | $scope.questions = data; 98 | }); 99 | 100 | var reEnvaluePasswordForm = function() { 101 | $scope.passwordstate.isInvalid = $scope.passwordstate.oldpasswordIncorrect || $scope.passwordstate.repasswordnotsame; 102 | }; 103 | 104 | var oldpasswordWatch = $scope.$watch('user.oldpassword', function(newValue) { 105 | if (!newValue) { 106 | return; 107 | } 108 | $scope.passwordstate.oldpasswordIncorrect = user.password !== newValue; 109 | reEnvaluePasswordForm(); 110 | }); 111 | var repasswordWatch = $scope.$watch('user.repassword', function(newValue) { 112 | $scope.passwordstate.repasswordnotsame = $scope.user.password !== newValue; 113 | reEnvaluePasswordForm(); 114 | }); 115 | var passwordWatch = $scope.$watch('user.password', function(newValue) { 116 | $scope.passwordstate.repasswordnotsame = $scope.user.repassword !== newValue; 117 | reEnvaluePasswordForm(); 118 | }); 119 | 120 | var checkAddress = debounce(function(value) { 121 | var stat; 122 | try { 123 | stat = fs.statSync(value); 124 | $scope.dbstate.invalidAddress = !stat.isDirectory(); 125 | } catch (e) { 126 | $scope.dbstate.invalidAddress = true; 127 | } 128 | $scope.$apply(); 129 | }, 300); 130 | 131 | var undbwatch = $scope.$watch('db.address', function(newValue) { 132 | if (!newValue) { 133 | return; 134 | } 135 | checkAddress(newValue); 136 | }); 137 | 138 | 139 | $scope.$on('$destroy', function() { 140 | oldpasswordWatch(); 141 | repasswordWatch(); 142 | passwordWatch(); 143 | undbwatch(); 144 | }); 145 | }; 146 | 147 | module.exports = [ 148 | '$scope', 149 | '$routeParams', 150 | 'events', 151 | 'AuthService', 152 | 'DbService', 153 | 'utils', 154 | SettingController 155 | ]; 156 | -------------------------------------------------------------------------------- /src/js/features/manager/directive/ItemEdit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the ItemEdit Directive 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 25, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var ItemInput = function(utils) { 11 | return { 12 | restrict: 'A', 13 | scope: { 14 | itemEdit: '=', 15 | onRemove: '&' 16 | }, 17 | link: function($scope, element, attrs) { 18 | 19 | $scope.next = function($event) { 20 | if ($event.keyCode !== 13) { 21 | return; 22 | } 23 | if (!$scope.itemEdit.key) { 24 | return; 25 | } 26 | element.find('input')[1].focus(); 27 | }; 28 | 29 | $scope.finish = function($event) { 30 | if ($event.keyCode !== 13) { 31 | return; 32 | } 33 | if (!$scope.itemEdit.key || !$scope.itemEdit.value) { 34 | return; 35 | } 36 | $scope.disableValue(); 37 | }; 38 | 39 | $scope.editKey = function() { 40 | if ($scope.itemEdit.keyEditable) { 41 | return; 42 | } 43 | $scope.itemEdit.keyEditable = true; 44 | element.find('i').removeClass('show'); 45 | }; 46 | 47 | $scope.editValue = function() { 48 | if ($scope.itemEdit.valueEditable) { 49 | return; 50 | } 51 | $scope.itemEdit.valueEditable = true; 52 | element.find('i').removeClass('show'); 53 | }; 54 | 55 | $scope.disableKey = function() { 56 | $scope.itemEdit.keyEditable = false; 57 | }; 58 | 59 | $scope.disableValue = function() { 60 | $scope.itemEdit.valueEditable = false; 61 | }; 62 | 63 | element.on('mouseenter', function() { 64 | if ($scope.itemEdit.keyEditable || $scope.itemEdit.valueEditable) { 65 | return; 66 | } 67 | element.find('i').addClass('show'); 68 | }); 69 | 70 | element.on('mouseleave', function() { 71 | element.find('i').removeClass('show'); 72 | }); 73 | 74 | $scope.$on('$destroy', function() { 75 | element.off('mouseenter'); 76 | element.off('mouseleave'); 77 | }); 78 | }, 79 | template: '' 80 | }; 81 | }; 82 | 83 | module.exports = ['utils', ItemInput]; 84 | -------------------------------------------------------------------------------- /src/js/features/manager/directive/ItemInput.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the ItemInput Directive 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 25, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var ItemInput = function(utils) { 11 | return { 12 | restrict: 'A', 13 | scope: { 14 | itemInput: '=', 15 | onSubmit: '&' 16 | }, 17 | link: function($scope, element, attrs) { 18 | 19 | $scope.next = function($event) { 20 | if ($event.keyCode !== 13) { 21 | return; 22 | } 23 | if (!$scope.itemInput.key) { 24 | return; 25 | } 26 | element.find('input')[1].focus(); 27 | }; 28 | 29 | $scope.finish = function($event) { 30 | if ($event.keyCode !== 13) { 31 | return; 32 | } 33 | if (!$scope.itemInput.key || !$scope.itemInput.value) { 34 | return; 35 | } 36 | $scope.onSubmit(); 37 | utils.delay(function() { 38 | $scope.itemInput.key = ''; 39 | $scope.itemInput.value = ''; 40 | element.find('input')[0].focus(); 41 | }, 10); 42 | }; 43 | }, 44 | template: '' 45 | }; 46 | }; 47 | 48 | module.exports = ['utils', ItemInput]; 49 | -------------------------------------------------------------------------------- /src/js/features/manager/filter/SearchFilter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the SearchFilter 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 24, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var SearchFilter = function() { 11 | return function(input, txt, params) { 12 | if (!txt) { 13 | return input; 14 | } 15 | return input.filter(function(item) { 16 | return params.filter(function(key) { 17 | return item[key].indexOf(txt) > -1; 18 | }).length; 19 | }); 20 | }; 21 | }; 22 | 23 | module.exports = [SearchFilter]; 24 | -------------------------------------------------------------------------------- /src/js/features/manager/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ****************************************************************************************************** 3 | * 4 | * Defines a manager feature 5 | * 6 | * @author Howard.Zuo 7 | * @date Nov 25, 2015 8 | * 9 | * ****************************************************************************************************** 10 | */ 11 | 'use strict'; 12 | var FeatureBase = require('lib/FeatureBase'); 13 | var Routes = require('./Routes'); 14 | var ManagerController = require('./controller/ManagerController'); 15 | var SettingController = require('./controller/SettingController'); 16 | var CreateController = require('./controller/CreateController'); 17 | var ManagerService = require('./service/ManagerService'); 18 | var SearchFilter = require('./filter/SearchFilter'); 19 | var ItemInput = require('./directive/ItemInput'); 20 | var ItemEdit = require('./directive/ItemEdit'); 21 | 22 | class Feature extends FeatureBase { 23 | 24 | constructor() { 25 | super('manager'); 26 | this.routes = Routes; 27 | } 28 | 29 | execute() { 30 | this.controller('ManagerController', ManagerController); 31 | this.controller('SettingController', SettingController); 32 | this.controller('CreateController', CreateController); 33 | this.service('ManagerService', ManagerService); 34 | this.filter('searchFilter', SearchFilter); 35 | this.directive('itemInput', ItemInput); 36 | this.directive('itemEdit', ItemEdit); 37 | } 38 | } 39 | 40 | module.exports = Feature; 41 | -------------------------------------------------------------------------------- /src/js/features/manager/partials/create.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |

6 | 新增机密 7 |

8 | 9 | 10 | 11 | 12 | 确认保存 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
30 |
31 |
32 |
33 | -------------------------------------------------------------------------------- /src/js/features/manager/partials/manager.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |

7 | 查看机密 8 |

9 | 10 | 11 | 12 | 13 | 确认保存 14 | 15 | 16 |
17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 |
34 | 35 |

保密局

36 | 37 | 38 | {{ ::user.name }} 39 | 40 | 41 | 42 | 43 | 44 | 新增机密 45 | 46 | 47 | 48 | 49 | 50 | 51 | 设置 52 | 53 | 54 | 55 | 56 | 57 | 58 | 登出系统 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
71 | 72 |
73 | 74 |
75 |

没有任何秘密可言哦,来创建吧!

76 |
77 | 78 |
79 | 80 | 81 |
82 |

{{ secret.name }}

83 |

84 | {{ secret.desc }} 85 |

86 |
87 | 88 | 89 | 90 | 查看 91 | 92 | 93 | 94 | 95 | 96 | 删除 97 | 98 | 99 |
100 |
101 |
102 |
103 | -------------------------------------------------------------------------------- /src/js/features/manager/partials/setting.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 密码重置 6 | 7 |
8 | 9 | 10 | 11 |
12 |
原密码错误
13 |
14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
重复密码与新密码不一致
26 |
27 |
28 | 29 | 30 | 确认变更 31 | 32 |
33 |
34 |
35 | 36 | 37 | 提示问题变更 38 | 39 |
40 | 41 | 42 | 43 | 44 | {{ question.label }} 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 确认变更 56 | 57 |
58 |
59 |
60 | 61 | 62 | 数据库位置 63 | 64 |
65 | 66 | 67 | 68 |
69 |
路径输入错误!请选择正确的文件路径
70 |
71 |
72 | 73 | 74 | 确认变更 75 | 76 |
77 |
78 |
79 |
80 |
81 |
82 | -------------------------------------------------------------------------------- /src/js/features/manager/service/ManagerService.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the ManagerService 3 | * 4 | * @author Howard.Zuo 5 | * @date Dec 1, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var ManagerService = function(utils, DbService) { 11 | 12 | this.getInfos = function*(userId) { 13 | 14 | var secretDB = DbService.getSecretDb(); 15 | var cursor = secretDB.find({ 16 | userId: utils.encryptTxt(userId) 17 | }).sort({updateDate: -1}); 18 | var finder = utils.promisify(cursor.exec).bind(cursor); 19 | var docs = yield finder(); 20 | 21 | return docs.map(function(doc) { 22 | var info = { 23 | id: utils.decryptTxt(doc.id), 24 | userId: utils.decryptTxt(doc.userId), 25 | name: utils.decryptTxt(doc.name), 26 | desc: utils.decryptTxt(doc.desc), 27 | createDate: doc.createDate, 28 | updateDate: doc.updateDate, 29 | items: doc.items.map(function(item) { 30 | return { 31 | key: utils.decryptTxt(item.key), 32 | value: utils.decryptTxt(item.value) 33 | }; 34 | }) 35 | }; 36 | return info; 37 | }); 38 | }; 39 | 40 | this.addInfo = function*(info) { 41 | 42 | var encryptInfo = { 43 | id: utils.encryptTxt(utils.ID()), 44 | userId: utils.encryptTxt(info.userId), 45 | name: utils.encryptTxt(info.name), 46 | desc: utils.encryptTxt(info.desc), 47 | createDate: new Date().getTime(), 48 | updateDate: new Date().getTime(), 49 | items: info.items.map(function(item) { 50 | return { 51 | key: utils.encryptTxt(item.key), 52 | value: utils.encryptTxt(item.value) 53 | }; 54 | }) 55 | }; 56 | 57 | var secretDB = DbService.getSecretDb(); 58 | var cursor = secretDB.count({ 59 | userId: encryptInfo.userId, 60 | name: encryptInfo.name 61 | }); 62 | var finder = utils.promisify(cursor.exec).bind(cursor); 63 | var inserter = utils.promisify(secretDB.insert).bind(secretDB); 64 | var count = yield finder(); 65 | if (count > 0) { 66 | return utils.reject('该秘密信息已被注册,请重试'); 67 | } 68 | 69 | var doc = yield inserter(encryptInfo); 70 | return { 71 | id: utils.decryptTxt(doc.id), 72 | userId: utils.decryptTxt(doc.userId), 73 | name: utils.decryptTxt(doc.name), 74 | desc: utils.decryptTxt(doc.desc), 75 | createDate: doc.createDate, 76 | updateDate: doc.updateDate, 77 | items: doc.items.map(function(item) { 78 | return { 79 | key: utils.decryptTxt(item.key), 80 | value: utils.decryptTxt(item.value) 81 | }; 82 | }) 83 | }; 84 | }; 85 | 86 | this.updateInfo = function*(info) { 87 | 88 | var id = utils.encryptTxt(info.id); 89 | var secretDB = DbService.getSecretDb(); 90 | var findOner = utils.promisify(secretDB.findOne).bind(secretDB); 91 | var updater = utils.promisify(secretDB.update).bind(secretDB); 92 | 93 | var encryptInfo = { 94 | userId: utils.encryptTxt(info.userId), 95 | name: utils.encryptTxt(info.name), 96 | desc: utils.encryptTxt(info.desc), 97 | createDate: info.createDate, 98 | updateDate: new Date().getTime(), 99 | items: info.items.map(function(item) { 100 | return { 101 | key: utils.encryptTxt(item.key), 102 | value: utils.encryptTxt(item.value) 103 | }; 104 | }) 105 | }; 106 | 107 | var doc = yield findOner({id: id}); 108 | if (!doc) { 109 | return utils.reject('秘密信息不存在,或许已被删除,请检查'); 110 | } 111 | var numReplaced = yield updater({id: id}, {$set: encryptInfo}, {}); 112 | return info; 113 | }; 114 | 115 | this.removeInfo = function*(info) { 116 | var secretDB = DbService.getSecretDb(); 117 | var deleter = utils.promisify(secretDB.remove).bind(secretDB); 118 | var id = utils.encryptTxt(info.id); 119 | return deleter({id: id}, {}); 120 | }; 121 | }; 122 | 123 | module.exports = ['utils', 'DbService', ManagerService]; 124 | -------------------------------------------------------------------------------- /src/js/fw/config/MaterialConfig.js: -------------------------------------------------------------------------------- 1 | /** 2 | * MaterialConfig set angular-material needed configuration 3 | * 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 20, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | var ConfiguratorBase = require('lib/ConfiguratorBase'); 11 | 12 | class Configurator extends ConfiguratorBase { 13 | constructor(features, app) { 14 | super(features, app); 15 | } 16 | 17 | execute() { 18 | this.app.config([ 19 | '$mdThemingProvider', 20 | function($mdThemingProvider) { 21 | $mdThemingProvider 22 | .theme('default') 23 | .primaryPalette('blue') 24 | .accentPalette('orange') 25 | .warnPalette('red'); 26 | } 27 | ]); 28 | } 29 | } 30 | 31 | module.exports = Configurator; 32 | -------------------------------------------------------------------------------- /src/js/fw/config/RouterConfig.js: -------------------------------------------------------------------------------- 1 | /** 2 | * RouterConfig collect route information from each feature and combine them 3 | * with ngRoute. 4 | * 5 | * 6 | * @author Howard.Zuo 7 | * @date Nov 20, 2015 8 | * 9 | */ 10 | 'use strict'; 11 | var ConfiguratorBase = require('lib/ConfiguratorBase'); 12 | var pluck = require('lib/Pluck'); 13 | var omit = require('lib/Omit'); 14 | 15 | class Configurator extends ConfiguratorBase { 16 | constructor(features, app) { 17 | super(features, app); 18 | } 19 | 20 | execute() { 21 | if (!this.features || this.features.length === 0) { 22 | console.warn('No features loaded'); 23 | return; 24 | } 25 | 26 | var routes = this.features 27 | .filter(function(feature) { 28 | return feature.routes && feature.routes.length > 0; 29 | }) 30 | .map(function(feature) { 31 | return feature.routes; 32 | }) 33 | .reduce(function(previous, current) { 34 | return previous.concat(current); 35 | }, []); 36 | 37 | 38 | var defaultRoutes = routes.filter(function(route) { 39 | return route.isDefault; 40 | }); 41 | 42 | if (defaultRoutes.length === 0) { 43 | console.warn('There is no any default route set. Try setting isDefault to the route you preferred'); 44 | } else if (defaultRoutes.length > 1) { 45 | var defaultWhens = pluck(defaultRoutes, 'when'); 46 | console.warn('You have set [' + defaultRoutes.length + '] default routes, they are [' + defaultWhens.join(', ') + ']. Try to correct it'); 47 | } 48 | 49 | var routeWhens = pluck(routes, 'when').sort(); 50 | 51 | for (var i = 0; i < routeWhens.length - 1; i++) { 52 | if (routeWhens[i] === routeWhens[i + 1]) { 53 | throw new Error('Duplicated Route: [ ' + routeWhens[i] + ' ]'); 54 | } 55 | } 56 | 57 | this.app.constant('Routes', routes); 58 | 59 | this.app.config([ 60 | '$locationProvider', 61 | '$routeProvider', 62 | function($locationProvider, $routeProvider) { 63 | 64 | //config each router 65 | routes.forEach(function(ro) { 66 | $routeProvider 67 | .when(ro.when, omit(ro, ['when'])); 68 | }); 69 | 70 | //config default page 71 | var defaultRouter = routes.filter(function(route) { 72 | return route.isDefault; 73 | })[0]; 74 | 75 | if (defaultRouter) { 76 | $routeProvider.otherwise({ 77 | redirectTo: defaultRouter.when 78 | }); 79 | } 80 | 81 | $locationProvider.html5Mode(false); 82 | 83 | } 84 | ]); 85 | } 86 | } 87 | 88 | module.exports = Configurator; 89 | -------------------------------------------------------------------------------- /src/js/fw/config/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrance of config 3 | * 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 20, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | var material = require('./MaterialConfig'); 11 | var router = require('./RouterConfig'); 12 | 13 | module.exports = [material, router]; 14 | -------------------------------------------------------------------------------- /src/js/fw/ext/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Return all information of external angular resources 3 | * 4 | * @author Howard.Zuo 5 | * @date Dec 3, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | require('angular-animate'); 10 | require('angular-route'); 11 | require('angular-messages'); 12 | require('angular-material'); 13 | require('angular-promiseify'); 14 | 15 | module.exports = [ 16 | 'ngAnimate', 17 | 'ngRoute', 18 | 'angular-promiseify', 19 | 'ngMessages', 20 | 'ngMaterial' 21 | ]; 22 | -------------------------------------------------------------------------------- /src/js/fw/init/Validator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Validate the features is loaded correctly 3 | * 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 20, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | var InitBase = require('lib/InitBase'); 11 | var pluck = require('lib/Pluck'); 12 | 13 | class Initializer extends InitBase { 14 | constructor(features, app) { 15 | super(features, app); 16 | } 17 | 18 | execute() { 19 | if (!this.features || this.features.length === 0) { 20 | console.warn('No features loaded'); 21 | return; 22 | } 23 | 24 | var modNames = pluck(this.features, 'export').sort(); 25 | for (var i = 0; i < modNames.length - 1; i++) { 26 | if (modNames[i] === modNames[i + 1]) { 27 | throw new Error('Duplicated Module: [ ' + modNames[i] + ' ], you have to specify another name'); 28 | } 29 | } 30 | } 31 | } 32 | 33 | module.exports = Initializer; 34 | -------------------------------------------------------------------------------- /src/js/fw/init/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrance of init 3 | * 4 | * Note: this module is part of application-level framework, developers should 5 | * never touch this module 6 | * 7 | * 8 | * @author Howard.Zuo 9 | * @date Nov 20, 2015 10 | * 11 | */ 12 | 'use strict'; 13 | var validator = require('./Validator'); 14 | 15 | module.exports = [validator]; 16 | -------------------------------------------------------------------------------- /src/js/fw/lib/ConfiguratorBase.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ConfiguratorBase class 3 | * 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 18, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | class ConfiguratorBase { 11 | 12 | constructor(features, app) { 13 | this.features = features; 14 | this.app = app; 15 | } 16 | 17 | execute() {} 18 | } 19 | 20 | module.exports = ConfiguratorBase; 21 | -------------------------------------------------------------------------------- /src/js/fw/lib/Debounce.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns a function, that, as long as it continues to be invoked, will not 3 | * be triggered. The function will be called after it stops being called for 4 | * N milliseconds. If `immediate` is passed, trigger the function on the 5 | * leading edge, instead of the trailing 6 | * 7 | * @author Howard.Zuo 8 | * @date Oct 23, 2015 9 | * 10 | **/ 11 | 'use strict'; 12 | var debounce = function(func, wait, immediate) { 13 | var timeout; 14 | return function() { 15 | var context = this, 16 | args = arguments; 17 | var later = function() { 18 | timeout = null; 19 | if (!immediate) { 20 | func.apply(context, args); 21 | } 22 | }; 23 | var callNow = immediate && !timeout; 24 | clearTimeout(timeout); 25 | timeout = setTimeout(later, wait); 26 | if (callNow) { 27 | func.apply(context, args); 28 | } 29 | }; 30 | }; 31 | 32 | module.exports = debounce; 33 | -------------------------------------------------------------------------------- /src/js/fw/lib/FeatureBase.js: -------------------------------------------------------------------------------- 1 | /** 2 | * FeatureBase class 3 | * 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 24, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | var angular = require('angular'); 11 | 12 | class FeatureBase { 13 | 14 | constructor(name) { 15 | this.export = name; 16 | this.mod = angular.module(this.export, []); 17 | this.run = this.mod.run; 18 | this.controller = this.mod.controller; 19 | this.service = this.mod.service; 20 | this.factory = this.mod.factory; 21 | this.directive = this.mod.directive; 22 | this.filter = this.mod.filter; 23 | } 24 | 25 | beforeStart() {} 26 | 27 | execute() {} 28 | } 29 | 30 | module.exports = FeatureBase; 31 | -------------------------------------------------------------------------------- /src/js/fw/lib/Flatten.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Recursively flattens a nested array 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 18, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var angular = require('angular'); 11 | 12 | var flatten = function(arr) { 13 | var newArr = []; 14 | if (!arr) { 15 | return newArr; 16 | } 17 | if (!angular.isArray(arr)) { 18 | return newArr; 19 | } 20 | arr.forEach(function(item) { 21 | if (!angular.isArray(item)) { 22 | newArr.push(item); 23 | return; 24 | } 25 | Array.prototype.push.apply(newArr, flatten(item)); 26 | }); 27 | return newArr; 28 | }; 29 | 30 | module.exports = flatten; 31 | -------------------------------------------------------------------------------- /src/js/fw/lib/InitBase.js: -------------------------------------------------------------------------------- 1 | /** 2 | * InitBase class 3 | * 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 20, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | class InitBase { 11 | 12 | constructor(features) { 13 | this.features = features; 14 | } 15 | 16 | execute() {} 17 | } 18 | 19 | module.exports = InitBase; 20 | -------------------------------------------------------------------------------- /src/js/fw/lib/Omit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This method creates an object composed of the own and inherited enumerable properties of object that are not omitted. 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 18, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var angular = require('angular'); 11 | 12 | var clone = function(obj) { 13 | if (typeof obj !== 'object' || !obj) { 14 | return obj; 15 | } 16 | var copy = {}; 17 | for (var attr in obj) { 18 | if (obj.hasOwnProperty(attr)) { 19 | copy[attr] = obj[attr]; 20 | } 21 | } 22 | return copy; 23 | }; 24 | 25 | var omit = function(obj, keys) { 26 | if (!angular.isObject(obj)) { 27 | return obj; 28 | } 29 | if (angular.isArray(keys) && keys.length === 0) { 30 | return obj; 31 | } 32 | if (angular.isString(keys) && !keys) { 33 | return obj; 34 | } 35 | if (!angular.isString(keys) && !angular.isArray(keys)) { 36 | return obj; 37 | } 38 | var o = clone(obj); 39 | keys.forEach(function(key) { 40 | delete o[key]; 41 | }); 42 | return o; 43 | }; 44 | 45 | module.exports = omit; 46 | -------------------------------------------------------------------------------- /src/js/fw/lib/Pluck.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gets the property value of path from all elements in collection. 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 20, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | 10 | var angular = require('angular'); 11 | 12 | var pluck = function(arr, key) { 13 | if (!angular.isArray(arr) || arr.length === 0) { 14 | return []; 15 | } 16 | if (!key) { 17 | return arr; 18 | } 19 | return arr.map(function(a) { 20 | return a[key]; 21 | }); 22 | }; 23 | 24 | module.exports = pluck; 25 | -------------------------------------------------------------------------------- /src/js/fw/lib/ServiceBase.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ServiceBase class 3 | * 4 | * 5 | * @author Howard.Zuo 6 | * @date Nov 20, 2015 7 | * 8 | */ 9 | 'use strict'; 10 | class ServiceBase { 11 | 12 | constructor(features, app) { 13 | this.features = features; 14 | this.app = app; 15 | } 16 | 17 | execute() {} 18 | } 19 | 20 | module.exports = ServiceBase; 21 | -------------------------------------------------------------------------------- /src/js/fw/service/Events.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Defines `events` service which helps developer 4 | * control EVENT system 5 | * 6 | * 7 | * @author Howard.Zuo 8 | * @date Nov 20, 2015 9 | * 10 | */ 11 | 'use strict'; 12 | var ServiceBase = require('lib/ServiceBase'); 13 | var angular = require('angular'); 14 | 15 | class Service extends ServiceBase { 16 | constructor(features, app) { 17 | super(features, app); 18 | } 19 | 20 | execute() { 21 | this.app.factory('events', [ 22 | '$rootScope', 23 | function($rootScope) { 24 | var factory = {}; 25 | 26 | var listeners = {}; 27 | 28 | factory.emit = function(eventName, data) { 29 | if (!eventName) { 30 | return; 31 | } 32 | $rootScope.$broadcast(eventName, data); 33 | }; 34 | 35 | factory.on = function(eventName, callback) { 36 | if (!listeners[eventName]) { 37 | listeners[eventName] = []; 38 | $rootScope.$on(eventName, function(event, data) { 39 | listeners[eventName].forEach(function(listener) { 40 | listener(data); 41 | }); 42 | }); 43 | 44 | } 45 | if (angular.isFunction(callback)) { 46 | listeners[eventName].push(callback); 47 | } 48 | }; 49 | 50 | factory.off = function(eventName, callback) { 51 | if (!listeners[eventName]) { 52 | return; 53 | } 54 | for (var i = 0; i < listeners[eventName].length; i++) { 55 | if (listeners[eventName][i] === callback) { 56 | listeners[eventName].splice(i, 1); 57 | return; 58 | } 59 | } 60 | }; 61 | 62 | return factory; 63 | } 64 | ]); 65 | } 66 | } 67 | 68 | module.exports = Service; 69 | -------------------------------------------------------------------------------- /src/js/fw/service/Utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Defines `utils` service 4 | * 5 | * 6 | * @author Howard.Zuo 7 | * @date Nov 24, 2015 8 | * 9 | */ 10 | 'use strict'; 11 | var ServiceBase = require('lib/ServiceBase'); 12 | 13 | var crypto = require('crypto'), 14 | algorithm = 'aes-256-ctr', 15 | password = '<%= password %>'; 16 | 17 | class Service extends ServiceBase { 18 | constructor(features, app) { 19 | super(features, app); 20 | } 21 | 22 | execute() { 23 | this.app.service('utils', [ 24 | '$q', 25 | '$timeout', 26 | '$location', 27 | 'promiseify', 28 | function($q, $timeout, $location, promiseify) { 29 | 30 | this.delay = function(func, delay) { 31 | return $timeout(func, delay); 32 | }; 33 | 34 | this.redirect = function(url) { 35 | $location.url(url); 36 | }; 37 | 38 | this.base64ToString = function(str) { 39 | return decodeURIComponent(escape(atob(str))); 40 | }; 41 | 42 | this.stringTobase64 = function(str) { 43 | return btoa(unescape(encodeURIComponent(str))); 44 | }; 45 | 46 | var promiseExtra = function(promise) { 47 | promise.success = function(fn) { 48 | promise.then(function(response) { 49 | fn(response); 50 | }); 51 | return promise; 52 | }; 53 | promise.error = function(fn) { 54 | promise.then(null, function(response) { 55 | fn(response); 56 | }); 57 | return promise; 58 | }; 59 | return promise; 60 | }; 61 | 62 | this.promise = function(func) { 63 | var promise = $q(func); 64 | return promiseExtra(promise); 65 | }; 66 | 67 | this.resolve = function(value) { 68 | var promise = $q.resolve(value); 69 | return promiseExtra(promise); 70 | }; 71 | 72 | this.reject = function(reason) { 73 | var promise = $q.reject(reason); 74 | return promiseExtra(promise); 75 | }; 76 | 77 | this.promisify = promiseify; 78 | 79 | this.stopEvent = function(e) { 80 | if (!e) { 81 | return; 82 | } 83 | if (e.stopPropagation) { 84 | e.stopPropagation(); 85 | } 86 | if (e.preventDefault) { 87 | e.preventDefault(); 88 | } 89 | }; 90 | 91 | this.encryptTxt = function(text) { 92 | var cipher = crypto.createCipher(algorithm, password); 93 | var crypted = cipher.update(text, 'utf8', 'hex'); 94 | crypted += cipher.final('hex'); 95 | return crypted; 96 | }; 97 | 98 | this.decryptTxt = function(text) { 99 | var decipher = crypto.createDecipher(algorithm, password); 100 | var dec = decipher.update(text, 'hex', 'utf8'); 101 | dec += decipher.final('utf8'); 102 | return dec; 103 | }; 104 | 105 | this.getEncryptInfo = function() { 106 | return { 107 | algorithm: algorithm, 108 | password: password 109 | }; 110 | }; 111 | 112 | this.ID = function() { 113 | return new Date().getTime() + ''; 114 | }; 115 | 116 | } 117 | ]); 118 | } 119 | } 120 | 121 | module.exports = Service; 122 | -------------------------------------------------------------------------------- /src/js/fw/service/events.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Defines `events` service which helps developer 4 | * control EVENT system 5 | * 6 | * 7 | * @author Howard.Zuo 8 | * @date Nov 20, 2015 9 | * 10 | */ 11 | 'use strict'; 12 | var ServiceBase = require('lib/ServiceBase'); 13 | var angular = require('angular'); 14 | 15 | class Service extends ServiceBase { 16 | constructor(features, app) { 17 | super(features, app); 18 | } 19 | 20 | execute() { 21 | this.app.factory('events', [ 22 | '$rootScope', 23 | function($rootScope) { 24 | var factory = {}; 25 | 26 | var listeners = {}; 27 | 28 | factory.emit = function(eventName, data) { 29 | if (!eventName) { 30 | return; 31 | } 32 | $rootScope.$broadcast(eventName, data); 33 | }; 34 | 35 | factory.on = function(eventName, callback) { 36 | if (!listeners[eventName]) { 37 | listeners[eventName] = []; 38 | $rootScope.$on(eventName, function(event, data) { 39 | listeners[eventName].forEach(function(listener) { 40 | listener(data); 41 | }); 42 | }); 43 | 44 | } 45 | if (angular.isFunction(callback)) { 46 | listeners[eventName].push(callback); 47 | } 48 | }; 49 | 50 | factory.off = function(eventName, callback) { 51 | if (!listeners[eventName]) { 52 | return; 53 | } 54 | for (var i = 0; i < listeners[eventName].length; i++) { 55 | if (listeners[eventName][i] === callback) { 56 | listeners[eventName].splice(i, 1); 57 | return; 58 | } 59 | } 60 | }; 61 | 62 | return factory; 63 | } 64 | ]); 65 | } 66 | } 67 | 68 | module.exports = Service; 69 | -------------------------------------------------------------------------------- /src/js/fw/service/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Entrance of services 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 20, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | var events = require('./Events'); 10 | var utils = require('./Utils'); 11 | 12 | module.exports = [events, utils]; 13 | -------------------------------------------------------------------------------- /src/js/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * index.js launch the application. 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 20, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | require('less/main.less'); 10 | var App = require('./main'); 11 | 12 | (new App()).run(); 13 | -------------------------------------------------------------------------------- /src/js/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * main.js manage the whole application. 3 | * 4 | * @author Howard.Zuo 5 | * @date Nov 20, 2015 6 | * 7 | */ 8 | 'use strict'; 9 | var angular = require('angular'); 10 | var Initializers = require('init/main'); 11 | var Extensions = require('ext/main'); 12 | var Configurators = require('config/main'); 13 | var Services = require('service/main'); 14 | var Features = require('features/main'); 15 | 16 | class App { 17 | 18 | constructor() { 19 | this.appName = 'windtalkerr'; 20 | this.features = []; 21 | Features.forEach(function(Feature) { 22 | this.features.push(new Feature()); 23 | }, this); 24 | } 25 | 26 | findDependencies() { 27 | this.depends = Extensions.slice(0); 28 | var featureNames = this.features.filter(function(feature) { 29 | return feature.export; 30 | }) 31 | .map(function(feature) { 32 | return feature.export; 33 | }); 34 | Array.prototype.push.apply(this.depends, featureNames); 35 | } 36 | 37 | beforeStart() { 38 | Initializers.forEach(function(Initializer) { 39 | (new Initializer(this.features)).execute(); 40 | }, this); 41 | 42 | this.features.forEach(function(feature) { 43 | feature.beforeStart(); 44 | }); 45 | } 46 | 47 | createApp() { 48 | this.features.forEach(function(feature) { 49 | feature.execute(); 50 | }); 51 | this.app = angular.module(this.appName, this.depends); 52 | } 53 | 54 | configApp() { 55 | Configurators.forEach(function(Configurator) { 56 | (new Configurator(this.features, this.app)).execute(); 57 | }, this); 58 | } 59 | 60 | registerService() { 61 | Services.forEach(function(Service) { 62 | (new Service(this.features, this.app)).execute(); 63 | }, this); 64 | } 65 | 66 | launch() { 67 | angular.bootstrap(document, [this.appName]); 68 | } 69 | 70 | run() { 71 | this.findDependencies(); 72 | this.beforeStart(); 73 | this.createApp(); 74 | this.configApp(); 75 | this.registerService(); 76 | this.launch(); 77 | } 78 | 79 | } 80 | 81 | module.exports = App; 82 | -------------------------------------------------------------------------------- /src/js/process/ExitHandler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ExitHandler = function(app) { 4 | app.on('window-all-closed', function() { 5 | if (process.platform !== 'darwin') { 6 | app.quit(); 7 | } 8 | }); 9 | }; 10 | 11 | module.exports = ExitHandler; 12 | -------------------------------------------------------------------------------- /src/js/process/Launcher.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var electron = require('electron'); 5 | var BrowserWindow = electron.BrowserWindow; 6 | 7 | var startupOpts = { 8 | useContentSize: true, 9 | center: true, 10 | resizable: process.env.NODE_ENV === 'dev', 11 | alwaysOnTop: false, 12 | fullscreen: false, 13 | skipTaskbar: false, 14 | kiosk: false, 15 | title: '', 16 | icon: null, 17 | show: false, 18 | frame: true, 19 | disableAutoHideCursor: false, 20 | autoHideMenuBar: false, 21 | titleBarStyle: 'default' 22 | }; 23 | 24 | var Launcher = function(app) { 25 | return new Promise(function(resolve, reject) { 26 | app.on('ready', function() { 27 | var mainWindow = new BrowserWindow(startupOpts); 28 | mainWindow.loadURL('file://' + path.resolve(__dirname, '..') + '/index.html'); 29 | mainWindow.on('closed', function() { 30 | mainWindow = null; 31 | }); 32 | // mainWindow.show(); 33 | resolve(mainWindow); 34 | }); 35 | }); 36 | }; 37 | 38 | module.exports = Launcher; 39 | -------------------------------------------------------------------------------- /src/js/process/SecretService.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var ipcMain = require('electron').ipcMain; 3 | 4 | var Datastore = require('nedb'); 5 | var path = require('path'); 6 | var crypto = require('crypto') 7 | 8 | var SECRETS_DB_NAME = 'secrets.db'; 9 | var secretDb, algorithm, password; 10 | 11 | var encryptTxt = function(text) { 12 | var cipher = crypto.createCipher(algorithm, password); 13 | var crypted = cipher.update(text, 'utf8', 'hex'); 14 | crypted += cipher.final('hex'); 15 | return crypted; 16 | }; 17 | 18 | var decryptTxt = function(text) { 19 | var decipher = crypto.createDecipher(algorithm, password); 20 | var dec = decipher.update(text, 'hex', 'utf8'); 21 | dec += decipher.final('utf8'); 22 | return dec; 23 | }; 24 | 25 | var SecretService = function(app, mainWindow) { 26 | ipcMain.on('db-address', function(e, opts) { 27 | algorithm = opts.algorithm; 28 | password = opts.password; 29 | var secretDbPath = path.join(opts.address, SECRETS_DB_NAME); 30 | secretDb = new Datastore({ 31 | filename: secretDbPath, 32 | autoload: true 33 | }); 34 | }); 35 | 36 | ipcMain.on('get-secrets', function(event, userId) { 37 | if (!secretDb) { 38 | process.nextTick(function() { 39 | event.sender.send('get-secrets-reply', { 40 | error: '数据库未设置' 41 | }); 42 | }); 43 | return; 44 | } 45 | secretDb 46 | .find({userId: encryptTxt(userId)}) 47 | .sort({updateDate: -1}) 48 | .exec(function(err, docs) { 49 | if (err) { 50 | event.sender.send('get-secrets-reply', { 51 | error: '读取秘密信息失败' 52 | }); 53 | return; 54 | } 55 | 56 | var infos = docs.map(function(doc) { 57 | var info = { 58 | id: decryptTxt(doc.id), 59 | userId: decryptTxt(doc.userId), 60 | name: decryptTxt(doc.name), 61 | desc: decryptTxt(doc.desc), 62 | createDate: doc.createDate, 63 | updateDate: doc.updateDate, 64 | items: doc.items.map(function(item) { 65 | return { 66 | key: decryptTxt(item.key), 67 | value: decryptTxt(item.value) 68 | }; 69 | }) 70 | }; 71 | return info; 72 | }); 73 | 74 | event.sender.send('get-secrets-reply', { 75 | data: infos 76 | }); 77 | }); 78 | 79 | }); 80 | 81 | }; 82 | 83 | module.exports = SecretService; 84 | -------------------------------------------------------------------------------- /src/js/process/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | process.env.ELECTRON_HIDE_INTERNAL_MODULES = 'true'; 3 | 4 | var electron = require('electron'); 5 | var app = electron.app; 6 | 7 | var exitHandler = require('./ExitHandler'); 8 | var launcher = require('./Launcher'); 9 | 10 | //register exit handler 11 | exitHandler(app); 12 | 13 | //launch the app 14 | launcher(app) 15 | .then(function(win) { 16 | return win; 17 | }); 18 | -------------------------------------------------------------------------------- /src/less/auth/auth.less: -------------------------------------------------------------------------------- 1 | body > div[ng-view] > form{ 2 | height: 100%; 3 | 4 | .top-back{ 5 | position: absolute; 6 | top: 0; 7 | left: 50%; 8 | margin-left: -51px; 9 | color: #fff; 10 | } 11 | 12 | .top-half-panel{ 13 | width: 100%; 14 | background-color: rgb(33,150,243); 15 | 16 | .hint.md-display-1{ 17 | color: #fff; 18 | padding: 20px; 19 | } 20 | } 21 | 22 | .content{ 23 | width: 420px; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/less/auth/forget.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leftstick/windtalker/99a177518297504fd28fea616f41a9f8104afc87/src/less/auth/forget.less -------------------------------------------------------------------------------- /src/less/auth/login.less: -------------------------------------------------------------------------------- 1 | #login{ 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/less/auth/setdb.less: -------------------------------------------------------------------------------- 1 | #setdb{ 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/less/auth/signup.less: -------------------------------------------------------------------------------- 1 | #signup{ 2 | } 3 | -------------------------------------------------------------------------------- /src/less/fontello.less: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'fontello'; 3 | src: url('../font/fontello.eot?69439756'); 4 | src: url('../font/fontello.eot?69439756#iefix') format('embedded-opentype'), 5 | url('../font/fontello.woff?69439756') format('woff'), 6 | url('../font/fontello.ttf?69439756') format('truetype'), 7 | url('../font/fontello.svg?69439756#fontello') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ 12 | /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ 13 | /* 14 | @media screen and (-webkit-min-device-pixel-ratio:0) { 15 | @font-face { 16 | font-family: 'fontello'; 17 | src: url('../font/fontello.svg?69439756#fontello') format('svg'); 18 | } 19 | } 20 | */ 21 | 22 | [class^="icon-"]:before, [class*=" icon-"]:before { 23 | font-family: "fontello"; 24 | font-style: normal; 25 | font-weight: normal; 26 | speak: none; 27 | 28 | display: inline-block; 29 | text-decoration: inherit; 30 | width: 1em; 31 | margin-right: .2em; 32 | text-align: center; 33 | /* opacity: .8; */ 34 | 35 | /* For safety - reset parent styles, that can break glyph codes*/ 36 | font-variant: normal; 37 | text-transform: none; 38 | 39 | /* fix buttons height, for twitter bootstrap */ 40 | line-height: 1em; 41 | 42 | /* Animation center compensation - margins should be symmetric */ 43 | /* remove if not needed */ 44 | margin-left: .2em; 45 | 46 | /* you can be more comfortable with increased icons size */ 47 | /* font-size: 120%; */ 48 | 49 | /* Font smoothing. That was taken from TWBS */ 50 | -webkit-font-smoothing: antialiased; 51 | -moz-osx-font-smoothing: grayscale; 52 | 53 | /* Uncomment for 3D effect */ 54 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 55 | } 56 | 57 | .icon-angle-double-down:before { content: '\e800'; } /* '' */ 58 | .icon-lock:before { content: '\e801'; } /* '' */ 59 | .icon-logout:before { content: '\e802'; } /* '' */ 60 | .icon-logout-1:before { content: '\e803'; } /* '' */ 61 | .icon-logout-2:before { content: '\e804'; } /* '' */ 62 | .icon-doc-new:before { content: '\e805'; } /* '' */ 63 | .icon-wrench:before { content: '\e806'; } /* '' */ 64 | .icon-eye:before { content: '\e807'; } /* '' */ 65 | .icon-floppy:before { content: '\e808'; } /* '' */ 66 | .icon-cancel:before { content: '\e809'; } /* '' */ 67 | .icon-cancel-circled:before { content: '\e80a'; } /* '' */ 68 | .icon-params:before { content: '\e80b'; } /* '' */ -------------------------------------------------------------------------------- /src/less/main.less: -------------------------------------------------------------------------------- 1 | @import "../../node_modules/angular-material/angular-material.css"; 2 | @import "../../node_modules/angular-material/angular-material.layouts.css"; 3 | @import "../../node_modules/animate.css/animate.css"; 4 | 5 | @import "./fontello"; 6 | @import "./toast"; 7 | 8 | @import "./auth/auth"; 9 | @import "./auth/login"; 10 | @import "./auth/setdb"; 11 | @import "./auth/signup"; 12 | @import "./auth/forget"; 13 | 14 | @import "./manager/manager"; 15 | 16 | html, body, div[ng-view]{ 17 | height: 100%; 18 | } 19 | 20 | html, body{ 21 | overflow: hidden; 22 | position: relative; 23 | } 24 | 25 | a{ 26 | text-decoration: none; 27 | } 28 | -------------------------------------------------------------------------------- /src/less/manager/manager.less: -------------------------------------------------------------------------------- 1 | @toolbar-height: 60px; 2 | 3 | .manager{ 4 | 5 | .top-toolbar{ 6 | .md-toolbar-tools{ 7 | 8 | height: @toolbar-height; 9 | max-height: @toolbar-height; 10 | h1{ 11 | font-size: 30px; 12 | } 13 | .logo{ 14 | line-height: @toolbar-height; 15 | height: @toolbar-height; 16 | font-size: 35px; 17 | } 18 | .icon-doc-new, 19 | .icon-params, 20 | .icon-logout-2{ 21 | font-size: 22px; 22 | } 23 | 24 | } 25 | } 26 | 27 | .searchbox{ 28 | padding: 0 16px; 29 | md-input-container{ 30 | margin-bottom: 0; 31 | } 32 | } 33 | 34 | .secrets-container{ 35 | margin-top: -20px; 36 | overflow-y: auto; 37 | z-index: 1; 38 | padding-bottom: 10px; 39 | md-list{ 40 | padding-top: 0; 41 | md-list-item{ 42 | transition: all 0.3s ease 0s; 43 | md-icon{ 44 | font-size: 23px; 45 | opacity: 0.6; 46 | transition: all 0.3s ease 0s; 47 | } 48 | } 49 | md-list-item:hover{ 50 | background-color: #FAFAFA; 51 | md-icon{ 52 | opacity: 1; 53 | } 54 | } 55 | } 56 | } 57 | 58 | .bottom-panel{ 59 | transform: translateY(0); 60 | padding: 0; 61 | border-top-width: 0; 62 | 63 | >md-content{ 64 | height: 100%; 65 | padding-bottom: 10px; 66 | } 67 | } 68 | 69 | .create-panel{ 70 | height: 550px; 71 | >md-content{ 72 | 73 | >md-toolbar{ 74 | margin-bottom: 20px; 75 | md-icon{ 76 | font-size: 20px; 77 | } 78 | } 79 | >md-input-container{ 80 | padding: 0 16px; 81 | >label{ 82 | padding-left: 0; 83 | left: 16px; 84 | } 85 | } 86 | } 87 | } 88 | 89 | .setting-panel{ 90 | height: 370px; 91 | md-tabs{ 92 | height: 100%; 93 | md-tab-content form{ 94 | padding: 20px 16px 10px 16px; 95 | } 96 | } 97 | } 98 | 99 | .view-secret-container{ 100 | max-width: 600px; 101 | width: 550px; 102 | 103 | > md-content{ 104 | height: 100%; 105 | padding-bottom: 10px; 106 | >md-input-container{ 107 | padding: 0 16px; 108 | >label{ 109 | padding-left: 0; 110 | left: 16px; 111 | } 112 | } 113 | } 114 | } 115 | 116 | .item-container{ 117 | padding: 0 16px; 118 | overflow-y: auto; 119 | .item{ 120 | position: relative; 121 | i{ 122 | position: absolute; 123 | right: 0; 124 | top: 0; 125 | color: red; 126 | font-size: 20px; 127 | cursor: pointer; 128 | display: none; 129 | } 130 | i.show{ 131 | display: block; 132 | } 133 | } 134 | md-input-container{ 135 | margin-bottom: 0; 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/less/toast.less: -------------------------------------------------------------------------------- 1 | @media (max-width: 959px){ 2 | md-toast{ 3 | padding: 0 !important; 4 | } 5 | } 6 | 7 | md-toast.error .md-toast-content{ 8 | background-color: red; 9 | } 10 | 11 | md-toast.warning .md-toast-content{ 12 | background-color: orange; 13 | } 14 | 15 | md-toast.success .md-toast-content{ 16 | background-color: green; 17 | } 18 | 19 | md-toast.info .md-toast-content{ 20 | background-color: #323232; 21 | } 22 | 23 | md-toast .md-toast-content{ 24 | background-color: transparent; 25 | box-shadow: none; 26 | padding: 8px 10px; 27 | } 28 | -------------------------------------------------------------------------------- /webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var webpack = require('webpack'); 4 | var autoprefixer = require('autoprefixer'); 5 | 6 | var presetsQuery = JSON.stringify({ 7 | presets: [ 8 | 'es2015' 9 | ], 10 | plugins: [ 11 | 'transform-runtime' 12 | ] 13 | }); 14 | 15 | module.exports = { 16 | entry: { 17 | index: './src/js/index.js', 18 | main: './src/js/process/index.js' 19 | }, 20 | output: { 21 | path: path.resolve(__dirname, 'src', 'build', 'js'), 22 | filename: '[name].bundle.js', 23 | chunkFilename: '[id].bundle.js', 24 | publicPath: 'js/' 25 | }, 26 | target: 'electron', 27 | node: { 28 | __dirname: false 29 | }, 30 | debug: true, 31 | devtool: 'sourcemap', 32 | module: { 33 | loaders: [ 34 | { 35 | test: /\.js$/, 36 | loader: 'babel?' + presetsQuery + '!modify?value=', 37 | exclude: /node_modules/ 38 | }, 39 | { 40 | test: /\.less$/, 41 | loader: 'style!css!postcss!less!' 42 | }, 43 | { 44 | test: /\.(eot|svg|ttf|woff|woff2)\w*/, 45 | loader: 'file' 46 | }, 47 | { 48 | test: /\.html$/, 49 | loader: 'raw' 50 | } 51 | ] 52 | }, 53 | postcss: function() { 54 | return [ 55 | autoprefixer({browsers: ['last 5 versions']}) 56 | ]; 57 | }, 58 | resolve: { 59 | root: [ 60 | path.resolve(__dirname, 'src'), 61 | path.resolve(__dirname, 'src', 'js'), 62 | path.resolve(__dirname, 'src', 'js', 'fw') 63 | ] 64 | }, 65 | plugins: [] 66 | }; 67 | -------------------------------------------------------------------------------- /webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var webpack = require('webpack'); 4 | var autoprefixer = require('autoprefixer'); 5 | 6 | var presetsQuery = JSON.stringify({ 7 | presets: [ 8 | 'es2015' 9 | ], 10 | plugins: [ 11 | 'transform-runtime' 12 | ] 13 | }); 14 | 15 | module.exports = { 16 | entry: { 17 | index: './src/js/index.js', 18 | main: './src/js/process/index.js' 19 | }, 20 | output: { 21 | path: path.resolve(__dirname, 'src', 'build', 'js'), 22 | filename: '[name].bundle.js', 23 | chunkFilename: '[id].bundle.js', 24 | publicPath: 'js/' 25 | }, 26 | target: 'electron', 27 | node: { 28 | __dirname: false 29 | }, 30 | module: { 31 | loaders: [ 32 | { 33 | test: /\.js$/, 34 | loader: 'babel?' + presetsQuery + '!modify?value=', 35 | exclude: /node_modules/ 36 | }, 37 | { 38 | test: /\.less$/, 39 | loader: 'style!css!postcss!less!' 40 | }, 41 | { 42 | test: /\.(eot|svg|ttf|woff|woff2)\w*/, 43 | loader: 'file' 44 | }, 45 | { 46 | test: /\.html$/, 47 | loader: 'raw' 48 | } 49 | ] 50 | }, 51 | postcss: function() { 52 | return [ 53 | autoprefixer({browsers: ['last 5 versions']}) 54 | ]; 55 | }, 56 | resolve: { 57 | root: [ 58 | path.resolve(__dirname, 'src'), 59 | path.resolve(__dirname, 'src', 'js'), 60 | path.resolve(__dirname, 'src', 'js', 'fw') 61 | ] 62 | }, 63 | plugins: [ 64 | new webpack.optimize.UglifyJsPlugin({ 65 | compress: { 66 | warnings: false 67 | } 68 | }) 69 | ] 70 | }; 71 | --------------------------------------------------------------------------------