├── .DS_Store ├── .gitignore ├── LICENSE ├── README.md ├── admin ├── Utils.js ├── admin.js ├── apps │ └── tasks │ │ ├── db │ │ ├── functions │ │ │ ├── create_bulk_tasks.sql │ │ │ ├── create_task.sql │ │ │ ├── delete_task.sql │ │ │ ├── select_task.sql │ │ │ └── update_task.sql │ │ └── script.sql │ │ └── install.json ├── config │ └── admin ├── controllers │ ├── Connection.js │ ├── Dashboard.js │ ├── Function.js │ ├── Route.js │ ├── User.js │ └── dbUtils.js ├── core │ └── models │ │ ├── Connection.js │ │ ├── Function.js │ │ ├── Route.js │ │ └── User.js ├── install │ ├── db │ │ ├── DB.sql │ │ ├── database.json │ │ └── functions │ │ │ ├── add_admin_user.sql │ │ │ ├── install_application.sql │ │ │ └── validate_session.sql │ └── index.js ├── models │ ├── Connection.js │ ├── Function.js │ ├── Route.js │ └── User.js ├── router │ └── index.js └── sql │ └── main.sql ├── dist └── admin │ ├── index.html │ ├── main.25406552.map │ ├── main.51c0438c.js │ ├── main.98aa3d49.css │ ├── main.bcb0b657.css │ ├── main.bcb0b657.js │ ├── main.bcb0b657.map │ └── pgAPI-icon.5a767025.png ├── index.js ├── package-lock.json └── package.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thrinz/pgapi/e27510461c95017e357f8a46dbd76a7376ac206f/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | .DS_Store 64 | -------------------------------------------------------------------------------- /admin/admin.js: -------------------------------------------------------------------------------- 1 | /* This file is part of pgAPI. 2 | pgAPI - Database as a service 3 | Copyright (C) 2018 Praveen Muralidhar 4 | 5 | pgAPI is a free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | any later version. 9 | 10 | pgAPI is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | const program = require('commander'); 20 | const { prompt } = require('inquirer'); 21 | const dotenv = require('dotenv'); 22 | const path = require('path'); 23 | const utils = require('./Utils'); 24 | const findUp = require('find-up'); 25 | 26 | // Load env variables from .env file 27 | dotenv.config({ 28 | path: findUp.sync('config.env') 29 | }) 30 | 31 | 32 | // Load Runtime variables 33 | utils.loadRuntimeEnvVariables({ 34 | DB_HOST : process.env.PG_HOST || process.env.DB_HOST, 35 | DB_USER: process.env.PG_USER || process.env.DB_USER, 36 | DB_PASSWORD: process.env.PG_PASSWORD || process.env.DB_PASSWORD, 37 | DB_NAME: process.env.PG_DATABASE || process.env.DB_NAME, 38 | DB_PORT: process.env.PG_PORT || process.env.DB_PORT, 39 | PGAPI_SECRET_KEY: process.env.TOKEN_SECRET_KEY || process.env.PGAPI_SECRET_KEY, 40 | DEMO_INSTALL: process.env.DEMO_INSTALL || 'Y' 41 | }); 42 | 43 | const userController = require('../admin/controllers/User'); 44 | 45 | const resetpasswd = [ 46 | { 47 | type : 'input', 48 | name : 'username', 49 | message : 'Enter Username ...' 50 | }, 51 | { 52 | type : 'input', 53 | name : 'password', 54 | message : 'Enter password ...' 55 | }, 56 | { 57 | type : 'input', 58 | name : 'confirmPassword', 59 | message : 'Enter password again ...' 60 | } 61 | ]; 62 | 63 | const addUserQ = [ 64 | { 65 | type : 'input', 66 | name : 'username', 67 | message : 'Enter Username ...' 68 | }, 69 | { 70 | type : 'input', 71 | name : 'firstname', 72 | message : 'Enter Firstname ...' 73 | }, 74 | { 75 | type : 'input', 76 | name : 'lastname', 77 | message : 'Enter Lastname ...' 78 | }, 79 | { 80 | type : 'input', 81 | name : 'password', 82 | message : 'Enter Password ...' 83 | }, 84 | { 85 | type : 'input', 86 | name : 'confirmPassword', 87 | message : 'Enter Password again ...' 88 | } 89 | ]; 90 | 91 | const deleteUserQ = [ 92 | { 93 | type : 'input', 94 | name : 'username', 95 | message : 'Enter Username ...' 96 | } 97 | ]; 98 | 99 | const enableDisableQ = [ 100 | { 101 | type : 'input', 102 | name : 'username', 103 | message : 'Enter Username ...' 104 | }, 105 | { 106 | type : 'input', 107 | name : 'enabled_flag', 108 | message : 'Enable or Disable (Y or N) ...' 109 | } 110 | ]; 111 | 112 | const validateSessionQ = [ 113 | { 114 | type : 'input', 115 | name : 'token', 116 | message : 'Enter Token ...' 117 | } 118 | ]; 119 | 120 | // const generateTokenQ = [ 121 | // { 122 | // type : 'input', 123 | // name : 'refresh_token', 124 | // message : 'Enter Refresh Token ...' 125 | // } 126 | // ]; 127 | 128 | 129 | const updateUserQ = [ 130 | { 131 | type : 'input', 132 | name : 'username', 133 | message : 'Enter Username ...' 134 | }, 135 | { 136 | type : 'input', 137 | name : 'firstname', 138 | message : 'Enter Firstname ...' 139 | }, 140 | { 141 | type : 'input', 142 | name : 'lastname', 143 | message : 'Enter Lastname ...' 144 | } 145 | ]; 146 | 147 | const loginQ = [ 148 | { 149 | type : 'input', 150 | name : 'username', 151 | message : 'Enter Username ...' 152 | }, 153 | { 154 | type : 'input', 155 | name : 'password', 156 | message : 'Enter Password ...' 157 | } 158 | ]; 159 | 160 | program 161 | .version('1.0.0') 162 | .description('API as a Service'); 163 | 164 | program 165 | .command('resetPassword') 166 | .alias('passwd') 167 | .description('Reset User Password') 168 | .action(() => { 169 | prompt(resetpasswd).then(answers => 170 | userController.resetPasswordCommand(answers.username,answers.password,answers.confirmPassword)); 171 | }); 172 | 173 | program 174 | .command('addUser') 175 | .alias('adduser') 176 | .description('Add New User') 177 | .action(() => { 178 | prompt(addUserQ).then(answers => 179 | userController.addUserCommand({username: answers.username,first_name: answers.firstname, last_name: answers.lastname, password: answers.password, confirmPassword: answers.confirmPassword})); 180 | }); 181 | 182 | program 183 | .command('updateUser') 184 | .alias('updateuser') 185 | .description('Update User') 186 | .action(() => { 187 | prompt(updateUserQ).then(answers => 188 | userController.updateUserCommand({username: answers.username,first_name: answers.firstname, last_name: answers.lastname})); 189 | }); 190 | 191 | program 192 | .command('deleteUser') 193 | .alias('deleteuser') 194 | .description('Delete User') 195 | .action(() => { 196 | prompt(deleteUserQ).then(answers => 197 | userController.deleteUserCommand({username: answers.username})); 198 | }); 199 | 200 | program 201 | .command('loginUser') 202 | .alias('login') 203 | .description('Login User') 204 | .action(() => { 205 | prompt(loginQ).then(answers => 206 | userController.loginCommand(answers.username, answers.password)); 207 | }); 208 | 209 | program 210 | .command('sessionValidate') 211 | .alias('session') 212 | .description('Validate Session') 213 | .action(() => { 214 | prompt(validateSessionQ).then(answers => 215 | userController.validateSessionCommand(answers.token)); 216 | }); 217 | 218 | program 219 | .command('enableDisableUser') 220 | .alias('userstatus') 221 | .description('Enable or Disable User') 222 | .action(() => { 223 | prompt(enableDisableQ).then(answers => 224 | userController.enableDisableUserCommand({username: answers.username, enabled_flag: answers.enabled_flag})); 225 | }); 226 | 227 | // program 228 | // .command('generateToken') 229 | // .alias('generatetoken') 230 | // .description('Generate Token') 231 | // .action(() => { 232 | // prompt(generateTokenQ).then(answers => 233 | // userController.generateTokenCommand({refresh_token: answers.refresh_token })); 234 | // }); 235 | 236 | 237 | 238 | program.parse(process.argv); -------------------------------------------------------------------------------- /admin/apps/tasks/db/functions/create_bulk_tasks.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION create_bulk_tasks ( p_data json) 2 | RETURNS json AS 3 | $BODY$ 4 | DECLARE 5 | l_out json; 6 | l_message_text text; 7 | l_exception_detail text; 8 | l_exception_hint text; 9 | -- 10 | l_id uuid; 11 | l_name text; 12 | l_description text; 13 | l_start_date timestamp; 14 | l_due_date timestamp; 15 | l_priority integer; 16 | l_task_record json; 17 | l_tasks_c CURSOR FOR SELECT json_array_elements(p_data->'tasks'); 18 | 19 | BEGIN 20 | 21 | OPEN l_tasks_c; 22 | LOOP 23 | FETCH l_tasks_c INTO l_task_record; 24 | EXIT WHEN NOT FOUND; 25 | 26 | l_id := md5(random()::text || clock_timestamp()::text)::uuid; 27 | l_name := (l_task_record->>'name')::text; 28 | l_description := (l_task_record->>'description')::text; 29 | l_start_date := NOW(); 30 | l_due_date := (l_task_record->>'due_date')::timestamp; 31 | l_priority := (l_task_record->>'priority')::integer; 32 | 33 | INSERT INTO tasks 34 | ( 35 | id, 36 | name, 37 | description, 38 | start_date, 39 | due_date, 40 | priority, 41 | created, 42 | updated 43 | ) 44 | VALUES 45 | ( 46 | l_id, 47 | l_name, 48 | l_description, 49 | l_start_date, 50 | l_due_date, 51 | l_priority, 52 | NOW(), 53 | NOW() 54 | ); 55 | 56 | 57 | END LOOP; 58 | CLOSE l_tasks_c; 59 | 60 | l_out := '{"status" : "S" , "message" : "OK" }'; 61 | RETURN l_out; 62 | 63 | EXCEPTION WHEN OTHERS THEN 64 | GET STACKED DIAGNOSTICS l_message_text = MESSAGE_TEXT, 65 | l_exception_detail = PG_EXCEPTION_DETAIL, 66 | l_exception_hint = PG_EXCEPTION_HINT; 67 | l_out := '{ "status" : "E" , "message" : "' || REPLACE(l_message_text, '"', E'\\"') || '" }'; 68 | return l_out; 69 | END 70 | $BODY$ 71 | LANGUAGE plpgsql; -------------------------------------------------------------------------------- /admin/apps/tasks/db/functions/create_task.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION create_task ( p_data json) 2 | RETURNS json AS 3 | $BODY$ 4 | DECLARE 5 | l_out json; 6 | l_message_text text; 7 | l_exception_detail text; 8 | l_exception_hint text; 9 | -- 10 | l_id uuid; 11 | l_name text; 12 | l_description text; 13 | l_start_date timestamp; 14 | l_due_date timestamp; 15 | l_priority integer; 16 | 17 | BEGIN 18 | l_id := md5(random()::text || clock_timestamp()::text)::uuid; 19 | l_name := (p_data->>'name')::text; 20 | l_description := (p_data->>'description')::text; 21 | l_start_date := NOW(); 22 | l_due_date := (p_data->>'due_date')::timestamp; 23 | l_priority := (p_data->>'priority')::integer; 24 | 25 | INSERT INTO tasks 26 | ( 27 | id, 28 | name, 29 | description, 30 | start_date, 31 | due_date, 32 | priority, 33 | created, 34 | updated 35 | ) 36 | VALUES 37 | ( 38 | l_id, 39 | l_name, 40 | l_description, 41 | l_start_date, 42 | l_due_date, 43 | l_priority, 44 | NOW(), 45 | NOW() 46 | ); 47 | 48 | l_out := '{"status" : "S" , "message" : "OK" , "id" : "' || l_id || '"}'; 49 | RETURN l_out; 50 | EXCEPTION WHEN OTHERS THEN 51 | GET STACKED DIAGNOSTICS l_message_text = MESSAGE_TEXT, 52 | l_exception_detail = PG_EXCEPTION_DETAIL, 53 | l_exception_hint = PG_EXCEPTION_HINT; 54 | l_out := '{ "status" : "E" , "message" : "' || REPLACE(l_message_text, '"', E'\\"') || '" }'; 55 | return l_out; 56 | END 57 | $BODY$ 58 | LANGUAGE plpgsql; -------------------------------------------------------------------------------- /admin/apps/tasks/db/functions/delete_task.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION delete_task ( p_data json) 2 | RETURNS json AS 3 | $BODY$ 4 | DECLARE 5 | l_out json; 6 | l_message_text text; 7 | l_exception_detail text; 8 | l_exception_hint text; 9 | -- 10 | l_id uuid; 11 | l_cnt int; 12 | BEGIN 13 | l_id := (p_data->>'id')::uuid; 14 | 15 | DELETE FROM tasks 16 | WHERE id = l_id; 17 | 18 | GET DIAGNOSTICS l_cnt = row_count; 19 | 20 | l_out := '{"status" : "S" , "message" : "OK" , "rows_affected" : "' || l_cnt || '"}'; 21 | RETURN l_out; 22 | EXCEPTION WHEN OTHERS THEN 23 | GET STACKED DIAGNOSTICS l_message_text = MESSAGE_TEXT, 24 | l_exception_detail = PG_EXCEPTION_DETAIL, 25 | l_exception_hint = PG_EXCEPTION_HINT; 26 | l_out := '{ "status" : "E" , "message" : "' || REPLACE(l_message_text, '"', E'\\"') || '" }'; 27 | return l_out; 28 | END 29 | $BODY$ 30 | LANGUAGE plpgsql; -------------------------------------------------------------------------------- /admin/apps/tasks/db/functions/select_task.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION select_task ( p_data json) 2 | RETURNS json AS 3 | $BODY$ 4 | DECLARE 5 | l_out json; 6 | l_message_text text; 7 | l_exception_detail text; 8 | l_exception_hint text; 9 | -- 10 | l_data text; 11 | l_id uuid; 12 | l_params json; 13 | BEGIN 14 | 15 | l_params := (p_data->>'urlparams')::json; 16 | 17 | IF l_params IS NOT NULL THEN 18 | l_id := (l_params->>'id')::uuid; 19 | END IF; 20 | 21 | IF l_id IS NULL THEN 22 | SELECT array_to_json(array_agg(row_to_json(t.*))) INTO l_data 23 | FROM (SELECT * FROM tasks) t; 24 | ELSE 25 | SELECT array_to_json(array_agg(row_to_json(t.*))) INTO l_data 26 | FROM (SELECT * FROM tasks WHERE id = l_id) t; 27 | END IF; 28 | 29 | l_out := '{"status" : "S" , "message" : "OK" , "data" : ' || l_data || '}'; 30 | RETURN l_out; 31 | EXCEPTION WHEN OTHERS THEN 32 | GET STACKED DIAGNOSTICS l_message_text = MESSAGE_TEXT, 33 | l_exception_detail = PG_EXCEPTION_DETAIL, 34 | l_exception_hint = PG_EXCEPTION_HINT; 35 | l_out := '{ "status" : "E" , "message" : "' || REPLACE(l_message_text, '"', E'\\"') || '" }'; 36 | return l_out; 37 | END 38 | $BODY$ 39 | LANGUAGE plpgsql; -------------------------------------------------------------------------------- /admin/apps/tasks/db/functions/update_task.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION update_task ( p_data json) 2 | RETURNS json AS 3 | $BODY$ 4 | DECLARE 5 | l_out json; 6 | l_message_text text; 7 | l_exception_detail text; 8 | l_exception_hint text; 9 | -- 10 | l_id uuid; 11 | l_name text; 12 | l_description text; 13 | l_due_date timestamp; 14 | l_priority integer; 15 | l_cnt int; 16 | BEGIN 17 | l_id := (p_data->>'id')::uuid; 18 | l_name := (p_data->>'name')::text; 19 | l_description := (p_data->>'description')::text; 20 | l_due_date := (p_data->>'due_date')::timestamp; 21 | l_priority := (p_data->>'priority')::integer; 22 | 23 | UPDATE tasks 24 | SET name = COALESCE(l_name,name) 25 | , description = COALESCE(l_description, description) 26 | , due_date = COALESCE(l_due_date, due_date) 27 | , priority = COALESCE(l_priority, priority) 28 | , updated = NOW() 29 | WHERE id = l_id; 30 | 31 | GET DIAGNOSTICS l_cnt = row_count; 32 | 33 | l_out := '{"status" : "S" , "message" : "OK" , "rows_affected" : "' || l_cnt || '"}'; 34 | RETURN l_out; 35 | EXCEPTION WHEN OTHERS THEN 36 | GET STACKED DIAGNOSTICS l_message_text = MESSAGE_TEXT, 37 | l_exception_detail = PG_EXCEPTION_DETAIL, 38 | l_exception_hint = PG_EXCEPTION_HINT; 39 | l_out := '{ "status" : "E" , "message" : "' || REPLACE(l_message_text, '"', E'\\"') || '" }'; 40 | return l_out; 41 | END 42 | $BODY$ 43 | LANGUAGE plpgsql; -------------------------------------------------------------------------------- /admin/apps/tasks/db/script.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS tasks ( 2 | id uuid NOT NULL, 3 | name text NOT NULL, 4 | description text NOT NULL, 5 | start_date timestamp with time zone NOT NULL, 6 | due_date timestamp with time zone NOT NULL, 7 | priority integer NOT NULL, 8 | created timestamp with time zone NOT NULL, 9 | updated timestamp with time zone NOT NULL 10 | ); -------------------------------------------------------------------------------- /admin/apps/tasks/install.json: -------------------------------------------------------------------------------- 1 | { 2 | "application" : 3 | { 4 | "identifier" : "1a6d3a4f-446e-84d5-fd5d-cd83f5fdc57c" 5 | , "name" : "Task Demo" 6 | , "short_code" : "task" 7 | } , 8 | "functions" : 9 | [{ 10 | "name" : "create_task" 11 | , "db_method" : "create_task" 12 | , "routes" : [{ 13 | "name" : "Create Task" 14 | , "route_url" : "/api/task/create" 15 | , "route_method" : "POST" 16 | , "sample_request" : "{\"name\":\"Task3\" , \"description\":\"Task Description 2\", \"priority\": 2, \"start_date\":\"2018-12-08 02:41:17\", \"due_date\":\"2018-12-12 01:31:10\"}" 17 | , "sample_response" : "{\"status\":\"S\",\"message\":\"OK\",\"result\":[{\"result\":{\"status\":\"S\",\"message\":\"OK\",\"id\":\"8003c392-d89a-e577-be11-5f42808cf28b\"}}]}" 18 | }] 19 | }, { 20 | "name" : "create_bulk_tasks" 21 | , "db_method" : "create_bulk_tasks" 22 | , "routes" : [{ 23 | "name" : "Create Bulk Task" 24 | , "route_url" : "/api/tasks/bulk/create" 25 | , "route_method" : "POST" 26 | , "sample_request" : "{\"tasks\": [{\"name\":\"Task4\" , \"description\":\"Task Description 4\", \"priority\": 2, \"start_date\":\"2018-12-08 02:41:17\", \"due_date\":\"2018-12-12 01:31:10\"},{\"name\":\"Task5\" , \"description\":\"Task Description 5\", \"priority\": 2, \"start_date\":\"2018-12-08 02:41:17\", \"due_date\":\"2018-12-12 01:31:10\"}]}" 27 | , "sample_response" : "{\"status\":\"S\",\"message\":\"OK\",\"result\":[{\"result\":{\"status\":\"S\",\"message\":\"OK\"}}]}" 28 | }] 29 | } , { 30 | "name" : "update_task" 31 | , "db_method" : "update_task" 32 | , "routes" : [{ 33 | "name" : "Update Task" 34 | , "route_url" : "/api/task/update" 35 | , "route_method" : "POST" 36 | , "sample_request" : "{\"id\":\"8003c392-d89a-e577-be11-5f42808cf28b\",\"name\":\"Task2\"}" 37 | , "sample_response" : "{\"status\":\"S\",\"message\":\"OK\",\"result\":[{\"result\":{\"status\":\"S\",\"message\":\"OK\",\"rows_affected\":\"1\"}}]}" 38 | }] 39 | } ,{ 40 | "name" : "delete_task" 41 | , "db_method" : "delete_task" 42 | , "routes" : [{ 43 | "name" : "Delete Task" 44 | , "route_url" : "/api/task/delete" 45 | , "route_method" : "POST" 46 | , "sample_request" : "{\"id\":\"8003c392-d89a-e577-be11-5f42808cf28b\"}" 47 | , "sample_response" : "{\"status\":\"S\",\"message\":\"OK\",\"result\":[{\"result\":{\"status\":\"S\",\"message\":\"OK\",\"rows_affected\":\"1\"}}]}" 48 | }] 49 | }, { 50 | "name" : "select_task" 51 | , "db_method" : "select_task" 52 | , "routes" : [{ 53 | "name" : "Select Task" 54 | , "route_url" : "/api/tasks" 55 | , "route_method" : "GET" 56 | , "sample_request" : "" 57 | , "sample_response" : "{\"status\":\"S\",\"message\":\"OK\",\"result\":[{\"result\":{\"status\":\"S\",\"message\":\"OK\",\"data\":[{\"id\":\"d716a072-be43-2301-1d9e-80998bb0c95e\",\"name\":\"Task4\",\"description\":\"Task Description 4\",\"start_date\":\"2018-12-22T23:56:29.495069+05:30\",\"due_date\":\"2018-12-12T01:31:10+05:30\",\"priority\":2,\"created\":\"2018-12-22T23:56:29.495069+05:30\",\"updated\":\"2018-12-22T23:56:29.495069+05:30\"},{\"id\":\"4ee0bec0-f5df-e75e-9180-25dc216bd021\",\"name\":\"Task5\",\"description\":\"Task Description 5\",\"start_date\":\"2018-12-22T23:56:29.495069+05:30\",\"due_date\":\"2018-12-12T01:31:10+05:30\",\"priority\":2,\"created\":\"2018-12-22T23:56:29.495069+05:30\",\"updated\":\"2018-12-22T23:56:29.495069+05:30\"}]}}]}" 58 | }, 59 | { 60 | "name" : "Select Task By Id" 61 | , "route_url" : "/api/tasks/:id" 62 | , "route_method" : "GET" 63 | , "sample_request": "" 64 | , "sample_response" : "{\"status\":\"S\",\"message\":\"OK\",\"result\":[{\"result\":{\"status\":\"S\",\"message\":\"OK\",\"data\":[{\"id\":\"d716a072-be43-2301-1d9e-80998bb0c95e\",\"name\":\"Task4\",\"description\":\"Task Description 4\",\"start_date\":\"2018-12-22T23:56:29.495069+05:30\",\"due_date\":\"2018-12-12T01:31:10+05:30\",\"priority\":2,\"created\":\"2018-12-22T23:56:29.495069+05:30\",\"updated\":\"2018-12-22T23:56:29.495069+05:30\"}]}}]}" 65 | }] 66 | } 67 | ] 68 | } -------------------------------------------------------------------------------- /admin/config/admin: -------------------------------------------------------------------------------- 1 | username=admin 2 | password=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXNzd29yZCI6ImFkbWluIiwidXNlcm5hbWUiOiJhZG1pbiIsImlhdCI6MTUzNjc4NDQxOH0.TsHgUUsnSHFRXA8tihL-X7_mdqfT0gphTDOTJNJfCO0 3 | encrypt=Y 4 | -------------------------------------------------------------------------------- /admin/controllers/Connection.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of pgAPI. 3 | pgAPI - Database as a service 4 | Copyright (C) 2018 Praveen Muralidhar 5 | 6 | pgAPI is a free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | pgAPI is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const model = require('../models/Connection'); 21 | const coremodel = require('../core/models/Connection'); 22 | const utils = require('../Utils'); 23 | 24 | const create = async function(record) { 25 | let validation = await model.validate(record,'I',true); 26 | 27 | if (validation.status === 'E') { 28 | return { 29 | status: validation.status, 30 | message: validation.message 31 | }; 32 | } 33 | return await model.insert(validation.record); 34 | 35 | } 36 | 37 | const addDefaultConnection = async function() { 38 | // Add the default Database Connection 39 | const db = JSON.parse(process.env.pgapi); 40 | let record = { 41 | name: "default-connection", 42 | host: db.DB_HOST, 43 | port: db.DB_PORT, 44 | database: db.DB_NAME, 45 | username: db.DB_USER, 46 | password: db.DB_PASSWORD, 47 | config_flag: "Y" 48 | }; 49 | 50 | let response = await create(record); 51 | 52 | if (response.status === "S") { 53 | console.log("Default connection entry added.") 54 | } else { 55 | console.log(`Failed to create default connection ${response.message}`.yellow); 56 | } 57 | 58 | } 59 | 60 | 61 | 62 | const createAPI = async function(req, res) { 63 | let record = req.body; 64 | let response = await create(record); 65 | res.json(response); 66 | res.end(); 67 | } 68 | 69 | const update = async function(req, res) { 70 | let id = req.params.id; 71 | 72 | if (!id) { 73 | res.json({ 74 | status: 'E', 75 | message: 'Missing id information' 76 | }); 77 | return; 78 | } 79 | 80 | let record = req.body; 81 | record.id = id; 82 | let validation = await model.validate(record,'U',true); 83 | if (validation.status === 'E') { 84 | res.json({ 85 | status: validation.status, 86 | message: validation.message 87 | }); 88 | return; 89 | } 90 | const response = await model.update(validation.record); 91 | res.json(response); 92 | } 93 | 94 | const deleteItem = async function(req, res) { 95 | let id = req.params.id; 96 | 97 | if (!id) { 98 | res.json({ 99 | status: 'E', 100 | message: 'Missing id information' 101 | }); 102 | return; 103 | } 104 | 105 | let record = { id: id}; 106 | let validation = await model.validate(record,'D',true); 107 | if (validation.status === 'E') { 108 | res.json({ 109 | status: validation.status, 110 | message: validation.message 111 | }); 112 | return; 113 | } 114 | const response = await model.delete(record); 115 | res.json(response); 116 | } 117 | 118 | const fetch = async function(req, res) { 119 | const response = await model.fetchAll(); 120 | res.json(response); 121 | }; 122 | 123 | const fetchByName = async function(record) { 124 | const response = await coremodel.fetchByName(record); 125 | return response; 126 | }; 127 | 128 | const testDBConnection = async function(req, res) { 129 | 130 | if (!!req.query && !!req.query.host && !!req.query.port && !!req.query.database 131 | && !!req.query.username && !!req.query.password ) { 132 | const record = req.query; 133 | if (!!record.id && record.decrypt === 'Y') { 134 | const passwd = record["password"]; 135 | record["password"] = utils.decryptByKey(passwd,'connection'); 136 | } 137 | const response = await utils.validateConnection(record); 138 | res.json({status: response.status , message: response.message }); 139 | return; 140 | } 141 | else { 142 | res.json({status: 'E' , message: 'Missing required parameters : [host,port,database,username,password]'}); 143 | return; 144 | } 145 | }; 146 | 147 | const testDBConnections = async function(req, res) { 148 | const connections = await model.fetchAll(); 149 | let output = {}; 150 | for (let i in connections) { 151 | const record = connections[i]; 152 | const passwd = record["password"]; 153 | record["password"] = utils.decryptByKey(passwd,'connection'); 154 | const response = await utils.validateConnection(record); 155 | if (response.status === "E") { 156 | output[record.id] = false; 157 | } else { 158 | output[record.id] = true; 159 | } 160 | 161 | } 162 | res.json(output); 163 | }; 164 | 165 | module.exports = { 166 | createAPI: createAPI, 167 | create: create, 168 | update: update, 169 | fetch: fetch, 170 | delete: deleteItem, 171 | testDBConnection: testDBConnection, 172 | testDBConnections: testDBConnections, 173 | addDefaultConnection: addDefaultConnection, 174 | fetchByName: fetchByName 175 | } -------------------------------------------------------------------------------- /admin/controllers/Dashboard.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of pgAPI. 3 | pgAPI - Database as a service 4 | Copyright (C) 2018 Praveen Muralidhar 5 | 6 | pgAPI is a free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | pgAPI is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const utils = require('../Utils'); 21 | const fs = require('fs'); 22 | const path = require('path'); 23 | const connectionModel = require('../core/models/Connection'); 24 | const functionModel = require('../core/models/Function'); 25 | const routeModel = require('../core/models/Route'); 26 | 27 | const exportConfig = async function(req, res) { 28 | if (!!req.query && !!req.query.secret_key) { 29 | const response = await utils.exportConfig(req.query.secret_key); 30 | res.json({status: 'S' , message: 'OK' , results : response}); 31 | return; 32 | } 33 | else { 34 | res.json({status: 'E' , message: 'Missing input parameter : secret_key' , results : []}); 35 | return 36 | } 37 | 38 | }; 39 | 40 | const deleteConfig = async function(req, res) { 41 | const response = await deleteConfigurations(); 42 | res.json(response); 43 | return; 44 | 45 | }; 46 | 47 | const importConfig = async function(req, res) { 48 | if (!!req.body) { 49 | if (!req.body.secret_key) { 50 | res.json({status: 'E' , message: 'Missing input parameter : secret_key' , results : []}); 51 | return 52 | } 53 | if (!req.body.secret_key) { 54 | res.json({status: 'E' , message: 'Missing input parameter : configuration file input' , results : []}); 55 | return 56 | 57 | } 58 | 59 | 60 | const response = await importConfigContent(req.body.secret_key, req.body.content ); 61 | res.json(response); 62 | return; 63 | } else { 64 | res.json({status: 'E' , message: 'Missing input parameter : secret_key , configuration file input' , results : []}); 65 | return 66 | } 67 | 68 | }; 69 | 70 | 71 | function CustomException() {} 72 | 73 | const processApplications = async function(applications, pool) { 74 | let applicationErrors = []; 75 | let status = 'S'; 76 | let message = 'OK'; 77 | 78 | 79 | if (!Array.isArray(applications)) { 80 | applicationErrors.push( 'Invalid File. The Applications must be of type = Array'); 81 | status = 'E'; 82 | message = 'Invalid File'; 83 | return {status: status, message: message, errors: applicationErrors}; 84 | } 85 | 86 | for (let i in applications) { 87 | if (typeof applications[i] === 'object') { 88 | let record = applications[i]; 89 | let response = await utils.callSQL("INSERT INTO APPLICATIONS (id,name,created,updated) VALUES ($1,$2,$3,$4) ",[record["id"],record["name"], record["created"], record["updated"]], pool); 90 | 91 | if (response.status === 'E') { 92 | applicationErrors.push(response.message); 93 | status = 'E'; 94 | message = 'Invalid File'; 95 | return {status: status, message: message, errors: applicationErrors}; 96 | } 97 | } else { 98 | applicationErrors.push('Invalid File. Record[' + i + '] -> The Applications array elements must be of type = Object'); 99 | 100 | if (applicationErrors.length > 0) { 101 | status = 'E'; 102 | message = 'Invalid File'; 103 | return {status: status, message: message, errors: applicationErrors}; 104 | } 105 | } 106 | 107 | } 108 | return {status: status, message: message, errors: [] }; 109 | } 110 | 111 | const processConnections = async function(secretKey, connections, client) { 112 | 113 | let connectionErrors = []; 114 | let status = 'S'; 115 | let message = 'OK'; 116 | 117 | 118 | if (!Array.isArray(connections)) { 119 | connectionErrors.push( 'Invalid File. The connections must be of type = Array'); 120 | status = 'E'; 121 | message = 'Invalid File'; 122 | return {status: status, message: message, errors: connectionErrors}; 123 | } 124 | 125 | for (let i in connections) { 126 | 127 | if (typeof connections[i] === 'object') { 128 | let record = connections[i]; 129 | 130 | if (!!record.id) { 131 | record.ref_source_id = record.id; 132 | record.id = null; 133 | } 134 | if (!!record.created) { 135 | record.created = null; 136 | } 137 | if (!!record.updated) { 138 | record.updated = null; 139 | } 140 | 141 | let validation = await connectionModel.validate(record, 'I', false, client); 142 | 143 | if (validation.status === 'E') { 144 | connectionErrors.push('Invalid File. Record[' + (i + 1) + '] -> ' + validation.message); 145 | status = 'E'; 146 | message = 'Invalid File'; 147 | return {status: status, message: message, errors: connectionErrors}; 148 | } 149 | 150 | if (connectionErrors.length == 0) 151 | { 152 | if ( record.encrypt === 'Y') { 153 | let passwd = record.password; 154 | let decryptPassword = utils.decryptByKey(passwd,secretKey); 155 | validation.record["password"] = utils.encryptByKey(decryptPassword, 'connection'); 156 | } else { 157 | let passwd = record.password; 158 | validation.record["password"] = utils.encryptByKey(passwd, 'connection'); 159 | } 160 | 161 | // do the insert 162 | let response = await connectionModel.insert(validation.record,client); 163 | 164 | if (response.status === 'E') { 165 | connectionErrors.push(response.message); 166 | status = 'E'; 167 | message = 'Invalid File'; 168 | return {status: status, message: message, errors: connectionErrors}; 169 | } 170 | } 171 | 172 | } else { 173 | connectionErrors.push('Invalid File. Record[' + i + '] -> The connections array elements must be of type = Object'); 174 | 175 | if (connectionErrors.length > 0) { 176 | status = 'E'; 177 | message = 'Invalid File'; 178 | return {status: status, message: message, errors: connectionErrors}; 179 | } 180 | } 181 | } 182 | 183 | return {status: status, message: message, errors: [] }; 184 | 185 | } 186 | 187 | const processFunctions = async function(functions, client) { 188 | 189 | let functionErrors = []; 190 | let status = 'S'; 191 | let message = 'OK'; 192 | 193 | 194 | if (!Array.isArray(functions)) { 195 | functionErrors.push( 'Invalid File. The Functions must be of type = Array'); 196 | status = 'E'; 197 | message = 'Invalid File'; 198 | return {status: status, message: message, errors: functionErrors}; 199 | } 200 | 201 | for (let i in functions) { 202 | 203 | if (typeof functions[i] === 'object') { 204 | let record = functions[i]; 205 | 206 | if (!!record.id) { 207 | record.ref_source_id = record.id; 208 | record.id = null; 209 | } 210 | if (!!record.created) { 211 | record.created = null; 212 | } 213 | if (!!record.updated) { 214 | record.updated = null; 215 | } 216 | 217 | // cross reference the connection ID 218 | const connectionArray = await utils.getSQLResults("SELECT id FROM connections WHERE ref_source_id = $1",[record.connection_id],client); 219 | const connectionID = connectionArray[0]["id"]; 220 | 221 | record.connection_id = connectionID; 222 | let validation = await functionModel.validate(record, 'I', false, client); 223 | 224 | if (validation.status === 'E') { 225 | functionErrors.push('Invalid File. Record[' + (i + 1) + '] -> ' + validation.message); 226 | status = 'E'; 227 | message = 'Invalid File'; 228 | return {status: status, message: message, errors: functionErrors}; 229 | } 230 | 231 | if (functionErrors.length == 0) 232 | { 233 | 234 | // do the insert 235 | let response = await functionModel.insert(validation.record,client); 236 | 237 | if (response.status === 'E') { 238 | functionErrors.push(response.message); 239 | status = 'E'; 240 | message = 'Invalid File'; 241 | return {status: status, message: message, errors: functionErrors}; 242 | } 243 | } 244 | 245 | } else { 246 | functionErrors.push('Invalid File. Record[' + i + '] -> The functions array elements must be of type = Object'); 247 | 248 | if (functionErrors.length > 0) { 249 | status = 'E'; 250 | message = 'Invalid File'; 251 | return {status: status, message: message, errors: functionErrors}; 252 | } 253 | } 254 | } 255 | 256 | return {status: status, message: message, errors: functionErrors}; 257 | } 258 | 259 | const processRoutes = async function(routes, client) { 260 | 261 | let routeErrors = []; 262 | let status = 'S'; 263 | let message = 'OK'; 264 | 265 | 266 | if (!Array.isArray(routes)) { 267 | routeErrors.push( 'Invalid File. The connections must be of type = Array'); 268 | status = 'E'; 269 | message = 'Invalid File'; 270 | return {status: status, message: message, errors: routeErrors}; 271 | } 272 | 273 | for (let i in routes) { 274 | 275 | if (typeof routes[i] === 'object') { 276 | let record = routes[i]; 277 | 278 | if (!!record.id) { 279 | record.ref_source_id = record.id; 280 | record.id = null; 281 | } 282 | if (!!record.created) { 283 | record.created = null; 284 | } 285 | if (!!record.updated) { 286 | record.updated = null; 287 | } 288 | 289 | // cross reference the connection ID 290 | const functionArray = await utils.getSQLResults("SELECT id FROM functions WHERE ref_source_id = $1",[record.function_id],client); 291 | const functionID = functionArray[0]["id"]; 292 | 293 | record.function_id = functionID; 294 | 295 | let validation = await routeModel.validate(record, 'I', false, client); 296 | 297 | if (validation.status === 'E') { 298 | routeErrors.push('Invalid File. Record[' + (i + 1) + '] -> ' + validation.message); 299 | status = 'E'; 300 | message = 'Invalid File'; 301 | return {status: status, message: message, errors: routeErrors}; 302 | } 303 | 304 | if (routeErrors.length == 0) 305 | { 306 | // do the insert 307 | let response = await routeModel.insert(validation.record,client); 308 | 309 | if (response.status === 'E') { 310 | routeErrors.push(response.message); 311 | status = 'E'; 312 | message = 'Invalid File'; 313 | return {status: status, message: message, errors: routeErrors}; 314 | } 315 | } 316 | 317 | } else { 318 | routeErrors.push('Invalid File. Record[' + i + '] -> The routes array elements must be of type = Object'); 319 | 320 | if (routeErrors.length > 0) { 321 | status = 'E'; 322 | message = 'Invalid File'; 323 | return {status: status, message: message, errors: routeErrors}; 324 | } 325 | } 326 | } 327 | 328 | return {status: status, message: message, errors: routeErrors}; 329 | } 330 | 331 | const getEncryptedPassword = function(connections) { 332 | for (let i in connections) { 333 | if (connections[i].encrypt === "Y") { 334 | return connections[i].password; 335 | } 336 | } 337 | 338 | return null 339 | } 340 | 341 | const deleteConfigurations = async function() { 342 | 343 | let status = 'S'; 344 | let message = 'OK'; 345 | 346 | const pool = await utils.getClient(); 347 | 348 | const client = await pool.connect(); 349 | 350 | try { 351 | 352 | await client.query('BEGIN'); 353 | 354 | let status = await utils.callSQL("DELETE FROM ROUTES WHERE 1 = $1",[1],client); 355 | 356 | if (status.status === "E") { 357 | status = "E"; 358 | message = "Error Deleting Configurations (routes)"; 359 | throw new CustomException(); 360 | } 361 | 362 | status = await utils.callSQL("DELETE FROM FUNCTIONS WHERE 1 = $1",[1],client); 363 | 364 | if (status.status === "E") { 365 | status = "E"; 366 | message = "Error Deleting Configurations (functions)"; 367 | throw new CustomException(); 368 | } 369 | 370 | status = await utils.callSQL("DELETE FROM CONNECTIONS WHERE 1 = $1",[1],client); 371 | 372 | if (status.status === "E") { 373 | status = "E"; 374 | message = "Error Deleting Configurations (connections)"; 375 | throw new CustomException(); 376 | } 377 | 378 | status = await utils.callSQL("DELETE FROM APPLICATIONS WHERE 1 = $1",[1],client); 379 | 380 | if (status.status === "E") { 381 | status = "E"; 382 | message = "Error Deleting Configurations (applications)"; 383 | throw new CustomException(); 384 | } 385 | 386 | await client.query('COMMIT'); 387 | 388 | 389 | } catch (e) { 390 | console.log(e); 391 | console.log("Rollback Called"); 392 | await client.query('ROLLBACK') 393 | // throw e 394 | } finally { 395 | client.release() 396 | } 397 | 398 | return {status: status , message: message }; 399 | 400 | } 401 | 402 | const importConfigContent = async function(secretKey, content) { 403 | let importObj = null; 404 | let status = 'S'; 405 | let message = 'OK'; 406 | 407 | try { 408 | importObj = JSON.parse(content); 409 | } catch(e) { 410 | console.log(e); 411 | return {status: 'E' , message: 'Invalid File Format. The file must contain JSON Content', result: {} }; 412 | 413 | } 414 | 415 | if (!importObj) { 416 | return {status: 'E' , message: 'File is empty', result: {} }; 417 | 418 | } 419 | 420 | if (!importObj["connections"] && !importObj["functions"] && !importObj["routes"]) { 421 | return {status: 'S' , message: 'OK', result: {connections : {count: 0 , errors: [] } , functions : {count: 0 , errors: [] } , routes : {count: 0 , errors: [] } 422 | } }; 423 | } 424 | 425 | const pool = await utils.getClient(); 426 | 427 | const client = await pool.connect(); 428 | let result = {connections: [] , functions: [] , routes:[] }; 429 | 430 | try { 431 | 432 | await client.query('BEGIN'); 433 | 434 | if (!!importObj["connections"]) { 435 | 436 | let encryptedPassword = getEncryptedPassword(importObj["connections"]); 437 | if (!!encryptedPassword && utils.decryptByKey(encryptedPassword,secretKey) === '') { 438 | return {status: "E" , message: "Invalid Secret Key", result: result}; 439 | } 440 | 441 | const connectionStatus = await processConnections(secretKey, importObj["connections"], client); 442 | 443 | if (!connectionStatus) { 444 | const connections = {count : 0 , errors : [] }; 445 | status = 'E'; 446 | message = 'Invalid File'; 447 | result.connections = connections; 448 | throw new CustomException(); 449 | 450 | } 451 | if (connectionStatus.status === "E") { 452 | const connections = {count : 0 , errors :connectionStatus.errors }; 453 | status = 'E'; 454 | message = 'Invalid File'; 455 | result.connections = connections; 456 | throw new CustomException(); 457 | } else { 458 | const connections = {count : importObj["connections"].length , errors :connectionStatus.errors }; 459 | result.connections = connections; 460 | } 461 | } 462 | 463 | if (!!importObj["functions"]) { 464 | const functionStatus = await processFunctions(importObj["functions"], client); 465 | if (functionStatus.status === "E") { 466 | const functions = {count : 0 , errors : functionStatus.errors }; 467 | status = 'E'; 468 | message = 'Invalid File'; 469 | result.functions = functions; 470 | throw new CustomException(); 471 | } else { 472 | const functions = {count : importObj["functions"].length , errors : functionStatus.errors }; 473 | result.functions = functions; 474 | } 475 | } 476 | 477 | if (!!importObj["routes"]) { 478 | const routeStatus = await processRoutes(importObj["routes"], client); 479 | if (routeStatus.status === "E") { 480 | const routes = {count : 0 , errors : routeStatus.errors }; 481 | status = 'E'; 482 | message = 'Invalid File'; 483 | result.routes = routes; 484 | throw new CustomException(); 485 | } else { 486 | const routes = {count : importObj["routes"].length , errors : routeStatus.errors }; 487 | result.routes = routes; 488 | } 489 | } 490 | 491 | if (!!importObj["applications"]) { 492 | const applicationStatus = await processApplications(importObj["applications"], client); 493 | if (applicationStatus.status === "E") { 494 | const applications = {count : 0 , errors : applicationStatus.errors }; 495 | status = 'E'; 496 | message = 'Invalid File'; 497 | result.applications = applications; 498 | throw new CustomException(); 499 | } else { 500 | const applications = {count : importObj["applications"].length , errors : applicationStatus.errors }; 501 | result.applications = applications; 502 | } 503 | } 504 | 505 | 506 | await client.query('COMMIT'); 507 | 508 | 509 | } catch (e) { 510 | console.log(e); 511 | console.log("Rollback Called"); 512 | await client.query('ROLLBACK') 513 | // throw e 514 | } finally { 515 | client.release() 516 | } 517 | 518 | return {status: status , message: message, result: result }; 519 | }; 520 | 521 | const importFileConfig = async function(secretKey, filename) { 522 | const fname = path.resolve(__dirname, filename ); 523 | let content = fs.readFileSync(fname, 'utf8'); 524 | return await importConfigContent(secretKey,content); 525 | } 526 | 527 | module.exports = { 528 | exportConfig: exportConfig, 529 | importConfig: importConfig, 530 | importConfigContent: importConfigContent, 531 | importFileConfig: importFileConfig, 532 | deleteConfigurations: deleteConfigurations, 533 | deleteConfig: deleteConfig 534 | 535 | } -------------------------------------------------------------------------------- /admin/controllers/Function.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of pgAPI. 3 | pgAPI - Database as a service 4 | Copyright (C) 2018 Praveen Muralidhar 5 | 6 | pgAPI is a free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | pgAPI is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const model = require('../models/Function'); 21 | const coremodel = require('../core/models/Function'); 22 | const utils = require('../Utils'); 23 | 24 | const create = async function(record) { 25 | let validation = await model.validate(record,'I',true); 26 | 27 | if (validation.status === 'E') { 28 | return { 29 | status: validation.status, 30 | message: validation.message 31 | }; 32 | } 33 | let response = await model.insert(validation.record); 34 | return response; 35 | } 36 | 37 | const createAPI = async function(req, res) { 38 | let record = req.body; 39 | let response = await create(record); 40 | res.json(response); 41 | res.end(); 42 | } 43 | 44 | const update = async function(req, res) { 45 | let id = req.params.id; 46 | 47 | if (!id) { 48 | res.json({ 49 | status: 'E', 50 | message: 'Missing id information' 51 | }); 52 | return; 53 | } 54 | 55 | let record = req.body; 56 | record.id = id; 57 | let validation = await model.validate(record,'U',true); 58 | if (validation.status === 'E') { 59 | res.json({ 60 | status: validation.status, 61 | message: validation.message 62 | }); 63 | return; 64 | } 65 | const response = await model.update(validation.record); 66 | res.json(response); 67 | } 68 | 69 | const deleteItem = async function(req, res) { 70 | let id = req.params.id; 71 | 72 | if (!id) { 73 | res.json({ 74 | status: 'E', 75 | message: 'Missing id information' 76 | }); 77 | return; 78 | } 79 | 80 | let record = { id: id}; 81 | let validation = await model.validate(record,'D',true); 82 | if (validation.status === 'E') { 83 | res.json({ 84 | status: validation.status, 85 | message: validation.message 86 | }); 87 | return; 88 | } 89 | const response = await model.delete(record); 90 | res.json(response); 91 | } 92 | 93 | const fetch = async function(req, res) { 94 | const response = await model.fetchAll(); 95 | res.json(response); 96 | }; 97 | 98 | const fetchByName = async function(record) { 99 | const response = await coremodel.fetchByName(record); 100 | return response; 101 | }; 102 | 103 | const testDBFunctions = async function(req, res) { 104 | const functions = await model.fetchAll(); 105 | 106 | let output = {}; 107 | for (let i in functions) { 108 | const record = functions[i]; 109 | const resultsObject = await utils.validateFunction(record["connection_id"],record["db_method"]); 110 | 111 | if (resultsObject.status === "E") { 112 | output[record.id] = { status: false, message: resultsObject.message}; 113 | 114 | } else { 115 | output[record.id] = { status: true, message: "Function is Valid"}; 116 | } 117 | } 118 | 119 | res.json(output); 120 | }; 121 | 122 | module.exports = { 123 | create: create, 124 | createAPI: createAPI, 125 | update: update, 126 | fetch: fetch, 127 | delete: deleteItem, 128 | testDBFunctions: testDBFunctions, 129 | fetchByName: fetchByName 130 | } -------------------------------------------------------------------------------- /admin/controllers/Route.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of pgAPI. 3 | pgAPI - Database as a service 4 | Copyright (C) 2018 Praveen Muralidhar 5 | 6 | pgAPI is a free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | pgAPI is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const model = require('../models/Route'); 21 | const utils = require('../Utils'); 22 | const colors = require('colors'); 23 | 24 | const create = async function(record) { 25 | 26 | let validation = await model.validate(record,'I',true); 27 | 28 | if (validation.status === 'E') { 29 | return { 30 | status: validation.status, 31 | message: validation.message 32 | }; 33 | 34 | } 35 | let response = await model.insert(validation.record); 36 | 37 | if (response.status === "S") { 38 | const dbRecord = await model.getRoutesByUrl(record.route_url); 39 | addRouteDetail(dbRecord.result[0]); 40 | } 41 | 42 | return response; 43 | } 44 | 45 | const createAPI = async function(req, res) { 46 | let record = req.body; 47 | let response = await create(record); 48 | 49 | res.json(response); 50 | res.end(); 51 | } 52 | 53 | const update = async function(req, res) { 54 | let id = req.params.id; 55 | let deleteRouteFlag = false; 56 | let addRouteFlag = false; 57 | 58 | if (!id) { 59 | res.json({ 60 | status: 'E', 61 | message: 'Missing id information' 62 | }); 63 | return; 64 | } 65 | 66 | let record = req.body; 67 | record.id = id; 68 | const dbRecord = await model.fetchById(id); 69 | 70 | let validation = await model.validate(record,'U',true); 71 | if (validation.status === 'E') { 72 | res.json({ 73 | status: validation.status, 74 | message: validation.message 75 | }); 76 | return; 77 | } 78 | const response = await model.update(validation.record); 79 | 80 | if (response.status === "S") { 81 | 82 | if (dbRecord[0].route_url !== record.route_url ) { 83 | deleteRoute(dbRecord[0].route_url); 84 | } 85 | 86 | if (dbRecord[0].enabled_flag !== record.enabled_flag && dbRecord[0].enabled_flag === "Y") { 87 | deleteRoute(dbRecord[0].route_url); 88 | } else { 89 | const dbRecord = await model.getRoutesByUrl(record.route_url); 90 | addRouteDetail(dbRecord.result[0]); 91 | } 92 | } 93 | 94 | res.json(response); 95 | } 96 | 97 | const deleteItem = async function(req, res) { 98 | let id = req.params.id; 99 | 100 | if (!id) { 101 | res.json({ 102 | status: 'E', 103 | message: 'Missing id information' 104 | }); 105 | return; 106 | } 107 | 108 | const record = { id: id}; 109 | const dbRecord = await model.fetchById(id); 110 | 111 | let validation = await model.validate(record,'D',true); 112 | if (validation.status === 'E') { 113 | res.json({ 114 | status: validation.status, 115 | message: validation.message 116 | }); 117 | return; 118 | } 119 | const response = await model.delete(record); 120 | 121 | if (response.status === 'S') { 122 | deleteRoute(dbRecord[0].route_url); 123 | } 124 | 125 | 126 | res.json(response); 127 | } 128 | 129 | const fetch = async function(req, res) { 130 | const response = await model.fetchAll(); 131 | res.json(response); 132 | }; 133 | 134 | const fetchById = async function(record) { 135 | const response = await model.fetchById(record); 136 | res.json(response); 137 | }; 138 | 139 | 140 | const callDB = async function(req, res, routeCallback,contentType,pool) { 141 | 142 | var data = req.body, results = [], 143 | queryString = ""; 144 | 145 | if (req.method === "GET") data = req.query; 146 | 147 | if (contentType === "file") data = { token : req.headers['authorization'] , file_name : req.file.originalname}; 148 | 149 | if (!utils.isEmpty(req.params)) { 150 | data["urlparams"] = req.params; 151 | } 152 | 153 | queryString = "select " + routeCallback + "( $1 ) as result"; 154 | 155 | let response = await utils.getSQLResultswithStatus(queryString,[ JSON.stringify(data)], pool); 156 | 157 | if (!!response.result && !!response.result[0] && !!response.result[0].result && !!response.result[0].result.private) { 158 | let privateInfo = response.result[0].result.private 159 | utils.handlePrivate(privateInfo, response.result[0].result, req, res).then(function (data) { 160 | delete response.result[0].result.private; 161 | res.json(response); 162 | }); 163 | 164 | } else { 165 | return res.json(response); 166 | } 167 | 168 | 169 | 170 | } 171 | 172 | // End of Call 173 | 174 | const addRouteDetail = function(routeInfo) { 175 | 176 | var flag = 0; 177 | var r = global.router; 178 | 179 | for (var i = r.stack.length - 1; i >= 0; i--) { 180 | if (r.stack[i].path === routeInfo.route_url) { 181 | global.router.stack.splice(i, 1); 182 | addRoute(routeInfo); 183 | flag = 1; 184 | } 185 | else if (r.stack[i].path === undefined) 186 | { 187 | if (r.stack[i].route) 188 | { 189 | if ( r.stack[i].route.path === routeInfo.route_url) 190 | { 191 | global.router.stack.splice(i, 1); 192 | addRoute(routeInfo); 193 | flag = 1; 194 | } 195 | } 196 | else 197 | { 198 | // console.log("Route Object empty"); 199 | } 200 | } 201 | } 202 | 203 | if (!flag) 204 | { 205 | addRoute(routeInfo); 206 | } 207 | 208 | } 209 | 210 | const addRoute = async function (routeObj) { 211 | if ( routeObj.route_method === "POST") 212 | { 213 | 214 | console.log("loading POST " + routeObj.route_url ); 215 | global.router.post(routeObj.route_url, async function(req, res) { 216 | let record = { 217 | host: routeObj.host 218 | , port: routeObj.port 219 | , username: routeObj.username 220 | , password: utils.decryptByKey(routeObj.password,'connection') 221 | , database: routeObj.database 222 | , name: routeObj.connection_name 223 | }; 224 | 225 | let pool = await utils.getPoolByName(routeObj.connection_name,record); 226 | callDB(req,res,routeObj.route_callback,routeObj.content_type, pool); 227 | 228 | }); 229 | } 230 | else 231 | { 232 | console.log("loading GET " + routeObj.route_url ); 233 | global.router.get(routeObj.route_url, async function(req, res) { 234 | let record = { 235 | host: routeObj.host 236 | , port: routeObj.port 237 | , username: routeObj.username 238 | , password: utils.decryptByKey(routeObj.password,'connection') 239 | , database: routeObj.database 240 | , name: routeObj.connection_name 241 | }; 242 | 243 | let pool = await utils.getPoolByName(routeObj.connection_name,record); 244 | callDB(req,res,routeObj.route_callback,routeObj.content_type,pool); 245 | 246 | }); 247 | } 248 | } 249 | 250 | const deleteRoute = function (url) { 251 | console.log("Deleting route " + url); 252 | 253 | for (var i = global.router.stack.length - 1; i >= 0; i--) { 254 | if (global.router.stack[i].path === url) { 255 | global.router.stack.splice(i, 1); 256 | } 257 | else if (global.router.stack[i].path === undefined) 258 | { 259 | if (global.router.stack[i].route) 260 | { 261 | if ( global.router.stack[i].route.path === url) 262 | { 263 | global.router.stack.splice(i, 1); 264 | } 265 | } 266 | else 267 | { 268 | // console.log("Route Object empty"); 269 | } 270 | } 271 | } 272 | } 273 | 274 | const getRoutes = async function () { 275 | 276 | let routesInfo = []; 277 | const response = await model.getRoutes(); 278 | 279 | if (response.status === "E") { 280 | return response; 281 | } 282 | 283 | routesInfo = response.result; 284 | 285 | global.router.get('/', (req, res) => { // 2 286 | res.send('Homepage!') 287 | }); 288 | 289 | console.log("Generating routes... ".blue) 290 | routesInfo.forEach(function(routeObj){ 291 | 292 | if (routeObj.enabled_flag === "Y") { 293 | addRouteDetail(routeObj); 294 | } 295 | 296 | }); 297 | 298 | return {status: "S", message : "OK"}; 299 | }; 300 | 301 | const refreshRoutes = async function(req, res) { 302 | global.router.stack.splice(0,global.router.stack.length); 303 | console.log("Refreshing routes"); 304 | 305 | const status = await getRoutes(); 306 | res.json(status); 307 | }; 308 | 309 | module.exports = { 310 | create: create, 311 | createAPI: createAPI, 312 | update: update, 313 | fetch: fetch, 314 | fetchById: fetchById, 315 | delete: deleteItem, 316 | getRoutes: getRoutes, 317 | refreshRoutes: refreshRoutes, 318 | deleteRoute: deleteRoute 319 | } -------------------------------------------------------------------------------- /admin/controllers/User.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of pgAPI. 3 | pgAPI - Database as a service 4 | Copyright (C) 2018 Praveen Muralidhar 5 | 6 | pgAPI is a free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | pgAPI is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const model = require('../models/User'); 21 | const utils = require('../Utils'); 22 | 23 | const resetPassword = async function(username, userPassword , confirmPassword) { 24 | 25 | if (!!userPassword) { 26 | if (!!confirmPassword) { 27 | if (userPassword === confirmPassword) { 28 | let dbRecord = await model.fetchByUsername(username); 29 | // Update Database 30 | // let response = await model.updatePassword({id: dbRecord.id , }) 31 | if (!!dbRecord && !!dbRecord.result && dbRecord.result.length > 0) { 32 | let record = dbRecord.result[0]; 33 | let encryptedPassword = utils.encryptPassword(userPassword); 34 | let response = await model.updatePassword({password: encryptedPassword , id: record.id }); 35 | return response; 36 | } else { 37 | return {status: "ERROR" , message: "Unable to reset the Password"}; 38 | } 39 | 40 | } else { 41 | return {status: "ERROR" , message: "The Password and Confirm Password must match"}; 42 | } 43 | } else { 44 | return { status: "ERROR" , message: "Invalid Confirm Password Information. THe password Information is blank" }; 45 | } 46 | } else { 47 | return { status: "ERROR" , message: "Invalid Password Information. THe password Information is blank" }; 48 | } 49 | 50 | } 51 | 52 | const validateLogin = async function(username, password) { 53 | let dbRecord = await model.fetchByUsername(username); 54 | 55 | if (!!dbRecord && !!dbRecord.status && dbRecord.status === "E") { 56 | return dbRecord; 57 | } 58 | 59 | if (dbRecord.result.length === 0 ) { 60 | return {status: "E" , message: "Unable to find user"}; 61 | } 62 | 63 | let record = dbRecord.result[0]; 64 | 65 | if (utils.isValidPassword(password,record.password)) { 66 | // valid login 67 | return {status:"S",message: "OK", record: record}; 68 | } else { 69 | return {status:"E" , message: "Invalid Credentials. Unable to login"}; 70 | } 71 | 72 | } 73 | 74 | const resetPasswordAPI = async function(req, res) { 75 | let record = req.body; 76 | 77 | let claims = req.claims; 78 | console.log(claims); 79 | 80 | if (!!record.password && !!record.newpassword && !!record.confirmpassword) { 81 | let response = await validateLogin(claims.username,record.password); 82 | let userPassword = record.newpassword; 83 | let confirmPassword = record.confirmpassword; 84 | if (response.status === "S") { 85 | if (userPassword === confirmPassword) { 86 | let dbRecord = await model.fetchByUsername(claims.username); 87 | // Update Database 88 | // let response = await model.updatePassword({id: dbRecord.id , }) 89 | if (!!dbRecord && !!dbRecord.result && dbRecord.result.length > 0) { 90 | let record1 = dbRecord.result[0]; 91 | let encryptedPassword = utils.encryptPassword(userPassword); 92 | let response = await model.updatePassword({password: encryptedPassword, id: record1.id }); 93 | res.json( response); 94 | res.end(); 95 | return; 96 | } else { 97 | res.json({status: "E" , message: "Unable to reset the Password"}); 98 | res.end(); 99 | return; 100 | } 101 | 102 | } else { 103 | res.json({status: "E" , message: "The Password and Confirm Password must match"}); 104 | res.end(); 105 | return; 106 | } 107 | } else { 108 | console.log("Error") 109 | res.json(response); 110 | res.end(); 111 | return; 112 | } 113 | } else { 114 | res.json({status: "E" , message: "Missing required Information [current password, new password, confirm password]"}); 115 | res.end(); 116 | return; 117 | } 118 | 119 | 120 | } 121 | 122 | const login = async function(username, password) { 123 | 124 | try { 125 | let response = await validateLogin(username,password); 126 | 127 | if (response.status === "S") { 128 | let d = new Date(); 129 | d.setMinutes(d.getMinutes() + 4880); 130 | let record = response.record; 131 | let claims = { id: record.id, username: record.username , first_name: record.first_name , last_name: record.last_name}; 132 | let tokenObj = { claims: claims , exp_time: d }; 133 | const token = utils.encryptObjectByKey(tokenObj, process.env.PGAPI_SECRET_KEY); 134 | let obj = { token: token , claims: claims }; 135 | 136 | let insertTokenResponse = await model.insertToken({token: token, user_id: record.id}); 137 | 138 | if (insertTokenResponse.status === "E") { 139 | return {status: "E" , message: insertTokenResponse.message }; 140 | } 141 | else { 142 | return {status: "S" , message: "OK" , data: obj}; 143 | } 144 | 145 | 146 | } else { 147 | return {status: "E" , message: response.message }; 148 | } 149 | } catch (e) { 150 | return {status: "E" , message: e.message }; 151 | } 152 | 153 | } 154 | 155 | const loginAPI = async function(req, res) { 156 | let record = req.body; 157 | 158 | let response = await login(record.username, record.password); 159 | 160 | if (response.status === "E") { 161 | res.json(response); 162 | return; 163 | } else { 164 | res.json(response); 165 | res.end(); 166 | } 167 | } 168 | 169 | const fetchAPI = async function(req, res) { 170 | let record = req.query; 171 | 172 | let response = await model.fetchByIdUser(record.id); 173 | 174 | res.json(response); 175 | res.end(); 176 | } 177 | 178 | const sessionAPI = async function(req, res) { 179 | let record = req.body; 180 | 181 | let response = await validateSession(record.refresh_token); 182 | 183 | if (response.status === "E") { 184 | res.json(response); 185 | return; 186 | } else { 187 | res.json(response); 188 | res.end(); 189 | } 190 | } 191 | 192 | 193 | const loginCommand = async function(username, password) { 194 | let response = await login(username, password); 195 | 196 | if (response.status === "E") { 197 | console.log(response.message); 198 | } else { 199 | console.log("Login Success."); 200 | console.log(response.data); 201 | } 202 | } 203 | 204 | const validateSession = async function(token) { 205 | let decoded = utils.decryptObjectByKey(token,process.env.PGAPI_SECRET_KEY); 206 | 207 | if (utils.isEmpty(decoded)) { 208 | return {status: "E" , message: "Invalid Session Information", code: "INVALID"}; 209 | } else { 210 | let expTime = new Date(decoded.exp_time); 211 | let currentTime = new Date(); 212 | 213 | if (currentTime > expTime) { 214 | return {status: "E" , message: "Session Timeout" , code: "RELOGIN"}; 215 | } 216 | 217 | let id = decoded.claims.id; 218 | 219 | const dbRecord = await model.validateSession({token: token}); 220 | 221 | if (!!dbRecord && dbRecord.length > 0) { 222 | let result = dbRecord[0].result; 223 | 224 | if (result.status === "E") { 225 | return {status: "E" , message: "Invalid Session Information.", code: result.message}; 226 | } else 227 | { 228 | return {status: "S" , message: "OK" , decoded: decoded}; 229 | } 230 | } else { 231 | return {status: "E" , message: "Invalid Session Information.", code: "INVALID_USER"}; 232 | } 233 | 234 | return {status: "S" , message: "OK" , decoded: decoded}; 235 | } 236 | } 237 | 238 | const validateSessionCommand = async function(token) { 239 | let response = await validateSession(token,process.env.PGAPI_SECRET_KEY); 240 | 241 | if (response.status === "E") { 242 | console.log(response.message); 243 | } else { 244 | console.log("Session is Valid."); 245 | console.log(response.decoded); 246 | } 247 | } 248 | 249 | 250 | 251 | const addUser = async function(record) { 252 | let validation = await model.validate(record,'I',true); 253 | 254 | if (validation.status === 'E') { 255 | return { 256 | status: validation.status, 257 | message: validation.message 258 | }; 259 | } 260 | let response = await model.insert(validation.record); 261 | 262 | return response; 263 | } 264 | 265 | const addUserCommand = async function(record) { 266 | console.log("Adding user"); 267 | let response = await addUser(record); 268 | 269 | if (response.status === "E") { 270 | console.log(response.message); 271 | } else { 272 | console.log("The User has been successfully added.") 273 | } 274 | } 275 | 276 | const updateUser = async function(record) { 277 | let id = record.id; 278 | 279 | if (!id) { 280 | return { 281 | status: 'E', 282 | message: 'Missing id information' 283 | }; 284 | } 285 | 286 | let validation = await model.validate(record,'U',true); 287 | if (validation.status === 'E') { 288 | return { 289 | status: validation.status, 290 | message: validation.message 291 | }; 292 | } 293 | 294 | const response = await model.update(validation.record); 295 | return response; 296 | } 297 | 298 | 299 | const updateUserAPI = async function(req, res) { 300 | let record = req.body; 301 | 302 | let response = await updateUser(record); 303 | 304 | if (response.status === "E") { 305 | res.json(response); 306 | return; 307 | } else { 308 | res.json(response); 309 | res.end(); 310 | } 311 | } 312 | 313 | const deleteUser = async function(record) { 314 | let id = record.id; 315 | 316 | if (!id) { 317 | return { 318 | status: 'E', 319 | message: 'Missing id information' 320 | }; 321 | } 322 | 323 | let validation = await model.validate(record,'D',true); 324 | if (validation.status === 'E') { 325 | return { 326 | status: validation.status, 327 | message: validation.message 328 | }; 329 | } 330 | 331 | const dbRecord = await model.fetchById(id); 332 | 333 | if (dbRecord[0].username === "admin") { 334 | return {status: "E" , message: "You are not allowed to delete admin account"}; 335 | } 336 | 337 | const response = await model.delete(record); 338 | 339 | if (response.status === "S") { 340 | let resp = await model.insertArchive({username: dbRecord[0].username, first_name: dbRecord[0].first_name , last_name: dbRecord[0].last_name 341 | , id: dbRecord[0].id}); 342 | return resp; 343 | } 344 | 345 | return response; 346 | } 347 | 348 | const deleteUserCommand = async function(record) { 349 | 350 | let dbRecord = await model.fetchByUsername(record.username); 351 | 352 | if (!!dbRecord && !!dbRecord.status && dbRecord.status === "E") { 353 | console.log(dbRecord.message); 354 | return; 355 | } 356 | 357 | if (dbRecord.result.length === 0 ) { 358 | console.log("Unable to find the user"); 359 | return; 360 | } 361 | 362 | record.id = dbRecord.result[0].id; 363 | 364 | let response = await deleteUser(record); 365 | 366 | if (response.status === "E") { 367 | console.log(response.message); 368 | } else { 369 | console.log("The User has been successfully deleted.") 370 | } 371 | } 372 | 373 | 374 | 375 | const updateUserCommand = async function(record) { 376 | 377 | let dbRecord = await model.fetchByUsername(record.username); 378 | 379 | if (!!dbRecord && !!dbRecord.status && dbRecord.status === "E") { 380 | console.log(dbRecord.message); 381 | return; 382 | } 383 | 384 | if (dbRecord.result.length === 0 ) { 385 | console.log("Unable to find the user"); 386 | return; 387 | } 388 | 389 | record.id = dbRecord.result[0].id; 390 | 391 | let response = await updateUser(record); 392 | 393 | if (response.status === "E") { 394 | console.log(response.message); 395 | } else { 396 | console.log("The User has been successfully updated.") 397 | } 398 | } 399 | 400 | const resetPasswordCommand = async function(username, userPassword , confirmPassword) { 401 | let response = await resetPassword(username, userPassword , confirmPassword); 402 | 403 | if (response.status === "E") { 404 | console.log(response.message); 405 | } else { 406 | console.log("Password has been changed Successfully.") 407 | } 408 | } 409 | 410 | const enableDisableUser = async function(record) { 411 | 412 | let id = record.id; 413 | 414 | if (!id) { 415 | return { 416 | status: 'E', 417 | message: 'Missing id information' 418 | }; 419 | } 420 | 421 | if (!!record && !!record.enabled_flag) { 422 | } else { 423 | return {status: "E" , message: "Invalid enabled_flag value. The value is either incorrect or not provided. Valid Values [Y,N]"} 424 | } 425 | 426 | 427 | const dbRecord = await model.fetchById(id); 428 | 429 | if (!!dbRecord && dbRecord.length > 0) { 430 | if (dbRecord[0].username === "admin") { 431 | return {status: "E" , message: "You are not allowed to modify admin account"}; 432 | } 433 | 434 | let response = await model.updateEnabledFlag(record); 435 | 436 | return response; 437 | 438 | } else 439 | { 440 | return {status:"E" , message: "Unable to find the user"}; 441 | } 442 | 443 | } 444 | 445 | const enableDisableUserCommand = async function(record) { 446 | let dbRecord = await model.fetchByUsername(record.username); 447 | 448 | if (!!dbRecord && !!dbRecord.status && dbRecord.status === "E") { 449 | console.log(dbRecord.message); 450 | return; 451 | } 452 | 453 | if (dbRecord.result.length === 0 ) { 454 | console.log("Unable to find the user"); 455 | return; 456 | } 457 | 458 | record.id = dbRecord.result[0].id; 459 | 460 | let response = await enableDisableUser(record); 461 | 462 | if (response.status === "E") { 463 | console.log(response.message); 464 | } else { 465 | console.log("The User status has been successfully updated.") 466 | } 467 | } 468 | 469 | // const generateToken = async function(record) { 470 | // if (!!record && !!record.refresh_token) { 471 | // let decoded = utils.decryptObjectByKey(record.refresh_token,process.env.REFRESH_SECRET_KEY); 472 | // if (utils.isEmpty(decoded)) { 473 | // return {status: "E" , message: "Invalid Refresh Token Information", code: "RELOGIN"}; 474 | // } else { 475 | // let id = decoded.claims.id; 476 | 477 | // const dbRecord = await model.fetchById(id); 478 | // // console.log(dbRecord); 479 | 480 | // if (!!dbRecord && dbRecord.length > 0) { 481 | 482 | // let d = new Date(); 483 | // d.setMinutes(d.getMinutes() + 30); 484 | // let claims = decoded.claims; 485 | // let tokenObj = { claims: claims , exp_time: d }; 486 | // const token = utils.encryptObjectByKey(tokenObj, process.env.PGAPI_SECRET_KEY); 487 | // let obj = { token: token , refresh_token: record.refresh_token , claims: claims }; 488 | // return {status: "S" , message: "OK", data: obj}; 489 | // } else { 490 | // return {status: "E" , message: "Invalid Session Information.", code: "INVALID_USER"}; 491 | // } 492 | // } 493 | 494 | // } else { 495 | // return {status: "E" , message: "Please provide the refresh token."}; 496 | // } 497 | // } 498 | 499 | // const generateTokenCommand = async function(record) { 500 | // let response = await generateToken(record); 501 | 502 | // if (response.status === "E") { 503 | // console.log(response.message); 504 | // } else { 505 | // console.log("The User has been validated.") 506 | // console.log(response.data); 507 | // } 508 | // } 509 | 510 | 511 | module.exports = { 512 | validateLogin: validateLogin, 513 | loginCommand: loginCommand, 514 | loginAPI: loginAPI, 515 | fetchAPI: fetchAPI, 516 | sessionAPI: sessionAPI, 517 | resetPassword: resetPassword, 518 | resetPasswordCommand: resetPasswordCommand, 519 | resetPasswordAPI: resetPasswordAPI, 520 | addUser: addUser, 521 | addUserCommand: addUserCommand, 522 | updateUserCommand: updateUserCommand, 523 | updateUserAPI: updateUserAPI, 524 | deleteUserCommand: deleteUserCommand, 525 | validateSession: validateSession, 526 | validateSessionCommand: validateSessionCommand, 527 | enableDisableUserCommand: enableDisableUserCommand, 528 | // generateTokenCommand: generateTokenCommand 529 | } -------------------------------------------------------------------------------- /admin/controllers/dbUtils.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of pgAPI. 3 | pgAPI - Database as a service 4 | Copyright (C) 2018 Praveen Muralidhar 5 | 6 | pgAPI is a free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | pgAPI is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const utils = require('../Utils'); 21 | const path = require('path'); 22 | const fs = require('fs'); 23 | 24 | const executeFunction = async function(req, res) { 25 | let record = req.body; 26 | 27 | try { 28 | let response = await utils.loadDBFunctionString(record.sql); 29 | res.json(response); 30 | } catch(e) { 31 | res.json({status:"E" , message: e.message}); 32 | } 33 | 34 | 35 | // res.end(); 36 | } 37 | 38 | const getDBFunction = async function(req, res) { 39 | let functionName = req.query.db_method; 40 | let connectionID = req.query.connection_id; 41 | let sql = "select pg_get_functiondef(oid) as result from pg_proc where proname = $1" 42 | const sqlResult = await utils.getSQLResultsByConnectionId(connectionID,sql, [functionName]); 43 | let result = ""; 44 | if (!!sqlResult && !!sqlResult.data && sqlResult.data.length > 0 ) { 45 | result = sqlResult["data"][0]["result"]; 46 | } 47 | const response = {status: "S" , response: "OK", data: result}; 48 | res.json(response); 49 | } 50 | 51 | const saveSQL = async function(req, res) { 52 | let record = req.body; 53 | 54 | try { 55 | fs.writeFileSync(path.resolve(__dirname, '../sql/main.sql'),record.sql,{encoding:'utf8',flag:'w'}) 56 | res.json({status: "S" , message: "OK"}); 57 | } catch(e) { 58 | // console.log(e); 59 | res.json({status:"E" , message: e.message}); 60 | } 61 | 62 | 63 | // res.end(); 64 | } 65 | 66 | const getSQL = async function(req, res) { 67 | try { 68 | let contents = fs.readFileSync(path.resolve(__dirname, '../sql/main.sql'), 'utf8'); 69 | res.json({status: "S" , message: "OK" , data: contents}); 70 | } catch(e) { 71 | // console.log(e); 72 | res.json({status:"E" , message: e.message}); 73 | } 74 | 75 | 76 | // res.end(); 77 | } 78 | 79 | 80 | module.exports = { 81 | executeFunction: executeFunction, 82 | saveSQL: saveSQL, 83 | getSQL: getSQL, 84 | getDBFunction: getDBFunction 85 | } -------------------------------------------------------------------------------- /admin/core/models/Connection.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of pgAPI. 3 | pgAPI - Database as a service 4 | Copyright (C) 2018 Praveen Muralidhar 5 | 6 | pgAPI is a free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | pgAPI is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const utils = require('../../Utils'); 21 | const modelName = "connections"; 22 | const metadata = { 23 | insert : { 24 | sql : 'INSERT INTO CONNECTIONS (id,name,host,port,database,username,password,ref_source_id,config_flag,created,updated) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)', 25 | params : function(record) { 26 | let uuid = ""; 27 | 28 | if (!record["id"]) { 29 | uuid = utils.getUUID(); 30 | } else { 31 | uuid = record["id"]; 32 | } 33 | 34 | let config = "N"; 35 | 36 | if (!!record["config_flag"]) { 37 | config = record["config_flag"]; 38 | } 39 | 40 | return [uuid,record.name,record.host,record.port,record.database,record.username,record.password,record.ref_source_id,config,record.created,record.updated]; 41 | }, 42 | failureMessage : function(e) { 43 | const status = 'E'; 44 | let message = 'Failed to create connection ' + e; 45 | 46 | return {status:status,message:message}; 47 | } 48 | }, 49 | update : { 50 | sql : 'UPDATE CONNECTIONS SET name = $1 , host = $2 , port = $3 ,database = $4,username = $5,password = $6, updated = $7 WHERE id = $8', 51 | params : function(record) { 52 | 53 | const d = new Date(); 54 | return [record.name 55 | , record.host 56 | , record.port 57 | , record.database 58 | , record.username 59 | , record.password 60 | , d 61 | , record.id]; 62 | }, 63 | failureMessage : function(e) { 64 | const status = 'E'; 65 | let message = 'Failed to update connection ' + e; 66 | 67 | return {status:status,message:message}; 68 | } 69 | 70 | }, 71 | delete : { 72 | sql : 'DELETE FROM CONNECTIONS WHERE id = $1', 73 | params : function(record) { 74 | return [record.id]; 75 | }, 76 | failureMessage : function(e) { 77 | const status = 'E'; 78 | let message = 'Failed to delete connection ' + e; 79 | 80 | return {status:status,message:message}; 81 | } 82 | }, 83 | fetch : { 84 | sql : 'SELECT * FROM CONNECTIONS', 85 | params : function(record) { 86 | return []; 87 | }, 88 | failureMessage : function(e) { 89 | return {}; 90 | } 91 | }, 92 | fetchByName : { 93 | sql : 'SELECT * FROM CONNECTIONS WHERE name = $1', 94 | params : function(record) { 95 | return [record.name]; 96 | }, 97 | failureMessage : function(e) { 98 | const status = 'E'; 99 | let message = 'Failed to Select connection ' + e; 100 | 101 | return {status:status,message:message}; 102 | } 103 | }, 104 | fetchById : { 105 | sql : 'SELECT * FROM CONNECTIONS WHERE id = $1', 106 | params : function(record) { 107 | return [record.id]; 108 | }, 109 | failureMessage : function(e) { 110 | const status = 'E'; 111 | let message = 'Failed to Select connection ' + e; 112 | 113 | return {status:status,message:message}; 114 | } 115 | } 116 | } 117 | 118 | 119 | const validate = async function(record, mode, custom) { 120 | let status = "S"; 121 | let message = "OK"; 122 | const model = utils.getModelMetadata(modelName); 123 | const newRecord = utils.handleDefaults(model,record,mode); 124 | let dbRecord = {}; 125 | 126 | // if (mode === "U" || mode === "D") { 127 | // // check the id information first 128 | // const existCount = await utils.checkId(modelName,record["id"]); 129 | // if (existCount === 0) { 130 | // status = 'E'; 131 | // message = 'No Data found for column [id]'; 132 | // return {status: status , message:message}; 133 | // } 134 | // } 135 | 136 | if (mode === "U" || mode === "D") { 137 | const dbRecordResponse = await fetchById({id: record["id"]}); 138 | if (!!dbRecordResponse && dbRecordResponse.length > 0 ) { 139 | dbRecord = dbRecordResponse[0]; 140 | } else { 141 | status = 'E'; 142 | message = 'No Data found for column [id]'; 143 | return {status: status , message:message}; 144 | } 145 | } 146 | 147 | switch(mode) { 148 | case 'I' : 149 | if (!!record) { 150 | const validateRequiredFields = utils.validateRequiredColumns(model,mode,newRecord); 151 | if (validateRequiredFields.status === "E") { 152 | return {status: validateRequiredFields.status , message: validateRequiredFields.message}; 153 | } 154 | 155 | let validateDatatypes = utils.validateDatatypes(model,mode,newRecord); 156 | 157 | if (validateDatatypes.status === "E") { 158 | return {status: validateDatatypes.status , message: validateDatatypes.message}; 159 | } 160 | 161 | const validateConstraints = await utils.validateConstraints(model,mode,newRecord); 162 | 163 | if (validateConstraints.status === "E") { 164 | return {status: validateConstraints.status , message: validateConstraints.message}; 165 | } 166 | 167 | const validateTableConstraints = await utils.validateTableConstraints(model,mode,newRecord); 168 | 169 | if (validateTableConstraints.status === "E") { 170 | return {status: validateTableConstraints.status , message: validateTableConstraints.message}; 171 | } 172 | 173 | // write your custom logic here 174 | if (custom) { 175 | const checkDB = await utils.validateConnection(newRecord); 176 | 177 | if (checkDB.status === "E") { 178 | return {status: checkDB.status , message: checkDB.message}; 179 | } 180 | newRecord["password"] = utils.encryptByKey(newRecord["password"],'connection'); 181 | } 182 | 183 | return {status: status , message:message, record: newRecord}; 184 | 185 | } else { 186 | status = 'E'; 187 | message = 'Missing Record Information'; 188 | return {status: status , message:message}; 189 | } 190 | case 'U': 191 | if (!!record) { 192 | const validateRequiredFields = utils.validateRequiredColumns(model,mode,newRecord); 193 | 194 | if (validateRequiredFields.status === "E") { 195 | return {status: validateRequiredFields.status , message: validateRequiredFields.message}; 196 | } 197 | 198 | const validateDatatypes = utils.validateDatatypes(model,mode,newRecord); 199 | 200 | if (validateDatatypes.status === "E") { 201 | return {status: validateDatatypes.status , message: validateDatatypes.message}; 202 | } 203 | 204 | const validateConstraints = await utils.validateConstraints(model,mode,newRecord); 205 | 206 | if (validateConstraints.status === "E") { 207 | return {status: validateConstraints.status , message: validateConstraints.message}; 208 | } 209 | 210 | const validateTableConstraints = await utils.validateTableConstraints(model,mode,newRecord); 211 | 212 | if (validateTableConstraints.status === "E") { 213 | return {status: validateTableConstraints.status , message: validateTableConstraints.message}; 214 | } 215 | 216 | // write your custom logic here 217 | if (custom) { 218 | 219 | const passwd = newRecord["password"]; 220 | 221 | if (dbRecord["password"] === passwd) { 222 | newRecord["password"] = utils.decryptByKey(passwd,'connection'); 223 | } 224 | 225 | const checkDB = await utils.validateConnection(newRecord); 226 | 227 | if (checkDB.status === "E") { 228 | return {status: checkDB.status , message: checkDB.message}; 229 | } 230 | newRecord["password"] = utils.encrypt(newRecord["password"]); 231 | } 232 | 233 | return {status: status , message:message, record: newRecord}; 234 | 235 | 236 | } else { 237 | status = 'E'; 238 | message = 'Missing Record Information'; 239 | return {status: status , message:message}; 240 | } 241 | case 'D': 242 | // write your custom logic here 243 | return {status: status , message:message}; 244 | } 245 | 246 | 247 | } 248 | 249 | 250 | const insert = async function(record,pool) { 251 | 252 | const metaObj = metadata["insert"]; 253 | try { 254 | 255 | const sql = metaObj.sql; 256 | const params = metaObj.params(record); 257 | 258 | return await utils.callSQL(sql,params,pool); 259 | 260 | } catch (e) { 261 | return metaObj.failureMessage(e); 262 | } 263 | 264 | } 265 | 266 | const update = async function(record) { 267 | 268 | const metaObj = metadata["update"]; 269 | try { 270 | const sql = metaObj.sql; 271 | const params = metaObj.params(record); 272 | 273 | return await utils.callSQL(sql,params); 274 | 275 | } catch (e) { 276 | return metaObj.failureMessage(e); 277 | } 278 | } 279 | 280 | const deleteRecord = async function(record) { 281 | 282 | const metaObj = metadata["delete"]; 283 | 284 | try { 285 | const sql = metaObj.sql; 286 | const params = metaObj.params(record); 287 | 288 | return await utils.callSQL(sql,params); 289 | 290 | } catch (e) { 291 | return metaObj.failureMessage(e); 292 | } 293 | } 294 | 295 | const fetchAll = async function() { 296 | 297 | const metaObj = metadata["fetch"]; 298 | try { 299 | const sql = metaObj.sql; 300 | const params = metaObj.params(); 301 | return await utils.getSQLResults(sql,params); 302 | 303 | } catch (e) { 304 | return metaObj.failureMessage(); 305 | } 306 | } 307 | 308 | const fetchByName = async function(record) { 309 | 310 | const metaObj = metadata["fetchByName"]; 311 | try { 312 | const sql = metaObj.sql; 313 | const params = metaObj.params(record); 314 | return await utils.getSQLResults(sql,params); 315 | 316 | } catch (e) { 317 | return metaObj.failureMessage(); 318 | } 319 | } 320 | 321 | const fetchById = async function(record) { 322 | 323 | const metaObj = metadata["fetchById"]; 324 | try { 325 | const sql = metaObj.sql; 326 | const params = metaObj.params(record); 327 | return await utils.getSQLResults(sql,params); 328 | 329 | } catch (e) { 330 | return metaObj.failureMessage(); 331 | } 332 | } 333 | 334 | module.exports = { 335 | validate: validate, 336 | insert: insert, 337 | update: update, 338 | delete: deleteRecord, 339 | fetchAll: fetchAll, 340 | fetchById: fetchById, 341 | metadata: metadata, 342 | modelName: modelName, 343 | fetchByName: fetchByName 344 | } -------------------------------------------------------------------------------- /admin/core/models/Function.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of pgAPI. 3 | pgAPI - Database as a service 4 | Copyright (C) 2018 Praveen Muralidhar 5 | 6 | pgAPI is a free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | pgAPI is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const utils = require('../../Utils'); 21 | const modelName = "functions"; 22 | const metadata = { 23 | insert : { 24 | sql : 'INSERT INTO FUNCTIONS (id,name,connection_id,db_method,ref_source_id,created,updated) VALUES ($1,$2,$3,$4,$5,$6,$7)', 25 | params : function(record) { 26 | let uuid = ""; 27 | 28 | if (!record["id"]) { 29 | uuid = utils.getUUID(); 30 | } else { 31 | uuid = record["id"]; 32 | } 33 | 34 | return [uuid,record.name,record.connection_id,record.db_method,record.ref_source_id,record.created,record.updated]; 35 | }, 36 | failureMessage : function(e) { 37 | const status = 'E'; 38 | let message = 'Failed to create Function ' + e; 39 | 40 | return {status:status,message:message}; 41 | } 42 | }, 43 | update : { 44 | sql : 'UPDATE FUNCTIONS SET name = $1 , connection_id = $2 , db_method = $3 , updated = $4 WHERE id = $5', 45 | params : function(record) { 46 | 47 | const d = new Date(); 48 | return [record.name 49 | , record.connection_id 50 | , record.db_method 51 | , d 52 | , record.id]; 53 | }, 54 | failureMessage : function(e) { 55 | const status = 'E'; 56 | let message = 'Failed to update Function ' + e; 57 | 58 | return {status:status,message:message}; 59 | } 60 | 61 | }, 62 | delete : { 63 | sql : 'DELETE FROM FUNCTIONS WHERE id = $1', 64 | params : function(record) { 65 | return [record.id]; 66 | }, 67 | failureMessage : function(e) { 68 | const status = 'E'; 69 | let message = 'Failed to delete function ' + e; 70 | 71 | return {status:status,message:message}; 72 | } 73 | }, 74 | fetch : { 75 | sql : 'SELECT a.*,b.name as connection_name FROM FUNCTIONS a, CONNECTIONS b WHERE a.connection_id = b.id', 76 | params : function(record) { 77 | return []; 78 | }, 79 | failureMessage : function(e) { 80 | return {}; 81 | } 82 | }, 83 | fetchByName: { 84 | sql : 'SELECT a.* FROM FUNCTIONS a WHERE name = $1', 85 | params : function(record) { 86 | return [record.name]; 87 | }, 88 | failureMessage : function(e) { 89 | const status = 'E'; 90 | let message = 'Failed to delete function ' + e; 91 | return {status:status,message:message}; 92 | } 93 | } 94 | } 95 | 96 | 97 | const validate = async function(record, mode, custom, pool) { 98 | let status = "S"; 99 | let message = "OK"; 100 | const model = utils.getModelMetadata(modelName); 101 | const newRecord = utils.handleDefaults(model,record,mode); 102 | 103 | if (mode === "U" || mode === "D") { 104 | // check the id information first 105 | const existCount = await utils.checkId(modelName,record["id"]); 106 | if (existCount === 0) { 107 | status = 'E'; 108 | message = 'No Data found for column [id]'; 109 | return {status: status , message:message}; 110 | } 111 | } 112 | 113 | switch(mode) { 114 | case 'I': 115 | if (!!record) { 116 | const validateRequiredFields = utils.validateRequiredColumns(model,mode,newRecord); 117 | if (validateRequiredFields.status === "E") { 118 | return {status: validateRequiredFields.status , message: validateRequiredFields.message}; 119 | } 120 | let validateDatatypes = utils.validateDatatypes(model,mode,newRecord); 121 | 122 | if (validateDatatypes.status === "E") { 123 | return {status: validateDatatypes.status , message: validateDatatypes.message}; 124 | } 125 | 126 | const validateConstraints = await utils.validateConstraints(model,mode,newRecord,pool); 127 | 128 | if (validateConstraints.status === "E") { 129 | return {status: validateConstraints.status , message: validateConstraints.message}; 130 | } 131 | 132 | const validateTableConstraints = await utils.validateTableConstraints(model,mode,newRecord,pool); 133 | 134 | if (validateTableConstraints.status === "E") { 135 | return {status: validateTableConstraints.status , message: validateTableConstraints.message}; 136 | } 137 | 138 | // write your custom logic here 139 | if (custom) { 140 | const checkFunction = await utils.validateFunction(record["connection_id"], record["db_method"]); 141 | if (checkFunction.status === "E") { 142 | return {status: checkFunction.status , message: checkFunction.message}; 143 | } 144 | } 145 | 146 | return {status: status , message:message, record: newRecord}; 147 | 148 | 149 | } else { 150 | status = 'E'; 151 | message = 'Missing Record Information'; 152 | return {status: status , message:message}; 153 | } 154 | case 'U': 155 | if (!!record) { 156 | const validateRequiredFields = utils.validateRequiredColumns(model,mode,newRecord); 157 | if (validateRequiredFields.status === "E") { 158 | return {status: validateRequiredFields.status , message: validateRequiredFields.message}; 159 | } 160 | 161 | const validateDatatypes = utils.validateDatatypes(model,mode,newRecord); 162 | 163 | if (validateDatatypes.status === "E") { 164 | return {status: validateDatatypes.status , message: validateDatatypes.message}; 165 | } 166 | 167 | const validateConstraints = await utils.validateConstraints(model,mode,newRecord); 168 | 169 | if (validateConstraints.status === "E") { 170 | return {status: validateConstraints.status , message: validateConstraints.message}; 171 | } 172 | 173 | const validateTableConstraints = await utils.validateTableConstraints(model,mode,newRecord,pool); 174 | 175 | if (validateTableConstraints.status === "E") { 176 | return {status: validateTableConstraints.status , message: validateTableConstraints.message}; 177 | } 178 | 179 | // write your custom logic heres 180 | if (custom) { 181 | const checkFunction = await utils.validateFunction(record["connection_id"],record["db_method"]); 182 | if (checkFunction.status === "E") { 183 | return {status: checkFunction.status , message: checkFunction.message}; 184 | } 185 | } 186 | 187 | return {status: status , message:message, record: newRecord}; 188 | 189 | 190 | } else { 191 | status = 'E'; 192 | message = 'Missing Record Information'; 193 | return {status: status , message:message}; 194 | } 195 | case 'D': 196 | // write your custom logic here 197 | return {status: status , message:message}; 198 | } 199 | 200 | 201 | } 202 | 203 | 204 | const insert = async function(record, pool) { 205 | 206 | const metaObj = metadata["insert"]; 207 | try { 208 | const sql = metaObj.sql; 209 | const params = metaObj.params(record); 210 | 211 | return await utils.callSQL(sql,params, pool); 212 | 213 | } catch (e) { 214 | return metaObj.failureMessage(e); 215 | } 216 | 217 | } 218 | 219 | const update = async function(record) { 220 | 221 | const metaObj = metadata["update"]; 222 | try { 223 | const sql = metaObj.sql; 224 | const params = metaObj.params(record); 225 | 226 | return await utils.callSQL(sql,params); 227 | 228 | } catch (e) { 229 | return metaObj.failureMessage(e); 230 | } 231 | } 232 | 233 | const deleteRecord = async function(record) { 234 | 235 | const metaObj = metadata["delete"]; 236 | console.log("Deleting Recrod"); 237 | 238 | try { 239 | const sql = metaObj.sql; 240 | const params = metaObj.params(record); 241 | 242 | return await utils.callSQL(sql,params); 243 | 244 | } catch (e) { 245 | return metaObj.failureMessage(e); 246 | } 247 | } 248 | 249 | const fetchAll = async function() { 250 | 251 | const metaObj = metadata["fetch"]; 252 | try { 253 | const sql = metaObj.sql; 254 | const params = metaObj.params(); 255 | return await utils.getSQLResults(sql,params); 256 | 257 | } catch (e) { 258 | return metaObj.failureMessage(); 259 | } 260 | } 261 | 262 | const fetchByName = async function(record) { 263 | 264 | const metaObj = metadata["fetchByName"]; 265 | try { 266 | const sql = metaObj.sql; 267 | const params = metaObj.params(record); 268 | return await utils.getSQLResults(sql,params); 269 | 270 | } catch (e) { 271 | return metaObj.failureMessage(); 272 | } 273 | } 274 | 275 | module.exports = { 276 | validate: validate, 277 | insert: insert, 278 | update: update, 279 | delete: deleteRecord, 280 | fetchAll: fetchAll, 281 | fetchByName: fetchByName 282 | } -------------------------------------------------------------------------------- /admin/core/models/Route.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of pgAPI. 3 | pgAPI - Database as a service 4 | Copyright (C) 2018 Praveen Muralidhar 5 | 6 | pgAPI is a free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | pgAPI is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const utils = require('../../Utils'); 21 | const modelName = "routes"; 22 | const metadata = { 23 | insert : { 24 | sql : 'INSERT INTO ROUTES (id,name,function_id,route_method,enabled_flag,route_url,description,sample_request,sample_response,ref_source_id,created,updated) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12)', 25 | params : function(record) { 26 | let uuid = ""; 27 | 28 | if (!record["id"]) { 29 | uuid = utils.getUUID(); 30 | } else { 31 | uuid = record["id"]; 32 | } 33 | 34 | return [uuid,record.name,record.function_id,record.route_method,'Y',record.route_url,record.description,record.sample_request,record.sample_response,record.ref_source_id,record.created,record.updated]; 35 | }, 36 | failureMessage : function(e) { 37 | const status = 'E'; 38 | let message = 'Failed to create Route ' + e; 39 | 40 | return {status:status,message:message}; 41 | } 42 | }, 43 | update : { 44 | sql : 'UPDATE ROUTES SET name = $1 , function_id = $2 , route_method = $3 , enabled_flag = $4, route_url = $5,description = $6,sample_request = $7,sample_response = $8, updated = $9 WHERE id = $10', 45 | params : function(record) { 46 | 47 | const d = new Date(); 48 | return [record.name 49 | , record.function_id 50 | , record.route_method 51 | , record.enabled_flag 52 | , record.route_url 53 | , record.description 54 | , record.sample_request 55 | , record.sample_response 56 | , d 57 | , record.id]; 58 | }, 59 | failureMessage : function(e) { 60 | const status = 'E'; 61 | let message = 'Failed to update Route ' + e; 62 | 63 | return {status:status,message:message}; 64 | } 65 | 66 | }, 67 | delete : { 68 | sql : 'DELETE FROM ROUTES WHERE id = $1', 69 | params : function(record) { 70 | return [record.id]; 71 | }, 72 | failureMessage : function(e) { 73 | const status = 'E'; 74 | let message = 'Failed to delete routes ' + e; 75 | 76 | return {status:status,message:message}; 77 | } 78 | }, 79 | fetch : { 80 | sql : 'SELECT a.* , b.name as function_name, c.name as connection_name FROM routes a , functions b, connections c where a.function_id = b.id and c.id = b.connection_id', 81 | params : function(record) { 82 | return []; 83 | }, 84 | failureMessage : function(e) { 85 | return {}; 86 | } 87 | }, 88 | fetchById : { 89 | sql : 'SELECT a.* , b.name as function_name, c.name as connection_name FROM routes a , functions b, connections c ' + 90 | 'where a.function_id = b.id and c.id = b.connection_id and a.id = $1', 91 | params : function(id) { 92 | return [id]; 93 | }, 94 | failureMessage : function(e) { 95 | return {}; 96 | } 97 | } 98 | } 99 | 100 | 101 | const validate = async function(record, mode, custom,pool) { 102 | let status = "S"; 103 | let message = "OK"; 104 | const model = utils.getModelMetadata(modelName); 105 | const newRecord = utils.handleDefaults(model,record,mode); 106 | 107 | if (mode === "U" || mode === "D") { 108 | // check the id information first 109 | const existCount = await utils.checkId(modelName,record["id"]); 110 | if (existCount === 0) { 111 | status = 'E'; 112 | message = 'No Data found for column [id]'; 113 | return {status: status , message:message}; 114 | } 115 | 116 | } 117 | switch(mode) { 118 | case 'I' : 119 | if (!!record) { 120 | const validateRequiredFields = utils.validateRequiredColumns(model,mode,newRecord); 121 | if (validateRequiredFields.status === "E") { 122 | return {status: validateRequiredFields.status , message: validateRequiredFields.message}; 123 | } 124 | 125 | let validateDatatypes = utils.validateDatatypes(model,mode,newRecord); 126 | 127 | if (validateDatatypes.status === "E") { 128 | return {status: validateDatatypes.status , message: validateDatatypes.message}; 129 | } 130 | 131 | const validateConstraints = await utils.validateConstraints(model,mode,newRecord,pool); 132 | 133 | if (validateConstraints.status === "E") { 134 | return {status: validateConstraints.status , message: validateConstraints.message}; 135 | } 136 | 137 | const validateTableConstraints = await utils.validateTableConstraints(model,mode,newRecord,pool); 138 | 139 | if (validateTableConstraints.status === "E") { 140 | return {status: validateTableConstraints.status , message: validateTableConstraints.message}; 141 | } 142 | 143 | // write your custom logic here 144 | if (custom) { 145 | 146 | } 147 | 148 | return {status: status , message:message, record: newRecord}; 149 | 150 | 151 | } else { 152 | status = 'E'; 153 | message = 'Missing Record Information'; 154 | return {status: status , message:message}; 155 | } 156 | case 'U': 157 | if (!!record) { 158 | const validateRequiredFields = utils.validateRequiredColumns(model,mode,newRecord); 159 | 160 | if (validateRequiredFields.status === "E") { 161 | return {status: validateRequiredFields.status , message: validateRequiredFields.message}; 162 | } 163 | 164 | const validateDatatypes = utils.validateDatatypes(model,mode,newRecord); 165 | 166 | if (validateDatatypes.status === "E") { 167 | return {status: validateDatatypes.status , message: validateDatatypes.message}; 168 | } 169 | 170 | const validateConstraints = await utils.validateConstraints(model,mode,newRecord,pool); 171 | 172 | if (validateConstraints.status === "E") { 173 | return {status: validateConstraints.status , message: validateConstraints.message}; 174 | } 175 | 176 | const validateTableConstraints = await utils.validateTableConstraints(model,mode,newRecord,pool); 177 | 178 | if (validateTableConstraints.status === "E") { 179 | return {status: validateTableConstraints.status , message: validateTableConstraints.message}; 180 | } 181 | 182 | // write your custom logic here 183 | if (custom) { 184 | 185 | } 186 | 187 | return {status: status , message:message, record: newRecord}; 188 | 189 | } else { 190 | status = 'E'; 191 | message = 'Missing Record Information'; 192 | return {status: status , message:message}; 193 | } 194 | case 'D': 195 | // write your custom logic here 196 | return {status: status , message:message}; 197 | } 198 | 199 | } 200 | 201 | 202 | const insert = async function(record, pool) { 203 | 204 | const metaObj = metadata["insert"]; 205 | try { 206 | const sql = metaObj.sql; 207 | const params = metaObj.params(record); 208 | 209 | return await utils.callSQL(sql,params, pool); 210 | 211 | } catch (e) { 212 | return metaObj.failureMessage(e); 213 | } 214 | 215 | } 216 | 217 | const update = async function(record) { 218 | 219 | const metaObj = metadata["update"]; 220 | try { 221 | const sql = metaObj.sql; 222 | const params = metaObj.params(record); 223 | 224 | return await utils.callSQL(sql,params); 225 | 226 | } catch (e) { 227 | return metaObj.failureMessage(e); 228 | } 229 | } 230 | 231 | const deleteRecord = async function(record) { 232 | 233 | const metaObj = metadata["delete"]; 234 | 235 | try { 236 | const sql = metaObj.sql; 237 | const params = metaObj.params(record); 238 | 239 | return await utils.callSQL(sql,params); 240 | 241 | } catch (e) { 242 | return metaObj.failureMessage(e); 243 | } 244 | } 245 | 246 | const fetchAll = async function() { 247 | 248 | const metaObj = metadata["fetch"]; 249 | try { 250 | const sql = metaObj.sql; 251 | const params = metaObj.params(); 252 | return await utils.getSQLResults(sql,params); 253 | 254 | } catch (e) { 255 | return metaObj.failureMessage(); 256 | } 257 | } 258 | 259 | const fetchById = async function(id) { 260 | 261 | const metaObj = metadata["fetchById"]; 262 | try { 263 | const sql = metaObj.sql; 264 | const params = metaObj.params(id); 265 | return await utils.getSQLResults(sql,params); 266 | 267 | } catch (e) { 268 | return metaObj.failureMessage(); 269 | } 270 | } 271 | 272 | 273 | module.exports = { 274 | validate: validate, 275 | insert: insert, 276 | update: update, 277 | delete: deleteRecord, 278 | fetchAll: fetchAll, 279 | fetchById: fetchById 280 | } 281 | -------------------------------------------------------------------------------- /admin/core/models/User.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of pgAPI. 3 | pgAPI - Database as a service 4 | Copyright (C) 2018 Praveen Muralidhar 5 | 6 | pgAPI is a free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | pgAPI is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const utils = require('../../Utils'); 21 | const modelName = "users"; 22 | const metadata = { 23 | insert : { 24 | sql : 'INSERT INTO USERS (id,username,first_name,last_name,password,ref_user_id,enabled_flag,created,updated) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)', 25 | params : function(record) { 26 | let uuid = ""; 27 | 28 | if (!record["id"]) { 29 | uuid = utils.getUUID(); 30 | } else { 31 | uuid = record["id"]; 32 | } 33 | 34 | const passwordHash = utils.encryptPassword(record.password); 35 | 36 | return [uuid,record.username,record.first_name,record.last_name,passwordHash,record.ref_user_id, 'Y', record.created,record.updated]; 37 | }, 38 | failureMessage : function(e) { 39 | const status = 'E'; 40 | let message = 'Failed to create User ' + e; 41 | 42 | return {status:status,message:message}; 43 | } 44 | }, 45 | update : { 46 | sql : 'UPDATE USERS SET first_name = $1 , last_name = $2 , updated = $3 WHERE id = $4', 47 | params : function(record) { 48 | 49 | const d = new Date(); 50 | return [record.first_name 51 | , record.last_name 52 | , d 53 | , record.id]; 54 | }, 55 | failureMessage : function(e) { 56 | const status = 'E'; 57 | let message = 'Failed to update User ' + e; 58 | 59 | return {status:status,message:message}; 60 | } 61 | 62 | }, 63 | updatePassword : { 64 | sql : 'UPDATE USERS SET password = $1 , updated = $2 WHERE id = $3', 65 | params : function(record) { 66 | 67 | const d = new Date(); 68 | return [record.password 69 | , d 70 | , record.id]; 71 | }, 72 | failureMessage : function(e) { 73 | const status = 'E'; 74 | let message = 'Failed to update User PAssword ' + e; 75 | 76 | return {status:status,message:message}; 77 | } 78 | 79 | }, 80 | updateEnabledFlag : { 81 | sql : 'UPDATE USERS SET enabled_flag = $1 , updated = $2 WHERE id = $3', 82 | params : function(record) { 83 | 84 | const d = new Date(); 85 | return [record.enabled_flag 86 | , d 87 | , record.id]; 88 | }, 89 | failureMessage : function(e) { 90 | const status = 'E'; 91 | let message = 'Failed to update User PAssword ' + e; 92 | 93 | return {status:status,message:message}; 94 | } 95 | }, 96 | delete : { 97 | sql : 'DELETE FROM USERS WHERE id = $1', 98 | params : function(record) { 99 | return [record.id]; 100 | }, 101 | failureMessage : function(e) { 102 | const status = 'E'; 103 | let message = 'Failed to delete function ' + e; 104 | 105 | return {status:status,message:message}; 106 | } 107 | }, 108 | fetch : { 109 | sql : 'SELECT a.* FROM USERS a', 110 | params : function(record) { 111 | return []; 112 | }, 113 | failureMessage : function(e) { 114 | return {}; 115 | } 116 | }, 117 | fetchById : { 118 | sql : 'SELECT a.* FROM USERS a WHERE a.id = $1', 119 | params : function(record) { 120 | return [record.id]; 121 | }, 122 | failureMessage : function(e) { 123 | return {}; 124 | } 125 | }, 126 | fetchByIdUser : { 127 | sql : 'SELECT id , username , first_name , last_name FROM USERS a WHERE a.id = $1', 128 | params : function(record) { 129 | return [record.id]; 130 | }, 131 | failureMessage : function(e) { 132 | return {}; 133 | } 134 | }, 135 | fetchByUsername : { 136 | sql : 'SELECT a.* FROM USERS a WHERE a.username = $1', 137 | params : function(record) { 138 | return [record.username]; 139 | }, 140 | failureMessage : function(e) { 141 | return {}; 142 | } 143 | }, 144 | insertArchive: { 145 | sql : 'INSERT INTO deleted_users (id , username , first_name , last_name , created , cwho) VALUES ($1,$2,$3,$4,$5,$6)', 146 | params : function(record) { 147 | let uuid = ""; 148 | 149 | if (!record["id"]) { 150 | uuid = utils.getUUID(); 151 | } else { 152 | uuid = record["id"]; 153 | } 154 | 155 | const d = new Date(); 156 | 157 | if (!record["created_by"]) { 158 | record["created_by"] = 0; 159 | } 160 | 161 | return [uuid,record.username,record.first_name,record.last_name,d,record.cwho]; 162 | }, 163 | failureMessage : function(e) { 164 | const status = 'E'; 165 | let message = 'Failed to create User Archive ' + e; 166 | 167 | return {status:status,message:message}; 168 | } 169 | }, 170 | insertToken: { 171 | sql : 'INSERT INTO tokens( token , start_time , user_id , timeout_time , created , updated ) VALUES ($1, $2, $3, $4, $5, $6)', 172 | params : function(record) { 173 | const d = new Date(); 174 | let timeoutTime = new Date(); 175 | timeoutTime.setMinutes(timeoutTime.getMinutes() + 60); 176 | return [record.token,d, record.user_id, timeoutTime, d, d]; 177 | }, 178 | failureMessage : function(e) { 179 | const status = 'E'; 180 | let message = 'Failed to create Token record ' + e; 181 | 182 | return {status:status,message:message}; 183 | } 184 | }, 185 | fetchToken : { 186 | sql : 'SELECT a.* FROM TOKENS a WHERE a.token = $1', 187 | params : function(record) { 188 | return [record.token]; 189 | }, 190 | failureMessage : function(e) { 191 | return {}; 192 | } 193 | }, 194 | updateToken: { 195 | sql : 'UPDATE tokens SET timeout_time = $1 , updated = $2 WHERE token = $3', 196 | params : function(record) { 197 | const d = new Date(); 198 | let timeoutTime = new Date(); 199 | timeoutTime.setMinutes(timeoutTime.getMinutes() + 60); 200 | return [timeoutTime,d, record.token]; 201 | }, 202 | failureMessage : function(e) { 203 | const status = 'E'; 204 | let message = 'Failed to create Token record ' + e; 205 | 206 | return {status:status,message:message}; 207 | } 208 | }, 209 | validateSession: { 210 | sql : "SELECT validate_session($1) as result", 211 | params : function(record) { 212 | return [record.token]; 213 | }, 214 | failureMessage : function(e) { 215 | const status = 'E'; 216 | let message = 'Invalid Session Information ' + e; 217 | 218 | return {status:status,message:message}; 219 | } 220 | } 221 | } 222 | 223 | 224 | const validate = async function(record, mode, custom, pool) { 225 | let status = "S"; 226 | let message = "OK"; 227 | const model = utils.getModelMetadata(modelName); 228 | const newRecord = utils.handleDefaults(model,record,mode); 229 | 230 | if (mode === "U" || mode === "D") { 231 | // check the id information first 232 | const existCount = await utils.checkId(modelName,record["id"]); 233 | if (existCount === 0) { 234 | status = 'E'; 235 | message = 'No Data found for column [id]'; 236 | return {status: status , message:message}; 237 | } 238 | 239 | } 240 | switch(mode) { 241 | case 'I' : 242 | if (!!record) { 243 | const validateRequiredFields = utils.validateRequiredColumns(model,mode,newRecord); 244 | 245 | if (validateRequiredFields.status === "E") { 246 | return {status: validateRequiredFields.status , message: validateRequiredFields.message}; 247 | } 248 | let validateDatatypes = utils.validateDatatypes(model,mode,newRecord); 249 | 250 | if (validateDatatypes.status === "E") { 251 | return {status: validateDatatypes.status , message: validateDatatypes.message}; 252 | } 253 | 254 | const validateConstraints = await utils.validateConstraints(model,mode,newRecord,pool); 255 | 256 | if (validateConstraints.status === "E") { 257 | return {status: validateConstraints.status , message: validateConstraints.message}; 258 | } 259 | 260 | const validateTableConstraints = await utils.validateTableConstraints(model,mode,newRecord,pool); 261 | 262 | if (validateTableConstraints.status === "E") { 263 | return {status: validateTableConstraints.status , message: validateTableConstraints.message}; 264 | } 265 | 266 | // write your custom logic here 267 | if (custom) { 268 | 269 | } 270 | 271 | return {status: status , message:message, record: newRecord}; 272 | 273 | 274 | } else { 275 | status = 'E'; 276 | message = 'Missing Record Information'; 277 | return {status: status , message:message}; 278 | } 279 | case 'U': 280 | if (!!record) { 281 | const validateRequiredFields = utils.validateRequiredColumns(model,mode,newRecord); 282 | if (validateRequiredFields.status === "E") { 283 | return {status: validateRequiredFields.status , message: validateRequiredFields.message}; 284 | } 285 | 286 | const validateDatatypes = utils.validateDatatypes(model,mode,newRecord); 287 | 288 | if (validateDatatypes.status === "E") { 289 | return {status: validateDatatypes.status , message: validateDatatypes.message}; 290 | } 291 | 292 | const validateConstraints = await utils.validateConstraints(model,mode,newRecord); 293 | 294 | if (validateConstraints.status === "E") { 295 | return {status: validateConstraints.status , message: validateConstraints.message}; 296 | } 297 | 298 | const validateTableConstraints = await utils.validateTableConstraints(model,mode,newRecord,pool); 299 | 300 | if (validateTableConstraints.status === "E") { 301 | return {status: validateTableConstraints.status , message: validateTableConstraints.message}; 302 | } 303 | 304 | // write your custom logic here 305 | if (custom) { 306 | 307 | } 308 | 309 | return {status: status , message:message, record: newRecord}; 310 | 311 | 312 | } else { 313 | status = 'E'; 314 | message = 'Missing Record Information'; 315 | return {status: status , message:message}; 316 | } 317 | case 'D': 318 | // write your custom logic here 319 | return {status: status , message:message}; 320 | } 321 | 322 | 323 | } 324 | 325 | 326 | const insert = async function(record) { 327 | 328 | const metaObj = metadata["insert"]; 329 | try { 330 | const sql = metaObj.sql; 331 | const params = metaObj.params(record); 332 | 333 | return await utils.callSQL(sql,params); 334 | 335 | } catch (e) { 336 | return metaObj.failureMessage(e); 337 | } 338 | 339 | } 340 | 341 | const insertArchive = async function(record) { 342 | 343 | const metaObj = metadata["insertArchive"]; 344 | try { 345 | const sql = metaObj.sql; 346 | const params = metaObj.params(record); 347 | 348 | return await utils.callSQL(sql,params); 349 | 350 | } catch (e) { 351 | return metaObj.failureMessage(e); 352 | } 353 | 354 | } 355 | 356 | const insertToken = async function(record) { 357 | 358 | const metaObj = metadata["insertToken"]; 359 | try { 360 | const sql = metaObj.sql; 361 | const params = metaObj.params(record); 362 | 363 | return await utils.callSQL(sql,params); 364 | 365 | } catch (e) { 366 | return metaObj.failureMessage(e); 367 | } 368 | 369 | } 370 | 371 | const update = async function(record) { 372 | 373 | const metaObj = metadata["update"]; 374 | try { 375 | const sql = metaObj.sql; 376 | const params = metaObj.params(record); 377 | 378 | return await utils.callSQL(sql,params); 379 | 380 | } catch (e) { 381 | return metaObj.failureMessage(e); 382 | } 383 | } 384 | 385 | const updatePassword = async function(record) { 386 | 387 | const metaObj = metadata["updatePassword"]; 388 | try { 389 | const sql = metaObj.sql; 390 | 391 | const params = metaObj.params(record); 392 | 393 | return await utils.callSQL(sql,params); 394 | 395 | } catch (e) { 396 | return metaObj.failureMessage(e); 397 | } 398 | } 399 | 400 | 401 | const updateEnabledFlag = async function(record) { 402 | 403 | const metaObj = metadata["updateEnabledFlag"]; 404 | try { 405 | const sql = metaObj.sql; 406 | 407 | const params = metaObj.params(record); 408 | 409 | return await utils.callSQL(sql,params); 410 | 411 | } catch (e) { 412 | return metaObj.failureMessage(e); 413 | } 414 | } 415 | const deleteRecord = async function(record) { 416 | 417 | const metaObj = metadata["delete"]; 418 | 419 | try { 420 | const sql = metaObj.sql; 421 | const params = metaObj.params(record); 422 | 423 | return await utils.callSQL(sql,params); 424 | 425 | } catch (e) { 426 | return metaObj.failureMessage(e); 427 | } 428 | } 429 | 430 | const fetchAll = async function() { 431 | 432 | const metaObj = metadata["fetch"]; 433 | try { 434 | const sql = metaObj.sql; 435 | const params = metaObj.params(); 436 | return await utils.getSQLResults(sql,params); 437 | 438 | } catch (e) { 439 | return metaObj.failureMessage(); 440 | } 441 | } 442 | 443 | const fetchByUsername = async function(username) { 444 | 445 | const metaObj = metadata["fetchByUsername"]; 446 | try { 447 | const sql = metaObj.sql; 448 | const params = metaObj.params({username: username}); 449 | return await utils.getSQLResultswithStatus(sql,params); 450 | 451 | } catch (e) { 452 | return metaObj.failureMessage(); 453 | } 454 | } 455 | 456 | 457 | 458 | const fetchById = async function(id) { 459 | 460 | const metaObj = metadata["fetchById"]; 461 | try { 462 | const sql = metaObj.sql; 463 | const params = metaObj.params({id: id}); 464 | return await utils.getSQLResults(sql,params); 465 | 466 | } catch (e) { 467 | return metaObj.failureMessage(); 468 | } 469 | } 470 | 471 | 472 | const fetchByIdUser = async function(id) { 473 | 474 | const metaObj = metadata["fetchByIdUser"]; 475 | try { 476 | const sql = metaObj.sql; 477 | const params = metaObj.params({id: id}); 478 | return await utils.getSQLResults(sql,params); 479 | 480 | } catch (e) { 481 | return metaObj.failureMessage(); 482 | } 483 | } 484 | 485 | const fetchToken = async function(id) { 486 | 487 | const metaObj = metadata["fetchToken"]; 488 | try { 489 | const sql = metaObj.sql; 490 | const params = metaObj.params({id: id}); 491 | return await utils.getSQLResults(sql,params); 492 | 493 | } catch (e) { 494 | return metaObj.failureMessage(); 495 | } 496 | } 497 | const validateSession = async function(record) { 498 | 499 | const metaObj = metadata["validateSession"]; 500 | try { 501 | const sql = metaObj.sql; 502 | const params = metaObj.params(record); 503 | return await utils.getSQLResults(sql,params); 504 | 505 | } catch (e) { 506 | return metaObj.failureMessage(); 507 | } 508 | } 509 | 510 | const updateToken = async function(record) { 511 | 512 | const metaObj = metadata["updateToken"]; 513 | try { 514 | const sql = metaObj.sql; 515 | 516 | const params = metaObj.params(record); 517 | 518 | return await utils.callSQL(sql,params); 519 | 520 | } catch (e) { 521 | return metaObj.failureMessage(e); 522 | } 523 | } 524 | 525 | 526 | module.exports = { 527 | validate: validate, 528 | insert: insert, 529 | insertArchive: insertArchive, 530 | insertToken: insertToken, 531 | update: update, 532 | delete: deleteRecord, 533 | fetchAll: fetchAll, 534 | fetchById: fetchById, 535 | fetchByUsername: fetchByUsername, 536 | fetchToken: fetchToken, 537 | fetchByIdUser: fetchByIdUser, 538 | updatePassword: updatePassword, 539 | updateEnabledFlag: updateEnabledFlag, 540 | updateToken: updateToken, 541 | validateSession: validateSession 542 | } -------------------------------------------------------------------------------- /admin/install/db/DB.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS CONNECTIONS 2 | ( id text NOT NULL 3 | , name text NOT NULL 4 | , host text NOT NULL 5 | , port integer NOT NULL 6 | , database text NOT NULL 7 | , username text NOT NULL 8 | , password text NOT NULL 9 | , ref_source_id text 10 | , config_flag text NOT NULL 11 | , created timestamp with time zone NOT NULL 12 | , created_by text 13 | , updated timestamp with time zone NOT NULL 14 | , updated_by text 15 | , UNIQUE (name) 16 | ); 17 | 18 | CREATE TABLE IF NOT EXISTS FUNCTIONS 19 | ( id text NOT NULL 20 | , name text NOT NULL 21 | , connection_id text NOT NULL 22 | , db_method text NOT NULL 23 | , ref_source_id text 24 | , created timestamp with time zone NOT NULL 25 | , created_by text 26 | , updated timestamp with time zone NOT NULL 27 | , updated_by text 28 | , UNIQUE (name) 29 | ); 30 | 31 | CREATE TABLE IF NOT EXISTS ROUTES 32 | ( id text NOT NULL 33 | , name text NOT NULL 34 | , function_id text NOT NULL 35 | , route_method text NOT NULL 36 | , route_url text NOT NULL 37 | , enabled_flag text NOT NULL 38 | , description text 39 | , sample_request text 40 | , sample_response text 41 | , ref_source_id text 42 | , created timestamp with time zone NOT NULL 43 | , created_by text 44 | , updated timestamp with time zone NOT NULL 45 | , updated_by text 46 | , UNIQUE (name) 47 | ); 48 | 49 | CREATE TABLE IF NOT EXISTS USERS 50 | ( id text NOT NULL 51 | , username text NOT NULL 52 | , first_name text 53 | , last_name text 54 | , password text NOT NULL 55 | , ref_user_id text 56 | , enabled_flag text NOT NULL 57 | , created timestamp with time zone NOT NULL 58 | , created_by text 59 | , updated timestamp with time zone NOT NULL 60 | , updated_by text 61 | , UNIQUE (username) 62 | ); 63 | 64 | CREATE TABLE IF NOT EXISTS DELETED_USERS 65 | ( id text NOT NULL 66 | , username text NOT NULL 67 | , first_name text 68 | , last_name text 69 | , created timestamp with time zone NOT NULL 70 | , created_by text 71 | , updated timestamp with time zone NOT NULL 72 | , updated_by text 73 | , UNIQUE (username) 74 | ); 75 | 76 | CREATE TABLE IF NOT EXISTS tokens 77 | ( 78 | token text NOT NULL 79 | , start_time timestamp with time zone NOT NULL 80 | , user_id text NOT NULL 81 | , timeout_time timestamp with time zone NOT NULL 82 | , created timestamp with time zone NOT NULL 83 | , created_by text 84 | , updated timestamp with time zone NOT NULL 85 | , updated_by text 86 | , UNIQUE (token) 87 | ); 88 | 89 | CREATE TABLE IF NOT EXISTS applications 90 | ( 91 | id text NOT NULL 92 | , name text NOT NULL 93 | , app_short_code text 94 | , created timestamp with time zone NOT NULL 95 | , created_by text 96 | , updated timestamp with time zone NOT NULL 97 | , updated_by text 98 | , UNIQUE (name) 99 | , UNIQUE (app_short_code) 100 | ); 101 | 102 | CREATE TABLE IF NOT EXISTS current_version 103 | ( 104 | created timestamp with time zone NOT NULL 105 | , version text NOT NULL 106 | ); -------------------------------------------------------------------------------- /admin/install/db/database.json: -------------------------------------------------------------------------------- 1 | { 2 | "connections" : { 3 | "name" : "connections", 4 | "overwrite" : false , 5 | "columns" : { 6 | "name" : { 7 | "datatype" : "string", 8 | "datalength": -1, 9 | "constraints" : ["UNIQUE"], 10 | "validate" : { "I" : ["UNIQUE","REQUIRED"] , "U" : ["UNIQUE_EXCLUDE","REQUIRED"], "D" : []}, 11 | "insertable" : true, 12 | "updatable" : true, 13 | "selectable" : true, 14 | "primary" : false 15 | }, 16 | "id" : { 17 | "datatype" : "string", 18 | "datalength": -1, 19 | "constraints" : ["PRIMARY"], 20 | "validate" : { "I" : ["REQUIRED"] , "U" : ["EXISTS" , "REQUIRED"], "D" : ["EXISTS","REQUIRED"]}, 21 | "insertable" : true, 22 | "updatable" : false, 23 | "selectable" : true, 24 | "default" : {"I" : {"value": "UUID" , "overwrite" : false } }, 25 | "primary" : true 26 | }, 27 | "host" : { 28 | "datatype" : "string", 29 | "datalength": -1, 30 | "constraints" : ["REQUIRED"], 31 | "validate" : { "I" : ["REQUIRED"] , "U" : ["REQUIRED"]}, 32 | "insertable" : true, 33 | "updatable" : true, 34 | "selectable" : true, 35 | "primary" : false 36 | }, 37 | "port" : { 38 | "datatype" : "number", 39 | "datalength": -1, 40 | "constraints" : ["REQUIRED"], 41 | "validate" : { "I" : ["REQUIRED","NUMBER"] , "U" : ["REQUIRED","NUMBER"]}, 42 | "insertable" : true, 43 | "updatable" : true, 44 | "selectable" : true, 45 | "primary" : false 46 | }, 47 | "database" : { 48 | "datatype" : "string", 49 | "datalength": -1, 50 | "constraints" : ["REQUIRED"], 51 | "validate" : { "I" : ["REQUIRED"] , "U" : ["REQUIRED"]}, 52 | "insertable" : true, 53 | "updatable" : true, 54 | "selectable" : true, 55 | "primary" : false 56 | }, 57 | "username" : { 58 | "datatype" : "string", 59 | "datalength": -1, 60 | "constraints" : ["REQUIRED"], 61 | "validate" : { "I" : ["REQUIRED"] , "U" : ["REQUIRED"]}, 62 | "insertable" : true, 63 | "updatable" : true, 64 | "selectable" : true, 65 | "primary" : false 66 | }, 67 | "password" : { 68 | "datatype" : "string", 69 | "datalength": -1, 70 | "constraints" : ["REQUIRED"], 71 | "validate" : { "I" : ["REQUIRED"] , "U" : ["REQUIRED"]}, 72 | "insertable" : true, 73 | "updatable" : true, 74 | "selectable" : false, 75 | "primary" : false, 76 | "encrypt" : true 77 | }, 78 | "ref_source_id" : { 79 | "datatype" : "string", 80 | "datalength": -1, 81 | "constraints" : [], 82 | "insertable" : true, 83 | "updatable" : true, 84 | "selectable" : true, 85 | "primary" : false 86 | }, 87 | "config_flag" : { 88 | "datatype" : "string", 89 | "datalength": -1, 90 | "constraints" : [], 91 | "insertable" : true, 92 | "updatable" : true, 93 | "selectable" : true, 94 | "primary" : false 95 | }, 96 | "created" : { 97 | "datatype" : "timestamp", 98 | "tz_enabled" : true, 99 | "datalength": -1, 100 | "constraints" : ["REQUIRED"], 101 | "validate" : { "I" : ["REQUIRED","DATE"]}, 102 | "insertable" : true, 103 | "updatable" : false, 104 | "selectable" : false, 105 | "primary" : false, 106 | "default" : {"I" : {"value": "NOW" , "overwrite" : false } , "U" : {"value": "NOW" , "overwrite" : false } }, 107 | "encrypt" : false 108 | }, 109 | "updated" : { 110 | "datatype" : "timestamp", 111 | "tz_enabled" : true, 112 | "datalength": -1, 113 | "constraints" : ["REQUIRED"], 114 | "validate" : { "I" : ["REQUIRED","DATE"] , "U" : ["REQUIRED","DATE"]}, 115 | "insertable" : true, 116 | "updatable" : false, 117 | "selectable" : false, 118 | "primary" : false, 119 | "default" : {"I" : {"value": "NOW" , "overwrite" : false } , "U" : {"value": "NOW" , "overwrite" : false } }, 120 | "encrypt" : false 121 | } 122 | } 123 | }, 124 | "functions" : { 125 | "name" : "functions", 126 | "overwrite" : false , 127 | "columns" : { 128 | "name" : { 129 | "datatype" : "string", 130 | "datalength": -1, 131 | "constraints" : ["PRIMARY"], 132 | "validate" : { "I" : ["UNIQUE","REQUIRED"] , "U" : ["UNIQUE_EXCLUDE","REQUIRED"], "D" : []}, 133 | "insertable" : true, 134 | "updatable" : true, 135 | "selectable" : true, 136 | "primary" : false 137 | }, 138 | "id" : { 139 | "datatype" : "string", 140 | "datalength": -1, 141 | "constraints" : ["PRIMARY"], 142 | "validate" : { "I" : ["REQUIRED"] , "U" : ["EXISTS" , "REQUIRED"], "D" : ["EXISTS","REQUIRED"]}, 143 | "insertable" : true, 144 | "updatable" : false, 145 | "selectable" : true, 146 | "default" : {"I" : {"value": "UUID" , "overwrite" : false } }, 147 | "primary" : true 148 | }, 149 | "connection_id" : { 150 | "datatype" : "string", 151 | "datalength": -1, 152 | "constraints" : ["PRIMARY","$REF.CONNECTIONS(ID)"], 153 | "validate" : { "I" : ["REQUIRED","$REF.CONNECTIONS-id,connection_id"] , "U" : ["REQUIRED","$REF.CONNECTIONS-id,connection_id"], "D" : []}, 154 | "insertable" : true, 155 | "updatable" : true, 156 | "selectable" : true, 157 | "primary" : false 158 | }, 159 | "db_method" : { 160 | "datatype" : "string", 161 | "datalength": -1, 162 | "constraints" : ["REQUIRED"], 163 | "validate" : { "I" : ["REQUIRED"] , "U" : ["REQUIRED"], "D" : []}, 164 | "insertable" : true, 165 | "updatable" : false, 166 | "selectable" : true, 167 | "primary" : false 168 | }, 169 | "ref_source_id" : { 170 | "datatype" : "string", 171 | "datalength": -1, 172 | "constraints" : [], 173 | "insertable" : true, 174 | "updatable" : true, 175 | "selectable" : true, 176 | "primary" : false 177 | }, 178 | "created" : { 179 | "datatype" : "timestamp", 180 | "tz_enabled" : true, 181 | "datalength": -1, 182 | "constraints" : ["REQUIRED"], 183 | "validate" : { "I" : ["REQUIRED","DATE"]}, 184 | "insertable" : true, 185 | "updatable" : false, 186 | "selectable" : false, 187 | "primary" : false, 188 | "default" : {"I" : {"value": "NOW" , "overwrite" : false } , "U" : {"value": "NOW" , "overwrite" : false } }, 189 | "encrypt" : false 190 | }, 191 | "updated" : { 192 | "datatype" : "timestamp", 193 | "tz_enabled" : true, 194 | "datalength": -1, 195 | "constraints" : ["REQUIRED"], 196 | "validate" : { "I" : ["REQUIRED","DATE"] , "U" : ["REQUIRED","DATE"]}, 197 | "insertable" : true, 198 | "updatable" : false, 199 | "selectable" : false, 200 | "primary" : false, 201 | "default" : {"I" : {"value": "NOW" , "overwrite" : false } , "U" : {"value": "NOW" , "overwrite" : false } }, 202 | "encrypt" : false 203 | } 204 | } 205 | }, 206 | "routes" : { 207 | "name" : "routes", 208 | "overwrite" : false , 209 | "validate" : {"I" : ["UNIQUE(route_method,route_url)"],"U" : ["UNIQUE_EXCLUDE(route_method,route_url)"]}, 210 | "columns" : { 211 | "name" : { 212 | "datatype" : "string", 213 | "datalength": -1, 214 | "constraints" : ["PRIMARY"], 215 | "validate" : { "I" : ["UNIQUE","REQUIRED"] , "U" : ["UNIQUE_EXCLUDE","REQUIRED"], "D" : []}, 216 | "insertable" : true, 217 | "updatable" : true, 218 | "selectable" : true, 219 | "primary" : false 220 | }, 221 | "id" : { 222 | "datatype" : "string", 223 | "datalength": -1, 224 | "constraints" : ["PRIMARY"], 225 | "validate" : { "I" : ["REQUIRED"] , "U" : ["EXISTS" , "REQUIRED"], "D" : ["EXISTS","REQUIRED"]}, 226 | "insertable" : true, 227 | "updatable" : false, 228 | "selectable" : true, 229 | "default" : {"I" : {"value": "UUID" , "overwrite" : false } }, 230 | "primary" : true 231 | }, 232 | "function_id" : { 233 | "datatype" : "string", 234 | "datalength": -1, 235 | "constraints" : ["PRIMARY","$REF.FUNCTIONS(ID)"], 236 | "validate" : { "I" : ["REQUIRED","$REF.FUNCTIONS-id,function_id"] , "U" : ["REQUIRED","$REF.FUNCTIONS-id,function_id"], "D" : []}, 237 | "insertable" : true, 238 | "updatable" : true, 239 | "selectable" : true, 240 | "primary" : false 241 | }, 242 | "route_method" : { 243 | "datatype" : "string", 244 | "datalength": -1, 245 | "constraints" : ["REQUIRED"], 246 | "validate" : { "I" : ["REQUIRED", "LIST-GET;POST"] , "U" : ["REQUIRED", "LIST-GET;POST"], "D" : []}, 247 | "insertable" : true, 248 | "updatable" : true, 249 | "selectable" : true, 250 | "primary" : false 251 | }, 252 | "enabled_flag" : { 253 | "datatype" : "string", 254 | "datalength": -1, 255 | "constraints" : ["REQUIRED"], 256 | "validate" : { "I" : [] , "U" : ["REQUIRED"], "D" : []}, 257 | "insertable" : true, 258 | "updatable" : true, 259 | "selectable" : true, 260 | "primary" : false 261 | }, 262 | "route_url" : { 263 | "datatype" : "string", 264 | "datalength": -1, 265 | "constraints" : ["REQUIRED"], 266 | "validate" : { "I" : ["REQUIRED","URL","UNIQUE"] , "U" : ["REQUIRED","URL","UNIQUE_EXCLUDE"], "D" : []}, 267 | "insertable" : true, 268 | "updatable" : true, 269 | "selectable" : true, 270 | "primary" : false 271 | }, 272 | "ref_source_id" : { 273 | "datatype" : "string", 274 | "datalength": -1, 275 | "constraints" : [], 276 | "insertable" : true, 277 | "updatable" : true, 278 | "selectable" : true, 279 | "primary" : false 280 | }, 281 | "description" : { 282 | "datatype" : "string", 283 | "datalength": -1, 284 | "constraints" : [], 285 | "insertable" : true, 286 | "updatable" : true, 287 | "selectable" : true, 288 | "primary" : false 289 | }, 290 | "sample_response" : { 291 | "datatype" : "string", 292 | "datalength": -1, 293 | "constraints" : [], 294 | "insertable" : true, 295 | "updatable" : true, 296 | "selectable" : true, 297 | "primary" : false 298 | }, 299 | "sample_request" : { 300 | "datatype" : "string", 301 | "datalength": -1, 302 | "constraints" : [], 303 | "insertable" : true, 304 | "updatable" : true, 305 | "selectable" : true, 306 | "primary" : false 307 | }, 308 | "created" : { 309 | "datatype" : "timestamp", 310 | "tz_enabled" : true, 311 | "datalength": -1, 312 | "constraints" : ["REQUIRED"], 313 | "validate" : { "I" : ["REQUIRED","DATE"]}, 314 | "insertable" : true, 315 | "updatable" : false, 316 | "selectable" : false, 317 | "primary" : false, 318 | "default" : {"I" : {"value": "NOW" , "overwrite" : false } , "U" : {"value": "NOW" , "overwrite" : false } }, 319 | "encrypt" : false 320 | }, 321 | "updated" : { 322 | "datatype" : "timestamp", 323 | "tz_enabled" : true, 324 | "datalength": -1, 325 | "constraints" : ["REQUIRED"], 326 | "validate" : { "I" : ["REQUIRED","DATE"] , "U" : ["REQUIRED","DATE"]}, 327 | "insertable" : true, 328 | "updatable" : false, 329 | "selectable" : false, 330 | "primary" : false, 331 | "default" : {"I" : {"value": "NOW" , "overwrite" : false } , "U" : {"value": "NOW" , "overwrite" : false } }, 332 | "encrypt" : false 333 | } 334 | } 335 | }, 336 | "users" : { 337 | "name" : "users", 338 | "overwrite" : false , 339 | "validate" : {"I" : ["UNIQUE(username)"],"U" : []}, 340 | "columns" : { 341 | "username" : { 342 | "datatype" : "string", 343 | "datalength": -1, 344 | "constraints" : ["PRIMARY"], 345 | "validate" : { "I" : ["UNIQUE","REQUIRED"] , "U" : [], "D" : []}, 346 | "insertable" : true, 347 | "updatable" : false, 348 | "selectable" : true, 349 | "primary" : false 350 | }, 351 | "id" : { 352 | "datatype" : "string", 353 | "datalength": -1, 354 | "constraints" : ["PRIMARY"], 355 | "validate" : { "I" : ["REQUIRED"] , "U" : ["EXISTS" , "REQUIRED"], "D" : ["EXISTS","REQUIRED"]}, 356 | "insertable" : true, 357 | "updatable" : false, 358 | "selectable" : true, 359 | "default" : {"I" : {"value": "UUID" , "overwrite" : false } }, 360 | "primary" : true 361 | }, 362 | "first_name" : { 363 | "datatype" : "string", 364 | "datalength": -1, 365 | "constraints" : [], 366 | "validate" : { "I" : [] , "U" : [], "D" : []}, 367 | "insertable" : true, 368 | "updatable" : true, 369 | "selectable" : true, 370 | "primary" : false 371 | }, 372 | "last_name" : { 373 | "datatype" : "string", 374 | "datalength": -1, 375 | "constraints" : [], 376 | "validate" : { "I" : [] , "U" : [], "D" : []}, 377 | "insertable" : true, 378 | "updatable" : true, 379 | "selectable" : true, 380 | "primary" : false 381 | }, 382 | "password" : { 383 | "datatype" : "string", 384 | "datalength": -1, 385 | "constraints" : ["REQUIRED"], 386 | "validate" : { "I" : ["REQUIRED"] , "U" : [], "D" : []}, 387 | "insertable" : true, 388 | "updatable" : true, 389 | "selectable" : true, 390 | "primary" : false 391 | }, 392 | "ref_user_id" : { 393 | "datatype" : "string", 394 | "datalength": -1, 395 | "constraints" : [], 396 | "insertable" : true, 397 | "updatable" : true, 398 | "selectable" : true, 399 | "primary" : false 400 | }, 401 | "created" : { 402 | "datatype" : "timestamp", 403 | "tz_enabled" : true, 404 | "datalength": -1, 405 | "constraints" : ["REQUIRED"], 406 | "validate" : { "I" : ["REQUIRED","DATE"]}, 407 | "insertable" : true, 408 | "updatable" : false, 409 | "selectable" : false, 410 | "primary" : false, 411 | "default" : {"I" : {"value": "NOW" , "overwrite" : false } , "U" : {"value": "NOW" , "overwrite" : false } }, 412 | "encrypt" : false 413 | }, 414 | "updated" : { 415 | "datatype" : "timestamp", 416 | "tz_enabled" : true, 417 | "datalength": -1, 418 | "constraints" : ["REQUIRED"], 419 | "validate" : { "I" : ["REQUIRED","DATE"] , "U" : ["REQUIRED","DATE"]}, 420 | "insertable" : true, 421 | "updatable" : false, 422 | "selectable" : false, 423 | "primary" : false, 424 | "default" : {"I" : {"value": "NOW" , "overwrite" : false } , "U" : {"value": "NOW" , "overwrite" : false } }, 425 | "encrypt" : false 426 | } 427 | } 428 | }, 429 | "tokens" : { 430 | "name" : "users", 431 | "overwrite" : false , 432 | "validate" : {"I" : ["UNIQUE(username)"],"U" : []}, 433 | "columns" : { 434 | "token" : { 435 | "datatype" : "string", 436 | "datalength": -1, 437 | "constraints" : ["PRIMARY"], 438 | "validate" : { "I" : ["REQUIRED"] , "U" : ["EXISTS" , "REQUIRED"], "D" : ["EXISTS","REQUIRED"]}, 439 | "insertable" : true, 440 | "updatable" : false, 441 | "selectable" : true, 442 | "primary" : true 443 | }, 444 | "start_time" : { 445 | "datatype" : "timestamp", 446 | "tz_enabled" : true, 447 | "datalength": -1, 448 | "constraints" : ["REQUIRED"], 449 | "validate" : { "I" : ["REQUIRED","DATE"]}, 450 | "insertable" : true, 451 | "updatable" : false, 452 | "selectable" : false, 453 | "primary" : false, 454 | "default" : {"I" : {"value": "NOW" , "overwrite" : false } , "U" : {"value": "NOW" , "overwrite" : false } }, 455 | "encrypt" : false 456 | }, 457 | "user_id" : { 458 | "datatype" : "string", 459 | "tz_enabled" : true, 460 | "datalength": -1, 461 | "constraints" : ["REQUIRED"], 462 | "validate" : { "I" : ["REQUIRED"]}, 463 | "insertable" : true, 464 | "updatable" : false, 465 | "selectable" : false, 466 | "primary" : false, 467 | "encrypt" : false 468 | }, 469 | "timeout_time" : { 470 | "datatype" : "timestamp", 471 | "tz_enabled" : true, 472 | "datalength": -1, 473 | "constraints" : ["REQUIRED"], 474 | "validate" : { "I" : ["REQUIRED","DATE"] , "U" : ["REQUIRED","DATE"]}, 475 | "insertable" : true, 476 | "updatable" : false, 477 | "selectable" : false, 478 | "primary" : false, 479 | "encrypt" : false 480 | }, 481 | "created" : { 482 | "datatype" : "timestamp", 483 | "tz_enabled" : true, 484 | "datalength": -1, 485 | "constraints" : ["REQUIRED"], 486 | "validate" : { "I" : ["REQUIRED","DATE"]}, 487 | "insertable" : true, 488 | "updatable" : false, 489 | "selectable" : false, 490 | "primary" : false, 491 | "default" : {"I" : {"value": "NOW" , "overwrite" : false } , "U" : {"value": "NOW" , "overwrite" : false } }, 492 | "encrypt" : false 493 | }, 494 | "updated" : { 495 | "datatype" : "timestamp", 496 | "tz_enabled" : true, 497 | "datalength": -1, 498 | "constraints" : ["REQUIRED"], 499 | "validate" : { "I" : ["REQUIRED","DATE"] , "U" : ["REQUIRED","DATE"]}, 500 | "insertable" : true, 501 | "updatable" : false, 502 | "selectable" : false, 503 | "primary" : false, 504 | "default" : {"I" : {"value": "NOW" , "overwrite" : false } , "U" : {"value": "NOW" , "overwrite" : false } }, 505 | "encrypt" : false 506 | } 507 | } 508 | } 509 | } -------------------------------------------------------------------------------- /admin/install/db/functions/add_admin_user.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION add_admin_user( p_id text , p_hash text , p_password text) 2 | RETURNS json AS 3 | $BODY$ 4 | DECLARE 5 | l_out json; 6 | l_id text; 7 | BEGIN 8 | SELECT id 9 | INTO l_id 10 | FROM USERS 11 | WHERE username = 'admin'; 12 | IF l_id IS NULL OR l_id = '' THEN 13 | INSERT INTO USERS 14 | ( id 15 | , username 16 | , password 17 | , enabled_flag 18 | , created 19 | , updated 20 | ) 21 | VALUES ( 22 | p_id 23 | , 'admin' 24 | , p_password 25 | , 'Y' 26 | , now() 27 | , now() 28 | ); 29 | END IF; 30 | RETURN '{"status" : "SUCCESS" , "message" : "OK"}'; 31 | END 32 | $BODY$ 33 | LANGUAGE plpgsql; -------------------------------------------------------------------------------- /admin/install/db/functions/install_application.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION install_application(p_data json ) 2 | RETURNS json AS 3 | $BODY$ 4 | DECLARE 5 | l_out json; 6 | l_message_text text; 7 | l_exception_detail text; 8 | l_exception_hint text; 9 | l_connection_id text; 10 | l_functions_json json; 11 | l_routes_json json; 12 | 13 | functions_c CURSOR FOR SELECT json_array_elements(p_data->'functions'); 14 | routes_c CURSOR(p_functions json) FOR SELECT json_array_elements(p_functions->'routes'); 15 | l_function_record json; 16 | l_db_method text; 17 | l_name text; 18 | l_db_name text; 19 | l_datatype text; 20 | l_arguments text; 21 | l_id uuid; 22 | l_route_name text; 23 | l_route_method text; 24 | l_route_url text; 25 | l_sample_request text; 26 | l_sample_response text; 27 | l_application json; 28 | l_app_id text; 29 | l_app_name text; 30 | l_route_id uuid; 31 | l_function_id uuid; 32 | 33 | BEGIN 34 | SELECT id 35 | INTO l_connection_id 36 | FROM connections 37 | WHERE config_flag = 'Y' 38 | LIMIT 1; 39 | 40 | IF l_connection_id IS NULL OR l_connection_id = '' THEN 41 | RETURN '{"status":"E", "message" : "Unable to find default configuration"}'; 42 | END IF; 43 | 44 | BEGIN 45 | l_functions_json := (p_data->>'functions')::json; 46 | EXCEPTION WHEN OTHERS THEN 47 | RETURN '{"status":"E", "message" : "Invalid json input for functions tag"}'; 48 | END; 49 | 50 | BEGIN 51 | l_application := (p_data->>'application')::json; 52 | EXCEPTION WHEN OTHERS THEN 53 | RETURN '{"status":"E", "message" : "Invalid json input for application tag"}'; 54 | END; 55 | 56 | IF l_application IS NULL THEN 57 | RETURN '{"status":"E", "message" : "Invalid Application File. Missing Application Info"}'; 58 | END IF; 59 | 60 | BEGIN 61 | l_app_id := (l_application->>'identifier')::text; 62 | EXCEPTION WHEN OTHERS THEN 63 | RETURN '{"status":"E", "message" : "Invalid uuid input for application identifier tag"}'; 64 | END; 65 | 66 | l_app_name := (l_application->>'name')::text; 67 | 68 | IF l_app_id IS NULL OR l_app_id = '' THEN 69 | RETURN '{"status" : "E" , "message" : "Invalid Application File. Missing Application Identifier"}'; 70 | END IF; 71 | 72 | IF l_app_name IS NULL OR l_app_name = '' THEN 73 | RETURN '{"status" : "E" , "message" : "Invalid Application File. Missing Application Name"}'; 74 | END IF; 75 | 76 | IF EXISTS ( SELECT 1 FROM applications WHERE id = l_app_id) THEN 77 | RETURN '{"status" : "E" , "message" : "This Application ['|| l_app_name|| '] is already installed. If you want to re-install please delete record from the table applications for this identifier to re install the application."}'; 78 | END IF; 79 | 80 | IF l_functions_json IS NULL THEN 81 | RETURN '{"status" : "S" , "message" : "OK"}'; 82 | END IF; 83 | 84 | OPEN functions_c; 85 | 86 | LOOP 87 | -- fetch row into the film 88 | FETCH functions_c INTO l_function_record; 89 | -- exit when no more row to fetch 90 | EXIT WHEN NOT FOUND; 91 | 92 | RAISE INFO '%',l_function_record; 93 | 94 | l_name := (l_function_record->>'name')::text; 95 | l_db_method := (l_function_record->>'db_method')::text; 96 | 97 | IF l_name IS NULL OR l_name = '' THEN 98 | RETURN '{"status" : "E" , "message" : "Function Name need to be provided"}'; 99 | END IF; 100 | 101 | IF l_db_method IS NULL OR l_db_method = '' THEN 102 | RETURN '{"status" : "E" , "message" : "Database Function name need to be provided"}'; 103 | END IF; 104 | 105 | SELECT p.proname as name, 106 | pg_catalog.pg_get_function_result(p.oid) as result_datatype, 107 | pg_catalog.pg_get_function_arguments(p.oid) as arguments 108 | INTO l_db_name , l_datatype , l_arguments 109 | FROM pg_catalog.pg_proc p 110 | LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace 111 | WHERE pg_catalog.pg_function_is_visible(p.oid) 112 | AND n.nspname <> 'pg_catalog' 113 | AND n.nspname <> 'information_schema' 114 | AND LOWER(p.proname) = LOWER(l_db_method); 115 | 116 | IF l_db_name IS NULL OR l_db_name = '' THEN 117 | RETURN '{"status" : "E" , "message" : "Function ' ||l_db_method ||' does not exist"}'; 118 | END IF; 119 | 120 | IF LOWER(l_datatype) <> 'json' THEN 121 | RETURN '{"status" : "E" , "message" : "Function Return Type must be json"}'; 122 | END IF; 123 | 124 | IF l_arguments LIKE '%,%' THEN 125 | RETURN '{"status" : "E" , "message" : "Function Argument must contain only one argument and the datatype must be json"}'; 126 | END IF; 127 | 128 | OPEN routes_c(l_function_record); 129 | 130 | LOOP 131 | FETCH routes_c INTO l_routes_json; 132 | -- exit when no more row to fetch 133 | EXIT WHEN NOT FOUND; 134 | 135 | l_route_name := l_routes_json->>'name'; 136 | l_route_url := l_routes_json->>'route_url'; 137 | l_route_method := l_routes_json->>'route_method'; 138 | 139 | IF l_route_name IS NULL OR l_route_name = '' THEN 140 | RETURN '{"status" : "E" , "message" : "Route name must be provided"}'; 141 | END IF; 142 | 143 | IF l_route_url IS NULL OR l_route_url = '' THEN 144 | RETURN '{"status" : "E" , "message" : "Route url must be provided"}'; 145 | END IF; 146 | 147 | IF l_route_method IS NULL OR l_route_method = '' THEN 148 | RETURN '{"status" : "E" , "message" : "Route Method must be provided"}'; 149 | ELSE 150 | IF l_route_method NOT IN ('GET','POST') THEN 151 | RETURN '{"status" : "E" , "message" : "Invalid Route Method value. Only GET,POST is supported."}'; 152 | END IF; 153 | END IF; 154 | 155 | l_route_id := NULL; 156 | 157 | SELECT id 158 | INTO l_route_id 159 | FROM routes 160 | WHERE name = l_route_name; 161 | 162 | IF l_route_id IS NOT NULL THEN 163 | RETURN '{"status" : "E" , "message" : "Route Name alread exist. Please use different route name ['||l_route_name||']"}'; 164 | END IF; 165 | 166 | l_route_id := NULL; 167 | 168 | SELECT id 169 | INTO l_route_id 170 | FROM routes 171 | WHERE route_method = l_route_method 172 | AND route_url = l_route_url; 173 | 174 | IF l_route_id IS NOT NULL THEN 175 | RETURN '{"status" : "E" , "message" : "Route Url and the Route Method alread exist. Please use different route url or route method ['||l_route_url||','||l_route_method||']"}'; 176 | END IF; 177 | 178 | END LOOP; 179 | 180 | -- Close the cursor 181 | CLOSE routes_c; 182 | 183 | END LOOP; 184 | 185 | -- Close the cursor 186 | CLOSE functions_c; 187 | 188 | OPEN functions_c; 189 | 190 | LOOP 191 | -- fetch row into the film 192 | FETCH functions_c INTO l_function_record; 193 | -- exit when no more row to fetch 194 | EXIT WHEN NOT FOUND; 195 | 196 | RAISE INFO '%',l_function_record; 197 | 198 | l_function_id := md5(random()::text || clock_timestamp()::text)::uuid; 199 | l_name := (l_function_record->>'name')::text; 200 | l_db_method := (l_function_record->>'db_method')::text; 201 | 202 | 203 | INSERT INTO functions 204 | ( id 205 | , name 206 | , connection_id 207 | , db_method 208 | , created 209 | , updated 210 | ) VALUES ( 211 | l_function_id 212 | , l_name 213 | , l_connection_id 214 | , l_db_method 215 | , NOW() 216 | , NOW() 217 | ); 218 | 219 | OPEN routes_c(l_function_record); 220 | 221 | LOOP 222 | FETCH routes_c INTO l_routes_json; 223 | -- exit when no more row to fetch 224 | EXIT WHEN NOT FOUND; 225 | 226 | l_route_name := l_routes_json->>'name'; 227 | l_route_url := l_routes_json->>'route_url'; 228 | l_route_method := l_routes_json->>'route_method'; 229 | l_route_id := md5(random()::text || clock_timestamp()::text)::uuid; 230 | l_sample_request := l_routes_json->>'sample_request'; 231 | l_sample_response := l_routes_json->>'sample_response'; 232 | 233 | INSERT INTO routes 234 | ( id 235 | , name 236 | , function_id 237 | , route_method 238 | , route_url 239 | , enabled_flag 240 | , sample_request 241 | , sample_response 242 | , created 243 | , updated 244 | ) VALUES 245 | ( 246 | l_route_id 247 | , l_route_name 248 | , l_function_id 249 | , l_route_method 250 | , l_route_url 251 | , 'Y' 252 | , l_sample_request 253 | , l_sample_response 254 | , NOW() 255 | , NOW() 256 | ); 257 | END LOOP; 258 | 259 | -- Close the cursor 260 | CLOSE routes_c; 261 | 262 | END LOOP; 263 | 264 | -- Close the cursor 265 | CLOSE functions_c; 266 | 267 | INSERT INTO applications 268 | ( 269 | id 270 | , name 271 | , created 272 | , updated 273 | ) VALUES ( 274 | l_app_id 275 | , l_app_name 276 | , NOW() 277 | , NOW() 278 | ); 279 | 280 | 281 | 282 | RETURN '{"status" : "S" , "message" : "OK"}'; 283 | EXCEPTION WHEN OTHERS THEN 284 | GET STACKED DIAGNOSTICS l_message_text = MESSAGE_TEXT, 285 | l_exception_detail = PG_EXCEPTION_DETAIL, 286 | l_exception_hint = PG_EXCEPTION_HINT; 287 | l_out := '{ "status" : "E" , "message" : "' || REPLACE(l_message_text, '"', E'\\"') || '" }'; 288 | return l_out; 289 | END 290 | $BODY$ 291 | LANGUAGE plpgsql; -------------------------------------------------------------------------------- /admin/install/db/functions/validate_session.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION validate_session( p_token text 2 | ) 3 | RETURNS json AS 4 | $BODY$ 5 | DECLARE 6 | l_out json; 7 | l_token text; 8 | l_expire_time timestamp; 9 | l_user_id text; 10 | l_enabled_flag text; 11 | l_start_time timestamp; 12 | text_var1 text; 13 | text_var2 text; 14 | text_var3 text; 15 | l_detail text; 16 | BEGIN 17 | l_token := p_token; 18 | 19 | if (l_token IS NULL OR l_token = '' ) THEN 20 | l_out := '{ "status" : "E" , "message" : "SESSION_BAD_INPUT" }'; 21 | RETURN l_out; 22 | 23 | END IF; 24 | 25 | RAISE INFO '1 ' ; 26 | 27 | SELECT start_time , timeout_time , user_id 28 | INTO l_start_time , l_expire_time , l_user_id 29 | FROM tokens 30 | WHERE token = l_token; 31 | 32 | IF l_start_time IS NULL THEN 33 | l_out := '{ "status" : "E" , "message" : "SESSION_INVALID" }'; 34 | RETURN l_out; 35 | END IF; 36 | 37 | 38 | IF l_expire_time < now() THEN 39 | l_out := '{ "status" : "E" , "message" : "SESSION_EXPIRED" }'; 40 | RETURN l_out; 41 | 42 | END IF; 43 | 44 | 45 | 46 | SELECT enabled_flag 47 | INTO l_enabled_flag 48 | FROM USERS 49 | WHERE id = l_user_id; 50 | 51 | IF l_enabled_flag IS NULL OR l_enabled_flag = 'N' THEN 52 | l_out := '{ "status" : "E" , "message" : "INVALID_USER" }'; 53 | RETURN l_out; 54 | 55 | END IF; 56 | 57 | 58 | UPDATE tokens 59 | SET timeout_time = NOW() + interval '1h' * 1 60 | , updated = NOW() 61 | WHERE token = l_token; 62 | 63 | l_out := '{ "status" : "SUCCESS" , "message" : "OK"}'; 64 | return l_out; 65 | EXCEPTION WHEN OTHERS THEN 66 | GET STACKED DIAGNOSTICS text_var1 = MESSAGE_TEXT, 67 | text_var2 = PG_EXCEPTION_DETAIL, 68 | text_var3 = PG_EXCEPTION_HINT; 69 | l_out := '{ "status" : "ERROR" , "message" : "' || REPLACE(text_var1, '"', E'\\"') || '" }'; 70 | return l_out; 71 | END 72 | $BODY$ 73 | LANGUAGE plpgsql; -------------------------------------------------------------------------------- /admin/install/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of pgAPI. 3 | pgAPI - Database as a service 4 | Copyright (C) 2018 Praveen Muralidhar 5 | 6 | pgAPI is a free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | pgAPI is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const { Client } = require('pg') 21 | const utils = require('../Utils'); 22 | const path = require('path') 23 | const fs = require('fs'); 24 | const connectionController = require('../controllers/Connection'); 25 | const colors = require('colors'); 26 | 27 | process.env.VERSION = process.env.VERSION || "1.0.0"; 28 | 29 | const addAdminUser = async function () { 30 | const passwordHash = utils.encryptPassword("admin"); 31 | const uuid = utils.getUUID(); 32 | let sql = "SELECT add_admin_user($1, $2, $3) as result"; 33 | let params = [uuid,'',passwordHash]; 34 | let response = await utils.callSQL(sql,params); 35 | return response; 36 | } 37 | 38 | const startLog = function(filename) { 39 | // console.log("Compiling File : " + filename); 40 | } 41 | 42 | const endLog = function(filename) { 43 | //console.log("File compilation completed : " + filename); 44 | } 45 | 46 | const errorLog = function(filename) { 47 | console.log(`Error compiling File: ${filename}`.blue); 48 | } 49 | 50 | const loadDB = async function (demo) { 51 | 52 | console.log("pgAPI - Compiling admin table script...".blue) 53 | 54 | await utils.loadDDL (path.resolve(__dirname, './db/DB.sql'),startLog, endLog, errorLog); 55 | 56 | console.log("pgAPI - Compiling admin database functions...".blue) 57 | 58 | await utils.executeDBFunctionsFromDir(path.resolve(__dirname, './db/functions')); 59 | 60 | let response = await utils.getSQLResults("SELECT * FROM current_version",[]); 61 | 62 | if (response.length === 0) { 63 | let response = await utils.callSQL("INSERT INTO current_version(created,version) VALUES(NOW(),$1)",[process.env.VERSION]); 64 | await addAdminUser (); 65 | console.log("pgAPI - Adding default connection...".blue) 66 | await connectionController.addDefaultConnection(); 67 | 68 | if (process.env.DEMO_INSTALL === "Y") { 69 | const demoAppName = "tasks"; 70 | let response = await utils.installApplication(demoAppName); 71 | if (response.status === "S") { 72 | console.log(`pgAPI - Successfully installed application [${demoAppName}]`.green) 73 | } else { 74 | console.log(`pgAPI - Application installation failed.. [${demoAppName}] - ${response.message}`.yellow) 75 | } 76 | } 77 | } 78 | } 79 | 80 | module.exports = { 81 | loadDB: loadDB 82 | 83 | } -------------------------------------------------------------------------------- /admin/models/Connection.js: -------------------------------------------------------------------------------- 1 | const utils = require('../Utils'); 2 | const coreModel = require('../core/models/Connection'); 3 | 4 | 5 | const isValidDBConnection = function(record) { 6 | const client = utils.getClientbyRecord(record); 7 | client.connect(); 8 | } 9 | 10 | /* 11 | This file is part of pgAPI. 12 | pgAPI - Database as a service 13 | Copyright (C) 2018 Praveen Muralidhar 14 | 15 | pgAPI is a free software: you can redistribute it and/or modify 16 | it under the terms of the GNU General Public License as published by 17 | the Free Software Foundation, either version 3 of the License, or 18 | any later version. 19 | 20 | pgAPI is distributed in the hope that it will be useful, 21 | but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | GNU General Public License for more details. 24 | 25 | You should have received a copy of the GNU General Public License 26 | along with this program. If not, see . 27 | */ 28 | 29 | const validate = async function(record,mode, custom, pool) { 30 | 31 | const response = await coreModel.validate(record,mode,custom,pool); 32 | 33 | // write your custom code here 34 | 35 | return response; 36 | 37 | } 38 | 39 | 40 | const insert = async function(record) { 41 | 42 | // Before Insert: write your custom code here 43 | 44 | const response = await coreModel.insert(record); 45 | 46 | // After Insert: write your custom code here 47 | 48 | return response; 49 | 50 | } 51 | 52 | const update = async function(record) { 53 | 54 | // Before Update: write your custom code here 55 | 56 | const response = await coreModel.update(record); 57 | 58 | // After Update: write your custom code here 59 | 60 | return response; 61 | } 62 | 63 | const deleteRecord = async function(record) { 64 | 65 | //Before Delete: write your custom code here 66 | 67 | const response = await coreModel.delete(record); 68 | 69 | //After Delete: write your custom code here 70 | 71 | return response; 72 | } 73 | 74 | const fetchAll = async function() { 75 | 76 | //Before Fetch: write your custom code here 77 | 78 | const response = await coreModel.fetchAll(); 79 | 80 | //After Fetch: write your custom code here 81 | 82 | return response; 83 | } 84 | 85 | module.exports = { 86 | isValidDBConnection: isValidDBConnection, 87 | validate: validate, 88 | insert: insert, 89 | update: update, 90 | delete: deleteRecord, 91 | fetchAll: fetchAll 92 | } -------------------------------------------------------------------------------- /admin/models/Function.js: -------------------------------------------------------------------------------- 1 | const utils = require('../Utils'); 2 | const coreModel = require('../core/models/Function'); 3 | 4 | 5 | const validate = async function(record, mode, custom, pool) { 6 | 7 | const response = await coreModel.validate(record,mode, custom, pool); 8 | 9 | // write your custom code here 10 | 11 | return response; 12 | 13 | } 14 | 15 | 16 | const insert = async function(record) { 17 | 18 | // Before Insert: write your custom code here 19 | 20 | const response = await coreModel.insert(record); 21 | 22 | // After Insert: write your custom code here 23 | 24 | return response; 25 | 26 | } 27 | 28 | const update = async function(record) { 29 | 30 | // Before Update: write your custom code here 31 | 32 | const response = await coreModel.update(record); 33 | 34 | // After Update: write your custom code here 35 | 36 | return response; 37 | } 38 | 39 | const deleteRecord = async function(record) { 40 | 41 | //Before Delete: write your custom code here 42 | 43 | const response = await coreModel.delete(record); 44 | 45 | //After Delete: write your custom code here 46 | 47 | return response; 48 | } 49 | 50 | const fetchAll = async function() { 51 | 52 | //Before Fetch: write your custom code here 53 | 54 | const response = await coreModel.fetchAll(); 55 | 56 | //After Fetch: write your custom code here 57 | 58 | return response; 59 | } 60 | 61 | module.exports = { 62 | validate: validate, 63 | insert: insert, 64 | update: update, 65 | delete: deleteRecord, 66 | fetchAll: fetchAll 67 | } -------------------------------------------------------------------------------- /admin/models/Route.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of pgAPI. 3 | pgAPI - Database as a service 4 | Copyright (C) 2018 Praveen Muralidhar 5 | 6 | pgAPI is a free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | pgAPI is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const utils = require('../Utils'); 21 | const coreModel = require('../core/models/Route'); 22 | 23 | 24 | const validate = async function(record,mode,custom,pool) { 25 | 26 | const response = await coreModel.validate(record,mode,custom,pool); 27 | 28 | let sql = ""; 29 | let params = ""; 30 | let status = "S"; 31 | let message = "OK"; 32 | 33 | // write your custom code here 34 | if (custom) { 35 | if (response.status === "S" && ( mode === "I" || mode === "U")) { 36 | if (mode === "I") { 37 | sql = "SELECT 1 FROM routes WHERE route_method = $1 AND route_url = $2"; 38 | params = [record.route_method,record.route_url]; 39 | } else if (mode === "U") { 40 | sql = "SELECT 1 FROM routes WHERE route_method = $1 AND route_url = $2 AND id!= $3"; 41 | params = [record.route_method,record.route_url, record.id]; 42 | } 43 | const resCount = await utils.checkAttribute(sql,params); 44 | if (resCount > 0) { 45 | status = "E"; 46 | message = "Unique Validation failed for the columns[route_method,route_url]"; 47 | return {status: status, message: message}; 48 | } 49 | } 50 | } 51 | 52 | return response; 53 | 54 | } 55 | 56 | 57 | const insert = async function(record) { 58 | 59 | // Before Insert: write your custom code here 60 | 61 | const response = await coreModel.insert(record); 62 | 63 | // After Insert: write your custom code here 64 | 65 | return response; 66 | 67 | } 68 | 69 | const update = async function(record) { 70 | 71 | // Before Update: write your custom code here 72 | 73 | const response = await coreModel.update(record); 74 | 75 | // After Update: write your custom code here 76 | 77 | return response; 78 | } 79 | 80 | const deleteRecord = async function(record) { 81 | 82 | //Before Delete: write your custom code here 83 | 84 | const response = await coreModel.delete(record); 85 | 86 | //After Delete: write your custom code here 87 | 88 | return response; 89 | } 90 | 91 | const fetchAll = async function() { 92 | 93 | //Before Fetch: write your custom code here 94 | 95 | const response = await coreModel.fetchAll(); 96 | 97 | //After Fetch: write your custom code here 98 | 99 | return response; 100 | } 101 | 102 | const fetchById = async function(id) { 103 | 104 | //Before Fetch: write your custom code here 105 | 106 | const response = await coreModel.fetchById(id); 107 | 108 | //After Fetch: write your custom code here 109 | 110 | return response; 111 | } 112 | 113 | const getRoutes = async function() { 114 | const sql = "select r.id route_id , r.enabled_flag, 'json' as content_type , r.route_method , r.route_url , f.db_method as route_callback, c.database , c.host , c.username , c.port , c.password , c.name as connection_name " + 115 | " from routes r " + 116 | " , functions f " + 117 | " , connections c " + 118 | " where r.function_id = f.id " + 119 | " and c.id = f.connection_id "; 120 | 121 | let out = {}; 122 | 123 | try { 124 | 125 | return await utils.getSQLResultswithStatus(sql,[]); 126 | } catch (e) { 127 | console.log("Error"); 128 | return e; 129 | } 130 | } 131 | 132 | const getRoutesByUrl = async function(url) { 133 | const sql = "select r.id route_id , 'json' as content_type , r.route_method , r.route_url , f.db_method as route_callback, c.database , c.host , c.username , c.port , c.password , c.name as connection_name " + 134 | " from routes r " + 135 | " , functions f " + 136 | " , connections c " + 137 | " where r.function_id = f.id " + 138 | " and c.id = f.connection_id and r.route_url = $1"; 139 | 140 | let out = {}; 141 | 142 | try { 143 | return await utils.getSQLResultswithStatus(sql,[url]); 144 | } catch (e) { 145 | console.log("Error"); 146 | return e; 147 | } 148 | } 149 | 150 | 151 | module.exports = { 152 | validate: validate, 153 | insert: insert, 154 | update: update, 155 | delete: deleteRecord, 156 | fetchAll: fetchAll, 157 | getRoutes: getRoutes, 158 | fetchById: fetchById, 159 | getRoutesByUrl: getRoutesByUrl 160 | } -------------------------------------------------------------------------------- /admin/models/User.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of pgAPI. 3 | pgAPI - Database as a service 4 | Copyright (C) 2018 Praveen Muralidhar 5 | 6 | pgAPI is a free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | pgAPI is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const utils = require('../Utils'); 21 | const coreModel = require('../core/models/User'); 22 | 23 | const validate = async function(record, mode, custom, pool) { 24 | 25 | const response = await coreModel.validate(record,mode, custom, pool); 26 | 27 | // write your custom code here 28 | 29 | return response; 30 | 31 | } 32 | 33 | const insert = async function(record) { 34 | 35 | // Before Insert: write your custom code here 36 | 37 | const response = await coreModel.insert(record); 38 | 39 | // After Insert: write your custom code here 40 | 41 | return response; 42 | 43 | } 44 | 45 | const insertArchive = async function(record) { 46 | 47 | // Before Insert: write your custom code here 48 | 49 | const response = await coreModel.insertArchive(record); 50 | 51 | // After Insert: write your custom code here 52 | 53 | return response; 54 | 55 | } 56 | 57 | const insertToken = async function(record) { 58 | 59 | // Before Insert: write your custom code here 60 | 61 | const response = await coreModel.insertToken(record); 62 | 63 | // After Insert: write your custom code here 64 | 65 | return response; 66 | 67 | } 68 | 69 | const update = async function(record) { 70 | 71 | // Before Update: write your custom code here 72 | 73 | const response = await coreModel.update(record); 74 | 75 | // After Update: write your custom code here 76 | 77 | return response; 78 | } 79 | 80 | const updatePassword = async function(record) { 81 | 82 | // Before Update: write your custom code here 83 | 84 | const response = await coreModel.updatePassword(record); 85 | 86 | // After Update: write your custom code here 87 | 88 | return response; 89 | } 90 | 91 | const updateToken = async function(record) { 92 | 93 | // Before Update: write your custom code here 94 | 95 | const response = await coreModel.updateToken(record); 96 | 97 | // After Update: write your custom code here 98 | 99 | return response; 100 | } 101 | 102 | const updateEnabledFlag = async function(record) { 103 | 104 | // Before Update: write your custom code here 105 | 106 | const response = await coreModel.updateEnabledFlag(record); 107 | 108 | // After Update: write your custom code here 109 | 110 | return response; 111 | } 112 | 113 | const deleteRecord = async function(record) { 114 | 115 | //Before Delete: write your custom code here 116 | 117 | const response = await coreModel.delete(record); 118 | 119 | //After Delete: write your custom code here 120 | 121 | return response; 122 | } 123 | 124 | const fetchAll = async function() { 125 | 126 | //Before Fetch: write your custom code here 127 | 128 | const response = await coreModel.fetchAll(); 129 | 130 | //After Fetch: write your custom code here 131 | 132 | return response; 133 | } 134 | 135 | const fetchById = async function(id) { 136 | 137 | //Before Fetch: write your custom code here 138 | 139 | const response = await coreModel.fetchById(id); 140 | 141 | //After Fetch: write your custom code here 142 | 143 | return response; 144 | } 145 | 146 | 147 | const fetchByIdUser = async function(id) { 148 | 149 | //Before Fetch: write your custom code here 150 | 151 | const response = await coreModel.fetchByIdUser(id); 152 | 153 | //After Fetch: write your custom code here 154 | 155 | return response; 156 | } 157 | 158 | const fetchByUsername = async function(username) { 159 | 160 | //Before Fetch: write your custom code here 161 | 162 | const response = await coreModel.fetchByUsername(username); 163 | 164 | //After Fetch: write your custom code here 165 | 166 | return response; 167 | } 168 | 169 | const fetchToken = async function(token) { 170 | 171 | //Before Fetch: write your custom code here 172 | 173 | const response = await coreModel.fetchToken({token:token}); 174 | 175 | //After Fetch: write your custom code here 176 | 177 | return response; 178 | } 179 | 180 | 181 | const validateSession = async function(record) { 182 | 183 | //Before Fetch: write your custom code here 184 | 185 | const response = await coreModel.validateSession(record); 186 | 187 | //After Fetch: write your custom code here 188 | 189 | return response; 190 | } 191 | 192 | module.exports = { 193 | insert: insert, 194 | insertArchive: insertArchive, 195 | insertToken: insertToken, 196 | update: update, 197 | updatePassword: updatePassword, 198 | updateToken: updateToken, 199 | delete: deleteRecord, 200 | fetchAll: fetchAll, 201 | fetchById: fetchById, 202 | fetchToken: fetchToken, 203 | fetchByUsername: fetchByUsername, 204 | fetchByIdUser: fetchByIdUser, 205 | validate: validate, 206 | updateEnabledFlag: updateEnabledFlag, 207 | validateSession: validateSession 208 | } -------------------------------------------------------------------------------- /admin/router/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | router = express.Router(); 3 | 4 | 5 | var connection = require('../controllers/Connection'); 6 | var _function = require('../controllers/Function'); 7 | var routes = require('../controllers/Route'); 8 | var dashboard = require('../controllers/Dashboard'); 9 | let user = require('../controllers/User'); 10 | const dbUtils = require('../controllers/dbUtils'); 11 | const utils = require('../Utils'); 12 | 13 | router.use(async function(req, res, next) { 14 | 15 | // Website you wish to allow to connect 16 | res.setHeader('Access-Control-Allow-Origin', '*'); 17 | // Request methods you wish to allow 18 | res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE'); 19 | 20 | // Request headers you wish to allow 21 | res.setHeader('Access-Control-Allow-Headers', 'Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization'); 22 | 23 | res.setHeader("Access-Control-Allow-Credentials", "true"); 24 | 25 | let authRequired = true; 26 | 27 | if (req.originalUrl === '/admin/api/login') { 28 | authRequired = false; 29 | } 30 | 31 | if (authRequired) { 32 | if (!req.headers.authorization) { 33 | return res.status(403).json({ error: 'No credentials sent!' }); 34 | } 35 | 36 | if (!!req.headers.authorization) { 37 | let token = req.headers.authorization; 38 | token = token.replace("Bearer",'').trim(); 39 | let response = await user.validateSession(token); 40 | 41 | if (!!response.decoded && !!response.decoded.claims) { 42 | req.claims = response.decoded.claims; 43 | } 44 | 45 | if (response.status === "E") { 46 | return res.status(403).json({ error: response.code }); 47 | } 48 | 49 | } 50 | } 51 | 52 | next(); 53 | }); 54 | 55 | router.get('/connections', connection.fetch ); 56 | router.put('/connection/:id', connection.update); 57 | router.post('/connection', connection.createAPI); 58 | router.delete('/connection/:id', connection.delete); 59 | router.get('/connection/test', connection.testDBConnection); 60 | router.get('/connections/status', connection.testDBConnections); 61 | 62 | router.get('/functions', _function.fetch ); 63 | router.put('/function/:id', _function.update); 64 | router.post('/function', _function.createAPI); 65 | router.delete('/function/:id', _function.delete); 66 | router.get('/functions/status', _function.testDBFunctions ); 67 | 68 | router.get('/routes', routes.fetch ); 69 | router.put('/route/:id', routes.update); 70 | router.post('/route', routes.createAPI); 71 | router.post('/routes/reload', routes.refreshRoutes); 72 | router.delete('/route/:id', routes.delete); 73 | 74 | router.get('/export', dashboard.exportConfig ); 75 | router.post('/import', dashboard.importConfig ); 76 | router.post('/delete', dashboard.deleteConfig ); 77 | 78 | router.post('/login', user.loginAPI ); 79 | router.post('/generatetoken', user.sessionAPI ); 80 | router.get('/user', user.fetchAPI ); 81 | router.put('/user', user.updateUserAPI ); 82 | router.post('/password/reset', user.resetPasswordAPI ); 83 | 84 | router.get('/db/function/code', dbUtils.getDBFunction); 85 | router.post('/db/function/execute', dbUtils.executeFunction); 86 | router.post('/db/sql/save', dbUtils.saveSQL); 87 | router.get('/db/sql/get', dbUtils.getSQL); 88 | 89 | module.exports = router; -------------------------------------------------------------------------------- /admin/sql/main.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION install_task_demo(p_data json ) 2 | RETURNS json AS 3 | $BODY$ 4 | DECLARE 5 | l_out json; 6 | l_connection_id text; 7 | l_functions_json json; 8 | l_routes_json json; 9 | 10 | functions_c CURSOR FOR SELECT json_array_elements(p_data->'functions'); 11 | routes_c CURSOR FOR SELECT json_array_elements(p_data->'routes'); 12 | l_function_record json; 13 | l_db_method text; 14 | l_name text; 15 | l_db_name text; 16 | l_datatype text; 17 | l_arguments text; 18 | l_id uuid; 19 | l_route_name text; 20 | l_route_method text; 21 | l_route_url text; 22 | l_route_id uuid; 23 | 24 | BEGIN 25 | SELECT id 26 | INTO l_connection_id 27 | FROM connections 28 | WHERE config_flag = 'Y' 29 | LIMIT 1; 30 | 31 | IF l_connection_id IS NULL OR l_connection_id = '' THEN 32 | RETURN '{"status":"E", "message" : "Unable to find default configuration"}'; 33 | END IF; 34 | 35 | l_functions_json := (p_data->>'functions')::json; 36 | 37 | RAISE INFO '%',l_functions_json; 38 | 39 | IF l_functions_json IS NULL THEN 40 | RETURN '{"status" : "S" , "message" : "OK"}'; 41 | END IF; 42 | 43 | OPEN functions_c; 44 | 45 | LOOP 46 | -- fetch row into the film 47 | FETCH functions_c INTO l_function_record; 48 | -- exit when no more row to fetch 49 | EXIT WHEN NOT FOUND; 50 | 51 | RAISE INFO '%',l_function_record; 52 | 53 | ç := (l_function_record->>'name')::text; 54 | l_db_method := (l_function_record->>'db_method')::text; 55 | 56 | IF l_name IS NULL OR l_name = '' THEN 57 | RETURN '{"status" : "E" , "message" : "Function Name need to be provided"}'; 58 | END IF; 59 | 60 | IF l_db_method IS NULL OR l_db_method = '' THEN 61 | RETURN '{"status" : "E" , "message" : "Function Name need to be provided"}'; 62 | END IF; 63 | 64 | SELECT p.proname as name, 65 | pg_catalog.pg_get_function_result(p.oid) as result_datatype, 66 | pg_catalog.pg_get_function_arguments(p.oid) as arguments 67 | INTO l_db_name , l_datatype , l_arguments 68 | FROM pg_catalog.pg_proc p 69 | LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace 70 | WHERE pg_catalog.pg_function_is_visible(p.oid) 71 | AND n.nspname <> 'pg_catalog' 72 | AND n.nspname <> 'information_schema' 73 | AND LOWER(p.proname) = LOWER(l_db_method); 74 | 75 | IF l_db_name IS NULL OR l_db_name = '' THEN 76 | RETURN '{"status" : "E" , "message" : "Function ' ||l_db_method ||' does not exist"}'; 77 | END IF; 78 | 79 | IF LOWER(l_datatype) <> 'json' THEN 80 | RETURN '{"status" : "E" , "message" : "Function Return Type must be json"}'; 81 | END IF; 82 | 83 | IF ',' IN l_arguments THEN 84 | RETURN '{"status" : "E" , "message" : "Function Argument must contain only one argument and the datatype must be json"}'; 85 | END IF; 86 | 87 | 88 | END LOOP; 89 | 90 | -- Close the cursor 91 | CLOSE functions_c; 92 | 93 | OPEN routes_c; 94 | 95 | LOOP 96 | FETCH routes_c INTO l_routes_json; 97 | -- exit when no more row to fetch 98 | EXIT WHEN NOT FOUND; 99 | 100 | l_route_name := l_routes_json->>'name'; 101 | l_route_url := l_routes_json->>'route_url'; 102 | l_route_method := l_routes_json->>'route_method'; 103 | 104 | IF l_route_name IS NULL OR l_route_name = '' THEN 105 | RETURN '{"status" : "E" , "message" : "Route name must be provided"}'; 106 | END IF; 107 | 108 | IF l_route_url IS NULL OR l_route_url = '' THEN 109 | RETURN '{"status" : "E" , "message" : "Route url must be provided"}'; 110 | END IF; 111 | 112 | IF l_route_method IS NULL OR l_route_method = '' THEN 113 | RETURN '{"status" : "E" , "message" : "Route Method must be provided"}'; 114 | ELSE 115 | IF l_route_method IN ('GET','POST') THEN 116 | RETURN '{"status" : "E" , "message" : "Invalid Route Method value. Only GET,POST is supported."}'; 117 | END IF; 118 | END IF; 119 | 120 | l_route_id := NULL; 121 | 122 | SELECT id 123 | INTO l_route_id 124 | FROM routes 125 | WHERE name = l_route_name; 126 | 127 | IF l_route_id IS NOT NULL THEN 128 | RETURN '{"status" : "E" , "message" : "Route Name alread exist. Please use different route name ['||l_route_name||']"}'; 129 | END IF; 130 | 131 | l_route_id := NULL; 132 | 133 | SELECT id 134 | INTO l_route_id 135 | FROM routes 136 | WHERE route_method = l_route_method 137 | AND route_url = l_route_url; 138 | 139 | IF l_route_id IS NOT NULL THEN 140 | RETURN '{"status" : "E" , "message" : "Route Url and the Route Method alread exist. Please use different route url or route method ['||l_route_url||','||l_route_method||']"}'; 141 | END IF; 142 | 143 | SELECT 144 | 145 | END LOOP; 146 | 147 | -- Close the cursor 148 | CLOSE routes_c; 149 | 150 | l_id := md5(random()::text || clock_timestamp()::text)::uuid; 151 | 152 | 153 | 154 | RETURN '{"status" : "S" , "message" : "OK"}'; 155 | END 156 | $BODY$ 157 | LANGUAGE plpgsql; 158 | 159 | select install_task_demo('{"functions" : [{"name" : "sdsd"}]}') 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | -------------------------------------------------------------------------------- /dist/admin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | pgAPI - Database as a Service 7 | 8 | 9 | 10 | 67 | 68 | 69 |
70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /dist/admin/pgAPI-icon.5a767025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thrinz/pgapi/e27510461c95017e357f8a46dbd76a7376ac206f/dist/admin/pgAPI-icon.5a767025.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | /* This file is part of pgAPI. 3 | pgAPI - Database as a service 4 | Copyright (C) 2018 Praveen Muralidhar 5 | 6 | pgAPI is a free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | pgAPI is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const path = require('path'); 21 | const utils = require('./admin/Utils'); 22 | const colors = require('colors'); 23 | const install = require('./admin/install'); 24 | const routeHandler = require('./admin/controllers/Route'); 25 | 26 | const routes = () => { 27 | } 28 | 29 | const adminRouter = () => { 30 | const adminRouter = require('./admin/router'); 31 | return adminRouter; 32 | } 33 | 34 | const apiRouter = (router) => { 35 | global.pool = {}; 36 | global.router = router; 37 | const s = routeHandler.getRoutes(); 38 | } 39 | 40 | const initialize = async (input) => { 41 | try { 42 | if (!!input && !!input.DB_HOST 43 | && !!input.DB_USER 44 | && !!input.DB_PASSWORD 45 | && !!input.DB_NAME 46 | && !!input.DB_PORT 47 | && !!input.PGAPI_SECRET_KEY 48 | ) { 49 | utils.loadRuntimeEnvVariables({ 50 | DB_HOST : input.DB_HOST, 51 | DB_USER: input.DB_USER, 52 | DB_PASSWORD: input.DB_PASSWORD, 53 | DB_NAME: input.DB_NAME, 54 | DB_PORT: input.DB_PORT, 55 | PGAPI_SECRET_KEY: input.PGAPI_SECRET_KEY, 56 | DEMO_INSTALL: input.DEMO_INSTALL, 57 | SMS_PROVIDER: input.SMS_PROVIDER 58 | }); 59 | 60 | // let ssl = false; 61 | 62 | // if (!!input.DB_SSL && input.DB_SSL === 'Y') { 63 | // ssl = true; 64 | // } 65 | 66 | let response = await utils.validateConnection({ 67 | host : input.DB_HOST, 68 | port : input.DB_PORT, 69 | database : input.DB_NAME, 70 | username : input.DB_USER, 71 | password : input.DB_PASSWORD, 72 | }); 73 | 74 | if (response.status === "E") { 75 | throw("pgAPI - failed to initialize - invalid database connection information"); 76 | } 77 | 78 | await install.loadDB(true); 79 | 80 | } else { 81 | throw("pgAPI - failed to initialize - invalid input parameters"); 82 | } 83 | 84 | } catch(e) { 85 | throw(e) 86 | } 87 | 88 | } 89 | 90 | const clientSourcePath = () => { 91 | return path.join(path.resolve(__dirname, './dist')); 92 | } 93 | 94 | module.exports = { 95 | routes, 96 | adminRouter, 97 | initialize, 98 | clientSourcePath, 99 | apiRouter 100 | } 101 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@thrinz/pgapi", 3 | "version": "1.0.6", 4 | "description": "pgAPI - Database as a service", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Praveen Muralidhar (thrinz@gmail.com)", 10 | "license": "GPL-3.0-or-later", 11 | "dependencies": { 12 | "axios": "^0.18.1", 13 | "babel-polyfill": "^6.26.0", 14 | "bcryptjs": "^2.4.3", 15 | "body-parser": "^1.18.3", 16 | "colors": "^1.3.2", 17 | "commander": "^2.19.0", 18 | "dotenv": "^5.0.1", 19 | "express": "^4.16.3", 20 | "find-up": "^3.0.0", 21 | "inquirer": "^6.2.0", 22 | "jsonwebtoken": "^8.3.0", 23 | "material-design-icons-iconfont": "^3.0.3", 24 | "moment": "^2.22.2", 25 | "parcel-bundler": "^1.12.3", 26 | "pg": "^8.9.0", 27 | "twilio": "^3.35.1", 28 | "uuid": "^3.3.2", 29 | "vue-highlightjs": "^1.3.3", 30 | "vue2-ace-editor": "0.0.11", 31 | "vuetify": "^1.4.0" 32 | }, 33 | "devDependencies": { 34 | "@vue/component-compiler-utils": "^1.0.0", 35 | "autoprefixer": "^8.3.0", 36 | "babel-core": "^6.26.3", 37 | "babel-plugin-transform-runtime": "^6.23.0", 38 | "babel-preset-env": "^1.6.1", 39 | "nodemon": "^1.17.3", 40 | "npm-run-all": "^4.1.2", 41 | "postcss-modules": "^1.1.0", 42 | "vue": "^2.5.16", 43 | "vue-hot-reload-api": "^2.3.0", 44 | "vue-router": "^3.0.1", 45 | "vue-template-compiler": "^2.5.16", 46 | "vuex": "^3.0.1" 47 | } 48 | } 49 | --------------------------------------------------------------------------------