├── .DS_Store ├── .gitignore ├── LICENSE ├── README.md ├── bower.json ├── bower_components └── angular-csv-import │ ├── .bower.json │ ├── .travis.yml │ ├── Gruntfile.js │ ├── LICENSE │ ├── bower.json │ ├── dist │ ├── angular-csv-import.js │ └── angular-csv-import.min.js │ ├── examples │ ├── .editorconfig │ ├── .gitattributes │ ├── .travis.yml │ ├── Gruntfile.js │ ├── app │ │ ├── .buildignore │ │ ├── .htaccess │ │ ├── 404.html │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── robots.txt │ │ ├── scripts │ │ │ ├── app.js │ │ │ └── controllers │ │ │ │ ├── about.js │ │ │ │ └── main.js │ │ ├── styles │ │ │ └── main.css │ │ └── views │ │ │ └── main.html │ └── test │ │ └── karma.conf.js │ └── lib │ ├── .DS_Store │ ├── angular-csv-import.css │ └── angular-csv-import.js ├── creds.txt ├── karma.conf.js ├── package.json ├── src ├── client │ ├── app.js │ ├── controllers │ │ ├── dbController.js │ │ ├── homeController.js │ │ ├── importController.js │ │ └── tableController.js │ ├── directives │ │ └── tablenav.js │ ├── index.html │ ├── partials │ │ ├── create.html │ │ ├── home.html │ │ ├── navbar.html │ │ ├── table.html │ │ └── tableselect.html │ ├── services │ │ ├── dbService.js │ │ └── tableService.js │ └── styles │ │ └── style.css └── server │ ├── addTable.js │ ├── authCtrl.js │ ├── dbCtrl.js │ ├── server.js │ ├── testdata.js │ └── userCtrl.js └── test ├── client ├── dbControllerTest.js ├── homeControllerTest.js ├── serviceTest.js └── tableControllerTest.js └── supertest.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CS-HAMJ/dbview/21cc1ff16da5e8e0887765cb0685ec0f5207bf7a/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 dbviews 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 | # DBView 2 | 3 | DBView is a lightweight tool to view and manage your remotely hosted databases in the browser. While desktop database management software is powerful, it can be overwhelming and require unecessary overhead when your only needs are viewing tables and performing standard tasks (especially for those with limited database experience). On the other hand, CLIs are lightweight, but they are not well suited to viewing data, and using them to build large queries can be frustrating. DBView was created to provide developers an easy way to acesss their databases without any installation. This project is still in the very early stages of development. 4 | 5 | ## Installation 6 | ``` 7 | git clone https://github.com/dbviews/dbview 8 | cd dbview 9 | npm install 10 | ``` 11 | Then run `npm start` to start the local server. 12 | We also provide an npm script to create a sample table in your database. To execute this script, run `npm run fillDB`. 13 | 14 | ## Running tests 15 | We use [mocha](https://mochajs.org/), [supertest](https://github.com/visionmedia/supertest), and [karma](http://karma-runner.github.io/1.0/index.html) to run tests. 16 | 17 | ### Server side tests 18 | Located in `test/supertest.js` 19 | ``` 20 | npm test 21 | ``` 22 | 23 | ### Client side tests 24 | It's easiest to run the client side tests if you install the karma CLI globally. 25 | ``` 26 | npm install -g karma-cli 27 | ``` 28 | Then, navigate to `dview/` and run the tests: 29 | ``` 30 | karma start 31 | ``` 32 | 33 | These tests are located in `test/client/`. -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dbview", 3 | "description": "", 4 | "main": "index.js", 5 | "authors": [ 6 | "Hoon Choi " 7 | ], 8 | "license": "ISC", 9 | "homepage": "https://github.com/hoonchoi/dbview", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ], 17 | "dependencies": { 18 | "angular-csv-import": "^0.0.37" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /bower_components/angular-csv-import/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-csv-import", 3 | "version": "0.0.37", 4 | "main": "dist/angular-csv-import.js", 5 | "license": "MIT", 6 | "authors": [ 7 | "Bahaaldine AZARMI " 8 | ], 9 | "homepage": "https://github.com/cybadave/angular-csv-import", 10 | "description": "angular component let's you import a CSV file", 11 | "keywords": [ 12 | "angular", 13 | "csv", 14 | "import" 15 | ], 16 | "ignore": [ 17 | "source", 18 | "spec", 19 | ".bowerrc", 20 | ".gitignore", 21 | ".jshintignore", 22 | ".jshintrc", 23 | "bower.json", 24 | "gruntfile.js", 25 | "package.json", 26 | "README.md" 27 | ], 28 | "dependencies": {}, 29 | "devDependencies": {}, 30 | "_release": "0.0.37", 31 | "_resolution": { 32 | "type": "version", 33 | "tag": "v0.0.37", 34 | "commit": "18d66e299cc28df9e5bea583b1a52c5463ca4853" 35 | }, 36 | "_source": "https://github.com/bahaaldine/angular-csv-import.git", 37 | "_target": "^0.0.37", 38 | "_originalSource": "angular-csv-import", 39 | "_direct": true 40 | } -------------------------------------------------------------------------------- /bower_components/angular-csv-import/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.8 4 | - 0.10 5 | before_install: 6 | - npm update npm -g -------------------------------------------------------------------------------- /bower_components/angular-csv-import/Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global require, module:false*/ 2 | module.exports = function(grunt) { 3 | 'use strict'; 4 | 5 | // Load grunt tasks automatically 6 | require('load-grunt-tasks')(grunt); 7 | 8 | // Time how long tasks take. Can help when optimizing build times 9 | require('time-grunt')(grunt); 10 | 11 | // Project configuration. 12 | grunt.initConfig({ 13 | // Metadata. 14 | pkg: grunt.file.readJSON('package.json'), 15 | banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + 16 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' + 17 | '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' + 18 | '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + 19 | ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n', 20 | // Task configuration. 21 | concat: { 22 | options: { 23 | banner: '<%= banner %>', 24 | stripBanners: true 25 | }, 26 | dist: { 27 | src: ['lib/<%= pkg.name %>.js'], 28 | dest: 'dist/<%= pkg.name %>.js' 29 | }, 30 | examples: { 31 | src: ['lib/<%= pkg.name %>.js'], 32 | dest: 'examples/app/bower_components/angular-csv-import/dist/<%= pkg.name %>.js' 33 | } 34 | }, 35 | bump: { 36 | options: { 37 | files: ['package.json', 'bower.json'], 38 | updateConfigs: [], 39 | commit: true, 40 | commitMessage: 'Release v%VERSION%', 41 | commitFiles: ['package.json', 'bower.json'], 42 | createTag: true, 43 | tagName: 'v%VERSION%', 44 | tagMessage: 'Version %VERSION%', 45 | push: true, 46 | pushTo: 'origin', 47 | gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d' 48 | } 49 | }, 50 | uglify: { 51 | options: { 52 | banner: '<%= banner %>' 53 | }, 54 | dist: { 55 | src: '<%= concat.dist.dest %>', 56 | dest: 'dist/<%= pkg.name %>.min.js' 57 | }, 58 | examples: { 59 | src: '<%= concat.dist.dest %>', 60 | dest: 'examples/app/bower_components/angular-csv-import/dist/<%= pkg.name %>.min.js' 61 | } 62 | }, 63 | jshint: { 64 | options: { 65 | curly: true, 66 | eqeqeq: true, 67 | immed: true, 68 | latedef: true, 69 | newcap: true, 70 | noarg: true, 71 | sub: true, 72 | undef: true, 73 | unused: true, 74 | boss: true, 75 | eqnull: true, 76 | browser: true, 77 | globalstrict: true, 78 | globals: { 79 | jQuery: true, 80 | angular: false, 81 | Odometer: false 82 | } 83 | }, 84 | gruntfile: { 85 | src: 'Gruntfile.js' 86 | }, 87 | lib_test: { 88 | src: ['lib/**/*.js', 'test/**/*.js'] 89 | } 90 | }, 91 | qunit: { 92 | files: ['test/**/*.html'] 93 | }, 94 | watch: { 95 | gruntfile: { 96 | files: '<%= jshint.gruntfile.src %>', 97 | tasks: ['jshint:gruntfile'] 98 | }, 99 | lib_test: { 100 | files: '<%= jshint.lib_test.src %>', 101 | tasks: ['jshint:lib_test', 'qunit'] 102 | } 103 | }, 104 | cssmin: { 105 | minify: { 106 | expand: true, 107 | cwd: 'lib/', 108 | src: ['*.css', '!*.min.css'], 109 | dest: 'dist/', 110 | ext: '.min.css' 111 | } 112 | }, 113 | 'gh-pages': { 114 | options: { 115 | base: 'examples/dist' 116 | }, 117 | src: '**/*' 118 | } 119 | }); 120 | 121 | // Default task. 122 | grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'cssmin']); 123 | 124 | grunt.registerTask('pages', ['gh-pages']); 125 | 126 | }; 127 | -------------------------------------------------------------------------------- /bower_components/angular-csv-import/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Bahaaldine 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. -------------------------------------------------------------------------------- /bower_components/angular-csv-import/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-csv-import", 3 | "version": "0.0.37", 4 | "main": "dist/angular-csv-import.js", 5 | "license": "MIT", 6 | "authors": [ 7 | "Bahaaldine AZARMI " 8 | ], 9 | "homepage": "https://github.com/cybadave/angular-csv-import", 10 | "description": "angular component let's you import a CSV file", 11 | "keywords": [ 12 | "angular", 13 | "csv", 14 | "import" 15 | ], 16 | "ignore": [ 17 | "source", 18 | "spec", 19 | ".bowerrc", 20 | ".gitignore", 21 | ".jshintignore", 22 | ".jshintrc", 23 | "bower.json", 24 | "gruntfile.js", 25 | "package.json", 26 | "README.md" 27 | ], 28 | "dependencies": { 29 | }, 30 | "devDependencies": { 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /bower_components/angular-csv-import/dist/angular-csv-import.js: -------------------------------------------------------------------------------- 1 | /*! angular-csv-import - v0.0.36 - 2016-11-01 2 | * Copyright (c) 2016 ; Licensed */ 3 | 'use strict'; 4 | 5 | var csvImport = angular.module('ngCsvImport', []); 6 | 7 | csvImport.directive('ngCsvImport', function() { 8 | return { 9 | restrict: 'E', 10 | transclude: true, 11 | replace: true, 12 | scope:{ 13 | content:'=?', 14 | header: '=?', 15 | headerVisible: '=?', 16 | separator: '=?', 17 | separatorVisible: '=?', 18 | result: '=?', 19 | encoding: '=?', 20 | encodingVisible: '=?', 21 | accept: '=?', 22 | acceptSize: '=?', 23 | acceptSizeExceedCallback: '=?', 24 | callback: '=?', 25 | mdButtonClass: '@?', 26 | mdInputClass: '@?', 27 | mdButtonTitle: '@?', 28 | mdSvgIcon: '@?', 29 | uploadButtonLabel: '=' 30 | }, 31 | template: function(element, attrs) { 32 | var material = angular.isDefined(attrs.material); 33 | var multiple = angular.isDefined(attrs.multiple); 34 | return '
'+ 35 | '
Header
' + 36 | (material ? '
' : 37 | '') + 38 | '
Encoding
{{encoding}}
'+ 39 | '
'+ 40 | '
Seperator
'+ 41 | ''+ 42 | '
'+ 43 | '
' + 44 | '' + 45 | (material ? ' {{mdButtonTitle}}' : '') + 46 | '
'+ 47 | '
'; 48 | }, 49 | link: function(scope, element, attrs) { 50 | scope.separatorVisible = !!scope.separatorVisible; 51 | scope.headerVisible = !!scope.headerVisible; 52 | scope.acceptSize = scope.acceptSize || Number.POSITIVE_INFINITY; 53 | scope.material = angular.isDefined(attrs.material); 54 | scope.multiple = angular.isDefined(attrs.multiple); 55 | if (scope.multiple) { 56 | throw new Error("Multiple attribute is not supported yet."); 57 | } 58 | var input = angular.element(element[0].querySelector('input[type="file"]')); 59 | var inputContainer = angular.element(element[0].querySelector('md-input-container')); 60 | 61 | if (scope.material && input) { 62 | input.removeClass("ng-show"); 63 | input.addClass("ng-hide"); 64 | if (inputContainer) { 65 | var errorSpacer = angular.element(inputContainer[0].querySelector('div.md-errors-spacer')); 66 | if (errorSpacer) { 67 | errorSpacer.remove(); 68 | } 69 | } 70 | scope.onClick = function() { 71 | input.click(); 72 | }; 73 | } 74 | 75 | angular.element(element[0].querySelector('.separator-input')).on('keyup', function(e) { 76 | if ( scope.content != null ) { 77 | var content = { 78 | csv: scope.content, 79 | header: scope.header, 80 | separator: e.target.value, 81 | encoding: scope.encoding 82 | }; 83 | scope.result = csvToJSON(content); 84 | scope.$apply(); 85 | if ( typeof scope.callback === 'function' ) { 86 | scope.callback(e); 87 | } 88 | } 89 | }); 90 | 91 | element.on('change', function(onChangeEvent) { 92 | if (!onChangeEvent.target.files.length){ 93 | return; 94 | } 95 | 96 | if (onChangeEvent.target.files[0].size > scope.acceptSize){ 97 | if ( typeof scope.acceptSizeExceedCallback === 'function' ) { 98 | scope.acceptSizeExceedCallback(onChangeEvent.target.files[0]); 99 | } 100 | return; 101 | } 102 | 103 | scope.filename = onChangeEvent.target.files[0].name; 104 | var reader = new FileReader(); 105 | reader.onload = function(onLoadEvent) { 106 | scope.$apply(function() { 107 | var content = { 108 | csv: onLoadEvent.target.result.replace(/\r\n|\r/g,'\n'), 109 | header: scope.header, 110 | separator: scope.separator 111 | }; 112 | scope.content = content.csv; 113 | scope.result = csvToJSON(content); 114 | scope.result.filename = scope.filename; 115 | scope.$$postDigest(function(){ 116 | if ( typeof scope.callback === 'function' ) { 117 | scope.callback(onChangeEvent); 118 | } 119 | }); 120 | }); 121 | }; 122 | 123 | if ( (onChangeEvent.target.type === "file") && (onChangeEvent.target.files != null || onChangeEvent.srcElement.files != null) ) { 124 | reader.readAsText((onChangeEvent.srcElement || onChangeEvent.target).files[0], scope.encoding); 125 | } else { 126 | if ( scope.content != null ) { 127 | var content = { 128 | csv: scope.content, 129 | header: !scope.header, 130 | separator: scope.separator 131 | }; 132 | scope.result = csvToJSON(content); 133 | scope.$$postDigest(function(){ 134 | if ( typeof scope.callback === 'function' ) { 135 | scope.callback(onChangeEvent); 136 | } 137 | }); 138 | } 139 | } 140 | }); 141 | 142 | var csvToJSON = function(content) { 143 | var lines=content.csv.split(new RegExp('\n(?![^"]*"(?:(?:[^"]*"){2})*[^"]*$)')); 144 | var result = []; 145 | var start = 0; 146 | var columnCount = lines[0].split(content.separator).length; 147 | 148 | var headers = []; 149 | if (content.header) { 150 | headers=lines[0].split(content.separator); 151 | start = 1; 152 | } 153 | 154 | for (var i=start; i
Header
'+(c?'
':'')+'
Encoding
{{encoding}}
Seperator
'+(c?' {{mdButtonTitle}}':"")+"
"},link:function(a,b,c){if(a.separatorVisible=!!a.separatorVisible,a.headerVisible=!!a.headerVisible,a.acceptSize=a.acceptSize||Number.POSITIVE_INFINITY,a.material=angular.isDefined(c.material),a.multiple=angular.isDefined(c.multiple),a.multiple)throw new Error("Multiple attribute is not supported yet.");var d=angular.element(b[0].querySelector('input[type="file"]')),e=angular.element(b[0].querySelector("md-input-container"));if(a.material&&d){if(d.removeClass("ng-show"),d.addClass("ng-hide"),e){var f=angular.element(e[0].querySelector("div.md-errors-spacer"));f&&f.remove()}a.onClick=function(){d.click()}}angular.element(b[0].querySelector(".separator-input")).on("keyup",function(b){if(null!=a.content){var c={csv:a.content,header:a.header,separator:b.target.value,encoding:a.encoding};a.result=g(c),a.$apply(),"function"==typeof a.callback&&a.callback(b)}}),b.on("change",function(b){if(b.target.files.length){if(b.target.files[0].size>a.acceptSize)return void("function"==typeof a.acceptSizeExceedCallback&&a.acceptSizeExceedCallback(b.target.files[0]));a.filename=b.target.files[0].name;var c=new FileReader;if(c.onload=function(c){a.$apply(function(){var d={csv:c.target.result.replace(/\r\n|\r/g,"\n"),header:a.header,separator:a.separator};a.content=d.csv,a.result=g(d),a.result.filename=a.filename,a.$$postDigest(function(){"function"==typeof a.callback&&a.callback(b)})})},"file"!==b.target.type||null==b.target.files&&null==b.srcElement.files){if(null!=a.content){var d={csv:a.content,header:!a.header,separator:a.separator};a.result=g(d),a.$$postDigest(function(){"function"==typeof a.callback&&a.callback(b)})}}else c.readAsText((b.srcElement||b.target).files[0],a.encoding)}});var g=function(a){var b=a.csv.split(new RegExp('\n(?![^"]*"(?:(?:[^"]*"){2})*[^"]*$)')),c=[],d=0,e=b[0].split(a.separator).length,f=[];a.header&&(f=b[0].split(a.separator),d=1);for(var g=d;g/scripts/{,*/}*.js'], 36 | tasks: ['newer:jshint:all'], 37 | options: { 38 | livereload: '<%= connect.options.livereload %>' 39 | } 40 | }, 41 | jsTest: { 42 | files: ['test/spec/{,*/}*.js'], 43 | tasks: ['newer:jshint:test', 'karma'] 44 | }, 45 | styles: { 46 | files: ['<%= yeoman.app %>/styles/{,*/}*.css'], 47 | tasks: ['newer:copy:styles', 'autoprefixer'] 48 | }, 49 | gruntfile: { 50 | files: ['Gruntfile.js'] 51 | }, 52 | livereload: { 53 | options: { 54 | livereload: '<%= connect.options.livereload %>' 55 | }, 56 | files: [ 57 | '<%= yeoman.app %>/{,*/}*.html', 58 | '.tmp/styles/{,*/}*.css', 59 | '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}' 60 | ] 61 | } 62 | }, 63 | 64 | // The actual grunt server settings 65 | connect: { 66 | options: { 67 | port: 9000, 68 | // Change this to '0.0.0.0' to access the server from outside. 69 | hostname: 'localhost', 70 | livereload: 35729 71 | }, 72 | livereload: { 73 | options: { 74 | open: true, 75 | base: [ 76 | '.tmp', 77 | '<%= yeoman.app %>' 78 | ] 79 | } 80 | }, 81 | test: { 82 | options: { 83 | port: 9001, 84 | base: [ 85 | '.tmp', 86 | 'test', 87 | '<%= yeoman.app %>' 88 | ] 89 | } 90 | }, 91 | dist: { 92 | options: { 93 | open: true, 94 | base: '<%= yeoman.dist %>' 95 | } 96 | } 97 | }, 98 | 99 | // Make sure code styles are up to par and there are no obvious mistakes 100 | jshint: { 101 | options: { 102 | jshintrc: '.jshintrc', 103 | reporter: require('jshint-stylish') 104 | }, 105 | all: { 106 | src: [ 107 | 'Gruntfile.js', 108 | '<%= yeoman.app %>/scripts/{,*/}*.js' 109 | ] 110 | }, 111 | test: { 112 | options: { 113 | jshintrc: 'test/.jshintrc' 114 | }, 115 | src: ['test/spec/{,*/}*.js'] 116 | } 117 | }, 118 | 119 | // Empties folders to start fresh 120 | clean: { 121 | dist: { 122 | files: [{ 123 | dot: true, 124 | src: [ 125 | '.tmp', 126 | '<%= yeoman.dist %>/{,*/}*', 127 | '!<%= yeoman.dist %>/.git*' 128 | ] 129 | }] 130 | }, 131 | server: '.tmp' 132 | }, 133 | 134 | // Add vendor prefixed styles 135 | autoprefixer: { 136 | options: { 137 | browsers: ['last 1 version'] 138 | }, 139 | dist: { 140 | files: [{ 141 | expand: true, 142 | cwd: '.tmp/styles/', 143 | src: '{,*/}*.css', 144 | dest: '.tmp/styles/' 145 | }] 146 | } 147 | }, 148 | 149 | // Automatically inject Bower components into the app 150 | wiredep: { 151 | app: { 152 | src: ['<%= yeoman.app %>/index.html'], 153 | ignorePath: new RegExp('^<%= yeoman.app %>/') 154 | } 155 | }, 156 | 157 | // Renames files for browser caching purposes 158 | filerev: { 159 | dist: { 160 | src: [ 161 | '<%= yeoman.dist %>/scripts/{,*/}*.js', 162 | '<%= yeoman.dist %>/styles/{,*/}*.css', 163 | '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', 164 | '<%= yeoman.dist %>/styles/fonts/*' 165 | ] 166 | } 167 | }, 168 | 169 | // Reads HTML for usemin blocks to enable smart builds that automatically 170 | // concat, minify and revision files. Creates configurations in memory so 171 | // additional tasks can operate on them 172 | useminPrepare: { 173 | html: '<%= yeoman.app %>/index.html', 174 | options: { 175 | dest: '<%= yeoman.dist %>', 176 | flow: { 177 | html: { 178 | steps: { 179 | js: ['concat', 'uglifyjs'], 180 | css: ['cssmin'] 181 | }, 182 | post: {} 183 | } 184 | } 185 | } 186 | }, 187 | 188 | // Performs rewrites based on filerev and the useminPrepare configuration 189 | usemin: { 190 | html: ['<%= yeoman.dist %>/{,*/}*.html'], 191 | css: ['<%= yeoman.dist %>/styles/{,*/}*.css'], 192 | options: { 193 | assetsDirs: ['<%= yeoman.dist %>','<%= yeoman.dist %>/images'] 194 | } 195 | }, 196 | 197 | imagemin: { 198 | dist: { 199 | files: [{ 200 | expand: true, 201 | cwd: '<%= yeoman.app %>/images', 202 | src: '{,*/}*.{png,jpg,jpeg,gif}', 203 | dest: '<%= yeoman.dist %>/images' 204 | }] 205 | } 206 | }, 207 | 208 | svgmin: { 209 | dist: { 210 | files: [{ 211 | expand: true, 212 | cwd: '<%= yeoman.app %>/images', 213 | src: '{,*/}*.svg', 214 | dest: '<%= yeoman.dist %>/images' 215 | }] 216 | } 217 | }, 218 | 219 | htmlmin: { 220 | dist: { 221 | options: { 222 | collapseWhitespace: true, 223 | conservativeCollapse: true, 224 | collapseBooleanAttributes: true, 225 | removeCommentsFromCDATA: true, 226 | removeOptionalTags: true 227 | }, 228 | files: [{ 229 | expand: true, 230 | cwd: '<%= yeoman.dist %>', 231 | src: ['*.html', 'views/{,*/}*.html'], 232 | dest: '<%= yeoman.dist %>' 233 | }] 234 | } 235 | }, 236 | 237 | // ngmin tries to make the code safe for minification automatically by 238 | // using the Angular long form for dependency injection. It doesn't work on 239 | // things like resolve or inject so those have to be done manually. 240 | ngmin: { 241 | dist: { 242 | files: [{ 243 | expand: true, 244 | cwd: '.tmp/concat/scripts', 245 | src: '*.js', 246 | dest: '.tmp/concat/scripts' 247 | }] 248 | } 249 | }, 250 | 251 | // Replace Google CDN references 252 | cdnify: { 253 | dist: { 254 | html: ['<%= yeoman.dist %>/*.html'] 255 | } 256 | }, 257 | 258 | // Copies remaining files to places other tasks can use 259 | copy: { 260 | dist: { 261 | files: [{ 262 | expand: true, 263 | dot: true, 264 | cwd: '<%= yeoman.app %>', 265 | dest: '<%= yeoman.dist %>', 266 | src: [ 267 | '*.{ico,png,txt}', 268 | '.htaccess', 269 | '*.html', 270 | 'views/{,*/}*.html', 271 | 'images/{,*/}*.{webp}', 272 | 'fonts/*' 273 | ] 274 | }, { 275 | expand: true, 276 | cwd: '.tmp/images', 277 | dest: '<%= yeoman.dist %>/images', 278 | src: ['generated/*'] 279 | }, { 280 | expand: true, 281 | dot: true, 282 | cwd: '<%= yeoman.app %>/bower_components/font-awesome/', 283 | dest: '<%= yeoman.dist %>', 284 | src: [ 285 | 'fonts/*', 286 | ] 287 | }] 288 | }, 289 | styles: { 290 | expand: true, 291 | cwd: '<%= yeoman.app %>/styles', 292 | dest: '.tmp/styles/', 293 | src: '{,*/}*.css' 294 | } 295 | }, 296 | 297 | // Run some tasks in parallel to speed up the build process 298 | concurrent: { 299 | server: [ 300 | 'copy:styles' 301 | ], 302 | test: [ 303 | 'copy:styles' 304 | ], 305 | dist: [ 306 | 'copy:styles', 307 | 'imagemin', 308 | 'svgmin' 309 | ] 310 | }, 311 | 312 | // Test settings 313 | karma: { 314 | unit: { 315 | configFile: 'test/karma.conf.js', 316 | singleRun: true 317 | } 318 | } 319 | }); 320 | 321 | 322 | grunt.registerTask('serve', 'Compile then start a connect web server', function (target) { 323 | if (target === 'dist') { 324 | return grunt.task.run(['build', 'connect:dist:keepalive']); 325 | } 326 | 327 | grunt.task.run([ 328 | 'clean:server', 329 | 'wiredep', 330 | 'concurrent:server', 331 | 'autoprefixer', 332 | 'connect:livereload', 333 | 'watch' 334 | ]); 335 | }); 336 | 337 | grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) { 338 | grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); 339 | grunt.task.run(['serve:' + target]); 340 | }); 341 | 342 | grunt.registerTask('test', [ 343 | 'clean:server', 344 | 'concurrent:test', 345 | 'autoprefixer', 346 | 'connect:test' 347 | ]); 348 | 349 | grunt.registerTask('build', [ 350 | 'clean:dist', 351 | 'wiredep', 352 | 'useminPrepare', 353 | 'concurrent:dist', 354 | 'autoprefixer', 355 | 'concat', 356 | 'ngmin', 357 | 'copy:dist', 358 | 'cdnify', 359 | 'cssmin', 360 | 'uglify', 361 | 'filerev', 362 | 'usemin', 363 | 'htmlmin' 364 | ]); 365 | 366 | grunt.registerTask('default', [ 367 | 'newer:jshint', 368 | 'test', 369 | 'build' 370 | ]); 371 | }; 372 | -------------------------------------------------------------------------------- /bower_components/angular-csv-import/examples/app/.buildignore: -------------------------------------------------------------------------------- 1 | *.coffee -------------------------------------------------------------------------------- /bower_components/angular-csv-import/examples/app/.htaccess: -------------------------------------------------------------------------------- 1 | # Apache Configuration File 2 | 3 | # (!) Using `.htaccess` files slows down Apache, therefore, if you have access 4 | # to the main server config file (usually called `httpd.conf`), you should add 5 | # this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html. 6 | 7 | # ############################################################################## 8 | # # CROSS-ORIGIN RESOURCE SHARING (CORS) # 9 | # ############################################################################## 10 | 11 | # ------------------------------------------------------------------------------ 12 | # | Cross-domain AJAX requests | 13 | # ------------------------------------------------------------------------------ 14 | 15 | # Enable cross-origin AJAX requests. 16 | # http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity 17 | # http://enable-cors.org/ 18 | 19 | # 20 | # Header set Access-Control-Allow-Origin "*" 21 | # 22 | 23 | # ------------------------------------------------------------------------------ 24 | # | CORS-enabled images | 25 | # ------------------------------------------------------------------------------ 26 | 27 | # Send the CORS header for images when browsers request it. 28 | # https://developer.mozilla.org/en/CORS_Enabled_Image 29 | # http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html 30 | # http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/ 31 | 32 | 33 | 34 | 35 | SetEnvIf Origin ":" IS_CORS 36 | Header set Access-Control-Allow-Origin "*" env=IS_CORS 37 | 38 | 39 | 40 | 41 | # ------------------------------------------------------------------------------ 42 | # | Web fonts access | 43 | # ------------------------------------------------------------------------------ 44 | 45 | # Allow access from all domains for web fonts 46 | 47 | 48 | 49 | Header set Access-Control-Allow-Origin "*" 50 | 51 | 52 | 53 | 54 | # ############################################################################## 55 | # # ERRORS # 56 | # ############################################################################## 57 | 58 | # ------------------------------------------------------------------------------ 59 | # | 404 error prevention for non-existing redirected folders | 60 | # ------------------------------------------------------------------------------ 61 | 62 | # Prevent Apache from returning a 404 error for a rewrite if a directory 63 | # with the same name does not exist. 64 | # http://httpd.apache.org/docs/current/content-negotiation.html#multiviews 65 | # http://www.webmasterworld.com/apache/3808792.htm 66 | 67 | Options -MultiViews 68 | 69 | # ------------------------------------------------------------------------------ 70 | # | Custom error messages / pages | 71 | # ------------------------------------------------------------------------------ 72 | 73 | # You can customize what Apache returns to the client in case of an error (see 74 | # http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.: 75 | 76 | ErrorDocument 404 /404.html 77 | 78 | 79 | # ############################################################################## 80 | # # INTERNET EXPLORER # 81 | # ############################################################################## 82 | 83 | # ------------------------------------------------------------------------------ 84 | # | Better website experience | 85 | # ------------------------------------------------------------------------------ 86 | 87 | # Force IE to render pages in the highest available mode in the various 88 | # cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf. 89 | 90 | 91 | Header set X-UA-Compatible "IE=edge" 92 | # `mod_headers` can't match based on the content-type, however, we only 93 | # want to send this header for HTML pages and not for the other resources 94 | 95 | Header unset X-UA-Compatible 96 | 97 | 98 | 99 | # ------------------------------------------------------------------------------ 100 | # | Cookie setting from iframes | 101 | # ------------------------------------------------------------------------------ 102 | 103 | # Allow cookies to be set from iframes in IE. 104 | 105 | # 106 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" 107 | # 108 | 109 | # ------------------------------------------------------------------------------ 110 | # | Screen flicker | 111 | # ------------------------------------------------------------------------------ 112 | 113 | # Stop screen flicker in IE on CSS rollovers (this only works in 114 | # combination with the `ExpiresByType` directives for images from below). 115 | 116 | # BrowserMatch "MSIE" brokenvary=1 117 | # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1 118 | # BrowserMatch "Opera" !brokenvary 119 | # SetEnvIf brokenvary 1 force-no-vary 120 | 121 | 122 | # ############################################################################## 123 | # # MIME TYPES AND ENCODING # 124 | # ############################################################################## 125 | 126 | # ------------------------------------------------------------------------------ 127 | # | Proper MIME types for all files | 128 | # ------------------------------------------------------------------------------ 129 | 130 | 131 | 132 | # Audio 133 | AddType audio/mp4 m4a f4a f4b 134 | AddType audio/ogg oga ogg 135 | 136 | # JavaScript 137 | # Normalize to standard type (it's sniffed in IE anyways): 138 | # http://tools.ietf.org/html/rfc4329#section-7.2 139 | AddType application/javascript js jsonp 140 | AddType application/json json 141 | 142 | # Video 143 | AddType video/mp4 mp4 m4v f4v f4p 144 | AddType video/ogg ogv 145 | AddType video/webm webm 146 | AddType video/x-flv flv 147 | 148 | # Web fonts 149 | AddType application/font-woff woff 150 | AddType application/vnd.ms-fontobject eot 151 | 152 | # Browsers usually ignore the font MIME types and sniff the content, 153 | # however, Chrome shows a warning if other MIME types are used for the 154 | # following fonts. 155 | AddType application/x-font-ttf ttc ttf 156 | AddType font/opentype otf 157 | 158 | # Make SVGZ fonts work on iPad: 159 | # https://twitter.com/FontSquirrel/status/14855840545 160 | AddType image/svg+xml svg svgz 161 | AddEncoding gzip svgz 162 | 163 | # Other 164 | AddType application/octet-stream safariextz 165 | AddType application/x-chrome-extension crx 166 | AddType application/x-opera-extension oex 167 | AddType application/x-shockwave-flash swf 168 | AddType application/x-web-app-manifest+json webapp 169 | AddType application/x-xpinstall xpi 170 | AddType application/xml atom rdf rss xml 171 | AddType image/webp webp 172 | AddType image/x-icon ico 173 | AddType text/cache-manifest appcache manifest 174 | AddType text/vtt vtt 175 | AddType text/x-component htc 176 | AddType text/x-vcard vcf 177 | 178 | 179 | 180 | # ------------------------------------------------------------------------------ 181 | # | UTF-8 encoding | 182 | # ------------------------------------------------------------------------------ 183 | 184 | # Use UTF-8 encoding for anything served as `text/html` or `text/plain`. 185 | AddDefaultCharset utf-8 186 | 187 | # Force UTF-8 for certain file formats. 188 | 189 | AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml 190 | 191 | 192 | 193 | # ############################################################################## 194 | # # URL REWRITES # 195 | # ############################################################################## 196 | 197 | # ------------------------------------------------------------------------------ 198 | # | Rewrite engine | 199 | # ------------------------------------------------------------------------------ 200 | 201 | # Turning on the rewrite engine and enabling the `FollowSymLinks` option is 202 | # necessary for the following directives to work. 203 | 204 | # If your web host doesn't allow the `FollowSymlinks` option, you may need to 205 | # comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the 206 | # performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks 207 | 208 | # Also, some cloud hosting services require `RewriteBase` to be set: 209 | # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site 210 | 211 | 212 | Options +FollowSymlinks 213 | # Options +SymLinksIfOwnerMatch 214 | RewriteEngine On 215 | # RewriteBase / 216 | 217 | 218 | # ------------------------------------------------------------------------------ 219 | # | Suppressing / Forcing the "www." at the beginning of URLs | 220 | # ------------------------------------------------------------------------------ 221 | 222 | # The same content should never be available under two different URLs especially 223 | # not with and without "www." at the beginning. This can cause SEO problems 224 | # (duplicate content), therefore, you should choose one of the alternatives and 225 | # redirect the other one. 226 | 227 | # By default option 1 (no "www.") is activated: 228 | # http://no-www.org/faq.php?q=class_b 229 | 230 | # If you'd prefer to use option 2, just comment out all the lines from option 1 231 | # and uncomment the ones from option 2. 232 | 233 | # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME! 234 | 235 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 236 | 237 | # Option 1: rewrite www.example.com → example.com 238 | 239 | 240 | RewriteCond %{HTTPS} !=on 241 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] 242 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] 243 | 244 | 245 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 246 | 247 | # Option 2: rewrite example.com → www.example.com 248 | 249 | # Be aware that the following might not be a good idea if you use "real" 250 | # subdomains for certain parts of your website. 251 | 252 | # 253 | # RewriteCond %{HTTPS} !=on 254 | # RewriteCond %{HTTP_HOST} !^www\..+$ [NC] 255 | # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] 256 | # 257 | 258 | 259 | # ############################################################################## 260 | # # SECURITY # 261 | # ############################################################################## 262 | 263 | # ------------------------------------------------------------------------------ 264 | # | Content Security Policy (CSP) | 265 | # ------------------------------------------------------------------------------ 266 | 267 | # You can mitigate the risk of cross-site scripting and other content-injection 268 | # attacks by setting a Content Security Policy which whitelists trusted sources 269 | # of content for your site. 270 | 271 | # The example header below allows ONLY scripts that are loaded from the current 272 | # site's origin (no inline scripts, no CDN, etc). This almost certainly won't 273 | # work as-is for your site! 274 | 275 | # To get all the details you'll need to craft a reasonable policy for your site, 276 | # read: http://html5rocks.com/en/tutorials/security/content-security-policy (or 277 | # see the specification: http://w3.org/TR/CSP). 278 | 279 | # 280 | # Header set Content-Security-Policy "script-src 'self'; object-src 'self'" 281 | # 282 | # Header unset Content-Security-Policy 283 | # 284 | # 285 | 286 | # ------------------------------------------------------------------------------ 287 | # | File access | 288 | # ------------------------------------------------------------------------------ 289 | 290 | # Block access to directories without a default document. 291 | # Usually you should leave this uncommented because you shouldn't allow anyone 292 | # to surf through every directory on your server (which may includes rather 293 | # private places like the CMS's directories). 294 | 295 | 296 | Options -Indexes 297 | 298 | 299 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 300 | 301 | # Block access to hidden files and directories. 302 | # This includes directories used by version control systems such as Git and SVN. 303 | 304 | 305 | RewriteCond %{SCRIPT_FILENAME} -d [OR] 306 | RewriteCond %{SCRIPT_FILENAME} -f 307 | RewriteRule "(^|/)\." - [F] 308 | 309 | 310 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 311 | 312 | # Block access to backup and source files. 313 | # These files may be left by some text editors and can pose a great security 314 | # danger when anyone has access to them. 315 | 316 | 317 | Order allow,deny 318 | Deny from all 319 | Satisfy All 320 | 321 | 322 | # ------------------------------------------------------------------------------ 323 | # | Secure Sockets Layer (SSL) | 324 | # ------------------------------------------------------------------------------ 325 | 326 | # Rewrite secure requests properly to prevent SSL certificate warnings, e.g.: 327 | # prevent `https://www.example.com` when your certificate only allows 328 | # `https://secure.example.com`. 329 | 330 | # 331 | # RewriteCond %{SERVER_PORT} !^443 332 | # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L] 333 | # 334 | 335 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 336 | 337 | # Force client-side SSL redirection. 338 | 339 | # If a user types "example.com" in his browser, the above rule will redirect him 340 | # to the secure version of the site. That still leaves a window of opportunity 341 | # (the initial HTTP connection) for an attacker to downgrade or redirect the 342 | # request. The following header ensures that browser will ONLY connect to your 343 | # server via HTTPS, regardless of what the users type in the address bar. 344 | # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/ 345 | 346 | # 347 | # Header set Strict-Transport-Security max-age=16070400; 348 | # 349 | 350 | # ------------------------------------------------------------------------------ 351 | # | Server software information | 352 | # ------------------------------------------------------------------------------ 353 | 354 | # Avoid displaying the exact Apache version number, the description of the 355 | # generic OS-type and the information about Apache's compiled-in modules. 356 | 357 | # ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`! 358 | 359 | # ServerTokens Prod 360 | 361 | 362 | # ############################################################################## 363 | # # WEB PERFORMANCE # 364 | # ############################################################################## 365 | 366 | # ------------------------------------------------------------------------------ 367 | # | Compression | 368 | # ------------------------------------------------------------------------------ 369 | 370 | 371 | 372 | # Force compression for mangled headers. 373 | # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping 374 | 375 | 376 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding 377 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding 378 | 379 | 380 | 381 | # Compress all output labeled with one of the following MIME-types 382 | # (for Apache versions below 2.3.7, you don't need to enable `mod_filter` 383 | # and can remove the `` and `` lines 384 | # as `AddOutputFilterByType` is still in the core directives). 385 | 386 | AddOutputFilterByType DEFLATE application/atom+xml \ 387 | application/javascript \ 388 | application/json \ 389 | application/rss+xml \ 390 | application/vnd.ms-fontobject \ 391 | application/x-font-ttf \ 392 | application/x-web-app-manifest+json \ 393 | application/xhtml+xml \ 394 | application/xml \ 395 | font/opentype \ 396 | image/svg+xml \ 397 | image/x-icon \ 398 | text/css \ 399 | text/html \ 400 | text/plain \ 401 | text/x-component \ 402 | text/xml 403 | 404 | 405 | 406 | 407 | # ------------------------------------------------------------------------------ 408 | # | Content transformations | 409 | # ------------------------------------------------------------------------------ 410 | 411 | # Prevent some of the mobile network providers from modifying the content of 412 | # your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5. 413 | 414 | # 415 | # Header set Cache-Control "no-transform" 416 | # 417 | 418 | # ------------------------------------------------------------------------------ 419 | # | ETag removal | 420 | # ------------------------------------------------------------------------------ 421 | 422 | # Since we're sending far-future expires headers (see below), ETags can 423 | # be removed: http://developer.yahoo.com/performance/rules.html#etags. 424 | 425 | # `FileETag None` is not enough for every server. 426 | 427 | Header unset ETag 428 | 429 | 430 | FileETag None 431 | 432 | # ------------------------------------------------------------------------------ 433 | # | Expires headers (for better cache control) | 434 | # ------------------------------------------------------------------------------ 435 | 436 | # The following expires headers are set pretty far in the future. If you don't 437 | # control versioning with filename-based cache busting, consider lowering the 438 | # cache time for resources like CSS and JS to something like 1 week. 439 | 440 | 441 | 442 | ExpiresActive on 443 | ExpiresDefault "access plus 1 month" 444 | 445 | # CSS 446 | ExpiresByType text/css "access plus 1 year" 447 | 448 | # Data interchange 449 | ExpiresByType application/json "access plus 0 seconds" 450 | ExpiresByType application/xml "access plus 0 seconds" 451 | ExpiresByType text/xml "access plus 0 seconds" 452 | 453 | # Favicon (cannot be renamed!) 454 | ExpiresByType image/x-icon "access plus 1 week" 455 | 456 | # HTML components (HTCs) 457 | ExpiresByType text/x-component "access plus 1 month" 458 | 459 | # HTML 460 | ExpiresByType text/html "access plus 0 seconds" 461 | 462 | # JavaScript 463 | ExpiresByType application/javascript "access plus 1 year" 464 | 465 | # Manifest files 466 | ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" 467 | ExpiresByType text/cache-manifest "access plus 0 seconds" 468 | 469 | # Media 470 | ExpiresByType audio/ogg "access plus 1 month" 471 | ExpiresByType image/gif "access plus 1 month" 472 | ExpiresByType image/jpeg "access plus 1 month" 473 | ExpiresByType image/png "access plus 1 month" 474 | ExpiresByType video/mp4 "access plus 1 month" 475 | ExpiresByType video/ogg "access plus 1 month" 476 | ExpiresByType video/webm "access plus 1 month" 477 | 478 | # Web feeds 479 | ExpiresByType application/atom+xml "access plus 1 hour" 480 | ExpiresByType application/rss+xml "access plus 1 hour" 481 | 482 | # Web fonts 483 | ExpiresByType application/font-woff "access plus 1 month" 484 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month" 485 | ExpiresByType application/x-font-ttf "access plus 1 month" 486 | ExpiresByType font/opentype "access plus 1 month" 487 | ExpiresByType image/svg+xml "access plus 1 month" 488 | 489 | 490 | 491 | # ------------------------------------------------------------------------------ 492 | # | Filename-based cache busting | 493 | # ------------------------------------------------------------------------------ 494 | 495 | # If you're not using a build process to manage your filename version revving, 496 | # you might want to consider enabling the following directives to route all 497 | # requests such as `/css/style.12345.css` to `/css/style.css`. 498 | 499 | # To understand why this is important and a better idea than `*.css?v231`, read: 500 | # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring 501 | 502 | # 503 | # RewriteCond %{REQUEST_FILENAME} !-f 504 | # RewriteCond %{REQUEST_FILENAME} !-d 505 | # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L] 506 | # 507 | 508 | # ------------------------------------------------------------------------------ 509 | # | File concatenation | 510 | # ------------------------------------------------------------------------------ 511 | 512 | # Allow concatenation from within specific CSS and JS files, e.g.: 513 | # Inside of `script.combined.js` you could have 514 | # 515 | # 516 | # and they would be included into this single file. 517 | 518 | # 519 | # 520 | # Options +Includes 521 | # AddOutputFilterByType INCLUDES application/javascript application/json 522 | # SetOutputFilter INCLUDES 523 | # 524 | # 525 | # Options +Includes 526 | # AddOutputFilterByType INCLUDES text/css 527 | # SetOutputFilter INCLUDES 528 | # 529 | # 530 | 531 | # ------------------------------------------------------------------------------ 532 | # | Persistent connections | 533 | # ------------------------------------------------------------------------------ 534 | 535 | # Allow multiple requests to be sent over the same TCP connection: 536 | # http://httpd.apache.org/docs/current/en/mod/core.html#keepalive. 537 | 538 | # Enable if you serve a lot of static content but, be aware of the 539 | # possible disadvantages! 540 | 541 | # 542 | # Header set Connection Keep-Alive 543 | # 544 | -------------------------------------------------------------------------------- /bower_components/angular-csv-import/examples/app/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 141 | 142 | 143 |
144 |

Not found :(

145 |

Sorry, but the page you were trying to view does not exist.

146 |

It looks like this was the result of either:

147 |
    148 |
  • a mistyped address
  • 149 |
  • an out-of-date link
  • 150 |
151 | 154 | 155 |
156 | 157 | 158 | -------------------------------------------------------------------------------- /bower_components/angular-csv-import/examples/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CS-HAMJ/dbview/21cc1ff16da5e8e0887765cb0685ec0f5207bf7a/bower_components/angular-csv-import/examples/app/favicon.ico -------------------------------------------------------------------------------- /bower_components/angular-csv-import/examples/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular CSV Import 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 |
34 |
35 |
36 | 37 | 38 | 39 | 48 | 49 | 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 | -------------------------------------------------------------------------------- /bower_components/angular-csv-import/examples/app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /bower_components/angular-csv-import/examples/app/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc overview 5 | * @name examplesApp 6 | * @description 7 | * # examplesApp 8 | * 9 | * Main module of the application. 10 | */ 11 | angular 12 | .module('examplesApp', [ 13 | 'ngResource', 14 | 'ngRoute', 15 | 'ngCsvImport', 16 | 'hljs' 17 | ]) 18 | .config(function ($routeProvider) { 19 | $routeProvider 20 | .when('/', { 21 | templateUrl: 'views/main.html', 22 | controller: 'MainCtrl' 23 | }) 24 | .when('/about', { 25 | templateUrl: 'views/about.html', 26 | controller: 'AboutCtrl' 27 | }) 28 | .otherwise({ 29 | redirectTo: '/' 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /bower_components/angular-csv-import/examples/app/scripts/controllers/about.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name examplesApp.controller:AboutCtrl 6 | * @description 7 | * # AboutCtrl 8 | * Controller of the examplesApp 9 | */ 10 | angular.module('examplesApp') 11 | .controller('AboutCtrl', function ($scope) { 12 | $scope.awesomeThings = [ 13 | 'HTML5 Boilerplate', 14 | 'AngularJS', 15 | 'Karma' 16 | ]; 17 | }); 18 | -------------------------------------------------------------------------------- /bower_components/angular-csv-import/examples/app/scripts/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('examplesApp') 4 | .controller('MainCtrl', ['$scope', '$parse', function ($scope, $parse) { 5 | $scope.csv = { 6 | content: null, 7 | header: true, 8 | headerVisible: true, 9 | separator: ',', 10 | separatorVisible: true, 11 | result: null, 12 | encoding: 'ISO-8859-1', 13 | encodingVisible: true, 14 | uploadButtonLabel: "upload a csv file" 15 | }; 16 | 17 | var _lastGoodResult = ''; 18 | $scope.toPrettyJSON = function (json, tabWidth) { 19 | var objStr = JSON.stringify(json); 20 | var obj = null; 21 | try { 22 | obj = $parse(objStr)({}); 23 | } catch(e){ 24 | // eat $parse error 25 | return _lastGoodResult; 26 | } 27 | 28 | var result = JSON.stringify(obj, null, Number(tabWidth)); 29 | _lastGoodResult = result; 30 | 31 | return result; 32 | }; 33 | }]); 34 | -------------------------------------------------------------------------------- /bower_components/angular-csv-import/examples/app/styles/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 20px; 3 | padding-bottom: 20px; 4 | background: #2c3e50; 5 | } 6 | 7 | div.container { 8 | width: 80%; 9 | margin: 0 auto; 10 | text-align: center; 11 | } 12 | 13 | div.block.row { 14 | width: 100%; 15 | margin: 10px 0px; 16 | } 17 | 18 | div.block.row > div { 19 | display: inline-block; 20 | vertical-align: middle; 21 | } 22 | 23 | div.block.row > div.title { 24 | width: 30%; 25 | color: white; 26 | } 27 | 28 | div.import>div>div.label { 29 | display: inline-block; 30 | width: 30%; 31 | text-align: left; 32 | color: white; 33 | font-family: 'Verdana'; 34 | font-size: 14px; 35 | } 36 | 37 | div.import > div { 38 | margin: 15px 0px; 39 | } 40 | 41 | div.import > div > span { 42 | color: white; 43 | } 44 | 45 | div.import > div> span > input[type="text"] { 46 | font-size: 18px; 47 | color: #d6492f; 48 | font-weight: bold; 49 | border-radius: none; 50 | outline: none; 51 | } 52 | 53 | div.import>div>div>input[type="file"] { 54 | color: transparent; 55 | padding: 0; 56 | } 57 | 58 | div.import>div>div>input[type="file"]::-webkit-file-upload-button { 59 | visibility: hidden; 60 | } 61 | 62 | div.import>div>div>input[type="file"]::before { 63 | content: attr(upload-button-label); 64 | display: inline-block; 65 | background-image: linear-gradient(to top, #f9f9f9 0px, #f9f9f9 50%, #e3e3e3 50%, #e3e3e3 100% ); 66 | color: #444; 67 | border: 1px solid #999; 68 | border-radius: 3px; 69 | padding: 5px 8px; 70 | outline: none; 71 | white-space: nowrap; 72 | -webkit-user-select: none; 73 | cursor: pointer; 74 | text-shadow: 1px 1px #fff; 75 | font-weight: 700; 76 | font-size: 14px; 77 | } 78 | div.import>div>div>input[type="file"]:hover::before { 79 | border-color: black; 80 | } 81 | div.import>div>div>input[type="file"]:active::before { 82 | background: -webkit-linear-gradient(top, #e3e3e3, #f9f9f9); 83 | } 84 | 85 | div.block { 86 | display: inline-block; 87 | vertical-align: middle; 88 | text-align: left; 89 | } 90 | 91 | div.block.left { 92 | } 93 | 94 | div.block>div.content { 95 | border: 5px dashed #d6492f; 96 | background: none; 97 | padding: 0px; 98 | border-radius: 3px; 99 | } 100 | 101 | div.block>div.content>pre { 102 | font-size: 15px; 103 | font-family: 'Courier'; 104 | background: #F0F0F0; 105 | padding: 10px; 106 | margin: 0; 107 | } 108 | 109 | footer { 110 | } 111 | 112 | footer>div { 113 | color: white; 114 | bottom: 40px; 115 | display: inline-block; 116 | margin: 0px 50px; 117 | } 118 | 119 | footer>div a { 120 | color: white; 121 | text-decoration: none; 122 | font-size: 18px; 123 | } 124 | 125 | footer>div a:hover { 126 | text-decoration: underline; 127 | vertical-align: bottom; 128 | } 129 | 130 | footer>div.twitter { 131 | left: 40px; 132 | } 133 | 134 | footer>div.twitter>div { 135 | display: inline-block; 136 | vertical-align: middle; 137 | } 138 | 139 | footer>div.twitter>div.avatar { 140 | margin: 0px 5px 0px 0px; 141 | background: url("https://avatars1.githubusercontent.com/u/820299?v=3&s=460"); 142 | background-size: contain; 143 | border: solid 3px white; 144 | width: 40px; 145 | height: 40px; 146 | border-radius: 100px; 147 | } 148 | 149 | footer>div.github { 150 | right: 40px; 151 | } 152 | 153 | footer>div.github>div { 154 | display: inline-block; 155 | vertical-align: middle; 156 | } 157 | 158 | footer>div.github>div.icon { 159 | font-size: 35px; 160 | margin: 0px 5px 0px 0px; 161 | } 162 | 163 | -------------------------------------------------------------------------------- /bower_components/angular-csv-import/examples/app/views/main.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Select your CSV file:

4 |
5 | 16 |
17 |
18 | 19 |
20 |
21 |
22 | 23 | 27 | 28 | -------------------------------------------------------------------------------- /bower_components/angular-csv-import/examples/test/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.12/config/configuration-file.html 3 | // Generated on 2014-07-10 using 4 | // generator-karma 0.8.2 5 | 6 | module.exports = function(config) { 7 | config.set({ 8 | // enable / disable watching file and executing tests whenever any file changes 9 | autoWatch: true, 10 | 11 | // base path, that will be used to resolve files and exclude 12 | basePath: '../', 13 | 14 | // testing framework to use (jasmine/mocha/qunit/...) 15 | frameworks: ['jasmine'], 16 | 17 | // list of files / patterns to load in the browser 18 | files: [ 19 | 'app/bower_components/angular/angular.js', 20 | 'app/bower_components/angular-mocks/angular-mocks.js', 21 | 'app/bower_components/angular-resource/angular-resource.js', 22 | 'app/bower_components/angular-route/angular-route.js', 23 | 'app/scripts/**/*.js', 24 | 'test/mock/**/*.js', 25 | 'test/spec/**/*.js' 26 | ], 27 | 28 | // list of files / patterns to exclude 29 | exclude: [], 30 | 31 | // web server port 32 | port: 8080, 33 | 34 | // Start these browsers, currently available: 35 | // - Chrome 36 | // - ChromeCanary 37 | // - Firefox 38 | // - Opera 39 | // - Safari (only Mac) 40 | // - PhantomJS 41 | // - IE (only Windows) 42 | browsers: [ 43 | 'PhantomJS' 44 | ], 45 | 46 | // Which plugins to enable 47 | plugins: [ 48 | 'karma-phantomjs-launcher', 49 | 'karma-jasmine' 50 | ], 51 | 52 | // Continuous Integration mode 53 | // if true, it capture browsers, run tests and exit 54 | singleRun: false, 55 | 56 | colors: true, 57 | 58 | // level of logging 59 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 60 | logLevel: config.LOG_INFO, 61 | 62 | // Uncomment the following lines if you are using grunt's server to run the tests 63 | // proxies: { 64 | // '/': 'http://localhost:9000/' 65 | // }, 66 | // URL root prevent conflicts with the site root 67 | // urlRoot: '_karma_' 68 | }); 69 | }; 70 | -------------------------------------------------------------------------------- /bower_components/angular-csv-import/lib/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CS-HAMJ/dbview/21cc1ff16da5e8e0887765cb0685ec0f5207bf7a/bower_components/angular-csv-import/lib/.DS_Store -------------------------------------------------------------------------------- /bower_components/angular-csv-import/lib/angular-csv-import.css: -------------------------------------------------------------------------------- 1 | ng-csv-import, .ng-csv-import { 2 | display: inline-block; 3 | position: relative; 4 | padding: 2px; 5 | margin: 0; 6 | vertical-align: middle; 7 | } -------------------------------------------------------------------------------- /bower_components/angular-csv-import/lib/angular-csv-import.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var csvImport = angular.module('ngCsvImport', []); 4 | 5 | csvImport.directive('ngCsvImport', function() { 6 | return { 7 | restrict: 'E', 8 | transclude: true, 9 | replace: true, 10 | scope:{ 11 | content:'=?', 12 | header: '=?', 13 | headerVisible: '=?', 14 | separator: '=?', 15 | separatorVisible: '=?', 16 | result: '=?', 17 | encoding: '=?', 18 | encodingVisible: '=?', 19 | accept: '=?', 20 | acceptSize: '=?', 21 | acceptSizeExceedCallback: '=?', 22 | callback: '=?', 23 | mdButtonClass: '@?', 24 | mdInputClass: '@?', 25 | mdButtonTitle: '@?', 26 | mdSvgIcon: '@?', 27 | uploadButtonLabel: '=' 28 | }, 29 | template: function(element, attrs) { 30 | var material = angular.isDefined(attrs.material); 31 | var multiple = angular.isDefined(attrs.multiple); 32 | return '
'+ 33 | '
Header
' + 34 | (material ? '
' : 35 | '') + 36 | '
Encoding
{{encoding}}
'+ 37 | '
'+ 38 | '
Seperator
'+ 39 | ''+ 40 | '
'+ 41 | '
' + 42 | '' + 43 | (material ? ' {{mdButtonTitle}}' : '') + 44 | '
'+ 45 | '
'; 46 | }, 47 | link: function(scope, element, attrs) { 48 | scope.separatorVisible = !!scope.separatorVisible; 49 | scope.headerVisible = !!scope.headerVisible; 50 | scope.acceptSize = scope.acceptSize || Number.POSITIVE_INFINITY; 51 | scope.material = angular.isDefined(attrs.material); 52 | scope.multiple = angular.isDefined(attrs.multiple); 53 | if (scope.multiple) { 54 | throw new Error("Multiple attribute is not supported yet."); 55 | } 56 | var input = angular.element(element[0].querySelector('input[type="file"]')); 57 | var inputContainer = angular.element(element[0].querySelector('md-input-container')); 58 | 59 | if (scope.material && input) { 60 | input.removeClass("ng-show"); 61 | input.addClass("ng-hide"); 62 | if (inputContainer) { 63 | var errorSpacer = angular.element(inputContainer[0].querySelector('div.md-errors-spacer')); 64 | if (errorSpacer) { 65 | errorSpacer.remove(); 66 | } 67 | } 68 | scope.onClick = function() { 69 | input.click(); 70 | }; 71 | } 72 | 73 | angular.element(element[0].querySelector('.separator-input')).on('keyup', function(e) { 74 | if ( scope.content != null ) { 75 | var content = { 76 | csv: scope.content, 77 | header: scope.header, 78 | separator: e.target.value, 79 | encoding: scope.encoding 80 | }; 81 | scope.result = csvToJSON(content); 82 | scope.$apply(); 83 | if ( typeof scope.callback === 'function' ) { 84 | scope.callback(e); 85 | } 86 | } 87 | }); 88 | 89 | element.on('change', function(onChangeEvent) { 90 | if (!onChangeEvent.target.files.length){ 91 | return; 92 | } 93 | 94 | if (onChangeEvent.target.files[0].size > scope.acceptSize){ 95 | if ( typeof scope.acceptSizeExceedCallback === 'function' ) { 96 | scope.acceptSizeExceedCallback(onChangeEvent.target.files[0]); 97 | } 98 | return; 99 | } 100 | 101 | scope.filename = onChangeEvent.target.files[0].name; 102 | var reader = new FileReader(); 103 | reader.onload = function(onLoadEvent) { 104 | scope.$apply(function() { 105 | var content = { 106 | csv: onLoadEvent.target.result.replace(/\r\n|\r/g,'\n'), 107 | header: scope.header, 108 | separator: scope.separator 109 | }; 110 | scope.content = content.csv; 111 | scope.result = csvToJSON(content); 112 | scope.result.filename = scope.filename; 113 | scope.$$postDigest(function(){ 114 | if ( typeof scope.callback === 'function' ) { 115 | scope.callback(onChangeEvent); 116 | } 117 | }); 118 | }); 119 | }; 120 | 121 | if ( (onChangeEvent.target.type === "file") && (onChangeEvent.target.files != null || onChangeEvent.srcElement.files != null) ) { 122 | reader.readAsText((onChangeEvent.srcElement || onChangeEvent.target).files[0], scope.encoding); 123 | } else { 124 | if ( scope.content != null ) { 125 | var content = { 126 | csv: scope.content, 127 | header: !scope.header, 128 | separator: scope.separator 129 | }; 130 | scope.result = csvToJSON(content); 131 | scope.$$postDigest(function(){ 132 | if ( typeof scope.callback === 'function' ) { 133 | scope.callback(onChangeEvent); 134 | } 135 | }); 136 | } 137 | } 138 | }); 139 | 140 | var csvToJSON = function(content) { 141 | var lines=content.csv.split(new RegExp('\n(?![^"]*"(?:(?:[^"]*"){2})*[^"]*$)')); 142 | var result = []; 143 | var start = 0; 144 | var columnCount = lines[0].split(content.separator).length; 145 | 146 | var headers = []; 147 | if (content.header) { 148 | headers=lines[0].split(content.separator); 149 | start = 1; 150 | } 151 | 152 | for (var i=start; i { 24 | 25 | // add this table to the nav bar 26 | $scope.activateTable($scope, table, tableService); 27 | 28 | // save the data in table service 29 | tableService.addTableData(table, response.data); 30 | 31 | }) 32 | .then((response) => { 33 | $scope.viewTable(table); 34 | $state.go('^.table'); 35 | }) 36 | } 37 | 38 | // view a specific table (actual tablename is passed via $stateParams) 39 | $scope.viewTable = function (table) { 40 | console.log("this is logging", table) 41 | tableService.currentTable = table; 42 | }; 43 | // add table to nav bar if not already there 44 | $scope.activateTable = function($scope, table, tableService) { 45 | if (!$scope.onlineTables.includes(table)) { 46 | tableService.activateTable(table); 47 | $scope.onlineTables = tableService.activeTables; 48 | } 49 | }; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/client/controllers/homeController.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('Dbview.HomeController', ['ui.router']) 3 | .controller('HomeController', ['$scope', '$http', '$location', 'dbService', HomeController]) 4 | 5 | function HomeController($scope, $http, $location, dbService) { 6 | $scope.creds = { 7 | host: 'ec2-54-243-212-72.compute-1.amazonaws.com', 8 | database: 'd7ctrh5hg6aadj', 9 | user: 'dxrwecviorvrto', 10 | password: 'BDyJHAElIeyxjSLNxI1NBYu3Z4', 11 | port: '5432', 12 | dialect: 'postgres' 13 | }; 14 | // send post request to get list of all available tables, then navigate to db page 15 | $scope.post = function () { 16 | dbService.setCreds($scope.creds); 17 | $http({ 18 | method: 'POST', 19 | url: '/requestDB', 20 | headers: { 21 | 'Content-Type': 'application/json' 22 | }, 23 | data: { creds: $scope.creds }, 24 | }) 25 | .then((response) => { 26 | dbService.setTables(response.data); // save table names to dbService 27 | $location.path('/db'); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/client/controllers/importController.js: -------------------------------------------------------------------------------- 1 | 2 | /*jshint esversion: 6 */ 3 | angular 4 | .module('Dbview.ImportController', ['ui.router','ngSanitize', 'ngCsv']) 5 | .controller('ImportController', ['$scope', 'tableService', '$stateParams', 'dbService', '$http', '$state', '$timeout', importController]); 6 | 7 | function importController($scope, tableService, $stateParams, dbService, $http, $state, $timeout) { 8 | $scope.name = ''; 9 | $scope.visible = false; 10 | 11 | $("#filename").change(function(evt) { 12 | var file = evt.target.files[0]; 13 | Papa.parse(file, { 14 | header: true, 15 | dynamicTyping: true, 16 | complete: function(results) { 17 | $scope.$apply(function(){ 18 | $scope.headers = Object.keys(results.data[0]); 19 | $scope.body = results.data; 20 | $scope.visible = true; 21 | }) 22 | } 23 | }); 24 | 25 | }); 26 | 27 | 28 | $scope.importTable = function() { 29 | 30 | let tempHeader = {}; 31 | 32 | $scope.headers.forEach(function(header) { 33 | tempHeader[header] = typeof $scope.body[0][header] === 'number' ? 'Integer' : 'Varchar'; 34 | }); 35 | 36 | $scope.rowsToAdd = tempHeader; 37 | 38 | console.log('tempHeader', $scope.rowsToAdd); 39 | console.log('scopename', $scope.name); 40 | $http({ 41 | method: 'POST', 42 | url: '/createTable', 43 | headers: { 44 | 'Content-Type': 'application/json' 45 | }, 46 | data: { creds: dbService.creds, where: $scope.name, valuesToInsert: $scope.rowsToAdd, table: $scope.name } 47 | }) 48 | .then((response) => { 49 | $scope.body.forEach(function(row) { 50 | $http({ 51 | method: 'POST', 52 | url: '/insert', 53 | headers: { 54 | 'Content-Type': 'application/json' 55 | }, 56 | data: { creds: dbService.creds, where: undefined, valuesToInsert: row, table: $scope.name } 57 | }) 58 | }) 59 | tableService.addTableData($scope.name, response.data) 60 | alert('Table Created'); 61 | }); 62 | }; 63 | } 64 | 65 | -------------------------------------------------------------------------------- /src/client/controllers/tableController.js: -------------------------------------------------------------------------------- 1 | 2 | /*jshint esversion: 6 */ 3 | angular 4 | .module('Dbview.TableController', ['ui.router','ngSanitize', 'ngCsv']) 5 | .controller('TableController', ['$scope', 'tableService', '$stateParams', 'dbService', '$http', '$state', '$timeout', tableController]); 6 | 7 | function tableController($scope, tableService, $stateParams, dbService, $http, $state, $timeout) { 8 | //scope.name is the name of the table currently on display 9 | $scope.name = tableService.currentTable; 10 | $scope.displayName = tableService.currentTable; 11 | $scope.dataToDisplay = tableService.getData($scope.name); 12 | 13 | // reference the data that will be rendered to a table format 14 | $scope.gridData = { 15 | data: $scope.dataToDisplay, 16 | enableFiltering: true, 17 | }; 18 | $scope.queryOptions = ['Text Query', 'Create Table', 'Insert Rows', 'Update Rows', 'Delete Rows', 'Drop Table']; 19 | $scope.dataTypes = ['Integer', 'Varchar', 'Serial', 'Date', 'Time']; 20 | $scope.rowsToAdd = {}; 21 | $scope.saveEntry = (column, value) => { 22 | $scope.rowsToAdd[column] = value; 23 | $scope.columnName = ''; 24 | $scope.entryValue = ''; 25 | }; 26 | $scope.removeEntry = (column) => delete $scope.rowsToAdd[column]; 27 | 28 | $scope.queryData = {}; 29 | 30 | $scope.exportFile = function (){ 31 | let columns = {}; 32 | 33 | for (let key in tableService.tableData[tableService.currentTable][0]){ 34 | if(!columns[key]) columns[key] = key; 35 | } 36 | 37 | tableService.tableData[tableService.currentTable].unshift(columns); 38 | 39 | return tableService.tableData[tableService.currentTable]; 40 | } 41 | 42 | // execute a raw query and update displayed table 43 | $scope.executeQuery = function (query) { 44 | let route; 45 | let tableName = $scope.name; 46 | switch($scope.queryType) { 47 | case 'Create Table': route = '/createTable'; break; 48 | case 'Insert Rows': route = '/insert'; break; 49 | case 'Update Rows': route = '/update'; break; 50 | case 'Delete Rows': route = '/delete'; break; 51 | case 'Drop Table': route = '/dropTable'; break; 52 | case 'Text Query': route = '/query'; break; 53 | default: return; 54 | } 55 | 56 | $http({ 57 | method: 'POST', 58 | url: route, 59 | headers: { 60 | 'Content-Type': 'application/json' 61 | }, 62 | data: { creds: dbService.creds, where: tableName, valuesToInsert: $scope.rowsToAdd, table: tableName } 63 | }) 64 | .then((response) => { 65 | const columns = Object.keys(response.data[0]).map( (colname) => { 66 | console.log(colname); 67 | return { field: colname }; 68 | }); 69 | 70 | // save the data in table service and update grid data 71 | 72 | tableService.addTableData($scope.name, response.data) 73 | 74 | $scope.dataToDisplay = tableService.getData($scope.name); 75 | $scope.gridData = { 76 | columnDefs: columns, 77 | data: $scope.dataToDisplay, 78 | enableFiltering: true, 79 | }; 80 | $scope.displayName = 'Query Result'; 81 | }); 82 | }; 83 | } 84 | -------------------------------------------------------------------------------- /src/client/directives/tablenav.js: -------------------------------------------------------------------------------- 1 | angular.module('tablenav', []) 2 | .directive('tablenav', function() { 3 | return { 4 | restrict: 'E', 5 | templateUrl: './partials/navbar.html', 6 | controller: dbController, 7 | } 8 | }); -------------------------------------------------------------------------------- /src/client/index.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 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/client/partials/create.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Create Table

4 |
5 |
6 | 7 |
8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
{{ header }}
{{ record }}
22 |
23 |
24 | 25 |
26 | 27 |
28 |
29 | -------------------------------------------------------------------------------- /src/client/partials/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 28 | -------------------------------------------------------------------------------- /src/client/partials/navbar.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /src/client/partials/table.html: -------------------------------------------------------------------------------- 1 | 2 |

{{displayName}}

3 | 4 |
5 | 6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 14 |
Type full SQL command below
15 |
Table Name
16 |
WHERE clause
17 | 18 | 19 | 20 |
21 |
Column Name
22 |
23 | 24 | 25 | 26 |
27 | 28 |
29 |
Column Name
30 |
31 |
Value
32 | 33 |
34 | 35 | 36 |
37 |
38 |
39 | 40 |
41 |
Entries to be added
42 |
Columns to be created
43 |
{{key}} : {{value}}
44 |
45 |
46 | 47 |
48 | 49 | 50 | 51 |
52 | -------------------------------------------------------------------------------- /src/client/partials/tableselect.html: -------------------------------------------------------------------------------- 1 | 2 |

Connected to: {{dbname}}

3 |

Load tables:

4 | 5 |
6 | 9 |
10 | -------------------------------------------------------------------------------- /src/client/services/dbService.js: -------------------------------------------------------------------------------- 1 | class dbService { 2 | 3 | constructor() { 4 | this.onlineTables = []; 5 | this.tableData = {}; 6 | } 7 | 8 | setTables(jsonData) { 9 | this.tables = jsonData; 10 | } 11 | setCreds(creds) { 12 | this.creds = creds; 13 | } 14 | } 15 | 16 | angular.module('Dbview.dbService', []).service('dbService', [dbService]); 17 | -------------------------------------------------------------------------------- /src/client/services/tableService.js: -------------------------------------------------------------------------------- 1 | 2 | class tableService { 3 | constructor() { 4 | this.activeTables = []; 5 | this.tableData = {}; 6 | this.currentTable = ''; 7 | this.newTable = ''; 8 | } 9 | activateTable(tablename) { 10 | this.activeTables.push(tablename) 11 | } 12 | addTableData(table, data) { 13 | this.tableData[table] = data; 14 | } 15 | getData(table) { 16 | return this.tableData[table]; 17 | } 18 | } 19 | 20 | angular.module('Dbview.tableService', []).service('tableService', [tableService]); 21 | -------------------------------------------------------------------------------- /src/client/styles/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica, sans-serif; 3 | } 4 | 5 | .btn-block { 6 | display: block; 7 | } 8 | 9 | .query-input { 10 | margin: 50px auto 0 auto; 11 | } 12 | 13 | .dropdown { 14 | margin-left: 10px; 15 | } 16 | 17 | #query { 18 | width:100%; 19 | } 20 | 21 | .table-viewer input { 22 | width: 100%; 23 | border-radius: 3px; 24 | border: 1px solid darkblue; 25 | margin: 5px; 26 | padding: 2px 4px; 27 | } 28 | 29 | .table-button { 30 | width: 140px; 31 | border-radius: 4px; 32 | margin: 20 auto; 33 | } 34 | 35 | .submit-buttons { 36 | margin-top: 20px; 37 | width: 300px; 38 | margin-left: auto; 39 | margin-right: auto; 40 | margin-top: 10px; 41 | } 42 | .query-form { 43 | width: 100%; 44 | } 45 | 46 | .input-label { 47 | margin: 5px; 48 | font-weight: bold; 49 | margin-bottom: 2px; 50 | } 51 | 52 | .dropdown { 53 | margin: 5px; 54 | font-size: 18px; 55 | border: 2px solid grey; 56 | } 57 | 58 | .rows-to-add { 59 | margin: 5px; 60 | border: 1px solid grey; 61 | border-radius: 1px; 62 | width: 50%; 63 | padding: 2px; 64 | } 65 | 66 | .login { 67 | margin: 0 auto; 68 | width: 50%; 69 | } 70 | 71 | .login h1 { 72 | margin-bottom: 20px; 73 | } 74 | -------------------------------------------------------------------------------- /src/server/addTable.js: -------------------------------------------------------------------------------- 1 | const Sequelize = require('sequelize'); 2 | const testData = require('./testdata'); 3 | 4 | const sequelize = new Sequelize('d7ctrh5hg6aadj', 'dxrwecviorvrto', 'BDyJHAElIeyxjSLNxI1NBYu3Z4', { 5 | host: 'ec2-54-243-212-72.compute-1.amazonaws.com', 6 | dialect: 'postgres', 7 | dialectOptions: { 8 | ssl: true 9 | } 10 | }); 11 | 12 | const User = sequelize.define('user', { 13 | id: { 14 | type: Sequelize.INTEGER, 15 | primaryKey: true, 16 | autoIncrement: true, 17 | }, 18 | username: { 19 | type: Sequelize.STRING, 20 | allowNull: false, 21 | field: 'username' // Will result in an attribute that is firstName when user facing but first_name in the database 22 | }, 23 | password: { 24 | type: Sequelize.STRING, 25 | allowNull: false, 26 | }, 27 | elo: { 28 | allowNull: true, 29 | defaultValue: 1200, 30 | type: Sequelize.INTEGER, 31 | }, 32 | games: { 33 | allowNull: true, 34 | defaultValue: 0, 35 | type: Sequelize.INTEGER, 36 | }, 37 | wins: { 38 | allowNull: true, 39 | defaultValue: 0, 40 | type: Sequelize.INTEGER, 41 | }, 42 | league: { 43 | allowNull: true, 44 | type: Sequelize.STRING, 45 | }, 46 | imglink: { 47 | allowNull: true, 48 | type: Sequelize.STRING, 49 | } 50 | }); 51 | 52 | sequelize.sync({ force: true }) 53 | .then(() => User.bulkCreate(testData.userData)) 54 | .then(() => sequelize.close()); 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/server/authCtrl.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CS-HAMJ/dbview/21cc1ff16da5e8e0887765cb0685ec0f5207bf7a/src/server/authCtrl.js -------------------------------------------------------------------------------- /src/server/dbCtrl.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Sequelize = require('sequelize'); 4 | 5 | const dbCtrl = { 6 | showTables: (obj) => { 7 | // Object being passed in from userCtrl has a `creds` object that has all login credentials 8 | const sequelize = new Sequelize(obj.creds.database, obj.creds.user, obj.creds.password, { 9 | host: obj.creds.host, 10 | dialect: obj.creds.dialect, 11 | dialectOptions: { ssl: true } 12 | }); 13 | // Obtaining table names as strings and returning them in an array. Special tables in postgres not meant to be shown to user are excluded. 14 | return sequelize.query(`SELECT table_name FROM information_schema.tables WHERE table_schema NOT IN('pg_catalog', 'information_schema')`, { type: sequelize.QueryTypes.SELECT }) 15 | // Result is a large array full of smaller array, which each has a table name. Formatting to a simpler array before returning it to userCtrl. 16 | .then((results) => { return results.map(result => result[0]) }); 17 | }, 18 | 19 | getTable: (obj) => { 20 | // Object being passed in from userCtrl has a `creds` object that has all login credentials. 21 | const sequelize = new Sequelize(obj.creds.database, obj.creds.user, obj.creds.password, { 22 | host: obj.creds.host, 23 | dialect: obj.creds.dialect, 24 | dialectOptions: { ssl: true } 25 | }); 26 | // Returns table requested. Table name is requested using the `table` property of the object. 27 | return sequelize.query(`SELECT * FROM ${obj.table}`, { type: sequelize.QueryTypes.SELECT }); 28 | }, 29 | 30 | insertRow: (obj) => { 31 | // Object being passed in from userCtrl has a `creds` object that has all login credentials. 32 | //Need to find out if MySQL needs CreatedAt and UpdatedAt fields 33 | const sequelize = new Sequelize(obj.creds.database, obj.creds.user, obj.creds.password, { 34 | host: obj.creds.host, 35 | dialect: obj.creds.dialect, 36 | dialectOptions: { ssl: true } 37 | }); 38 | let columnsToAdd = ``; 39 | let valuesToAdd = ``; 40 | for (let n in obj.valuesToInsert) { 41 | columnsToAdd += ` ${n},`; 42 | valuesToAdd += ` '${obj.valuesToInsert[n]}',`; 43 | } 44 | columnsToAdd = columnsToAdd.slice(1, -1); 45 | columnsToAdd = `(${columnsToAdd}, "createdAt", "updatedAt")`; 46 | valuesToAdd = valuesToAdd.slice(1, -1); 47 | valuesToAdd = `(${valuesToAdd}, NOW(), NOW())`; 48 | 49 | // Inserting values (from the `valuesToInsert` property) and returning table. 50 | return sequelize.query(`INSERT INTO ${obj.table} ${columnsToAdd} VALUES ${valuesToAdd}`, { type: sequelize.QueryTypes.INSERT }) 51 | .then((results) => { return sequelize.query(`SELECT * FROM ${obj.table}`, { type: sequelize.QueryTypes.SELECT }) }); 52 | }, 53 | 54 | deleteRow: (obj) => { 55 | // Object being passed in from userCtrl has a `creds` object that has all login credentials. 56 | const sequelize = new Sequelize(obj.creds.database, obj.creds.user, obj.creds.password, { 57 | host: obj.creds.host, 58 | dialect: obj.creds.dialect, 59 | dialectOptions: { ssl: true } 60 | }); 61 | 62 | return sequelize.query(`DELETE FROM ${obj.table} WHERE ${obj.where}`, { type: sequelize.QueryTypes.DELETE }) 63 | .then((results) => { return sequelize.query(`SELECT * FROM ${obj.table}`, { type: sequelize.QueryTypes.SELECT }) }); 64 | }, 65 | 66 | updateRow: (obj) => { 67 | // Object being passed in from userCtrl has a `creds` object that has all login credentials. 68 | const sequelize = new Sequelize(obj.creds.database, obj.creds.user, obj.creds.password, { 69 | host: obj.creds.host, 70 | dialect: obj.creds.dialect, 71 | dialectOptions: { ssl: true } 72 | }); 73 | 74 | // Building string of columns tp update. 75 | let columnsToUpdate = ''; 76 | for (let n in obj.valuesToInsert) columnsToUpdate += ` ${n}=${obj.valuesToInsert[n]},`; 77 | columnsToUpdate = columnsToUpdate.slice(1, -1); 78 | 79 | // Updating row and returning table. 80 | return sequelize.query(`UPDATE ${obj.table} SET ${columnsToUpdate} WHERE ${obj.where}`, { type: sequelize.QueryTypes.UPDATE }) 81 | .then((results) => { return sequelize.query(`SELECT * FROM ${obj.table}`, { type: sequelize.QueryTypes.SELECT }) }); 82 | }, 83 | 84 | createTable: (obj) => { 85 | // Object being passed in from userCtrl has a `creds` object that has all login credentials. 86 | const sequelize = new Sequelize(obj.creds.database, obj.creds.user, obj.creds.password, { 87 | host: obj.creds.host, 88 | dialect: obj.creds.dialect, 89 | dialectOptions: { ssl: true } 90 | }); 91 | 92 | // Building string of columns and column types. 93 | let columnsToAdd = ``; 94 | for (let n in obj.valuesToInsert) columnsToAdd += ` "${n}" ${obj.valuesToInsert[n]},`; 95 | columnsToAdd += ` "createdAt" TIME, "updatedAt" TIME`; 96 | columnsToAdd = `(${columnsToAdd.slice(1)})`; 97 | 98 | // Creating table and returning it. 99 | return sequelize.query(`CREATE TABLE IF NOT EXISTS ${obj.where} ${columnsToAdd}`) 100 | .then((results) => { return sequelize.query(`SELECT * FROM ${obj.where}`, { type: sequelize.QueryTypes.SELECT }) }); 101 | }, 102 | 103 | dropTable: (obj) => { 104 | // Object being passed in from userCtrl has a `creds` object that has all login credentials. 105 | const sequelize = new Sequelize(obj.creds.database, obj.creds.user, obj.creds.password, { 106 | host: obj.creds.host, 107 | dialect: obj.creds.dialect, 108 | dialectOptions: { ssl: true } 109 | }); 110 | 111 | // Deleting table, then returning list of table names. 112 | return sequelize.query(`DROP TABLE ${obj.table}`) 113 | .then((results) => { 114 | return sequelize.query(`SELECT table_name FROM information_schema.tables WHERE table_schema NOT IN('pg_catalog', 'information_schema')`, { type: sequelize.QueryTypes.SELECT }) 115 | .then((results) => { return results.map(result => result[0]) }); 116 | }); 117 | }, 118 | 119 | commandLine: (obj) => { 120 | // Object being passed in from userCtrl has a `creds` object that has all login credentials 121 | const sequelize = new Sequelize(obj.creds.database, obj.creds.user, obj.creds.password, { 122 | host: obj.creds.host, 123 | dialect: obj.creds.dialect, 124 | dialectOptions: { ssl: true } 125 | }); 126 | // Executing raw command 127 | return sequelize.query(obj.where) 128 | // Return results 129 | .then((results) => { return results[0] }); 130 | } 131 | } 132 | 133 | module.exports = dbCtrl; 134 | 135 | // For Reference, loginObj should look like this: 136 | // 137 | // const loginObj = { 138 | // host: "ec2-54-243-212-72.compute-1.amazonaws.com", 139 | // database: "d7ctrh5hg6aadj", 140 | // user: "dxrwecviorvrto", 141 | // port: 5432, 142 | // password: "BDyJHAElIeyxjSLNxI1NBYu3Z4", 143 | // dialect: 'mysql'|'sqlite'|'postgres'|'mssql' 144 | // } -------------------------------------------------------------------------------- /src/server/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | const path = require('path'); 4 | const userCtrl = require('./userCtrl.js'); 5 | 6 | //extra middleware 7 | const bodyParser = require('body-parser'); 8 | 9 | app.use(bodyParser.urlencoded({extended: true}), bodyParser.json()); 10 | app.use(express.static(path.join(__dirname, '../../node_modules/'))); 11 | app.use(express.static(path.join(__dirname, '../client/'))); 12 | 13 | app.post('/requestDB', userCtrl.sendTableList, userCtrl.sendTable); 14 | app.post('/requestTable', userCtrl.sendTable); 15 | app.post('/createTable', userCtrl.createTable); 16 | app.post('/insert', userCtrl.insertEntry); 17 | app.post('/update', userCtrl.updateEntry); 18 | app.post('/delete', userCtrl.deleteEntry); 19 | app.post('/query', userCtrl.rawQuery); 20 | app.post('/dropTable', userCtrl.dropTable); 21 | 22 | app.listen(3000, ()=> console.log('listening on port 3000')); -------------------------------------------------------------------------------- /src/server/testdata.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | userData: [ 3 | { 4 | "username": "Ned_Stark", 5 | "elo": 1200, 6 | "games": 0, 7 | "wins": 0, 8 | "league": "Westeros", 9 | "imgLink": "#", 10 | "password": "cat" 11 | }, 12 | { 13 | "username": "Cersei_Lannister", 14 | "elo": 1200, 15 | "games": 0, 16 | "wins": 0, 17 | "league": "Westeros", 18 | "imgLink": "#", 19 | "password": "cat" 20 | }, 21 | { 22 | "username": "Tywin_Lannister", 23 | "elo": 1200, 24 | "games": 0, 25 | "wins": 0, 26 | "league": "Westeros", 27 | "imgLink": "#", 28 | "password": "cat" 29 | }, 30 | { 31 | "username": "Jon_Snow", 32 | "elo": 1200, 33 | "games": 0, 34 | "wins": 0, 35 | "league": "Westeros", 36 | "imgLink": "#", 37 | "password": "cat" 38 | }, 39 | { 40 | "username": "Daenerys Targaryen", 41 | "elo": 1200, 42 | "games": 0, 43 | "wins": 0, 44 | "league": "Westeros", 45 | "imgLink": "#", 46 | "password": "cat" 47 | }, 48 | { 49 | "username": "Hodor", 50 | "elo": 1200, 51 | "games": 0, 52 | "wins": 0, 53 | "league": "Westeros", 54 | "imgLink": "#", 55 | "password": "cat" 56 | }, 57 | { 58 | "username": "Petyr Baelish", 59 | "elo": 1200, 60 | "games": 0, 61 | "wins": 0, 62 | "league": "Westeros", 63 | "imgLink": "#", 64 | "password": "cat" 65 | }, 66 | { 67 | "username": "Melisandre", 68 | "elo": 1200, 69 | "games": 0, 70 | "wins": 0, 71 | "league": "Westeros", 72 | "imgLink": "#", 73 | "password": "cat" 74 | }, 75 | { 76 | "username": "Joffrey", 77 | "elo": 1200, 78 | "games": 0, 79 | "wins": 0, 80 | "league": "Westeros", 81 | "imgLink": "#", 82 | "password": "cat" 83 | }, 84 | { 85 | "username": "Khal Drogo", 86 | "elo": 1200, 87 | "games": 0, 88 | "wins": 0, 89 | "league": "Westeros", 90 | "imgLink": "#", 91 | "password": "cat" 92 | }, 93 | ] 94 | } -------------------------------------------------------------------------------- /src/server/userCtrl.js: -------------------------------------------------------------------------------- 1 | const dbCtrl = require('./dbCtrl'); 2 | 3 | const userCtrl = {}; 4 | 5 | userCtrl.sendTableList = (req, res) => { 6 | dbCtrl.showTables(req.body) 7 | .then( (result) => { 8 | res.json(result); 9 | }) 10 | .catch( (err) => { 11 | console.log(err); 12 | res.end('error') 13 | }); 14 | } 15 | 16 | userCtrl.sendTable = (req, res) => { 17 | dbCtrl.getTable(req.body) 18 | .then( (result) => { 19 | res.json(result); 20 | }) 21 | .catch( (err) => { 22 | console.log(err); 23 | res.end('error') 24 | }); 25 | } 26 | 27 | userCtrl.createTable = (req, res) => { 28 | dbCtrl.createTable(req.body) 29 | .then( (result) => { 30 | console.log(result); 31 | res.json(result); 32 | }) 33 | .catch( (err) => { 34 | console.log(err); 35 | res.end('error') 36 | }); 37 | } 38 | 39 | userCtrl.insertEntry = (req, res) => { 40 | dbCtrl.insertRow(req.body) 41 | .then( (result) => { 42 | res.json(result); 43 | }) 44 | .catch( (err) => { 45 | console.log(err); 46 | res.end('error') 47 | }); 48 | } 49 | 50 | userCtrl.updateEntry = (req, res) => { 51 | dbCtrl.updateRow(req.body) 52 | .then( (result) => { 53 | console.log(result); 54 | res.json(result); 55 | }) 56 | .catch( (err) => { 57 | console.log(err); 58 | res.end('error') 59 | }); 60 | } 61 | 62 | userCtrl.deleteEntry = (req, res) => { 63 | dbCtrl.deleteRow(req.body) 64 | .then( (result) => { 65 | res.json(result); 66 | }) 67 | .catch( (err) => { 68 | console.log(err); 69 | res.end('error') 70 | }); 71 | } 72 | 73 | userCtrl.rawQuery = (req, res) => { 74 | dbCtrl.commandLine(req.body) 75 | .then( (result) => { 76 | console.log(result); 77 | res.json(result); 78 | }) 79 | .catch( (err) => { 80 | console.log(err); 81 | res.end('error') 82 | }); 83 | } 84 | userCtrl.dropTable = (req, res) => { 85 | dbCtrl.dropTable(req.body) 86 | .then( (result) => { 87 | res.json(result); 88 | }) 89 | .catch( (err) => { 90 | console.log(err); 91 | res.end('error') 92 | }); 93 | } 94 | 95 | 96 | module.exports = userCtrl; 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /test/client/dbControllerTest.js: -------------------------------------------------------------------------------- 1 | 2 | describe('DbController', function () { 3 | var homeController, httpBackend, dbService, tableService, location; 4 | 5 | // load the required modules to run these tests 6 | beforeEach(module('Dbview.DbController')); 7 | beforeEach(module('Dbview.dbService')); 8 | beforeEach(module('Dbview.tableService')); 9 | 10 | 11 | // inject the controller and services we will need to run tests 12 | beforeEach(inject(function ($controller, $rootScope, $httpBackend, $location, _dbService_, _tableService_) { 13 | dbService = _dbService_; // karma accesses our own services by padding with underscores 14 | tableService = _tableService_ 15 | scope = $rootScope.$new(); 16 | httpBackend = $httpBackend; 17 | location = $location; 18 | dbController = $controller('DbController', { 19 | $scope: scope, 20 | $httpBackend: $httpBackend 21 | }); 22 | })); 23 | 24 | // actual tests 25 | describe('requestTable', function () { 26 | it('should send credentials and a table name in a post request', function () { 27 | dbService.creds = { username: 'Gregor' } 28 | httpBackend.expectPOST('/requestTable', { 29 | creds: { username: 'Gregor' }, 30 | table: 'Gregor\'s SQL Table' 31 | }).respond(201); 32 | scope.requestTable('Gregor\'s SQL Table'); 33 | httpBackend.flush(); 34 | }) 35 | 36 | it('should call activateTable with scope, tableName, and tableService after a succesful request', function () { 37 | httpBackend.whenPOST('/requestTable').respond(200, { table: 'Gregor\'s Table' }); 38 | sinon.spy(scope, 'activateTable'); 39 | scope.requestTable('Gregor\'s SQL Table'); 40 | httpBackend.flush(); 41 | expect(scope.activateTable.calledWith(scope, 'Gregor\'s SQL Table', tableService)).to.be.ok; 42 | }) 43 | 44 | it('should call addTable data to save the table data in the tableService', function () { 45 | httpBackend.whenPOST('/requestTable').respond(200, { data: '{Datas: 0}' }); 46 | sinon.spy(tableService, 'addTableData'); 47 | scope.requestTable('Gregor\'s SQL Table'); 48 | httpBackend.flush(); 49 | expect((tableService.addTableData).calledWith('Gregor\'s SQL Table', { data: '{Datas: 0}' })).to.be.ok; 50 | }) 51 | }) 52 | 53 | describe('activateTable', function () { 54 | it('should call activate table if table is not activated yet', function () { 55 | sinon.spy(tableService, 'activateTable') 56 | scope.activateTable(scope, 'myTable', tableService); 57 | expect((tableService.activateTable).calledWith('myTable')).to.be.ok; 58 | }) 59 | 60 | it('should not call activate table if table is already activated', function () { 61 | sinon.spy(tableService, 'activateTable') 62 | scope.onlineTables = ['myTable'] 63 | scope.activateTable(scope, 'myTable', tableService); 64 | expect((tableService.activateTable).called).to.not.be.ok; 65 | }) 66 | }) 67 | }); -------------------------------------------------------------------------------- /test/client/homeControllerTest.js: -------------------------------------------------------------------------------- 1 | 2 | describe('HomeController', function () { 3 | var homeController, httpBackend, dbService, location; 4 | 5 | // load the required modules to run these tests 6 | beforeEach(module('Dbview.HomeController')); 7 | beforeEach(module('Dbview.dbService')); 8 | 9 | // inject the controller and services we will need to run tests 10 | beforeEach(inject(function ($controller, $rootScope, $httpBackend, $location, _dbService_) { 11 | dbService = _dbService_; // karma accesses our own services by padding with underscores 12 | scope = $rootScope.$new(); 13 | httpBackend = $httpBackend; 14 | location = $location; 15 | homeController = $controller('HomeController', { 16 | $scope: scope, 17 | $httpBackend: $httpBackend 18 | }); 19 | })); 20 | 21 | // actual tests 22 | describe('post', function () { 23 | it('should send credentials in post', function () { 24 | scope.creds = { username: 'Gregor' } 25 | httpBackend.expectPOST('/requestDB', { creds: { username: 'Gregor' } }).respond(201); 26 | scope.post(); 27 | httpBackend.flush(); 28 | }) 29 | 30 | it('should call dbService setTable with the response to post', function () { 31 | httpBackend.whenPOST('/requestDB').respond(200, { table: 'Gregor\'s Table' }); 32 | var spy = sinon.spy(dbService, 'setTables'); 33 | scope.post(); 34 | httpBackend.flush(); 35 | expect(spy.args[0][0]).to.deep.equal({ table: 'Gregor\'s Table' }); 36 | }) 37 | 38 | it('should redirect to /db after a successful post request', function () { 39 | httpBackend.whenPOST('/requestDB').respond(200, { table: 'Gregor\'s Table' }); 40 | scope.post(); 41 | httpBackend.flush(); 42 | expect(location.path()).to.equal('/db'); 43 | }) 44 | 45 | it('should stay on home page if post request fails', function () { 46 | httpBackend.whenPOST('/requestDB').respond(400); 47 | scope.post(); 48 | httpBackend.flush(); 49 | expect(location.path()).to.equal(''); 50 | }) 51 | }); 52 | }); -------------------------------------------------------------------------------- /test/client/serviceTest.js: -------------------------------------------------------------------------------- 1 | describe('tableService', function(){ 2 | var tableService; 3 | beforeEach(module('Dbview.tableService')); 4 | beforeEach(inject(function (_tableService_) { 5 | tableService = _tableService_; 6 | })); 7 | 8 | it('should add data to rawdata property', function() { 9 | tableService.addTableData('myTable', {a: 3}); 10 | expect(tableService.tableData.myTable).to.deep.equal({a: 3}); 11 | }); 12 | 13 | it('should retrieve rawdata', function() { 14 | tableService.tableData.myTable = {a: 5}; 15 | expect(tableService.getData('myTable')).to.deep.equal({a: 5}); 16 | }); 17 | 18 | it('should add table to active tables array when table is activated', function () { 19 | tableService.activateTable('mytable'); 20 | expect(tableService.activeTables).to.contain('mytable'); 21 | }); 22 | 23 | }); 24 | 25 | describe('dbService', function(){ 26 | var dbService; 27 | beforeEach(module('Dbview.dbService')); 28 | beforeEach(inject(function (_dbService_) { 29 | dbService = _dbService_; 30 | })); 31 | 32 | it('should add data to table data property', function() { 33 | dbService.setTables({'myTable': [3]}); 34 | expect(dbService.tables).to.deep.equal({'myTable': [3]}); 35 | }); 36 | 37 | it('should add credentials to creds property', function() { 38 | dbService.setCreds({name: 'Gregor'}) 39 | expect(dbService.creds).to.deep.equal({name: 'Gregor'}); 40 | }); 41 | }); 42 | 43 | -------------------------------------------------------------------------------- /test/client/tableControllerTest.js: -------------------------------------------------------------------------------- 1 | 2 | describe('TableController', function () { 3 | var tableController, httpBackend, dbService, location; 4 | 5 | // load the required modules to run these tests 6 | beforeEach(module('Dbview.TableController')); 7 | beforeEach(module('Dbview.dbService')); 8 | beforeEach(module('Dbview.tableService')); 9 | 10 | // inject the controller and services we will need to run tests 11 | beforeEach(inject(function ($controller, $rootScope, $httpBackend, $location, _dbService_, _tableService_) { 12 | dbService = _dbService_; // karma accesses our own services by padding with underscores 13 | tableService = _tableService_ 14 | scope = $rootScope.$new(); 15 | httpBackend = $httpBackend; 16 | location = $location; 17 | tableController = $controller('TableController', { 18 | $scope: scope, 19 | $httpBackend: $httpBackend 20 | }); 21 | })); 22 | 23 | // actual tests 24 | describe('saveEntry', function () { 25 | it('should save column and value in scope', function () { 26 | scope.saveEntry('Gregor\'s Column', 'Gregor\'s Value'); 27 | expect(scope.rowsToAdd).to.deep.equal({ 'Gregor\'s Column': 'Gregor\'s Value' }); 28 | }) 29 | 30 | it('should clear column name and entry value forms', function () { 31 | scope.saveEntry('Gregor\'s Column', 'Gregor\'s Value'); 32 | expect(scope.columnName).to.equal(''); 33 | expect(scope.entryValue).to.equal(''); 34 | }) 35 | }); 36 | 37 | describe('executeQuery', function () { 38 | it('makes a post request to the correct route', function () { 39 | scope.queryType = 'Create Table'; 40 | httpBackend.expectPOST('/createTable', { "where": "query", "valuesToInsert": {}, "table": "" }).respond(200, [{ a: 3 }]); 41 | scope.executeQuery('query'); 42 | 43 | scope.queryType = 'Insert Rows'; 44 | httpBackend.expectPOST('/insert', { "where": "query", "valuesToInsert": {}, "table": "" }).respond(200, [{ a: 3 }]); 45 | scope.executeQuery('query'); 46 | 47 | scope.queryType = 'Update Rows'; 48 | httpBackend.expectPOST('/update', { "where": "query", "valuesToInsert": {}, "table": "" }).respond(200, [{ a: 3 }]); 49 | scope.executeQuery('query'); 50 | 51 | scope.queryType = 'Delete Rows'; 52 | httpBackend.expectPOST('/delete', { "where": "query", "valuesToInsert": {}, "table": "" }).respond(200, [{ a: 3 }]); 53 | scope.executeQuery('query'); 54 | 55 | scope.queryType = 'Drop Table'; 56 | httpBackend.expectPOST('/dropTable', { "where": "query", "valuesToInsert": {}, "table": "" }).respond(200, [{ a: 3 }]); 57 | scope.executeQuery('query'); 58 | 59 | scope.queryType = 'Text Query'; 60 | httpBackend.expectPOST('/query', { "where": "query", "valuesToInsert": {}, "table": "" }).respond(200, [{ a: 3 }]); 61 | scope.executeQuery('query'); 62 | httpBackend.flush(); 63 | }) 64 | 65 | it('sends object with creds, where clause, values to insert, and tablename', function () { 66 | scope.queryType = 'Insert Rows'; 67 | scope.rowsToAdd = { 'Gregor\'s Column': 'Gregor\'s Value' } 68 | scope.name = 'Gregor\'s Table'; 69 | httpBackend.expectPOST('/insert', { 70 | where: "query", 71 | valuesToInsert: { 'Gregor\'s Column': 'Gregor\'s Value' }, 72 | table: "Gregor\'s Table" 73 | }).respond(200, [{ a: 3 }]); 74 | scope.executeQuery('query'); 75 | httpBackend.flush(); 76 | }); 77 | 78 | it('should call add table data method of table service to save response data', function () { 79 | sinon.spy(tableService, 'addTableData'); 80 | scope.name = 'Gregor\'s Table'; 81 | scope.queryType = 'Update Rows'; 82 | httpBackend.whenPOST('/update').respond(200, [{ b: 3 }]); 83 | scope.executeQuery('query'); 84 | httpBackend.flush(); 85 | expect((tableService.addTableData).calledWith('Gregor\'s Table', [{ b: 3 }])).to.be.ok; 86 | }); 87 | 88 | it('should update grid data', function () { 89 | var stub = sinon.stub(tableService, 'getData') 90 | scope.gridData = {}; 91 | stub.returns([{ 'column 1': 'val' }]); 92 | scope.name = 'Gregor\'s Table'; 93 | scope.queryType = 'Update Rows'; 94 | httpBackend.whenPOST('/update').respond(200, [{ b: 3 }]); 95 | scope.executeQuery('query'); 96 | httpBackend.flush(); 97 | expect(scope.gridData.data).to.deep.equal([{ 'column 1': 'val' }]) 98 | }); 99 | 100 | }); 101 | }); -------------------------------------------------------------------------------- /test/supertest.js: -------------------------------------------------------------------------------- 1 | const request = require('supertest'); 2 | // Start server 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const assert = require('assert'); 6 | 7 | const app = require('../src/server/server.js'); 8 | 9 | const PORT = process.env.PORT || 3000; 10 | const HOST = `http://localhost:${PORT}`; 11 | 12 | const testLogin = { 13 | host: 'ec2-54-243-212-72.compute-1.amazonaws.com', 14 | database: 'd7ctrh5hg6aadj', 15 | user: 'dxrwecviorvrto', 16 | port: 5432, 17 | password: 'BDyJHAElIeyxjSLNxI1NBYu3Z4', 18 | dialect: 'postgres' 19 | } 20 | const testTable = 'users'; 21 | let testConnection; 22 | 23 | /** 24 | * include an assertion library here so that you can make assertions other than those 25 | * provided by supertest. Choose whichever assertion library suits you. 26 | */ 27 | // const expect = require('expect'); 28 | // const expect = require('chai').expect; 29 | // const assert = require('chai').assert; 30 | 31 | describe('Route integration', () => { 32 | //test to get index.html 33 | describe('/', () => { 34 | describe('GET', () => { 35 | it('responds to page load with 200 status and text/html content type', done => { 36 | request(HOST) 37 | .get('/') 38 | .expect('Content-Type', /text\/html/) 39 | .expect(200, done); 40 | }); 41 | }); 42 | }); 43 | //all tests for data validation and database interactivity are detailed below 44 | describe('database interaction', () => { 45 | describe('POST', () => { 46 | it('sending DB login info responds with list of tables', done => { 47 | request(HOST) 48 | .post('/requestDB') 49 | .send({ creds: testLogin }) 50 | .expect('Content-Type', /application\/json/) 51 | .end( (err, res) => { 52 | assert.equal(typeof res.body[0], 'string'); 53 | done(); 54 | }); 55 | }); 56 | it('sending a table name returns that table', done => { 57 | request(HOST) 58 | .post('/requestTable') 59 | .send({ creds: testLogin, table: 'users' }) 60 | .expect('Content-Type', /application\/json/) 61 | .end( (err, res) => { 62 | assert.equal(typeof res.body[0].id, 'number'); 63 | done(); 64 | }); 65 | }); 66 | //still need column name parameters 67 | it('creating a table creates a new table in the database', done => { 68 | request(HOST) 69 | .post('/createTable') 70 | .send({ creds: testLogin, table: 'users2', valuesToInsert: { id: 'serial', name: 'varchar', games: 'integer', createdAt: 'time', updatedAt: 'time'} }) 71 | .expect('Content-Type', /application\/json/) 72 | .end( (err, res) => { 73 | assert.equal(res.body instanceof Array, true); 74 | done(); 75 | }); 76 | }); 77 | it('can insert new rows into a table', done => { 78 | request(HOST) 79 | .post('/insert') 80 | .send({ creds: testLogin, table: 'users2', valuesToInsert: { name: 'Clegane', games: '8' }}) 81 | .expect('Content-Type', /application\/json/) 82 | .end( (err, res) => { 83 | assert.equal(res.body[0].name, 'Clegane'); 84 | done(); 85 | }); 86 | }); 87 | //go from here 88 | it('can update a row in a table', done => { 89 | request(HOST) 90 | .post('/update') 91 | .send({ creds: testLogin, table: 'users2', where: `name='Clegane'`, valuesToInsert: {'games': 8 } }) 92 | .expect('Content-Type', /application\/json/) 93 | .end( (err, res) => { 94 | assert.equal(res.body[0].games, 8); 95 | done(); 96 | }); 97 | }); 98 | it('can delete a row in a table', done => { 99 | request(HOST) 100 | .post('/delete') 101 | .send({ creds: testLogin, table: 'users2', where: `name='Clegane'` }) 102 | .expect('Content-Type', /application\/json/) 103 | .end( (err, res) => { 104 | console.log(res.body); 105 | assert.equal(res.body.length, 0); 106 | done(); 107 | }); 108 | }); 109 | it('can drop a table', done => { 110 | request(HOST) 111 | .post('/dropTable') 112 | .send({ creds: testLogin, where: 'users2' }) 113 | .expect('Content-Type', /application\/json/) 114 | .end( (err, res) => { 115 | console.log(res.body); 116 | 117 | done(); 118 | }); 119 | }); 120 | }); 121 | }); 122 | }); 123 | --------------------------------------------------------------------------------