├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── app.js ├── config └── settings.json.sample ├── docker-dev.md ├── docker-scripts └── launch-suez.sh └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/windows,macos,osx,linux,node 3 | 4 | ### Linux ### 5 | *~ 6 | 7 | # temporary files which can be created if a process still has a handle open of a deleted file 8 | .fuse_hidden* 9 | 10 | # KDE directory preferences 11 | .directory 12 | 13 | # Linux trash folder which might appear on any partition or disk 14 | .Trash-* 15 | 16 | # .nfs files are created when an open file is removed but is still being accessed 17 | .nfs* 18 | 19 | ### macOS ### 20 | *.DS_Store 21 | .AppleDouble 22 | .LSOverride 23 | 24 | # Icon must end with two \r 25 | Icon 26 | 27 | # Thumbnails 28 | ._* 29 | 30 | # Files that might appear in the root of a volume 31 | .DocumentRevisions-V100 32 | .fseventsd 33 | .Spotlight-V100 34 | .TemporaryItems 35 | .Trashes 36 | .VolumeIcon.icns 37 | .com.apple.timemachine.donotpresent 38 | 39 | # Directories potentially created on remote AFP share 40 | .AppleDB 41 | .AppleDesktop 42 | Network Trash Folder 43 | Temporary Items 44 | .apdisk 45 | 46 | ### Node ### 47 | # Logs 48 | logs 49 | *.log 50 | npm-debug.log* 51 | yarn-debug.log* 52 | yarn-error.log* 53 | 54 | # Runtime data 55 | pids 56 | *.pid 57 | *.seed 58 | *.pid.lock 59 | 60 | # Directory for instrumented libs generated by jscoverage/JSCover 61 | lib-cov 62 | 63 | # Coverage directory used by tools like istanbul 64 | coverage 65 | 66 | # nyc test coverage 67 | .nyc_output 68 | 69 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 70 | .grunt 71 | 72 | # Bower dependency directory (https://bower.io/) 73 | bower_components 74 | 75 | # node-waf configuration 76 | .lock-wscript 77 | 78 | # Compiled binary addons (http://nodejs.org/api/addons.html) 79 | build/Release 80 | 81 | # Dependency directories 82 | node_modules/ 83 | jspm_packages/ 84 | 85 | # Typescript v1 declaration files 86 | typings/ 87 | 88 | # Optional npm cache directory 89 | .npm 90 | 91 | # Optional eslint cache 92 | .eslintcache 93 | 94 | # Optional REPL history 95 | .node_repl_history 96 | 97 | # Output of 'npm pack' 98 | *.tgz 99 | 100 | # Yarn Integrity file 101 | .yarn-integrity 102 | 103 | # dotenv environment variables file 104 | .env 105 | 106 | 107 | ### OSX ### 108 | 109 | # Icon must end with two \r 110 | 111 | # Thumbnails 112 | 113 | # Files that might appear in the root of a volume 114 | 115 | # Directories potentially created on remote AFP share 116 | 117 | ### Windows ### 118 | # Windows thumbnail cache files 119 | Thumbs.db 120 | ehthumbs.db 121 | ehthumbs_vista.db 122 | 123 | # Folder config file 124 | Desktop.ini 125 | 126 | # Recycle Bin used on file shares 127 | $RECYCLE.BIN/ 128 | 129 | # Windows Installer files 130 | *.cab 131 | *.msi 132 | *.msm 133 | *.msp 134 | 135 | # Windows shortcuts 136 | *.lnk 137 | 138 | # End of https://www.gitignore.io/api/windows,macos,osx,linux,node 139 | 140 | # Project-specific files 141 | config/*.json -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Original source from https://hub.docker.com/_/node/ 2 | FROM keymetrics/pm2:latest-alpine 3 | LABEL maintainer="Martin DSouza " 4 | 5 | 6 | # WATCH: If true, will watch for changes in settings.json and restart the node.js app 7 | ENV WATCH="false" 8 | 9 | WORKDIR /app 10 | USER root 11 | RUN apk add \ 12 | git && \ 13 | chmod 777 /app 14 | 15 | USER node 16 | 17 | # For development 18 | # ENV TZ="GMT" \ 19 | # RUN cd suez && \ 20 | # VOLUME ["/app/suez"] 21 | 22 | # RUN TODO RESTORE git clone https://github.com/fuzziebrain/suez.git && \ 23 | RUN git clone https://github.com/martindsouza/suez.git && \ 24 | cd suez && \ 25 | rm -rf config && \ 26 | npm install 27 | 28 | # Volumes: 29 | VOLUME ["/app/suez/config"] 30 | 31 | # Ports 32 | EXPOSE 3000 33 | 34 | # Enable this if you want the container to permanently run 35 | CMD ["/app/suez/docker-scripts/launch-suez.sh"] 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Adrian Png 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | - [Introduction](#introduction) 4 | - [Software Requirements](#software-requirements) 5 | - [Installation and Deployment](#installation-and-deployment) 6 | - [Deployment](#deployment) 7 | - [Configuration](#configuration) 8 | - [Usage](#usage) 9 | - [Known Issues](#known-issues) 10 | - [Docker Support](#docker-support) 11 | - [Get Docker image](#get-docker-image) 12 | - [Run Container](#run-container) 13 | - [Container Parameters](#container-parameters) 14 | 15 | 16 | 17 | # Introduction 18 | Suez is a simple [Nodejs](https://nodejs.org/) application for proxying to web resources such as RESTful web services. While accessing web resources over a secure transport layer is strongly encouraged, certain software and budget limitations may prohibit us from adopting security best practices. 19 | 20 | Suez was written specifically as a workaround for [Oracle Application Express](https://apex.oracle.com/) developers who use and deploy applications to an [Oracle Database Express Edition](http://www.oracle.com/technetwork/database/database-technologies/express-edition/) database. The database software is provided free by Oracle and hence, lacks continued software support/upgrades and tools for managing the Oracle Wallet and SSL certificates. 21 | 22 | To circumvent this limitation, Suez acts as a bridge between secured web services and an APEX application. 23 | 24 | # Software Requirements 25 | 26 | * Node.js(r) - should run on both LTS and non-LTS versions 27 | 28 | # Installation and Deployment 29 | 30 | _Note see [Docker Support](#docker-support) if you want to use SUEZ's docker container instead._ 31 | 32 | *The following instructions are based on a CentOS 7 environment as recommended in the [OXAR](https://github.com/OraOpenSource/OXAR) project. Please adapt based on the operating system available to you.* 33 | 34 | 1. In the destination directory, e.g. `/opt`, clone the project. 35 | ```bash 36 | $ git clone https://github.com/fuzziebrain/suez.git 37 | ``` 38 | 39 | 2. Make a copy of the sample configuration file and adapt as needed. Please see 40 | the configuration section for details. 41 | 42 | 3. Run the application: 43 | ```bash 44 | $ node /opt/suez/app.js 45 | ``` 46 | 47 | ## Deployment 48 | 49 | Using [PM2](http://pm2.keymetrics.io/) to run Suez as a service is encouraged. Please see the [quick start](http://pm2.keymetrics.io/docs/usage/quick-start/) guide for more information. 50 | 51 | # Configuration 52 | A sample configuration file is [provided](../master/config/settings.json.sample) and shown below: 53 | 54 | ```json 55 | { 56 | "service": { 57 | "port": 3000 58 | }, 59 | "apiTargets": [ 60 | { 61 | "name": "typicode", 62 | "proxyTarget": "https://jsonplaceholder.typicode.com/" 63 | }, 64 | { 65 | "name": "acmeapi", 66 | "proxyTarget": "https://api.acme.com/" 67 | } 68 | ] 69 | } 70 | ``` 71 | 72 | 73 | In the example, Suez is configured to listen to port `3000` and will provide access to two different API targets: `typicode` and `acmeapi`. 74 | 75 | To configure Suez, make a copy of this file, change the service port number, if necessary, and then add `apiTargets` as required. Save this file and name it `settings.json`. 76 | 77 | # Usage 78 | The following example is based on the sample settings file provided. _Note: https://jsonplaceholder.typicode.com/ is a demo site for developers to test their RESTful API calls._ 79 | 80 | Suppose the developer would like to access the users via typicode's API. The URI provided by the vendor is `https://jsonplaceholder.typicode.com/users`. The corresponding PL/SQL code use to interact with service would look something like this: 81 | 82 | ```plsql 83 | l_response := apex_web_service.make_rest_request( 84 | p_url => 'http://typicode.localhost:3000/users' 85 | , p_http_method => 'GET' 86 | ); 87 | ``` 88 | 89 | # Known Issues 90 | 1. If the subdomain of `localhost` is unresolvable, add it to the hosts file, for example: 91 | 92 | ```bash 93 | # For Linux: /etc/hosts 94 | # For Windows: %SystemRoot%\System32\drivers\etc\hosts 95 | 127.0.0.1 localhost contosoapi.localhost acmeapi.localhost 96 | ``` 97 | 98 | # Docker Support 99 | 100 | If you don't want to install anything you can use the [SUEZ docker image](https://hub.docker.com/r/fuzziebrain/suez/). 101 | 102 | ## Get Docker image 103 | 104 | `docker pull fuzziebrain/suez` 105 | 106 | ## Run Container 107 | 108 | ```bash 109 | # Note my settings.json is stored in ~/docker/suez 110 | docker run -it -d \ 111 | --name=suez \ 112 | -v ~/docker/suez:/app/suez/config \ 113 | -p 8888:3000 \ 114 | fuzziebrain/suez:latest 115 | 116 | # Stopping immediately using the -t 1 117 | docker stop -t 1 suez 118 | 119 | docker start suez 120 | ``` 121 | 122 | ## Container Parameters 123 | 124 | Parameter | Description 125 | --- | --- 126 | `-e WATCH=true` | Optional: setting this to `WATCH=true` will restart the node service each time `settings.json` is changed. This is recommended for active development environments 127 | `--name` | Optional: Name to label container 128 | `-v :/app/suez/config` | Location where `settings.json` is stored 129 | `-p 8888:3000` | Suez uses port 3000 internally and this must be used, map it accordingly to your system. In this case port 8888 will be mapped to the container's port 3000. 130 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const 2 | express = require('express'), 3 | proxy = require('http-proxy-middleware'); 4 | 5 | var app = express(); 6 | 7 | var config = require('./config/settings.json'); 8 | 9 | // *** Docker specific settings *** 10 | // If run like "node app.js docker" then it will trigger the app to be run in docker mode 11 | config.docker = process.env.DOCKER == undefined ? false : true; 12 | console.log(`config.docker: ${config.docker}`); 13 | // If in docker mode then need to allow listen from all IPs (not just localhost) 14 | config.listenHost = config.docker ? '' : '127.0.0.1'; 15 | console.log(`config.listenHost: ${config.listenHost}`); 16 | 17 | // Docker relies on port 3000 (exposed port) 18 | config.service.port = config.docker ? 3000 : config.service.port; 19 | console.log(`config.service.port: ${config.service.port}`); 20 | 21 | app.use('/', proxy( 22 | { 23 | target: 'http://localhost/', 24 | changeOrigin: true, 25 | router: function(req) { 26 | var routeName = req.hostname.split('.')[0]; 27 | 28 | var proxyTarget = config.apiTargets.find( 29 | function(apiTarget) { 30 | return apiTarget.name == this; 31 | }, 32 | routeName 33 | ).proxyTarget; 34 | 35 | return proxyTarget; 36 | } 37 | } 38 | )); 39 | 40 | app.listen(config.service.port, config.listenHost); 41 | -------------------------------------------------------------------------------- /config/settings.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "service": { 3 | "port": 3000 4 | }, 5 | "apiTargets": [ 6 | { 7 | "name": "contosoapi", 8 | "proxyTarget": "https://api.contoso.com/" 9 | }, 10 | { 11 | "name": "acmeapi", 12 | "proxyTarget": "https://api.acme.com/" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /docker-dev.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | - [SUEZ Docker Development](#suez-docker-development) 4 | - [Preconfig](#preconfig) 5 | - [Run with normal config volume](#run-with-normal-config-volume) 6 | - [Run using local directory as source](#run-using-local-directory-as-source) 7 | 8 | 9 | 10 | # SUEZ Docker Development 11 | 12 | This documentation is used to help when working on the `Dockerfile` 13 | 14 | ## Preconfig 15 | 16 | ```bash 17 | SUEZ_ROOT=~/Documents/GitHub/martindsouza/suez 18 | 19 | ``` 20 | ## Run with normal config volume 21 | 22 | ```bash 23 | # Run and destroy 24 | docker run -it --rm \ 25 | -v $SUEZ_ROOT/config:/app/suez/config \ 26 | -p 3000:3000 \ 27 | suez:latest 28 | 29 | # Watch option 30 | docker run -it --rm \ 31 | -v $SUEZ_ROOT/config:/app/suez/config \ 32 | -e WATCH=true \ 33 | -p 3000:3000 \ 34 | suez:latest 35 | ``` 36 | 37 | ## Run using local directory as source 38 | 39 | _Note: need to change the `Dockerfile` to expose appropriate volume_ 40 | 41 | ```bash 42 | docker run -it --rm \ 43 | -v $SUEZ_ROOT/:/app/suez \ 44 | -p 3000:3000 \ 45 | suez:latest 46 | ``` -------------------------------------------------------------------------------- /docker-scripts/launch-suez.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This is a wrapped to determine if we launch via PM2 (watching) or nodejs 4 | if [ "$WATCH" = "true" ]; then 5 | DOCKER=true pm2-runtime /app/suez/app.js --watch; 6 | else 7 | DOCKER=true node /app/suez/app.js docker 8 | fi 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "suez", 3 | "version": "1.0.0", 4 | "description": "Configurable reverse proxy for accessing external APIs.", 5 | "main": "app.js", 6 | "author": "Adrian Png ", 7 | "license": "MIT", 8 | "dependencies": { 9 | "express": "^4.14.0", 10 | "http-proxy-middleware": "^0.17.3" 11 | } 12 | } --------------------------------------------------------------------------------