├── LICENSE ├── README.md ├── templates ├── development.json ├── production-workspace.json └── production.json ├── terminal.png └── terminal ├── .bash_profile ├── Dockerfile ├── bin ├── kubectl ├── oc ├── odo ├── setup-console.sh ├── setup-environ.sh ├── setup-volume.sh ├── start-butterfly.sh ├── start-console.sh ├── start-gateway.sh ├── start-singleuser.sh ├── start-terminal.sh └── start-webdav.sh ├── butterfly ├── fixup-styles.sh ├── install-fonts.sh ├── requirements.txt └── start-terminal.sh ├── containers ├── libpod.conf └── sudoers.d │ ├── buildah │ └── podman ├── etc ├── httpd-webdav.conf ├── motd ├── profile ├── profile.d │ ├── nodejs.sh │ └── sh.local ├── supervisor │ ├── gateway.conf │ ├── terminal.conf │ └── webdav.conf └── supervisord.conf ├── gateway ├── logger.js ├── package-lock.json ├── package.json ├── routes │ ├── console.js │ └── terminal.js └── server.js ├── profiles ├── httpd.sh └── python.sh └── s2i ├── assemble ├── run └── save-artifacts /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Workshop Terminal 2 | ================= 3 | 4 | This repository contains software for deploying a containerised user environment in OpenShift, where users are provided access to the environment via a terminal in their web browser. It can be used to support workshops where users need access to command line clients and other tools when working with OpenShift, and you want to avoid needing to have users install anything on their own local computer. 5 | 6 | ![](terminal.png) 7 | 8 | Command line clients, tools and software stacks which are included are: 9 | 10 | * Editors: ``vi``/``vim``, ``nano``. 11 | * Kubernetes clients: ``kubectl`` 12 | * OpenShift clients: ``oc``, ``odo``. 13 | * Language runtimes: ``java``, ``node.js``, ``python`` 14 | 15 | For the language runtimes, commonly used packaging tools for working with that language are also included. 16 | 17 | Quick start instructions 18 | ------------------------ 19 | 20 | To quickly see what the terminal environment looks like, run: 21 | 22 | ``` 23 | oc new-app https://raw.githubusercontent.com/openshift-homeroom/workshop-terminal/master/templates/production.json 24 | ``` 25 | 26 | This will deploy an instance of the user environment as a standalone deployment. The name of the deployment will by default be ``terminal``. 27 | 28 | To determine the hostname assigned to the route which you need to use in the URL to access the terminal, run: 29 | 30 | ``` 31 | oc get route/terminal 32 | ``` 33 | 34 | When you access the URL for the terminal, you will if necessary be redirected to the login page for the OpenShift cluster the terminal is deployed to. You should enter your login and password for the OpenShift cluster. 35 | 36 | After you have supplied your credentials, you will be granted access to the terminal. 37 | 38 | Note that you will only be granted access to the terminal if your are listed as a project admin for the project the terminal is deployed to. Users of the OpenShift cluster who are members of your project but who only have edit or view access, or users who are not a collaborator of your project, will not be granted access to the terminal. 39 | 40 | When you use the ``oc`` and ``kubectl`` command line tools from the terminal, you will already be logged into the cluster as a special service account user. You should have the same rights as a project admin for that project. If you need the full access rights of your original OpenShift user, run ``oc login`` and login as your actual user. 41 | 42 | To delete the deployment when done, run: 43 | 44 | ``` 45 | oc delete all,serviceaccount,rolebinding,configmap -l app=terminal 46 | ``` 47 | 48 | Creating multiple sessions 49 | -------------------------- 50 | 51 | Whenever you access the root URL for the terminal deployment, you will be redirected to the same session each time. That is, to the sub URL path of ``/terminal/session/1``. 52 | 53 | If you want to create multiple terminal sessions within the one user environment, create a new browser tab or window, enter in the same URL, but change ``1`` in the sub URL path to a different value. You can use any alphanumeric value for the session name, as well as dashes. 54 | 55 | Note that although this will provide you with a separate terminal session, it is still running your shell in the same container as all other terminal sessions you create with the same terminal deployment. Seperate containers are not created. 56 | 57 | This means you cannot use this mechanism as a means of providing access to multiple users. If you do and are using command line tools such as ``oc`` or ``kubectl``, the users will interfere with each other, as the terminal sessions share the same home directory. 58 | 59 | If you need to provide terminal sessions to multiple users, each user should create their own deployment for the terminal, or you should use the separate multi user [terminal spawner](https://github.com/openshift-labs/workshop-spawner) application. 60 | 61 | Creating a custom image 62 | ----------------------- 63 | 64 | As the contents of the terminal image dictates what tools you have available in the user environment, you may want to customise the image contents to add additional tools. You may also want to add content such as application source code, configuration files etc, to be used by someone in a workshop. 65 | 66 | There are two ways you can customise the contents of the image. 67 | 68 | The first is that the image is Source-to-Image (S2I) enabled. The image can therefore be used as an S2I builder to add additional content. In this case, because an S2I build runs as a non ``root`` user, you will not be able to install additional system packages. 69 | 70 | To create a custom image using S2I, run: 71 | 72 | ``` 73 | oc new-build --name myterminal quay.io/openshifthomeroom/workshop-terminal:master~https://github.com/yourusername/yourrepo 74 | ``` 75 | 76 | Anything in the Git repository will be copied into the ``/opt/app-root/src`` directory of the image. 77 | 78 | If you want to run your own steps during the build phase, after the files have been copied to the ``/opt/app-root/src`` directory, supply an executable shell script in the Git repository at the location ``.workshop/build``. Add to this script the extra build steps. 79 | 80 | If you want to have special steps run when the terminal instance is being started, supply an executable shell script in the Git repository at the location ``.workshop/setup``. Add to this script the extra steps to be run each time the container is started. 81 | 82 | The latter setup script can be used to modify files placed into the image based on the specific user environment. Note that the script is run each time the container is started, so any actions should take that into consideration. 83 | 84 | Once you have your custom image built, the easiest way to switch to it for an existing terminal deployment is to run: 85 | 86 | ``` 87 | oc tag myterminal:latest terminal:latest 88 | ``` 89 | 90 | Alternatively, if you have uploaded the custom terminal image to an accessible image registry, you can create a fresh deployment using the template by running: 91 | 92 | ``` 93 | $ oc new-app https://raw.githubusercontent.com/openshift-homeroom/workshop-terminal/master/templates/production.json \ 94 | --param SESSION_NAME=myterminal \ 95 | --param TERMINAL_IMAGE=quay.io/yourusername/youimagename:latest 96 | ``` 97 | 98 | The alternative to using an S2I build, where you need to install additional system packages, is to use a ``Dockerfile`` build. In order to integrate properly with the terminal S2I builder mechanism for build and setup steps, it is recommended you use a ``Dockefile`` containing: 99 | 100 | ``` 101 | FROM quay.io/openshifthomeroom/workshop-terminal:master 102 | 103 | USER root 104 | 105 | COPY . /tmp/src 106 | 107 | RUN rm -rf /tmp/src/.git* && \ 108 | chown -R 1001 /tmp/src && \ 109 | chgrp -R 0 /tmp/src && \ 110 | chmod -R g+w /tmp/src 111 | 112 | USER 1001 113 | 114 | RUN /usr/libexec/s2i/assemble 115 | ``` 116 | 117 | Add the additional steps which need to be run as ``root``, within the section where the ``USER`` is set to ``root``. 118 | 119 | Note that if installing anything into ``/opt/app-root`` from the ``Dockefile``, ensure that ownership of the files is changed to ``1001:0`` and the ``fix-permissions`` script is run on the ``/opt/app-root`` directory. These steps ensure that a user can still properly work with and edit the files which were added as ``root``. 120 | 121 | The resulting image created from the ``Dockerfile`` build would be used in the same way. 122 | 123 | Using versioned images 124 | ---------------------- 125 | 126 | The URL for the template used above, is taken from the ``master`` branch of this Git repository. It is therefore bound to the most recent tagged version of the terminal image. Similarly, ``master`` was used as the tag when explaining custom builds. 127 | 128 | As the GitFlow branching model is used, although ``master`` is only updated when a tag is made, if you want to be certain that what version you are using doesn't change, you should use a specific tag. 129 | 130 | For the template, either make a copy of the template from ``master``, or go to GitHub, identify a specific tag, and use the URL to the template from that tag. 131 | 132 | For the images when doing a custom build, you can find a specific tagged version by going to: 133 | 134 | * https://quay.io/repository/openshifthomeroom/workshop-terminal?tab=tags 135 | 136 | Customising deployment 137 | ---------------------- 138 | 139 | The template used above is the recommended method for deploying the terminal. The template will do the following for you: 140 | 141 | * Ensure that a secure HTTPS connection is used when accessing the terminal from your web browser. 142 | * Enable authentication via the OpenShift cluster login page, with access to the terminal only granted to users who have ``admin`` role in the project. 143 | * Create a service account under which the terminal container will run, where the service account has ``admin`` access to the project, and you will be automatically logged into the OpenShift cluster. 144 | * Ensure that the ``Recreate`` deployment strategy is used so will only can have one terminal pod in existance when doing a restart. 145 | 146 | If you need to customise the deployment, such as to add persistent storage, the preferred path is to copy the template and modify it to suit your requirements, preserving the features above. 147 | 148 | If you choose not to use the template and deploy the terminal image directly, there will be no authentication protecting access to the terminal session. From the terminal session, you will also need to explicitly log into the OpenShift cluster you want to use using ``oc login``. 149 | 150 | With knowledge that there is no authentication protecting access, you can deploy the terminal image directly by running: 151 | 152 | ``` 153 | oc new-app quay.io/openshifthomeroom/workshop-terminal:master --name terminal 154 | ``` 155 | 156 | To create a secure HTTPS route for accessing the terminal use: 157 | 158 | ``` 159 | oc create route edge --service terminal --insecure-policy Redirect 160 | ``` 161 | 162 | If you want to use simple authentication using HTTP Basic authentication, rather than link it to the OpenShift cluster user authentication, run: 163 | 164 | ``` 165 | oc set env dc/terminal AUTH_USERNAME=grumpy AUTH_PASSWORD=secret 166 | ``` 167 | 168 | or set the environment variables when using ``oc new-app`` to deploy the image. 169 | 170 | For authentication using the OpenShift cluster login page, refer to the template, as it requires additional resources to be created in order to work. 171 | -------------------------------------------------------------------------------- /templates/development.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Template", 3 | "apiVersion": "v1", 4 | "metadata": { 5 | "name": "workshop-terminal-development", 6 | "annotations": { 7 | "openshift.io/display-name": "Workshop Terminal (Development)" 8 | } 9 | }, 10 | "parameters": [ 11 | { 12 | "name": "SESSION_NAME", 13 | "value": "terminal", 14 | "required": true 15 | }, 16 | { 17 | "name": "NAME_PREFIX", 18 | "value": "", 19 | "required": false 20 | }, 21 | { 22 | "name": "AUTH_USERNAME", 23 | "value": "" 24 | }, 25 | { 26 | "name": "AUTH_PASSWORD", 27 | "generate": "expression", 28 | "from": "[a-zA-Z0-9]{16}" 29 | }, 30 | { 31 | "name": "OPENSHIFT_USERNAME", 32 | "value": "" 33 | }, 34 | { 35 | "name": "OPENSHIFT_PASSWORD", 36 | "value": "" 37 | }, 38 | { 39 | "name": "OPENSHIFT_TOKEN", 40 | "value": "" 41 | }, 42 | { 43 | "name": "TERMINAL_ENVVARS", 44 | "value": "" 45 | }, 46 | { 47 | "name": "OC_VERSION", 48 | "value": "" 49 | }, 50 | { 51 | "name": "ODO_VERSION", 52 | "value": "" 53 | }, 54 | { 55 | "name": "KUBECTL_VERSION", 56 | "value": "" 57 | } 58 | ], 59 | "objects": [ 60 | { 61 | "kind": "ServiceAccount", 62 | "apiVersion": "v1", 63 | "metadata": { 64 | "name": "${NAME_PREFIX}${SESSION_NAME}-user", 65 | "labels": { 66 | "app": "${NAME_PREFIX}${SESSION_NAME}" 67 | }, 68 | "annotations": { 69 | "serviceaccounts.openshift.io/oauth-redirectreference.first": "{\"kind\":\"OAuthRedirectReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"Route\",\"name\":\"${NAME_PREFIX}${SESSION_NAME}\"}}", 70 | "serviceaccounts.openshift.io/oauth-redirecturi.first": "oauth_callback", 71 | "serviceaccounts.openshift.io/oauth-want-challenges": "false" 72 | } 73 | } 74 | }, 75 | { 76 | "kind": "RoleBinding", 77 | "apiVersion": "v1", 78 | "metadata": { 79 | "name": "${NAME_PREFIX}${SESSION_NAME}-admin", 80 | "labels": { 81 | "app": "${NAME_PREFIX}${SESSION_NAME}" 82 | } 83 | }, 84 | "subjects": [ 85 | { 86 | "kind": "ServiceAccount", 87 | "name": "${NAME_PREFIX}${SESSION_NAME}-user" 88 | } 89 | ], 90 | "roleRef": { 91 | "name": "admin" 92 | } 93 | }, 94 | { 95 | "kind": "ImageStream", 96 | "apiVersion": "v1", 97 | "metadata": { 98 | "name": "${NAME_PREFIX}${SESSION_NAME}", 99 | "labels": { 100 | "app": "${NAME_PREFIX}${SESSION_NAME}" 101 | } 102 | }, 103 | "spec": { 104 | "lookupPolicy": { 105 | "local": true 106 | } 107 | } 108 | }, 109 | { 110 | "kind": "BuildConfig", 111 | "apiVersion": "v1", 112 | "metadata": { 113 | "name": "${NAME_PREFIX}${SESSION_NAME}", 114 | "labels": { 115 | "app": "${NAME_PREFIX}${SESSION_NAME}" 116 | } 117 | }, 118 | "spec": { 119 | "triggers": [ 120 | { 121 | "type": "ConfigChange" 122 | }, 123 | { 124 | "type": "ImageChange" 125 | } 126 | ], 127 | "source": { 128 | "type": "Git", 129 | "git": { 130 | "uri": "https://github.com/openshift-homeroom/workshop-terminal.git", 131 | "ref": "develop" 132 | }, 133 | "contextDir": "terminal" 134 | }, 135 | "strategy": { 136 | "type": "Docker", 137 | "dockerStrategy": { 138 | "from": { 139 | "kind": "DockerImage", 140 | "name": "centos/s2i-base-centos7:latest" 141 | } 142 | } 143 | }, 144 | "output": { 145 | "to": { 146 | "kind": "ImageStreamTag", 147 | "name": "${NAME_PREFIX}${SESSION_NAME}:latest" 148 | } 149 | } 150 | } 151 | }, 152 | { 153 | "kind": "ConfigMap", 154 | "apiVersion": "v1", 155 | "metadata": { 156 | "name": "${NAME_PREFIX}${SESSION_NAME}-env", 157 | "labels": { 158 | "app": "${NAME_PREFIX}${SESSION_NAME}" 159 | } 160 | }, 161 | "data": { 162 | "terminal.sh": "${TERMINAL_ENVVARS}" 163 | } 164 | }, 165 | { 166 | "kind": "DeploymentConfig", 167 | "apiVersion": "v1", 168 | "metadata": { 169 | "name": "${NAME_PREFIX}${SESSION_NAME}", 170 | "labels": { 171 | "app": "${NAME_PREFIX}${SESSION_NAME}" 172 | } 173 | }, 174 | "spec": { 175 | "strategy": { 176 | "type": "Recreate" 177 | }, 178 | "triggers": [ 179 | { 180 | "type": "ConfigChange" 181 | }, 182 | { 183 | "type": "ImageChange", 184 | "imageChangeParams": { 185 | "automatic": true, 186 | "containerNames": [ 187 | "terminal" 188 | ], 189 | "from": { 190 | "kind": "ImageStreamTag", 191 | "name": "${NAME_PREFIX}${SESSION_NAME}:latest" 192 | } 193 | } 194 | } 195 | ], 196 | "replicas": 1, 197 | "selector": { 198 | "app": "${NAME_PREFIX}${SESSION_NAME}", 199 | "deploymentconfig": "${NAME_PREFIX}${SESSION_NAME}" 200 | }, 201 | "template": { 202 | "metadata": { 203 | "labels": { 204 | "app": "${NAME_PREFIX}${SESSION_NAME}", 205 | "deploymentconfig": "${NAME_PREFIX}${SESSION_NAME}" 206 | } 207 | }, 208 | "spec": { 209 | "serviceAccountName": "${NAME_PREFIX}${SESSION_NAME}-user", 210 | "containers": [ 211 | { 212 | "name": "terminal", 213 | "ports": [ 214 | { 215 | "containerPort": 10080, 216 | "protocol": "TCP" 217 | } 218 | ], 219 | "env": [ 220 | { 221 | "name": "APPLICATION_NAME", 222 | "value": "${NAME_PREFIX}${SESSION_NAME}" 223 | }, 224 | { 225 | "name": "AUTH_USERNAME", 226 | "value": "${AUTH_USERNAME}" 227 | }, 228 | { 229 | "name": "AUTH_PASSWORD", 230 | "value": "${AUTH_PASSWORD}" 231 | }, 232 | { 233 | "name": "OAUTH_SERVICE_ACCOUNT", 234 | "value": "${NAME_PREFIX}${SESSION_NAME}-user" 235 | }, 236 | { 237 | "name": "OPENSHIFT_USERNAME", 238 | "value": "${OPENSHIFT_USERNAME}" 239 | }, 240 | { 241 | "name": "OPENSHIFT_PASSWORD", 242 | "value": "${OPENSHIFT_PASSWORD}" 243 | }, 244 | { 245 | "name": "OPENSHIFT_TOKEN", 246 | "value": "${OPENSHIFT_TOKEN}" 247 | } 248 | ], 249 | "volumeMounts": [ 250 | { 251 | "mountPath": "/opt/workshop/envvars", 252 | "name": "envvars" 253 | } 254 | ] 255 | } 256 | ], 257 | "volumes": [ 258 | { 259 | "name": "envvars", 260 | "configMap": { 261 | "name": "${NAME_PREFIX}${SESSION_NAME}-env", 262 | "defaultMode": 420 263 | } 264 | } 265 | ] 266 | } 267 | } 268 | } 269 | }, 270 | { 271 | "kind": "Service", 272 | "apiVersion": "v1", 273 | "metadata": { 274 | "name": "${NAME_PREFIX}${SESSION_NAME}", 275 | "labels": { 276 | "app": "${NAME_PREFIX}${SESSION_NAME}" 277 | } 278 | }, 279 | "spec": { 280 | "ports": [ 281 | { 282 | "name": "10080-tcp", 283 | "protocol": "TCP", 284 | "port": 10080, 285 | "targetPort": 10080 286 | } 287 | ], 288 | "selector": { 289 | "app": "${NAME_PREFIX}${SESSION_NAME}", 290 | "deploymentconfig": "${NAME_PREFIX}${SESSION_NAME}" 291 | } 292 | } 293 | }, 294 | { 295 | "kind": "Route", 296 | "apiVersion": "v1", 297 | "metadata": { 298 | "name": "${NAME_PREFIX}${SESSION_NAME}", 299 | "labels": { 300 | "app": "${NAME_PREFIX}${SESSION_NAME}" 301 | } 302 | }, 303 | "spec": { 304 | "host": "", 305 | "to": { 306 | "kind": "Service", 307 | "name": "${NAME_PREFIX}${SESSION_NAME}", 308 | "weight": 100 309 | }, 310 | "port": { 311 | "targetPort": "10080-tcp" 312 | }, 313 | "tls": { 314 | "termination": "edge", 315 | "insecureEdgeTerminationPolicy": "Redirect" 316 | } 317 | } 318 | } 319 | ] 320 | } 321 | -------------------------------------------------------------------------------- /templates/production-workspace.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Template", 3 | "apiVersion": "v1", 4 | "metadata": { 5 | "name": "workshop-terminal-production-workspace", 6 | "annotations": { 7 | "openshift.io/display-name": "Workshop Terminal (Production/Workspace)" 8 | } 9 | }, 10 | "parameters": [ 11 | { 12 | "name": "SESSION_NAME", 13 | "value": "terminal", 14 | "required": true 15 | }, 16 | { 17 | "name": "NAME_PREFIX", 18 | "value": "", 19 | "required": false 20 | }, 21 | { 22 | "name": "TERMINAL_IMAGE", 23 | "value": "quay.io/openshifthomeroom/workshop-terminal:3.4.2", 24 | "required": true 25 | }, 26 | { 27 | "name": "VOLUME_SIZE", 28 | "value": "1Gi" 29 | }, 30 | { 31 | "name": "AUTH_USERNAME", 32 | "value": "" 33 | }, 34 | { 35 | "name": "AUTH_PASSWORD", 36 | "generate": "expression", 37 | "from": "[a-zA-Z0-9]{16}" 38 | }, 39 | { 40 | "name": "OPENSHIFT_USERNAME", 41 | "value": "" 42 | }, 43 | { 44 | "name": "OPENSHIFT_PASSWORD", 45 | "value": "" 46 | }, 47 | { 48 | "name": "OPENSHIFT_TOKEN", 49 | "value": "" 50 | }, 51 | { 52 | "name": "TERMINAL_ENVVARS", 53 | "value": "" 54 | }, 55 | { 56 | "name": "OC_VERSION", 57 | "value": "" 58 | }, 59 | { 60 | "name": "ODO_VERSION", 61 | "value": "" 62 | }, 63 | { 64 | "name": "KUBECTL_VERSION", 65 | "value": "" 66 | } 67 | ], 68 | "objects": [ 69 | { 70 | "kind": "ServiceAccount", 71 | "apiVersion": "v1", 72 | "metadata": { 73 | "name": "${NAME_PREFIX}${SESSION_NAME}-user", 74 | "labels": { 75 | "app": "${NAME_PREFIX}${SESSION_NAME}" 76 | }, 77 | "annotations": { 78 | "serviceaccounts.openshift.io/oauth-redirectreference.first": "{\"kind\":\"OAuthRedirectReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"Route\",\"name\":\"${NAME_PREFIX}${SESSION_NAME}\"}}", 79 | "serviceaccounts.openshift.io/oauth-redirecturi.first": "oauth_callback", 80 | "serviceaccounts.openshift.io/oauth-want-challenges": "false" 81 | } 82 | } 83 | }, 84 | { 85 | "kind": "RoleBinding", 86 | "apiVersion": "v1", 87 | "metadata": { 88 | "name": "${NAME_PREFIX}${SESSION_NAME}-admin", 89 | "labels": { 90 | "app": "${NAME_PREFIX}${SESSION_NAME}" 91 | } 92 | }, 93 | "subjects": [ 94 | { 95 | "kind": "ServiceAccount", 96 | "name": "${NAME_PREFIX}${SESSION_NAME}-user" 97 | } 98 | ], 99 | "roleRef": { 100 | "name": "admin" 101 | } 102 | }, 103 | { 104 | "kind": "ImageStream", 105 | "apiVersion": "v1", 106 | "metadata": { 107 | "name": "${NAME_PREFIX}${SESSION_NAME}", 108 | "labels": { 109 | "app": "${NAME_PREFIX}${SESSION_NAME}" 110 | } 111 | }, 112 | "spec": { 113 | "lookupPolicy": { 114 | "local": true 115 | }, 116 | "tags": [ 117 | { 118 | "name": "latest", 119 | "from": { 120 | "kind": "DockerImage", 121 | "name": "${TERMINAL_IMAGE}" 122 | } 123 | } 124 | ] 125 | } 126 | }, 127 | { 128 | "kind": "ConfigMap", 129 | "apiVersion": "v1", 130 | "metadata": { 131 | "name": "${NAME_PREFIX}${SESSION_NAME}-env", 132 | "labels": { 133 | "app": "${NAME_PREFIX}${SESSION_NAME}" 134 | } 135 | }, 136 | "data": { 137 | "terminal.sh": "${TERMINAL_ENVVARS}" 138 | } 139 | }, 140 | { 141 | "kind": "DeploymentConfig", 142 | "apiVersion": "v1", 143 | "metadata": { 144 | "name": "${NAME_PREFIX}${SESSION_NAME}", 145 | "labels": { 146 | "app": "${NAME_PREFIX}${SESSION_NAME}" 147 | } 148 | }, 149 | "spec": { 150 | "strategy": { 151 | "type": "Recreate" 152 | }, 153 | "triggers": [ 154 | { 155 | "type": "ConfigChange" 156 | }, 157 | { 158 | "type": "ImageChange", 159 | "imageChangeParams": { 160 | "automatic": true, 161 | "containerNames": [ 162 | "setup-volume", 163 | "terminal" 164 | ], 165 | "from": { 166 | "kind": "ImageStreamTag", 167 | "name": "${NAME_PREFIX}${SESSION_NAME}:latest" 168 | } 169 | } 170 | } 171 | ], 172 | "replicas": 1, 173 | "selector": { 174 | "app": "${NAME_PREFIX}${SESSION_NAME}", 175 | "deploymentconfig": "${NAME_PREFIX}${SESSION_NAME}" 176 | }, 177 | "template": { 178 | "metadata": { 179 | "labels": { 180 | "app": "${NAME_PREFIX}${SESSION_NAME}", 181 | "deploymentconfig": "${NAME_PREFIX}${SESSION_NAME}" 182 | } 183 | }, 184 | "spec": { 185 | "serviceAccountName": "${NAME_PREFIX}${SESSION_NAME}-user", 186 | "initContainers": [ 187 | { 188 | "name": "setup-volume", 189 | "image": "${NAME_PREFIX}${SESSION_NAME}:latest", 190 | "command": [ 191 | "/opt/workshop/bin/setup-volume.sh", 192 | "/opt/app-root", 193 | "/mnt/workspace" 194 | ], 195 | "volumeMounts": [ 196 | { 197 | "name": "data", 198 | "mountPath": "/mnt" 199 | } 200 | ] 201 | } 202 | ], 203 | "containers": [ 204 | { 205 | "name": "terminal", 206 | "image": "${NAME_PREFIX}${SESSION_NAME}:latest", 207 | "ports": [ 208 | { 209 | "containerPort": 10080, 210 | "protocol": "TCP" 211 | } 212 | ], 213 | "env": [ 214 | { 215 | "name": "APPLICATION_NAME", 216 | "value": "${NAME_PREFIX}${SESSION_NAME}" 217 | }, 218 | { 219 | "name": "AUTH_USERNAME", 220 | "value": "${AUTH_USERNAME}" 221 | }, 222 | { 223 | "name": "AUTH_PASSWORD", 224 | "value": "${AUTH_PASSWORD}" 225 | }, 226 | { 227 | "name": "OAUTH_SERVICE_ACCOUNT", 228 | "value": "${NAME_PREFIX}${SESSION_NAME}-user" 229 | }, 230 | { 231 | "name": "OPENSHIFT_USERNAME", 232 | "value": "${OPENSHIFT_USERNAME}" 233 | }, 234 | { 235 | "name": "OPENSHIFT_PASSWORD", 236 | "value": "${OPENSHIFT_PASSWORD}" 237 | }, 238 | { 239 | "name": "OPENSHIFT_TOKEN", 240 | "value": "${OPENSHIFT_TOKEN}" 241 | } 242 | ], 243 | "volumeMounts": [ 244 | { 245 | "mountPath": "/opt/workshop/envvars", 246 | "name": "envvars" 247 | }, 248 | { 249 | "name": "data", 250 | "mountPath": "/opt/app-root", 251 | "subPath": "workspace" 252 | } 253 | ] 254 | } 255 | ], 256 | "volumes": [ 257 | { 258 | "name": "envvars", 259 | "configMap": { 260 | "name": "${NAME_PREFIX}${SESSION_NAME}-env", 261 | "defaultMode": 420 262 | } 263 | }, 264 | { 265 | "name": "data", 266 | "persistentVolumeClaim": { 267 | "claimName": "${NAME_PREFIX}${SESSION_NAME}-data" 268 | } 269 | } 270 | ] 271 | } 272 | } 273 | } 274 | }, 275 | { 276 | "kind": "Service", 277 | "apiVersion": "v1", 278 | "metadata": { 279 | "name": "${NAME_PREFIX}${SESSION_NAME}", 280 | "labels": { 281 | "app": "${NAME_PREFIX}${SESSION_NAME}" 282 | } 283 | }, 284 | "spec": { 285 | "ports": [ 286 | { 287 | "name": "10080-tcp", 288 | "protocol": "TCP", 289 | "port": 10080, 290 | "targetPort": 10080 291 | } 292 | ], 293 | "selector": { 294 | "app": "${NAME_PREFIX}${SESSION_NAME}", 295 | "deploymentconfig": "${NAME_PREFIX}${SESSION_NAME}" 296 | } 297 | } 298 | }, 299 | { 300 | "kind": "Route", 301 | "apiVersion": "v1", 302 | "metadata": { 303 | "name": "${NAME_PREFIX}${SESSION_NAME}", 304 | "labels": { 305 | "app": "${NAME_PREFIX}${SESSION_NAME}" 306 | } 307 | }, 308 | "spec": { 309 | "host": "", 310 | "to": { 311 | "kind": "Service", 312 | "name": "${NAME_PREFIX}${SESSION_NAME}", 313 | "weight": 100 314 | }, 315 | "port": { 316 | "targetPort": "10080-tcp" 317 | }, 318 | "tls": { 319 | "termination": "edge", 320 | "insecureEdgeTerminationPolicy": "Redirect" 321 | } 322 | } 323 | }, 324 | { 325 | "kind": "PersistentVolumeClaim", 326 | "apiVersion": "v1", 327 | "metadata": { 328 | "name": "${NAME_PREFIX}${SESSION_NAME}-data", 329 | "labels": { 330 | "app": "${NAME_PREFIX}${SESSION_NAME}" 331 | } 332 | }, 333 | "spec": { 334 | "accessModes": [ 335 | "ReadWriteOnce" 336 | ], 337 | "resources": { 338 | "requests": { 339 | "storage": "${VOLUME_SIZE}" 340 | } 341 | } 342 | } 343 | } 344 | ] 345 | } 346 | -------------------------------------------------------------------------------- /templates/production.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Template", 3 | "apiVersion": "v1", 4 | "metadata": { 5 | "name": "workshop-terminal-production", 6 | "annotations": { 7 | "openshift.io/display-name": "Workshop Terminal (Production)" 8 | } 9 | }, 10 | "parameters": [ 11 | { 12 | "name": "SESSION_NAME", 13 | "value": "terminal", 14 | "required": true 15 | }, 16 | { 17 | "name": "NAME_PREFIX", 18 | "value": "", 19 | "required": false 20 | }, 21 | { 22 | "name": "TERMINAL_IMAGE", 23 | "value": "quay.io/openshifthomeroom/workshop-terminal:3.4.2", 24 | "required": true 25 | }, 26 | { 27 | "name": "AUTH_USERNAME", 28 | "value": "" 29 | }, 30 | { 31 | "name": "AUTH_PASSWORD", 32 | "generate": "expression", 33 | "from": "[a-zA-Z0-9]{16}" 34 | }, 35 | { 36 | "name": "OPENSHIFT_USERNAME", 37 | "value": "" 38 | }, 39 | { 40 | "name": "OPENSHIFT_PASSWORD", 41 | "value": "" 42 | }, 43 | { 44 | "name": "OPENSHIFT_TOKEN", 45 | "value": "" 46 | }, 47 | { 48 | "name": "TERMINAL_ENVVARS", 49 | "value": "" 50 | }, 51 | { 52 | "name": "OC_VERSION", 53 | "value": "" 54 | }, 55 | { 56 | "name": "ODO_VERSION", 57 | "value": "" 58 | }, 59 | { 60 | "name": "KUBECTL_VERSION", 61 | "value": "" 62 | } 63 | ], 64 | "objects": [ 65 | { 66 | "kind": "ServiceAccount", 67 | "apiVersion": "v1", 68 | "metadata": { 69 | "name": "${NAME_PREFIX}${SESSION_NAME}-user", 70 | "labels": { 71 | "app": "${NAME_PREFIX}${SESSION_NAME}" 72 | }, 73 | "annotations": { 74 | "serviceaccounts.openshift.io/oauth-redirectreference.first": "{\"kind\":\"OAuthRedirectReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"Route\",\"name\":\"${NAME_PREFIX}${SESSION_NAME}\"}}", 75 | "serviceaccounts.openshift.io/oauth-redirecturi.first": "oauth_callback", 76 | "serviceaccounts.openshift.io/oauth-want-challenges": "false" 77 | } 78 | } 79 | }, 80 | { 81 | "kind": "RoleBinding", 82 | "apiVersion": "v1", 83 | "metadata": { 84 | "name": "${NAME_PREFIX}${SESSION_NAME}-admin", 85 | "labels": { 86 | "app": "${NAME_PREFIX}${SESSION_NAME}" 87 | } 88 | }, 89 | "subjects": [ 90 | { 91 | "kind": "ServiceAccount", 92 | "name": "${NAME_PREFIX}${SESSION_NAME}-user" 93 | } 94 | ], 95 | "roleRef": { 96 | "name": "admin" 97 | } 98 | }, 99 | { 100 | "kind": "ImageStream", 101 | "apiVersion": "v1", 102 | "metadata": { 103 | "name": "${NAME_PREFIX}${SESSION_NAME}", 104 | "labels": { 105 | "app": "${NAME_PREFIX}${SESSION_NAME}" 106 | } 107 | }, 108 | "spec": { 109 | "lookupPolicy": { 110 | "local": true 111 | }, 112 | "tags": [ 113 | { 114 | "name": "latest", 115 | "from": { 116 | "kind": "DockerImage", 117 | "name": "${TERMINAL_IMAGE}" 118 | } 119 | } 120 | ] 121 | } 122 | }, 123 | { 124 | "kind": "ConfigMap", 125 | "apiVersion": "v1", 126 | "metadata": { 127 | "name": "${NAME_PREFIX}${SESSION_NAME}-env", 128 | "labels": { 129 | "app": "${NAME_PREFIX}${SESSION_NAME}" 130 | } 131 | }, 132 | "data": { 133 | "terminal.sh": "${TERMINAL_ENVVARS}" 134 | } 135 | }, 136 | { 137 | "kind": "DeploymentConfig", 138 | "apiVersion": "v1", 139 | "metadata": { 140 | "name": "${NAME_PREFIX}${SESSION_NAME}", 141 | "labels": { 142 | "app": "${NAME_PREFIX}${SESSION_NAME}" 143 | } 144 | }, 145 | "spec": { 146 | "strategy": { 147 | "type": "Recreate" 148 | }, 149 | "triggers": [ 150 | { 151 | "type": "ConfigChange" 152 | }, 153 | { 154 | "type": "ImageChange", 155 | "imageChangeParams": { 156 | "automatic": true, 157 | "containerNames": [ 158 | "terminal" 159 | ], 160 | "from": { 161 | "kind": "ImageStreamTag", 162 | "name": "${NAME_PREFIX}${SESSION_NAME}:latest" 163 | } 164 | } 165 | } 166 | ], 167 | "replicas": 1, 168 | "selector": { 169 | "app": "${NAME_PREFIX}${SESSION_NAME}", 170 | "deploymentconfig": "${NAME_PREFIX}${SESSION_NAME}" 171 | }, 172 | "template": { 173 | "metadata": { 174 | "labels": { 175 | "app": "${NAME_PREFIX}${SESSION_NAME}", 176 | "deploymentconfig": "${NAME_PREFIX}${SESSION_NAME}" 177 | } 178 | }, 179 | "spec": { 180 | "serviceAccountName": "${NAME_PREFIX}${SESSION_NAME}-user", 181 | "containers": [ 182 | { 183 | "name": "terminal", 184 | "ports": [ 185 | { 186 | "containerPort": 10080, 187 | "protocol": "TCP" 188 | } 189 | ], 190 | "env": [ 191 | { 192 | "name": "APPLICATION_NAME", 193 | "value": "${NAME_PREFIX}${SESSION_NAME}" 194 | }, 195 | { 196 | "name": "AUTH_USERNAME", 197 | "value": "${AUTH_USERNAME}" 198 | }, 199 | { 200 | "name": "AUTH_PASSWORD", 201 | "value": "${AUTH_PASSWORD}" 202 | }, 203 | { 204 | "name": "OAUTH_SERVICE_ACCOUNT", 205 | "value": "${NAME_PREFIX}${SESSION_NAME}-user" 206 | }, 207 | { 208 | "name": "OPENSHIFT_USERNAME", 209 | "value": "${OPENSHIFT_USERNAME}" 210 | }, 211 | { 212 | "name": "OPENSHIFT_PASSWORD", 213 | "value": "${OPENSHIFT_PASSWORD}" 214 | }, 215 | { 216 | "name": "OPENSHIFT_TOKEN", 217 | "value": "${OPENSHIFT_TOKEN}" 218 | } 219 | ], 220 | "volumeMounts": [ 221 | { 222 | "mountPath": "/opt/workshop/envvars", 223 | "name": "envvars" 224 | } 225 | ] 226 | } 227 | ], 228 | "volumes": [ 229 | { 230 | "name": "envvars", 231 | "configMap": { 232 | "name": "${NAME_PREFIX}${SESSION_NAME}-env", 233 | "defaultMode": 420 234 | } 235 | } 236 | ] 237 | } 238 | } 239 | } 240 | }, 241 | { 242 | "kind": "Service", 243 | "apiVersion": "v1", 244 | "metadata": { 245 | "name": "${NAME_PREFIX}${SESSION_NAME}", 246 | "labels": { 247 | "app": "${NAME_PREFIX}${SESSION_NAME}" 248 | } 249 | }, 250 | "spec": { 251 | "ports": [ 252 | { 253 | "name": "10080-tcp", 254 | "protocol": "TCP", 255 | "port": 10080, 256 | "targetPort": 10080 257 | } 258 | ], 259 | "selector": { 260 | "app": "${NAME_PREFIX}${SESSION_NAME}", 261 | "deploymentconfig": "${NAME_PREFIX}${SESSION_NAME}" 262 | } 263 | } 264 | }, 265 | { 266 | "kind": "Route", 267 | "apiVersion": "v1", 268 | "metadata": { 269 | "name": "${NAME_PREFIX}${SESSION_NAME}", 270 | "labels": { 271 | "app": "${NAME_PREFIX}${SESSION_NAME}" 272 | } 273 | }, 274 | "spec": { 275 | "host": "", 276 | "to": { 277 | "kind": "Service", 278 | "name": "${NAME_PREFIX}${SESSION_NAME}", 279 | "weight": 100 280 | }, 281 | "port": { 282 | "targetPort": "10080-tcp" 283 | }, 284 | "tls": { 285 | "termination": "edge", 286 | "insecureEdgeTerminationPolicy": "Redirect" 287 | } 288 | } 289 | } 290 | ] 291 | } 292 | -------------------------------------------------------------------------------- /terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-homeroom/workshop-terminal/64069fa73e0554c82826f8a24178c7d7a26b9d0d/terminal.png -------------------------------------------------------------------------------- /terminal/.bash_profile: -------------------------------------------------------------------------------- 1 | # Enable kubectl bash completion. 2 | 3 | source <(kubectl completion bash) 4 | 5 | # Enable oc bash completion. 6 | 7 | source <(oc completion bash) 8 | 9 | -------------------------------------------------------------------------------- /terminal/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos/s2i-base-centos7:latest 2 | 3 | USER root 4 | 5 | # Install additional common utilities. 6 | 7 | RUN HOME=/root && \ 8 | INSTALL_PKGS="nano python-devel python-pip bash-completion \ 9 | bash-completion-extras cadaver jq tmux sudo buildah podman" && \ 10 | yum install -y centos-release-scl epel-release && \ 11 | yum -y --setopt=tsflags=nodocs install --enablerepo=centosplus $INSTALL_PKGS && \ 12 | yum -y clean all --enablerepo='*' && \ 13 | pip install --no-cache-dir supervisor==4.0.4 mercurial==5.0.2 14 | 15 | # Install Python. 16 | 17 | RUN HOME=/root && \ 18 | INSTALL_PKGS="rh-python36 rh-python36-python-devel \ 19 | rh-python36-python-setuptools rh-python36-python-pip \ 20 | httpd24 httpd24-httpd-devel httpd24-mod_ssl httpd24-mod_auth_kerb \ 21 | httpd24-mod_ldap httpd24-mod_session atlas-devel gcc-gfortran \ 22 | libffi-devel libtool-ltdl" && \ 23 | yum install -y centos-release-scl && \ 24 | yum install -y --setopt=tsflags=nodocs --enablerepo=centosplus $INSTALL_PKGS && \ 25 | rpm -V $INSTALL_PKGS && \ 26 | # Remove centos-logos (httpd dependency) to keep image size smaller. 27 | rpm -e --nodeps centos-logos && \ 28 | yum -y clean all --enablerepo='*' 29 | 30 | # Install Java JDK, Maven and Gradle. 31 | 32 | RUN HOME=/root && \ 33 | INSTALL_PKGS="bc java-1.8.0-openjdk java-1.8.0-openjdk-devel" && \ 34 | yum install -y --setopt=tsflags=nodocs --enablerepo=centosplus $INSTALL_PKGS && \ 35 | rpm -V $INSTALL_PKGS && \ 36 | yum -y clean all --enablerepo='*' 37 | 38 | RUN HOME=/root && \ 39 | (curl -s -0 https://downloads.apache.org/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz | \ 40 | tar -zx -C /usr/local) && \ 41 | mv /usr/local/apache-maven-3.6.3 /usr/local/maven && \ 42 | ln -sf /usr/local/maven/bin/mvn /usr/local/bin/mvn 43 | 44 | RUN HOME=/root && \ 45 | curl -sL -0 https://services.gradle.org/distributions/gradle-6.6.1-bin.zip -o /tmp/gradle-6.6.1-bin.zip && \ 46 | unzip /tmp/gradle-6.6.1-bin.zip -d /usr/local/ && \ 47 | rm /tmp/gradle-6.6.1-bin.zip && \ 48 | mv /usr/local/gradle-6.6.1 /usr/local/gradle && \ 49 | ln -sf /usr/local/gradle/bin/gradle /usr/local/bin/gradle 50 | 51 | # Install OpenShift clients. 52 | 53 | RUN curl -s -o /tmp/oc.tar.gz https://mirror.openshift.com/pub/openshift-v3/clients/3.10.176/linux/oc.tar.gz && \ 54 | tar -C /usr/local/bin -zxf /tmp/oc.tar.gz oc && \ 55 | mv /usr/local/bin/oc /usr/local/bin/oc-3.10 && \ 56 | rm /tmp/oc.tar.gz && \ 57 | curl -s -o /tmp/oc.tar.gz https://mirror.openshift.com/pub/openshift-v3/clients/3.11.153/linux/oc.tar.gz && \ 58 | tar -C /usr/local/bin -zxf /tmp/oc.tar.gz oc && \ 59 | mv /usr/local/bin/oc /usr/local/bin/oc-3.11 && \ 60 | rm /tmp/oc.tar.gz && \ 61 | curl -s -o /tmp/oc.tar.gz https://mirror.openshift.com/pub/openshift-v4/clients/oc/4.1/linux/oc.tar.gz && \ 62 | tar -C /usr/local/bin -zxf /tmp/oc.tar.gz oc && \ 63 | mv /usr/local/bin/oc /usr/local/bin/oc-4.1 && \ 64 | ln -s /usr/local/bin/oc-4.1 /usr/local/bin/kubectl-1.13 && \ 65 | rm /tmp/oc.tar.gz && \ 66 | curl -s -o /tmp/oc.tar.gz https://mirror.openshift.com/pub/openshift-v4/clients/oc/4.2/linux/oc.tar.gz && \ 67 | tar -C /usr/local/bin -zxf /tmp/oc.tar.gz oc && \ 68 | mv /usr/local/bin/oc /usr/local/bin/oc-4.2 && \ 69 | ln -s /usr/local/bin/oc-4.2 /usr/local/bin/kubectl-1.14 && \ 70 | rm /tmp/oc.tar.gz && \ 71 | curl -s -o /tmp/oc.tar.gz https://mirror.openshift.com/pub/openshift-v4/clients/oc/4.3/linux/oc.tar.gz && \ 72 | tar -C /usr/local/bin -zxf /tmp/oc.tar.gz oc && \ 73 | mv /usr/local/bin/oc /usr/local/bin/oc-4.3 && \ 74 | ln -s /usr/local/bin/oc-4.3 /usr/local/bin/kubectl-1.16 && \ 75 | rm /tmp/oc.tar.gz && \ 76 | curl -s -o /tmp/oc.tar.gz https://mirror.openshift.com/pub/openshift-v4/clients/oc/4.4/linux/oc.tar.gz && \ 77 | tar -C /usr/local/bin -zxf /tmp/oc.tar.gz oc && \ 78 | mv /usr/local/bin/oc /usr/local/bin/oc-4.4 && \ 79 | ln -s /usr/local/bin/oc-4.4 /usr/local/bin/kubectl-1.17 && \ 80 | rm /tmp/oc.tar.gz && \ 81 | curl -s -o /tmp/oc.tar.gz https://mirror.openshift.com/pub/openshift-v4/clients/oc/4.5/linux/oc.tar.gz && \ 82 | tar -C /usr/local/bin -zxf /tmp/oc.tar.gz oc && \ 83 | mv /usr/local/bin/oc /usr/local/bin/oc-4.5 && \ 84 | ln -s /usr/local/bin/oc-4.5 /usr/local/bin/kubectl-1.18 && \ 85 | rm /tmp/oc.tar.gz && \ 86 | curl -s -o /tmp/oc.tar.gz https://mirror.openshift.com/pub/openshift-v4/clients/oc/4.6/linux/oc.tar.gz && \ 87 | tar -C /usr/local/bin -zxf /tmp/oc.tar.gz oc && \ 88 | mv /usr/local/bin/oc /usr/local/bin/oc-4.6 && \ 89 | #ln -s /usr/local/bin/oc-4.6 /usr/local/bin/kubectl-1.16 && \ # 4.6 still TBD, but the tool is available 90 | rm /tmp/oc.tar.gz 91 | 92 | RUN curl -sL -o /usr/local/bin/odo-0.0.16 https://github.com/openshift/odo/releases/download/v0.0.16/odo-linux-amd64 && \ 93 | chmod +x /usr/local/bin/odo-0.0.16 && \ 94 | curl -sL -o /usr/local/bin/odo-0.0.17 https://github.com/openshift/odo/releases/download/v0.0.17/odo-linux-amd64 && \ 95 | chmod +x /usr/local/bin/odo-0.0.17 && \ 96 | curl -sL -o /usr/local/bin/odo-0.0.18 https://github.com/openshift/odo/releases/download/v0.0.18/odo-linux-amd64 && \ 97 | chmod +x /usr/local/bin/odo-0.0.18 && \ 98 | curl -sL -o /usr/local/bin/odo-0.0.19 https://github.com/openshift/odo/releases/download/v0.0.19/odo-linux-amd64 && \ 99 | chmod +x /usr/local/bin/odo-0.0.19 && \ 100 | curl -sL -o /usr/local/bin/odo-0.0.20 https://github.com/openshift/odo/releases/download/v0.0.20/odo-linux-amd64 && \ 101 | chmod +x /usr/local/bin/odo-0.0.20 && \ 102 | curl -sL -o /tmp/odo.tar.gz https://mirror.openshift.com/pub/openshift-v4/clients/odo/v1.0.0/odo-linux-amd64.tar.gz && \ 103 | tar -C /tmp -xf /tmp/odo.tar.gz && \ 104 | mv /tmp/odo /usr/local/bin/odo-1.0 && \ 105 | chmod +x /usr/local/bin/odo-1.0 && \ 106 | curl -sL -o /tmp/odo.tar.gz https://mirror.openshift.com/pub/openshift-v4/clients/odo/v1.2.6/odo-linux-amd64.tar.gz && \ 107 | tar -C /tmp -xf /tmp/odo.tar.gz && \ 108 | mv /tmp/odo /usr/local/bin/odo-1.2.6 && \ 109 | chmod +x /usr/local/bin/odo-1.2.6 && \ 110 | rm /tmp/odo.tar.gz 111 | 112 | # Install Kubernetes client. 113 | 114 | RUN curl -sL -o /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v1.10.0/bin/linux/amd64/kubectl && \ 115 | mv /usr/local/bin/kubectl /usr/local/bin/kubectl-1.10 && \ 116 | chmod +x /usr/local/bin/kubectl-1.10 && \ 117 | curl -sL -o /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v1.11.0/bin/linux/amd64/kubectl && \ 118 | mv /usr/local/bin/kubectl /usr/local/bin/kubectl-1.11 && \ 119 | chmod +x /usr/local/bin/kubectl-1.11 && \ 120 | curl -sL -o /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v1.12.0/bin/linux/amd64/kubectl && \ 121 | mv /usr/local/bin/kubectl /usr/local/bin/kubectl-1.12 && \ 122 | chmod +x /usr/local/bin/kubectl-1.12 123 | 124 | # Common environment variables. 125 | 126 | ENV PYTHONUNBUFFERED=1 \ 127 | PYTHONIOENCODING=UTF-8 \ 128 | LC_ALL=en_US.UTF-8 \ 129 | LANG=en_US.UTF-8 \ 130 | PIP_NO_CACHE_DIR=off 131 | 132 | # Install Butterfly using system Python 2.7. 133 | 134 | COPY butterfly /opt/workshop/butterfly 135 | 136 | RUN HOME=/opt/workshop/butterfly && \ 137 | cd /opt/workshop/butterfly && \ 138 | curl -s -o /tmp/get-pip.py https://bootstrap.pypa.io/get-pip.py && \ 139 | /usr/bin/python /tmp/get-pip.py --user && \ 140 | rm -f /tmp/get-pip.py && \ 141 | $HOME/.local/bin/pip install --no-cache-dir --user virtualenv && \ 142 | $HOME/.local/bin/virtualenv /opt/workshop/butterfly && \ 143 | source /opt/workshop/butterfly/bin/activate && \ 144 | pip install --no-cache-dir -r requirements.txt && \ 145 | /opt/workshop/butterfly/install-fonts.sh && \ 146 | /opt/workshop/butterfly/fixup-styles.sh && \ 147 | rm /opt/app-root/etc/scl_enable 148 | 149 | # Install gateway application using SCL Node.js 10. 150 | 151 | COPY gateway /opt/workshop/gateway 152 | 153 | RUN HOME=/opt/workshop/gateway && \ 154 | cd /opt/workshop/gateway && \ 155 | source scl_source enable rh-nodejs10 && \ 156 | npm install --production && \ 157 | chown -R 1001:0 /opt/workshop/gateway/node_modules && \ 158 | fix-permissions /opt/workshop/gateway/node_modules 159 | 160 | # Install mod_wsgi-express for starting Apache with WebDAV. 161 | 162 | RUN HOME=/opt/workshop/webdav && \ 163 | mkdir /opt/workshop/webdav && \ 164 | cd /opt/workshop/gateway && \ 165 | source scl_source enable rh-python36 && \ 166 | source scl_source enable httpd24 && \ 167 | virtualenv /opt/workshop/webdav && \ 168 | source /opt/workshop/webdav/bin/activate && \ 169 | pip install mod_wsgi 170 | 171 | # Finish environment setup. 172 | 173 | ENV BASH_ENV=/opt/workshop/etc/profile \ 174 | ENV=/opt/workshop/etc/profile \ 175 | PROMPT_COMMAND=". /opt/workshop/etc/profile" 176 | 177 | COPY s2i/. /usr/libexec/s2i/ 178 | 179 | COPY bin/. /opt/workshop/bin/ 180 | COPY etc/. /opt/workshop/etc/ 181 | 182 | COPY bin/start-singleuser.sh /opt/app-root/bin/ 183 | 184 | RUN echo "auth requisite pam_deny.so" >> /etc/pam.d/su && \ 185 | sed -i.bak -e 's/^%wheel/# %wheel/' /etc/sudoers && \ 186 | chmod g+w /etc/passwd 187 | 188 | RUN sed -i.bak -e 's/driver = "overlay"/driver = "vfs"/' \ 189 | /etc/containers/storage.conf 190 | 191 | RUN sed -i.bak \ 192 | -e "/\[registries.search\]/{N;s/registries = \[.*\]/registries = ['docker.io', 'registry.fedoraproject.org', 'quay.io', 'registry.centos.org']/}" \ 193 | -e "/\[registries.insecure\]/{N;s/registries = \[.*\]/registries = ['docker-registry.default.svc:5000','image-registry.openshift-image-registry.svc:5000']/}" \ 194 | /etc/containers/registries.conf 195 | 196 | COPY containers/libpod.conf /etc/containers/ 197 | 198 | # COPY containers/sudoers.d/ /etc/sudoers.d/ 199 | 200 | ENV BUILDAH_ISOLATION=chroot 201 | 202 | RUN mkdir -p /opt/app-root/etc/init.d && \ 203 | mkdir -p /opt/app-root/etc/profile.d && \ 204 | mkdir -p /opt/app-root/etc/supervisor && \ 205 | mkdir -p /opt/app-root/gateway/routes && \ 206 | chown -R 1001:0 /opt/app-root && \ 207 | fix-permissions /opt/app-root 208 | 209 | COPY .bash_profile /opt/app-root/src/.bash_profile 210 | 211 | RUN source scl_source enable rh-python36 && \ 212 | virtualenv /opt/app-root && \ 213 | source /opt/app-root/bin/activate && \ 214 | pip install -U pip setuptools wheel && \ 215 | pip install ansible==2.8.2 openshift==0.9.0 jmespath==0.9.4 && \ 216 | ln -s /opt/workshop/bin/oc /opt/app-root/bin/oc && \ 217 | ln -s /opt/workshop/bin/odo /opt/app-root/bin/odo && \ 218 | ln -s /opt/workshop/bin/kubectl /opt/app-root/bin/kubectl && \ 219 | chown -R 1001:0 /opt/app-root && \ 220 | fix-permissions /opt/app-root -P 221 | 222 | COPY profiles/. /opt/workshop/etc/profile.d/ 223 | 224 | RUN ln -s /opt/workshop/etc/supervisord.conf /etc/supervisord.conf 225 | 226 | LABEL io.k8s.display-name="Terminal" \ 227 | io.openshift.expose-services="10080:http" \ 228 | io.openshift.tags="builder,butterfly" \ 229 | io.openshift.s2i.scripts-url=image:///usr/libexec/s2i 230 | 231 | EXPOSE 10080 232 | 233 | USER 1001 234 | 235 | CMD [ "/usr/libexec/s2i/run" ] 236 | -------------------------------------------------------------------------------- /terminal/bin/kubectl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | case $KUBECTL_VERSION in 4 | 1.10|1.10+|1.10.*) 5 | KUBECTL_VERSION=1.10 6 | ;; 7 | 1.11|1.11+|1.11.*) 8 | KUBECTL_VERSION=1.11 9 | ;; 10 | 1.12|1.12+|1.12.*) 11 | KUBECTL_VERSION=1.12 12 | ;; 13 | 1.13|1.13+|1.13.*) 14 | KUBECTL_VERSION=1.13 15 | ;; 16 | 1.14|1.14+|1.14.*) 17 | KUBECTL_VERSION=1.14 18 | ;; 19 | 1.16|1.16+|1.16.*) 20 | KUBECTL_VERSION=1.16 21 | ;; 22 | 1.17|1.17+|1.17.*) 23 | KUBECTL_VERSION=1.17 24 | ;; 25 | 1.18|1.18+|1.18.*) 26 | KUBECTL_VERSION=1.18 27 | ;; 28 | *) 29 | KUBECTL_VERSION=1.18 30 | ;; 31 | esac 32 | 33 | exec /usr/local/bin/kubectl-$KUBECTL_VERSION "$@" 34 | -------------------------------------------------------------------------------- /terminal/bin/oc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | case $OC_VERSION in 4 | 3.10|3.10+|3.10.*) 5 | OC_VERSION=3.10 6 | ;; 7 | 3.11|3.11+|3.11.*) 8 | OC_VERSION=3.11 9 | ;; 10 | 4.1|4.1+|4.1.*) 11 | OC_VERSION=4.1 12 | ;; 13 | 4.2|4.2+|4.2.*) 14 | OC_VERSION=4.2 15 | ;; 16 | 4.3|4.3+|4.3.*) 17 | OC_VERSION=4.3 18 | ;; 19 | 4.4|4.4+|4.4.*) 20 | OC_VERSION=4.4 21 | ;; 22 | 4.5|4.5+|4.5.*) 23 | OC_VERSION=4.5 24 | ;; 25 | 4.6|4.6+|4.6.*) 26 | OC_VERSION=4.6 27 | ;; 28 | *) 29 | OC_VERSION=4.6 30 | ;; 31 | esac 32 | 33 | exec /usr/local/bin/oc-$OC_VERSION "$@" 34 | -------------------------------------------------------------------------------- /terminal/bin/odo: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | case $ODO_VERSION in 4 | 0.0.16|0.0.17|0.0.18|0.0.19|0.0.20) 5 | ;; 6 | 0.0|0.0.*) 7 | ODO_VERSION=0.0.20 8 | ;; 9 | 1.0|1.0.*) 10 | ODO_VERSION=1.0 11 | ;; 12 | 1.2.6|1.2.*) 13 | ODO_VERSION=1.2.6 14 | ;; 15 | *) 16 | ODO_VERSION=1.2.6 17 | ;; 18 | esac 19 | 20 | exec /usr/local/bin/odo-$ODO_VERSION "$@" 21 | -------------------------------------------------------------------------------- /terminal/bin/setup-console.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | set -eo pipefail 6 | 7 | # Setup environment, including login. 8 | 9 | . /opt/workshop/bin/setup-environ.sh 10 | 11 | # Copy console script to shared directory. 12 | 13 | if [ -d $TOKEN_DIRECTORY ]; then 14 | cp /opt/workshop/bin/start-console.sh $TOKEN_DIRECTORY 15 | fi 16 | -------------------------------------------------------------------------------- /terminal/bin/setup-environ.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Add entry to /etc/passwd file. 4 | 5 | STATUS=0 && whoami &> /dev/null || STATUS=$? && true 6 | 7 | if [[ "$STATUS" != "0" ]]; then 8 | cat /etc/passwd | sed -e "s/^default:/builder:/" > /tmp/passwd 9 | echo "default:x:$(id -u):$(id -g):,,,:/opt/app-root/src:/bin/bash" >> /tmp/passwd 10 | cat /tmp/passwd > /etc/passwd 11 | rm /tmp/passwd 12 | fi 13 | 14 | # Try and work out the correct version of the command line tools to use 15 | # if not explicitly specified in environment. This assumes you will be 16 | # using the same cluster the deployment is to. 17 | 18 | KUBERNETES_SERVER=$KUBERNETES_PORT_443_TCP_ADDR:$KUBERNETES_PORT_443_TCP_PORT 19 | 20 | if [ x"$KUBERNETES_SERVER" != x":" ]; then 21 | if [ -z "$KUBECTL_VERSION" ]; then 22 | KUBECTL_VERSION=`(curl -s -k https://$KUBERNETES_SERVER/version | \ 23 | python -c 'from __future__ import print_function; import sys, json; \ 24 | info = json.loads(sys.stdin.read()); \ 25 | info and print("%s.%s" % (info["major"], info["minor"]))') || true` 26 | fi 27 | fi 28 | 29 | if [ -z "$OC_VERSION" ]; then 30 | case "$KUBECTL_VERSION" in 31 | 1.10|1.10+) 32 | OC_VERSION=3.10 33 | ;; 34 | 1.11|1.11+) 35 | OC_VERSION=3.11 36 | ;; 37 | 1.12|1.12+) 38 | OC_VERSION=4.0 39 | ;; 40 | 1.13|1.13+) 41 | OC_VERSION=4.1 42 | ;; 43 | 1.14|1.14+) 44 | OC_VERSION=4.2 45 | ;; 46 | 1.15|1.15+) 47 | OC_VERSION=4.3 48 | ;; 49 | 1.16|1.16+) 50 | OC_VERSION=4.3 51 | ;; 52 | 1.17|1.17+) 53 | OC_VERSION=4.4 54 | ;; 55 | 1.18|1.18+) 56 | OC_VERSION=4.5 57 | ;; 58 | esac 59 | fi 60 | 61 | if [ -z "$ODO_VERSION" ]; then 62 | ODO_VERSION=1.2.6 63 | fi 64 | 65 | export OC_VERSION 66 | export KUBECTL_VERSION 67 | export ODO_VERSION 68 | 69 | # Setup 'oc' client configuration for the location of the OpenShift cluster. 70 | 71 | CA_FILE="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" 72 | 73 | if [ x"$KUBERNETES_SERVER" != x":" ]; then 74 | if [ -f $CA_FILE ]; then 75 | KUBECTL_CA_ARGS="--certificate-authority $CA_FILE" 76 | else 77 | KUBECTL_CA_ARGS="--insecure-skip-tls-verify" 78 | fi 79 | 80 | kubectl config set-cluster local $KUBECTL_CA_ARGS --server "https://$KUBERNETES_SERVER" 81 | 82 | CONTEXT_ARGS= 83 | 84 | if [ x"$PROJECT_NAMESPACE" != x"" ]; then 85 | CONTEXT_ARGS="--namespace=$PROJECT_NAMESPACE" 86 | fi 87 | 88 | kubectl config set-context local --cluster local $CONTEXT_ARGS 89 | kubectl config use-context local 90 | fi 91 | 92 | # Now attempt to login to the OpenShift cluster. First check whether we 93 | # inherited a user access token from shared directory volume initialised 94 | # from an init container. If not, see if we have been passed in a user 95 | # access token or username/password via an environment to use to login. 96 | # Finally, see if the service account token has been mounted into the 97 | # container. 98 | 99 | TOKEN_DIRECTORY="/var/run/workshop" 100 | USER_TOKEN_FILE="$TOKEN_DIRECTORY/token" 101 | ACCT_TOKEN_FILE="/var/run/secrets/kubernetes.io/serviceaccount/token" 102 | 103 | if [ x"$KUBERNETES_SERVER" != x":" ]; then 104 | if [ -f $USER_TOKEN_FILE ]; then 105 | oc login $KUBECTL_CA_ARGS --token `cat $USER_TOKEN_FILE` > /dev/null 2>&1 106 | else 107 | if [ x"$OPENSHIFT_TOKEN" != x"" ]; then 108 | oc login $KUBECTL_CA_ARGS --token "$OPENSHIFT_TOKEN" > /dev/null 2>&1 109 | if [ -d $TOKEN_DIRECTORY ]; then 110 | echo "$OPENSHIFT_TOKEN" > $USER_TOKEN_FILE.$$ 111 | mv $USER_TOKEN_FILE.$$ $USER_TOKEN_FILE 112 | fi 113 | else 114 | if [ x"$OPENSHIFT_USERNAME" != x"" -a x"$OPENSHIFT_PASSWORD" != x"" ]; then 115 | oc login $KUBECTL_CA_ARGS -u "$OPENSHIFT_USERNAME" -p "$OPENSHIFT_PASSWORD" > /dev/null 2>&1 116 | if [ -d $TOKEN_DIRECTORY ]; then 117 | oc whoami --show-token > $USER_TOKEN_FILE 118 | fi 119 | else 120 | if [ -f $ACCT_TOKEN_FILE ]; then 121 | oc login $KUBECTL_CA_ARGS --token `cat $ACCT_TOKEN_FILE` > /dev/null 2>&1 122 | fi 123 | fi 124 | fi 125 | fi 126 | fi 127 | 128 | # If we have been supplied the name of a OpenShift project to use, change 129 | # to that specific project, rather than rely on default selected, and try 130 | # and create the project if it doesn't exist. We need to override the 131 | # project namespace environment variable in this situation. 132 | 133 | if [ x"$OPENSHIFT_PROJECT" != x"" ]; then 134 | oc project "$OPENSHIFT_PROJECT" || oc new-project "$OPENSHIFT_PROJECT" > /dev/null 2>&1 135 | export PROJECT_NAMESPACE=$OPENSHIFT_PROJECT 136 | else 137 | if [ x"$PROJECT_NAMESPACE" != x"" ]; then 138 | oc project "$PROJECT_NAMESPACE" > /dev/null 2>&1 || true 139 | fi 140 | fi 141 | 142 | # Calculate the address for the internal OpenShift image registry. 143 | 144 | if [ x"$OPENSHIFT_IMAGE_REGISTRY" = x"" ]; then 145 | OPENSHIFT_IMAGE_REGISTRY="image-registry.openshift-image-registry.svc:5000" 146 | 147 | case "$KUBECTL_VERSION" in 148 | 1.10|1.10+|1.11|1.11+) 149 | OPENSHIFT_IMAGE_REGISTRY="docker-registry.default.svc:5000" 150 | ;; 151 | esac 152 | 153 | export OPENSHIFT_IMAGE_REGISTRY 154 | fi 155 | 156 | # If the host used in cluster subdomain for applications is not defined 157 | # try and determine by querying the REST API. Ignore any errors. 158 | 159 | if [ x"$CLUSTER_SUBDOMAIN" = x"" ]; then 160 | if [ x"$APPLICATION_NAME" = x"" ]; then 161 | APPLICATION_NAME=`echo $HOSTNAME | sed -e 's/-[0-9]*-[a-z0-9]*$//'` 162 | fi 163 | 164 | APPLICATION_HOST=`oc get route $APPLICATION_NAME -o template --template '{{.spec.host}}{{"\n"}}' 2>/dev/null || true` 165 | 166 | CLUSTER_SUBDOMAIN=`echo $APPLICATION_HOST | sed -e 's/[a-z0-9-]*\.//'` 167 | export CLUSTER_SUBDOMAIN 168 | fi 169 | 170 | # Setup WebDAV configuration for when running Apache. Done here so that 171 | # environment variables are available to the terminal to add an account. 172 | 173 | WEBDAV_REALM=workshop 174 | WEBDAV_USERFILE=/opt/app-root/etc/webdav.htdigest 175 | 176 | export WEBDAV_REALM 177 | export WEBDAV_USERFILE 178 | 179 | if [ ! -s $WEBDAV_USERFILE ]; then 180 | touch $WEBDAV_USERFILE 181 | if [ x"$OPENSHIFT_USERNAME" != x"" -a x"$OPENSHIFT_PASSWORD" != x"" ]; then 182 | DIGEST="$( printf "%s:%s:%s" "$OPENSHIFT_USERNAME" "$WEBDAV_REALM" "$OPENSHIFT_PASSWORD" | md5sum | awk '{print $1}' )" 183 | printf "%s:%s:%s\n" "$OPENSHIFT_USERNAME" "$WEBDAV_REALM" "$DIGEST" >> $WEBDAV_USERFILE 184 | fi 185 | fi 186 | -------------------------------------------------------------------------------- /terminal/bin/setup-volume.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | set -eo pipefail 6 | 7 | SRC=$1 8 | DEST=$2 9 | 10 | if [ -f $DEST/.delete-volume ]; then 11 | rm -rf $DEST 12 | fi 13 | 14 | if [ -d $DEST ]; then 15 | if [ -f $DEST/.sync-volume ]; then 16 | if ! [[ "$WORKSHOP_SYNC_VOLUME" =~ ^(false|no|n|0)$ ]]; then 17 | WORKSHOP_SYNC_VOLUME=yes 18 | fi 19 | fi 20 | 21 | if [[ "$WORKSHOP_SYNC_VOLUME" =~ ^(true|yes|y|1)$ ]]; then 22 | rsync -ar --ignore-existing $SRC/. $DEST 23 | fi 24 | 25 | exit 26 | fi 27 | 28 | if [ -d $DEST.setup-volume ]; then 29 | rm -rf $DEST.setup-volume 30 | fi 31 | 32 | mkdir -p $DEST.setup-volume 33 | 34 | tar -C $SRC -cf - . | tar -C $DEST.setup-volume -xvf - 35 | 36 | mv $DEST.setup-volume $DEST 37 | -------------------------------------------------------------------------------- /terminal/bin/start-butterfly.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | set -x 6 | 7 | URI_ROOT_PATH=/terminal 8 | export URI_ROOT_PATH 9 | 10 | if [ x"$JUPYTERHUB_SERVICE_PREFIX" != x"" ]; then 11 | URI_ROOT_PATH=${JUPYTERHUB_SERVICE_PREFIX%/}/terminal 12 | fi 13 | 14 | # Now execute the program. We need to supply a startup script for the 15 | # shell to setup the environment. 16 | 17 | MOTD_FILE=motd 18 | 19 | if [ -f /opt/workshop/etc/motd ]; then 20 | MOTD_FILE=/opt/workshop/etc/motd 21 | fi 22 | 23 | if [ -f /opt/app-root/etc/motd ]; then 24 | MOTD_FILE=/opt/app-root/etc/motd 25 | fi 26 | 27 | cd /opt/workshop/butterfly 28 | 29 | exec /opt/workshop/butterfly/bin/butterfly.server.py --port=10081 \ 30 | --host=0.0.0.0 --uri-root-path="$URI_ROOT_PATH" --unsecure \ 31 | --i-hereby-declare-i-dont-want-any-security-whatsoever \ 32 | --shell=/opt/workshop/butterfly/start-terminal.sh --motd=$MOTD_FILE 33 | -------------------------------------------------------------------------------- /terminal/bin/start-console.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | set -eo pipefail 6 | 7 | if [ x"$OPENSHIFT_TOKEN" != x"" ]; then 8 | export BRIDGE_K8S_AUTH_BEARER_TOKEN=$OPENSHIFT_TOKEN 9 | export BRIDGE_K8S_AUTH=bearer-token 10 | else 11 | if [ -f /var/run/workshop/token ]; then 12 | export BRIDGE_K8S_AUTH_BEARER_TOKEN=`cat /var/run/workshop/token` 13 | export BRIDGE_K8S_AUTH=bearer-token 14 | fi 15 | fi 16 | 17 | exec /opt/bridge/bin/bridge 18 | -------------------------------------------------------------------------------- /terminal/bin/start-gateway.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | set -x 6 | 7 | if [ -f /opt/workshop/envvars/gateway.sh ]; then 8 | set -a 9 | . /opt/workshop/envvars/gateway.sh 10 | set +a 11 | fi 12 | 13 | if [ -f /opt/app-root/envvars/gateway.sh ]; then 14 | set -a 15 | . /opt/app-root/envvars/gateway.sh 16 | set +a 17 | fi 18 | 19 | URI_ROOT_PATH= 20 | export URI_ROOT_PATH 21 | 22 | if [ x"$JUPYTERHUB_SERVICE_PREFIX" != x"" ]; then 23 | URI_ROOT_PATH=${JUPYTERHUB_SERVICE_PREFIX%/} 24 | fi 25 | 26 | cd /opt/workshop/gateway 27 | 28 | NODE_PATH=`pwd`/node_modules 29 | export NODE_PATH 30 | 31 | exec npm start 32 | -------------------------------------------------------------------------------- /terminal/bin/start-singleuser.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec /usr/libexec/s2i/run "$@" 4 | -------------------------------------------------------------------------------- /terminal/bin/start-terminal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -f /opt/workshop/envvars/terminal.sh ]; then 4 | set -a 5 | . /opt/workshop/envvars/terminal.sh 6 | set +a 7 | fi 8 | 9 | if [ -f /opt/app-root/envvars/terminal.sh ]; then 10 | set -a 11 | . /opt/app-root/envvars/terminal.sh 12 | set +a 13 | fi 14 | 15 | exec /opt/workshop/bin/start-butterfly.sh 16 | -------------------------------------------------------------------------------- /terminal/bin/start-webdav.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | set -x 6 | 7 | WEBDAV_PREFIX=${WEBDAV_PREFIX:-${JUPYTERHUB_SERVICE_PREFIX%/}} 8 | export WEBDAV_PREFIX 9 | 10 | WEBDAV_PORT=${WEBDAV_PORT:-10084} 11 | 12 | ARGS="" 13 | 14 | ARGS="$ARGS --log-to-terminal" 15 | ARGS="$ARGS --port $WEBDAV_PORT" 16 | ARGS="$ARGS --application-type static" 17 | ARGS="$ARGS --include /opt/workshop/etc/httpd-webdav.conf" 18 | 19 | source /opt/workshop/webdav/bin/activate 20 | 21 | exec mod_wsgi-express start-server $ARGS 22 | -------------------------------------------------------------------------------- /terminal/butterfly/fixup-styles.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION=`python -c 'import sys; print(".".join(map(str,sys.version_info[:2])))'` 4 | 5 | FILENAME=/opt/workshop/butterfly/lib/python$VERSION/site-packages/butterfly/static/main.css 6 | 7 | cat << EOF >> $FILENAME 8 | 9 | @supports (-webkit-overflow-scrolling: touch) { 10 | html, body { 11 | height: 100%; 12 | overflow: auto; 13 | overflow-x: hidden; 14 | -webkit-overflow-scrolling: touch; 15 | } 16 | }} 17 | EOF 18 | -------------------------------------------------------------------------------- /terminal/butterfly/install-fonts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | set -eo pipefail 6 | 7 | curl -s -L -o /tmp/fonts.tar.gz \ 8 | https://github.com/powerline/fonts/archive/master.tar.gz 9 | 10 | mkdir powerline 11 | 12 | tar --strip-components 1 -C powerline -xf /tmp/fonts.tar.gz 13 | 14 | rm /tmp/fonts.tar.gz 15 | 16 | mkdir -p static/fonts 17 | 18 | cd static/fonts 19 | 20 | FONTS="../../powerline/SourceCodePro" 21 | 22 | ln -s "$FONTS/Source Code Pro Black for Powerline.otf" SourceCodePro-Black.otf 23 | ln -s "$FONTS/Source Code Pro Bold for Powerline.otf" SourceCodePro-Bold.otf 24 | ln -s "$FONTS/Source Code Pro ExtraLight for Powerline.otf" SourceCodePro-ExtraLight.otf 25 | ln -s "$FONTS/Source Code Pro Light for Powerline.otf" SourceCodePro-Light.otf 26 | ln -s "$FONTS/Source Code Pro Medium for Powerline.otf" SourceCodePro-Medium.otf 27 | ln -s "$FONTS/Source Code Pro for Powerline.otf" SourceCodePro-Regular.otf 28 | ln -s "$FONTS/Source Code Pro Semibold for Powerline.otf" SourceCodePro-Semibold.otf 29 | -------------------------------------------------------------------------------- /terminal/butterfly/requirements.txt: -------------------------------------------------------------------------------- 1 | butterfly==3.2.5 2 | -------------------------------------------------------------------------------- /terminal/butterfly/start-terminal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export SHELL=/bin/bash 4 | 5 | if [ x"$JUPYTERHUB_USER" != x"" ]; then 6 | export PS1="[$JUPYTERHUB_USER:\w] $ " 7 | else 8 | export PS1="[\w] $ " 9 | fi 10 | 11 | if [ x"$TERMINAL_HOME" != x"" ]; then 12 | cd $TERMINAL_HOME 13 | fi 14 | 15 | exec /bin/bash "$@" 16 | -------------------------------------------------------------------------------- /terminal/containers/libpod.conf: -------------------------------------------------------------------------------- 1 | # libpod.conf is the default configuration file for all tools using libpod to 2 | # manage containers 3 | 4 | # Default transport method for pulling and pushing for images 5 | image_default_transport = "docker://" 6 | 7 | # Paths to look for the Conmon container manager binary 8 | conmon_path = [ 9 | "/usr/libexec/podman/conmon", 10 | "/usr/local/libexec/podman/conmon", 11 | "/usr/local/lib/podman/conmon", 12 | "/usr/bin/conmon", 13 | "/usr/sbin/conmon", 14 | "/usr/local/bin/conmon", 15 | "/usr/local/sbin/conmon" 16 | ] 17 | 18 | # Environment variables to pass into conmon 19 | conmon_env_vars = [ 20 | "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 21 | ] 22 | 23 | # CGroup Manager - valid values are "systemd" and "cgroupfs" 24 | cgroup_manager = "cgroupfs" 25 | 26 | # Container init binary 27 | #init_path = "/usr/libexec/podman/catatonit" 28 | 29 | # Directory for persistent libpod files (database, etc) 30 | # By default, this will be configured relative to where containers/storage 31 | # stores containers 32 | # Uncomment to change location from this default 33 | #static_dir = "/var/lib/containers/storage/libpod" 34 | 35 | # Directory for temporary files. Must be tmpfs (wiped after reboot) 36 | tmp_dir = "/var/run/libpod" 37 | 38 | # Maximum size of log files (in bytes) 39 | # -1 is unlimited 40 | max_log_size = -1 41 | 42 | # Whether to use chroot instead of pivot_root in the runtime 43 | no_pivot_root = false 44 | 45 | # Directory containing CNI plugin configuration files 46 | cni_config_dir = "/etc/cni/net.d/" 47 | 48 | # Directories where the CNI plugin binaries may be located 49 | cni_plugin_dir = [ 50 | "/usr/libexec/cni", 51 | "/usr/lib/cni", 52 | "/usr/local/lib/cni", 53 | "/opt/cni/bin" 54 | ] 55 | 56 | # Default CNI network for libpod. 57 | # If multiple CNI network configs are present, libpod will use the network with 58 | # the name given here for containers unless explicitly overridden. 59 | # The default here is set to the name we set in the 60 | # 87-podman-bridge.conflist included in the repository. 61 | # Not setting this, or setting it to the empty string, will use normal CNI 62 | # precedence rules for selecting between multiple networks. 63 | cni_default_network = "podman" 64 | 65 | # Default libpod namespace 66 | # If libpod is joined to a namespace, it will see only containers and pods 67 | # that were created in the same namespace, and will create new containers and 68 | # pods in that namespace. 69 | # The default namespace is "", which corresponds to no namespace. When no 70 | # namespace is set, all containers and pods are visible. 71 | #namespace = "" 72 | 73 | # Default infra (pause) image name for pod infra containers 74 | infra_image = "k8s.gcr.io/pause:3.1" 75 | 76 | # Default command to run the infra container 77 | infra_command = "/pause" 78 | 79 | # Determines whether libpod will reserve ports on the host when they are 80 | # forwarded to containers. When enabled, when ports are forwarded to containers, 81 | # they are held open by conmon as long as the container is running, ensuring that 82 | # they cannot be reused by other programs on the host. However, this can cause 83 | # significant memory usage if a container has many ports forwarded to it. 84 | # Disabling this can save memory. 85 | #enable_port_reservation = true 86 | 87 | # Default libpod support for container labeling 88 | # label=true 89 | 90 | # The locking mechanism to use 91 | lock_type = "shm" 92 | 93 | # Number of locks available for containers and pods. 94 | # If this is changed, a lock renumber must be performed (e.g. with the 95 | # 'podman system renumber' command). 96 | num_locks = 2048 97 | 98 | # Directory for libpod named volumes. 99 | # By default, this will be configured relative to where containers/storage 100 | # stores containers. 101 | # Uncomment to change location from this default. 102 | #volume_path = "/var/lib/containers/storage/volumes" 103 | 104 | # Selects which logging mechanism to use for Podman events. Valid values 105 | # are `journald` or `file`. 106 | # events_logger = "journald" 107 | 108 | # Specify the keys sequence used to detach a container. 109 | # Format is a single character [a-Z] or a comma separated sequence of 110 | # `ctrl-`, where `` is one of: 111 | # `a-z`, `@`, `^`, `[`, `\`, `]`, `^` or `_` 112 | # 113 | # detach_keys = "ctrl-p,ctrl-q" 114 | 115 | # Default OCI runtime 116 | runtime = "runc" 117 | 118 | # List of the OCI runtimes that support --format=json. When json is supported 119 | # libpod will use it for reporting nicer errors. 120 | runtime_supports_json = ["runc"] 121 | 122 | # Paths to look for a valid OCI runtime (runc, runv, etc) 123 | [runtimes] 124 | runc = [ 125 | "/usr/bin/runc", 126 | "/usr/sbin/runc", 127 | "/usr/local/bin/runc", 128 | "/usr/local/sbin/runc", 129 | "/sbin/runc", 130 | "/bin/runc", 131 | "/usr/lib/cri-o-runc/sbin/runc" 132 | ] 133 | 134 | crun = [ 135 | "/usr/bin/crun", 136 | "/usr/local/bin/crun", 137 | ] 138 | 139 | # The [runtimes] table MUST be the last thing in this file. 140 | # (Unless another table is added) 141 | # TOML does not provide a way to end a table other than a further table being 142 | # defined, so every key hereafter will be part of [runtimes] and not the main 143 | # config. 144 | -------------------------------------------------------------------------------- /terminal/containers/sudoers.d/buildah: -------------------------------------------------------------------------------- 1 | %root ALL=(root) NOPASSWD:/usr/bin/buildah 2 | -------------------------------------------------------------------------------- /terminal/containers/sudoers.d/podman: -------------------------------------------------------------------------------- 1 | %root ALL=(root) NOPASSWD:/usr/bin/podman 2 | -------------------------------------------------------------------------------- /terminal/etc/httpd-webdav.conf: -------------------------------------------------------------------------------- 1 | 2 | LoadModule dav_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_dav.so' 3 | 4 | 5 | 6 | LoadModule dav_fs_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_dav_fs.so' 7 | 8 | 9 | 10 | LoadModule auth_digest_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_auth_digest.so' 11 | 12 | 13 | 14 | LoadModule authn_file_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_authn_file.so' 15 | 16 | 17 | 18 | LoadModule authz_user_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_authz_user.so' 19 | 20 | 21 | 22 | LoadModule autoindex_module '${MOD_WSGI_MODULES_DIRECTORY}/mod_autoindex.so' 23 | 24 | 25 | AddDefaultCharset utf-8 26 | 27 | DavLockDB /opt/app-root/DavLock 28 | 29 | Alias ${WEBDAV_PREFIX}/webdav/ /opt/app-root/src/ 30 | 31 | 32 | DAV on 33 | 34 | DAVDepthInfinity On 35 | 36 | Options +Indexes 37 | IndexOptions FancyIndexing NameWidth=* Charset=UTF-8 38 | 39 | DirectoryIndex disabled 40 | DirectorySlash Off 41 | 42 | AuthType Digest 43 | AuthName ${WEBDAV_REALM} 44 | AuthDigestDomain ${WEBDAV_PREFIX}/webdav/ 45 | AuthDigestProvider file 46 | AuthUserFile ${WEBDAV_USERFILE} 47 | 48 | Require valid-user 49 | 50 | -------------------------------------------------------------------------------- /terminal/etc/motd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-homeroom/workshop-terminal/64069fa73e0554c82826f8a24178c7d7a26b9d0d/terminal/etc/motd -------------------------------------------------------------------------------- /terminal/etc/profile: -------------------------------------------------------------------------------- 1 | unset BASH_ENV PROMPT_COMMAND ENV 2 | 3 | # Read in additional application profile files. 4 | 5 | for i in /opt/workshop/etc/profile.d/*.sh /opt/workshop/etc/profile.d/sh.local; do 6 | if [ -r "$i" ]; then 7 | . "$i" >/dev/null 8 | fi 9 | done 10 | 11 | for i in /opt/app-root/etc/profile.d/*.sh /opt/app-root/etc/profile.d/sh.local; do 12 | if [ -r "$i" ]; then 13 | . "$i" >/dev/null 14 | fi 15 | done 16 | -------------------------------------------------------------------------------- /terminal/etc/profile.d/nodejs.sh: -------------------------------------------------------------------------------- 1 | source scl_source enable rh-nodejs10 2 | -------------------------------------------------------------------------------- /terminal/etc/profile.d/sh.local: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openshift-homeroom/workshop-terminal/64069fa73e0554c82826f8a24178c7d7a26b9d0d/terminal/etc/profile.d/sh.local -------------------------------------------------------------------------------- /terminal/etc/supervisor/gateway.conf: -------------------------------------------------------------------------------- 1 | [program:gateway] 2 | command=/opt/workshop/bin/start-gateway.sh 3 | stopsignal=KILL 4 | redirect_stderr=true 5 | stdout_logfile=/dev/fd/1 6 | stdout_logfile_maxbytes=0 7 | -------------------------------------------------------------------------------- /terminal/etc/supervisor/terminal.conf: -------------------------------------------------------------------------------- 1 | [program:terminal] 2 | command=/opt/workshop/bin/start-terminal.sh 3 | stopsignal=KILL 4 | redirect_stderr=true 5 | stdout_logfile=/dev/fd/1 6 | stdout_logfile_maxbytes=0 7 | autostart=%(ENV_ENABLE_TERMINAL)s 8 | -------------------------------------------------------------------------------- /terminal/etc/supervisor/webdav.conf: -------------------------------------------------------------------------------- 1 | [program:webdav] 2 | process_name=webdav 3 | command=/opt/workshop/bin/start-webdav.sh 4 | stdout_logfile=/proc/1/fd/1 5 | stdout_logfile_maxbytes=0 6 | redirect_stderr=true 7 | autostart=%(ENV_ENABLE_WEBDAV)s 8 | -------------------------------------------------------------------------------- /terminal/etc/supervisord.conf: -------------------------------------------------------------------------------- 1 | ; Supervisor config file. 2 | ; 3 | ; For more information on the config file, please see: 4 | ; http://supervisord.org/configuration.html 5 | ; 6 | ; Notes: 7 | ; - Shell expansion ("~" or "$HOME") is not supported. Environment 8 | ; variables can be expanded using this syntax: "%(ENV_HOME)s". 9 | ; - Quotes around values are not supported, except in the case of 10 | ; the environment= options as shown below. 11 | ; - Comments must have a leading space: "a=b ;comment" not "a=b;comment". 12 | ; - Command will be truncated if it looks like a config file comment, e.g. 13 | ; "command=bash -c 'foo ; bar'" will truncate to "command=bash -c 'foo ". 14 | 15 | [unix_http_server] 16 | file=/tmp/supervisor.sock ; the path to the socket file 17 | ;chmod=0700 ; socket file mode (default 0700) 18 | ;chown=nobody:nogroup ; socket file uid:gid owner 19 | ;username=user ; default is no username (open server) 20 | ;password=123 ; default is no password (open server) 21 | 22 | ;[inet_http_server] ; inet (TCP) server disabled by default 23 | ;port=127.0.0.1:9001 ; ip_address:port specifier, *:port for all iface 24 | ;username=user ; default is no username (open server) 25 | ;password=123 ; default is no password (open server) 26 | 27 | [supervisord] 28 | logfile=/dev/fd/1 ; main log file; default stdout of pid 1 29 | logfile_maxbytes=0 ; max main logfile bytes b4 rotation; disabled 30 | logfile_backups=10 ; # of main logfile backups; 0 means none, default 10 31 | loglevel=info ; log level; default info; others: debug,warn,trace 32 | pidfile=/tmp/supervisord.pid ; supervisord pidfile; default supervisord.pid 33 | nodaemon=false ; start in foreground if true; default false 34 | minfds=1024 ; min. avail startup file descriptors; default 1024 35 | minprocs=200 ; min. avail process descriptors;default 200 36 | ;umask=022 ; process file creation umask; default 022 37 | ;user=chrism ; default is current user, required if root 38 | ;identifier=supervisor ; supervisord identifier, default is 'supervisor' 39 | ;directory=/tmp ; default is not to cd during start 40 | ;nocleanup=true ; don't clean up tempfiles at start; default false 41 | ;childlogdir=/tmp ; 'AUTO' child log dir, default $TEMP 42 | ;environment=KEY="value" ; key value pairs to add to environment 43 | ;strip_ansi=false ; strip ansi escape codes in logs; def. false 44 | 45 | ; The rpcinterface:supervisor section must remain in the config file for 46 | ; RPC (supervisorctl/web interface) to work. Additional interfaces may be 47 | ; added by defining them in separate [rpcinterface:x] sections. 48 | 49 | [rpcinterface:supervisor] 50 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface 51 | 52 | ; The supervisorctl section configures how supervisorctl will connect to 53 | ; supervisord. configure it match the settings in either the unix_http_server 54 | ; or inet_http_server section. 55 | 56 | [supervisorctl] 57 | serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket 58 | ;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket 59 | ;username=chris ; should be same as in [*_http_server] if set 60 | ;password=123 ; should be same as in [*_http_server] if set 61 | ;prompt=mysupervisor ; cmd line prompt (default "supervisor") 62 | ;history_file=~/.sc_history ; use readline history if available 63 | 64 | ; The sample program section below shows all possible program subsection values. 65 | ; Create one or more 'real' program: sections to be able to control them under 66 | ; supervisor. 67 | 68 | ;[program:theprogramname] 69 | ;command=/bin/cat ; the program (relative uses PATH, can take args) 70 | ;process_name=%(program_name)s ; process_name expr (default %(program_name)s) 71 | ;numprocs=1 ; number of processes copies to start (def 1) 72 | ;directory=/tmp ; directory to cwd to before exec (def no cwd) 73 | ;umask=022 ; umask for process (default None) 74 | ;priority=999 ; the relative start priority (default 999) 75 | ;autostart=true ; start at supervisord start (default: true) 76 | ;startsecs=1 ; # of secs prog must stay up to be running (def. 1) 77 | ;startretries=3 ; max # of serial start failures when starting (default 3) 78 | ;autorestart=unexpected ; when to restart if exited after running (def: unexpected) 79 | ;exitcodes=0,2 ; 'expected' exit codes used with autorestart (default 0,2) 80 | ;stopsignal=QUIT ; signal used to kill process (default TERM) 81 | ;stopwaitsecs=10 ; max num secs to wait b4 SIGKILL (default 10) 82 | ;stopasgroup=false ; send stop signal to the UNIX process group (default false) 83 | ;killasgroup=false ; SIGKILL the UNIX process group (def false) 84 | ;user=chrism ; setuid to this UNIX account to run the program 85 | ;redirect_stderr=true ; redirect proc stderr to stdout (default false) 86 | ;stdout_logfile=/a/path ; stdout log path, NONE for none; default AUTO 87 | ;stdout_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB) 88 | ;stdout_logfile_backups=10 ; # of stdout logfile backups (0 means none, default 10) 89 | ;stdout_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0) 90 | ;stdout_events_enabled=false ; emit events on stdout writes (default false) 91 | ;stderr_logfile=/a/path ; stderr log path, NONE for none; default AUTO 92 | ;stderr_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB) 93 | ;stderr_logfile_backups=10 ; # of stderr logfile backups (0 means none, default 10) 94 | ;stderr_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0) 95 | ;stderr_events_enabled=false ; emit events on stderr writes (default false) 96 | ;environment=A="1",B="2" ; process environment additions (def no adds) 97 | ;serverurl=AUTO ; override serverurl computation (childutils) 98 | 99 | ; The sample eventlistener section below shows all possible eventlistener 100 | ; subsection values. Create one or more 'real' eventlistener: sections to be 101 | ; able to handle event notifications sent by supervisord. 102 | 103 | ;[eventlistener:theeventlistenername] 104 | ;command=/bin/eventlistener ; the program (relative uses PATH, can take args) 105 | ;process_name=%(program_name)s ; process_name expr (default %(program_name)s) 106 | ;numprocs=1 ; number of processes copies to start (def 1) 107 | ;events=EVENT ; event notif. types to subscribe to (req'd) 108 | ;buffer_size=10 ; event buffer queue size (default 10) 109 | ;directory=/tmp ; directory to cwd to before exec (def no cwd) 110 | ;umask=022 ; umask for process (default None) 111 | ;priority=-1 ; the relative start priority (default -1) 112 | ;autostart=true ; start at supervisord start (default: true) 113 | ;startsecs=1 ; # of secs prog must stay up to be running (def. 1) 114 | ;startretries=3 ; max # of serial start failures when starting (default 3) 115 | ;autorestart=unexpected ; autorestart if exited after running (def: unexpected) 116 | ;exitcodes=0,2 ; 'expected' exit codes used with autorestart (default 0,2) 117 | ;stopsignal=QUIT ; signal used to kill process (default TERM) 118 | ;stopwaitsecs=10 ; max num secs to wait b4 SIGKILL (default 10) 119 | ;stopasgroup=false ; send stop signal to the UNIX process group (default false) 120 | ;killasgroup=false ; SIGKILL the UNIX process group (def false) 121 | ;user=chrism ; setuid to this UNIX account to run the program 122 | ;redirect_stderr=false ; redirect_stderr=true is not allowed for eventlisteners 123 | ;stdout_logfile=/a/path ; stdout log path, NONE for none; default AUTO 124 | ;stdout_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB) 125 | ;stdout_logfile_backups=10 ; # of stdout logfile backups (0 means none, default 10) 126 | ;stdout_events_enabled=false ; emit events on stdout writes (default false) 127 | ;stderr_logfile=/a/path ; stderr log path, NONE for none; default AUTO 128 | ;stderr_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB) 129 | ;stderr_logfile_backups=10 ; # of stderr logfile backups (0 means none, default 10) 130 | ;stderr_events_enabled=false ; emit events on stderr writes (default false) 131 | ;environment=A="1",B="2" ; process environment additions 132 | ;serverurl=AUTO ; override serverurl computation (childutils) 133 | 134 | ; The sample group section below shows all possible group values. Create one 135 | ; or more 'real' group: sections to create "heterogeneous" process groups. 136 | 137 | ;[group:thegroupname] 138 | ;programs=progname1,progname2 ; each refers to 'x' in [program:x] definitions 139 | ;priority=999 ; the relative start priority (default 999) 140 | 141 | ; The [include] section can just contain the "files" setting. This 142 | ; setting can list multiple files (separated by whitespace or 143 | ; newlines). It can also contain wildcards. The filenames are 144 | ; interpreted as relative to this file. Included files *cannot* 145 | ; include files themselves. 146 | 147 | [include] 148 | files = supervisor/*.conf /opt/app-root/etc/supervisor/*.conf 149 | -------------------------------------------------------------------------------- /terminal/gateway/logger.js: -------------------------------------------------------------------------------- 1 | const winston = require("winston"); 2 | 3 | const level = process.env.LOG_LEVEL || 'debug'; 4 | 5 | const logger = winston.createLogger({ 6 | transports: [ 7 | new winston.transports.Console({ 8 | level: level, 9 | timestamp: function () { 10 | return (new Date()).toISOString(); 11 | } 12 | }) 13 | ] 14 | }); 15 | 16 | module.exports = logger 17 | -------------------------------------------------------------------------------- /terminal/gateway/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workshop-gateway", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@dabh/diagnostics": { 8 | "version": "2.0.2", 9 | "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", 10 | "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", 11 | "requires": { 12 | "colorspace": "1.1.x", 13 | "enabled": "2.0.x", 14 | "kuler": "^2.0.0" 15 | } 16 | }, 17 | "@hapi/address": { 18 | "version": "2.1.4", 19 | "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", 20 | "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==" 21 | }, 22 | "@hapi/bourne": { 23 | "version": "1.3.2", 24 | "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz", 25 | "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==" 26 | }, 27 | "@hapi/hoek": { 28 | "version": "8.5.1", 29 | "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz", 30 | "integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==" 31 | }, 32 | "@hapi/joi": { 33 | "version": "15.1.1", 34 | "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.1.tgz", 35 | "integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==", 36 | "requires": { 37 | "@hapi/address": "2.x.x", 38 | "@hapi/bourne": "1.x.x", 39 | "@hapi/hoek": "8.x.x", 40 | "@hapi/topo": "3.x.x" 41 | } 42 | }, 43 | "@hapi/topo": { 44 | "version": "3.1.6", 45 | "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.6.tgz", 46 | "integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==", 47 | "requires": { 48 | "@hapi/hoek": "^8.3.0" 49 | } 50 | }, 51 | "@types/babel-types": { 52 | "version": "7.0.9", 53 | "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.9.tgz", 54 | "integrity": "sha512-qZLoYeXSTgQuK1h7QQS16hqLGdmqtRmN8w/rl3Au/l5x/zkHx+a4VHrHyBsi1I1vtK2oBHxSzKIu0R5p6spdOA==" 55 | }, 56 | "@types/babylon": { 57 | "version": "6.16.5", 58 | "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz", 59 | "integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==", 60 | "requires": { 61 | "@types/babel-types": "*" 62 | } 63 | }, 64 | "accepts": { 65 | "version": "1.3.7", 66 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 67 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 68 | "requires": { 69 | "mime-types": "~2.1.24", 70 | "negotiator": "0.6.2" 71 | } 72 | }, 73 | "acorn": { 74 | "version": "3.3.0", 75 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", 76 | "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" 77 | }, 78 | "acorn-globals": { 79 | "version": "3.1.0", 80 | "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", 81 | "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", 82 | "requires": { 83 | "acorn": "^4.0.4" 84 | }, 85 | "dependencies": { 86 | "acorn": { 87 | "version": "4.0.13", 88 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", 89 | "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" 90 | } 91 | } 92 | }, 93 | "align-text": { 94 | "version": "0.1.4", 95 | "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", 96 | "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", 97 | "requires": { 98 | "kind-of": "^3.0.2", 99 | "longest": "^1.0.1", 100 | "repeat-string": "^1.5.2" 101 | }, 102 | "dependencies": { 103 | "kind-of": { 104 | "version": "3.2.2", 105 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 106 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 107 | "requires": { 108 | "is-buffer": "^1.1.5" 109 | } 110 | } 111 | } 112 | }, 113 | "arr-diff": { 114 | "version": "4.0.0", 115 | "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", 116 | "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" 117 | }, 118 | "arr-flatten": { 119 | "version": "1.1.0", 120 | "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", 121 | "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" 122 | }, 123 | "arr-union": { 124 | "version": "3.1.0", 125 | "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", 126 | "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" 127 | }, 128 | "array-flatten": { 129 | "version": "1.1.1", 130 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 131 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 132 | }, 133 | "array-unique": { 134 | "version": "0.3.2", 135 | "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", 136 | "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" 137 | }, 138 | "asap": { 139 | "version": "2.0.6", 140 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", 141 | "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" 142 | }, 143 | "assign-symbols": { 144 | "version": "1.0.0", 145 | "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", 146 | "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" 147 | }, 148 | "async": { 149 | "version": "3.2.0", 150 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", 151 | "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" 152 | }, 153 | "atob": { 154 | "version": "2.1.2", 155 | "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", 156 | "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" 157 | }, 158 | "axios": { 159 | "version": "0.19.2", 160 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", 161 | "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", 162 | "requires": { 163 | "follow-redirects": "1.5.10" 164 | } 165 | }, 166 | "axios-retry": { 167 | "version": "3.1.8", 168 | "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.1.8.tgz", 169 | "integrity": "sha512-yPw5Y4Bg6Dgmhm35KaJFtlh23s1TecW0HsUerK4/IS1UKl0gtN2aJqdEKtVomiOS/bDo5w4P3sqgki/M10eF8Q==", 170 | "requires": { 171 | "is-retry-allowed": "^1.1.0" 172 | } 173 | }, 174 | "babel-runtime": { 175 | "version": "6.26.0", 176 | "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", 177 | "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", 178 | "requires": { 179 | "core-js": "^2.4.0", 180 | "regenerator-runtime": "^0.11.0" 181 | } 182 | }, 183 | "babel-types": { 184 | "version": "6.26.0", 185 | "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", 186 | "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", 187 | "requires": { 188 | "babel-runtime": "^6.26.0", 189 | "esutils": "^2.0.2", 190 | "lodash": "^4.17.4", 191 | "to-fast-properties": "^1.0.3" 192 | } 193 | }, 194 | "babylon": { 195 | "version": "6.18.0", 196 | "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", 197 | "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" 198 | }, 199 | "base": { 200 | "version": "0.11.2", 201 | "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", 202 | "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", 203 | "requires": { 204 | "cache-base": "^1.0.1", 205 | "class-utils": "^0.3.5", 206 | "component-emitter": "^1.2.1", 207 | "define-property": "^1.0.0", 208 | "isobject": "^3.0.1", 209 | "mixin-deep": "^1.2.0", 210 | "pascalcase": "^0.1.1" 211 | }, 212 | "dependencies": { 213 | "define-property": { 214 | "version": "1.0.0", 215 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", 216 | "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", 217 | "requires": { 218 | "is-descriptor": "^1.0.0" 219 | } 220 | }, 221 | "is-accessor-descriptor": { 222 | "version": "1.0.0", 223 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 224 | "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", 225 | "requires": { 226 | "kind-of": "^6.0.0" 227 | } 228 | }, 229 | "is-data-descriptor": { 230 | "version": "1.0.0", 231 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 232 | "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", 233 | "requires": { 234 | "kind-of": "^6.0.0" 235 | } 236 | }, 237 | "is-descriptor": { 238 | "version": "1.0.2", 239 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", 240 | "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", 241 | "requires": { 242 | "is-accessor-descriptor": "^1.0.0", 243 | "is-data-descriptor": "^1.0.0", 244 | "kind-of": "^6.0.2" 245 | } 246 | } 247 | } 248 | }, 249 | "basic-auth": { 250 | "version": "2.0.1", 251 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", 252 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", 253 | "requires": { 254 | "safe-buffer": "5.1.2" 255 | } 256 | }, 257 | "body-parser": { 258 | "version": "1.19.0", 259 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 260 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 261 | "requires": { 262 | "bytes": "3.1.0", 263 | "content-type": "~1.0.4", 264 | "debug": "2.6.9", 265 | "depd": "~1.1.2", 266 | "http-errors": "1.7.2", 267 | "iconv-lite": "0.4.24", 268 | "on-finished": "~2.3.0", 269 | "qs": "6.7.0", 270 | "raw-body": "2.4.0", 271 | "type-is": "~1.6.17" 272 | }, 273 | "dependencies": { 274 | "debug": { 275 | "version": "2.6.9", 276 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 277 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 278 | "requires": { 279 | "ms": "2.0.0" 280 | } 281 | } 282 | } 283 | }, 284 | "boom": { 285 | "version": "7.3.0", 286 | "resolved": "https://registry.npmjs.org/boom/-/boom-7.3.0.tgz", 287 | "integrity": "sha512-Swpoyi2t5+GhOEGw8rEsKvTxFLIDiiKoUc2gsoV6Lyr43LHBIzch3k2MvYUs8RTROrIkVJ3Al0TkaOGjnb+B6A==", 288 | "requires": { 289 | "hoek": "6.x.x" 290 | } 291 | }, 292 | "bourne": { 293 | "version": "1.1.2", 294 | "resolved": "https://registry.npmjs.org/bourne/-/bourne-1.1.2.tgz", 295 | "integrity": "sha512-b2dgVkTZhkQirNMohgC00rWfpVqEi9y5tKM1k3JvoNx05ODtfQoPPd4js9CYFQoY0IM8LAmnJulEuWv74zjUOg==" 296 | }, 297 | "braces": { 298 | "version": "2.3.2", 299 | "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", 300 | "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", 301 | "requires": { 302 | "arr-flatten": "^1.1.0", 303 | "array-unique": "^0.3.2", 304 | "extend-shallow": "^2.0.1", 305 | "fill-range": "^4.0.0", 306 | "isobject": "^3.0.1", 307 | "repeat-element": "^1.1.2", 308 | "snapdragon": "^0.8.1", 309 | "snapdragon-node": "^2.0.1", 310 | "split-string": "^3.0.2", 311 | "to-regex": "^3.0.1" 312 | }, 313 | "dependencies": { 314 | "extend-shallow": { 315 | "version": "2.0.1", 316 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 317 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 318 | "requires": { 319 | "is-extendable": "^0.1.0" 320 | } 321 | } 322 | } 323 | }, 324 | "bytes": { 325 | "version": "3.1.0", 326 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 327 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 328 | }, 329 | "cache-base": { 330 | "version": "1.0.1", 331 | "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", 332 | "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", 333 | "requires": { 334 | "collection-visit": "^1.0.0", 335 | "component-emitter": "^1.2.1", 336 | "get-value": "^2.0.6", 337 | "has-value": "^1.0.0", 338 | "isobject": "^3.0.1", 339 | "set-value": "^2.0.0", 340 | "to-object-path": "^0.3.0", 341 | "union-value": "^1.0.0", 342 | "unset-value": "^1.0.0" 343 | } 344 | }, 345 | "camelcase": { 346 | "version": "1.2.1", 347 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", 348 | "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" 349 | }, 350 | "center-align": { 351 | "version": "0.1.3", 352 | "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", 353 | "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", 354 | "requires": { 355 | "align-text": "^0.1.3", 356 | "lazy-cache": "^1.0.3" 357 | } 358 | }, 359 | "character-parser": { 360 | "version": "2.2.0", 361 | "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", 362 | "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", 363 | "requires": { 364 | "is-regex": "^1.0.3" 365 | } 366 | }, 367 | "class-utils": { 368 | "version": "0.3.6", 369 | "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", 370 | "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", 371 | "requires": { 372 | "arr-union": "^3.1.0", 373 | "define-property": "^0.2.5", 374 | "isobject": "^3.0.0", 375 | "static-extend": "^0.1.1" 376 | }, 377 | "dependencies": { 378 | "define-property": { 379 | "version": "0.2.5", 380 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 381 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 382 | "requires": { 383 | "is-descriptor": "^0.1.0" 384 | } 385 | } 386 | } 387 | }, 388 | "clean-css": { 389 | "version": "4.2.3", 390 | "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", 391 | "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", 392 | "requires": { 393 | "source-map": "~0.6.0" 394 | }, 395 | "dependencies": { 396 | "source-map": { 397 | "version": "0.6.1", 398 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 399 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" 400 | } 401 | } 402 | }, 403 | "cliui": { 404 | "version": "2.1.0", 405 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", 406 | "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", 407 | "requires": { 408 | "center-align": "^0.1.1", 409 | "right-align": "^0.1.1", 410 | "wordwrap": "0.0.2" 411 | } 412 | }, 413 | "collection-visit": { 414 | "version": "1.0.0", 415 | "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", 416 | "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", 417 | "requires": { 418 | "map-visit": "^1.0.0", 419 | "object-visit": "^1.0.0" 420 | } 421 | }, 422 | "color": { 423 | "version": "3.0.0", 424 | "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", 425 | "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", 426 | "requires": { 427 | "color-convert": "^1.9.1", 428 | "color-string": "^1.5.2" 429 | } 430 | }, 431 | "color-convert": { 432 | "version": "1.9.3", 433 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 434 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 435 | "requires": { 436 | "color-name": "1.1.3" 437 | } 438 | }, 439 | "color-name": { 440 | "version": "1.1.3", 441 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 442 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 443 | }, 444 | "color-string": { 445 | "version": "1.5.3", 446 | "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", 447 | "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", 448 | "requires": { 449 | "color-name": "^1.0.0", 450 | "simple-swizzle": "^0.2.2" 451 | } 452 | }, 453 | "colors": { 454 | "version": "1.4.0", 455 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", 456 | "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" 457 | }, 458 | "colorspace": { 459 | "version": "1.1.2", 460 | "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", 461 | "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", 462 | "requires": { 463 | "color": "3.0.x", 464 | "text-hex": "1.0.x" 465 | } 466 | }, 467 | "component-emitter": { 468 | "version": "1.3.0", 469 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", 470 | "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" 471 | }, 472 | "constantinople": { 473 | "version": "3.1.2", 474 | "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz", 475 | "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==", 476 | "requires": { 477 | "@types/babel-types": "^7.0.0", 478 | "@types/babylon": "^6.16.2", 479 | "babel-types": "^6.26.0", 480 | "babylon": "^6.18.0" 481 | } 482 | }, 483 | "content-disposition": { 484 | "version": "0.5.3", 485 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 486 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 487 | "requires": { 488 | "safe-buffer": "5.1.2" 489 | } 490 | }, 491 | "content-type": { 492 | "version": "1.0.4", 493 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 494 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 495 | }, 496 | "cookie": { 497 | "version": "0.4.0", 498 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 499 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 500 | }, 501 | "cookie-signature": { 502 | "version": "1.0.6", 503 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 504 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 505 | }, 506 | "copy-descriptor": { 507 | "version": "0.1.1", 508 | "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", 509 | "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" 510 | }, 511 | "core-js": { 512 | "version": "2.6.11", 513 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", 514 | "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" 515 | }, 516 | "core-util-is": { 517 | "version": "1.0.2", 518 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 519 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 520 | }, 521 | "date-fns": { 522 | "version": "2.16.1", 523 | "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz", 524 | "integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==" 525 | }, 526 | "debug": { 527 | "version": "3.1.0", 528 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 529 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 530 | "requires": { 531 | "ms": "2.0.0" 532 | } 533 | }, 534 | "decamelize": { 535 | "version": "1.2.0", 536 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 537 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 538 | }, 539 | "decode-uri-component": { 540 | "version": "0.2.0", 541 | "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", 542 | "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" 543 | }, 544 | "define-property": { 545 | "version": "2.0.2", 546 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", 547 | "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", 548 | "requires": { 549 | "is-descriptor": "^1.0.2", 550 | "isobject": "^3.0.1" 551 | }, 552 | "dependencies": { 553 | "is-accessor-descriptor": { 554 | "version": "1.0.0", 555 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 556 | "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", 557 | "requires": { 558 | "kind-of": "^6.0.0" 559 | } 560 | }, 561 | "is-data-descriptor": { 562 | "version": "1.0.0", 563 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 564 | "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", 565 | "requires": { 566 | "kind-of": "^6.0.0" 567 | } 568 | }, 569 | "is-descriptor": { 570 | "version": "1.0.2", 571 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", 572 | "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", 573 | "requires": { 574 | "is-accessor-descriptor": "^1.0.0", 575 | "is-data-descriptor": "^1.0.0", 576 | "kind-of": "^6.0.2" 577 | } 578 | } 579 | } 580 | }, 581 | "depd": { 582 | "version": "1.1.2", 583 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 584 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 585 | }, 586 | "destroy": { 587 | "version": "1.0.4", 588 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 589 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 590 | }, 591 | "doctypes": { 592 | "version": "1.1.0", 593 | "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", 594 | "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" 595 | }, 596 | "ee-first": { 597 | "version": "1.1.1", 598 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 599 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 600 | }, 601 | "enabled": { 602 | "version": "2.0.0", 603 | "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", 604 | "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" 605 | }, 606 | "encodeurl": { 607 | "version": "1.0.2", 608 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 609 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 610 | }, 611 | "escape-html": { 612 | "version": "1.0.3", 613 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 614 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 615 | }, 616 | "esutils": { 617 | "version": "2.0.3", 618 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 619 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" 620 | }, 621 | "etag": { 622 | "version": "1.8.1", 623 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 624 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 625 | }, 626 | "eventemitter3": { 627 | "version": "4.0.7", 628 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", 629 | "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" 630 | }, 631 | "expand-brackets": { 632 | "version": "2.1.4", 633 | "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", 634 | "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", 635 | "requires": { 636 | "debug": "^2.3.3", 637 | "define-property": "^0.2.5", 638 | "extend-shallow": "^2.0.1", 639 | "posix-character-classes": "^0.1.0", 640 | "regex-not": "^1.0.0", 641 | "snapdragon": "^0.8.1", 642 | "to-regex": "^3.0.1" 643 | }, 644 | "dependencies": { 645 | "debug": { 646 | "version": "2.6.9", 647 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 648 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 649 | "requires": { 650 | "ms": "2.0.0" 651 | } 652 | }, 653 | "define-property": { 654 | "version": "0.2.5", 655 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 656 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 657 | "requires": { 658 | "is-descriptor": "^0.1.0" 659 | } 660 | }, 661 | "extend-shallow": { 662 | "version": "2.0.1", 663 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 664 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 665 | "requires": { 666 | "is-extendable": "^0.1.0" 667 | } 668 | } 669 | } 670 | }, 671 | "express": { 672 | "version": "4.17.1", 673 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 674 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 675 | "requires": { 676 | "accepts": "~1.3.7", 677 | "array-flatten": "1.1.1", 678 | "body-parser": "1.19.0", 679 | "content-disposition": "0.5.3", 680 | "content-type": "~1.0.4", 681 | "cookie": "0.4.0", 682 | "cookie-signature": "1.0.6", 683 | "debug": "2.6.9", 684 | "depd": "~1.1.2", 685 | "encodeurl": "~1.0.2", 686 | "escape-html": "~1.0.3", 687 | "etag": "~1.8.1", 688 | "finalhandler": "~1.1.2", 689 | "fresh": "0.5.2", 690 | "merge-descriptors": "1.0.1", 691 | "methods": "~1.1.2", 692 | "on-finished": "~2.3.0", 693 | "parseurl": "~1.3.3", 694 | "path-to-regexp": "0.1.7", 695 | "proxy-addr": "~2.0.5", 696 | "qs": "6.7.0", 697 | "range-parser": "~1.2.1", 698 | "safe-buffer": "5.1.2", 699 | "send": "0.17.1", 700 | "serve-static": "1.14.1", 701 | "setprototypeof": "1.1.1", 702 | "statuses": "~1.5.0", 703 | "type-is": "~1.6.18", 704 | "utils-merge": "1.0.1", 705 | "vary": "~1.1.2" 706 | }, 707 | "dependencies": { 708 | "debug": { 709 | "version": "2.6.9", 710 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 711 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 712 | "requires": { 713 | "ms": "2.0.0" 714 | } 715 | } 716 | } 717 | }, 718 | "express-basic-auth": { 719 | "version": "1.2.0", 720 | "resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.0.tgz", 721 | "integrity": "sha512-iJ0h1Gk6fZRrFmO7tP9nIbxwNgCUJASfNj5fb0Hy15lGtbqqsxpt7609+wq+0XlByZjXmC/rslWQtnuSTVRIcg==", 722 | "requires": { 723 | "basic-auth": "^2.0.1" 724 | } 725 | }, 726 | "express-session": { 727 | "version": "1.17.1", 728 | "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz", 729 | "integrity": "sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q==", 730 | "requires": { 731 | "cookie": "0.4.0", 732 | "cookie-signature": "1.0.6", 733 | "debug": "2.6.9", 734 | "depd": "~2.0.0", 735 | "on-headers": "~1.0.2", 736 | "parseurl": "~1.3.3", 737 | "safe-buffer": "5.2.0", 738 | "uid-safe": "~2.1.5" 739 | }, 740 | "dependencies": { 741 | "debug": { 742 | "version": "2.6.9", 743 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 744 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 745 | "requires": { 746 | "ms": "2.0.0" 747 | } 748 | }, 749 | "depd": { 750 | "version": "2.0.0", 751 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 752 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 753 | }, 754 | "safe-buffer": { 755 | "version": "5.2.0", 756 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", 757 | "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" 758 | } 759 | } 760 | }, 761 | "extend-shallow": { 762 | "version": "3.0.2", 763 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", 764 | "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", 765 | "requires": { 766 | "assign-symbols": "^1.0.0", 767 | "is-extendable": "^1.0.1" 768 | }, 769 | "dependencies": { 770 | "is-extendable": { 771 | "version": "1.0.1", 772 | "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", 773 | "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", 774 | "requires": { 775 | "is-plain-object": "^2.0.4" 776 | } 777 | } 778 | } 779 | }, 780 | "extglob": { 781 | "version": "2.0.4", 782 | "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", 783 | "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", 784 | "requires": { 785 | "array-unique": "^0.3.2", 786 | "define-property": "^1.0.0", 787 | "expand-brackets": "^2.1.4", 788 | "extend-shallow": "^2.0.1", 789 | "fragment-cache": "^0.2.1", 790 | "regex-not": "^1.0.0", 791 | "snapdragon": "^0.8.1", 792 | "to-regex": "^3.0.1" 793 | }, 794 | "dependencies": { 795 | "define-property": { 796 | "version": "1.0.0", 797 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", 798 | "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", 799 | "requires": { 800 | "is-descriptor": "^1.0.0" 801 | } 802 | }, 803 | "extend-shallow": { 804 | "version": "2.0.1", 805 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 806 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 807 | "requires": { 808 | "is-extendable": "^0.1.0" 809 | } 810 | }, 811 | "is-accessor-descriptor": { 812 | "version": "1.0.0", 813 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 814 | "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", 815 | "requires": { 816 | "kind-of": "^6.0.0" 817 | } 818 | }, 819 | "is-data-descriptor": { 820 | "version": "1.0.0", 821 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 822 | "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", 823 | "requires": { 824 | "kind-of": "^6.0.0" 825 | } 826 | }, 827 | "is-descriptor": { 828 | "version": "1.0.2", 829 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", 830 | "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", 831 | "requires": { 832 | "is-accessor-descriptor": "^1.0.0", 833 | "is-data-descriptor": "^1.0.0", 834 | "kind-of": "^6.0.2" 835 | } 836 | } 837 | } 838 | }, 839 | "fast-safe-stringify": { 840 | "version": "2.0.7", 841 | "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", 842 | "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" 843 | }, 844 | "fecha": { 845 | "version": "4.2.0", 846 | "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", 847 | "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" 848 | }, 849 | "fill-range": { 850 | "version": "4.0.0", 851 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", 852 | "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", 853 | "requires": { 854 | "extend-shallow": "^2.0.1", 855 | "is-number": "^3.0.0", 856 | "repeat-string": "^1.6.1", 857 | "to-regex-range": "^2.1.0" 858 | }, 859 | "dependencies": { 860 | "extend-shallow": { 861 | "version": "2.0.1", 862 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 863 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 864 | "requires": { 865 | "is-extendable": "^0.1.0" 866 | } 867 | } 868 | } 869 | }, 870 | "finalhandler": { 871 | "version": "1.1.2", 872 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 873 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 874 | "requires": { 875 | "debug": "2.6.9", 876 | "encodeurl": "~1.0.2", 877 | "escape-html": "~1.0.3", 878 | "on-finished": "~2.3.0", 879 | "parseurl": "~1.3.3", 880 | "statuses": "~1.5.0", 881 | "unpipe": "~1.0.0" 882 | }, 883 | "dependencies": { 884 | "debug": { 885 | "version": "2.6.9", 886 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 887 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 888 | "requires": { 889 | "ms": "2.0.0" 890 | } 891 | } 892 | } 893 | }, 894 | "fn.name": { 895 | "version": "1.1.0", 896 | "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", 897 | "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" 898 | }, 899 | "follow-redirects": { 900 | "version": "1.5.10", 901 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", 902 | "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", 903 | "requires": { 904 | "debug": "=3.1.0" 905 | } 906 | }, 907 | "for-in": { 908 | "version": "1.0.2", 909 | "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", 910 | "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" 911 | }, 912 | "forwarded": { 913 | "version": "0.1.2", 914 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 915 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 916 | }, 917 | "fragment-cache": { 918 | "version": "0.2.1", 919 | "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", 920 | "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", 921 | "requires": { 922 | "map-cache": "^0.2.2" 923 | } 924 | }, 925 | "fresh": { 926 | "version": "0.5.2", 927 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 928 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 929 | }, 930 | "get-value": { 931 | "version": "2.0.6", 932 | "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", 933 | "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" 934 | }, 935 | "has-symbols": { 936 | "version": "1.0.1", 937 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", 938 | "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" 939 | }, 940 | "has-value": { 941 | "version": "1.0.0", 942 | "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", 943 | "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", 944 | "requires": { 945 | "get-value": "^2.0.6", 946 | "has-values": "^1.0.0", 947 | "isobject": "^3.0.0" 948 | } 949 | }, 950 | "has-values": { 951 | "version": "1.0.0", 952 | "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", 953 | "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", 954 | "requires": { 955 | "is-number": "^3.0.0", 956 | "kind-of": "^4.0.0" 957 | }, 958 | "dependencies": { 959 | "kind-of": { 960 | "version": "4.0.0", 961 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", 962 | "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", 963 | "requires": { 964 | "is-buffer": "^1.1.5" 965 | } 966 | } 967 | } 968 | }, 969 | "hoek": { 970 | "version": "6.1.3", 971 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", 972 | "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==" 973 | }, 974 | "http-errors": { 975 | "version": "1.7.2", 976 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 977 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 978 | "requires": { 979 | "depd": "~1.1.2", 980 | "inherits": "2.0.3", 981 | "setprototypeof": "1.1.1", 982 | "statuses": ">= 1.5.0 < 2", 983 | "toidentifier": "1.0.0" 984 | } 985 | }, 986 | "http-proxy": { 987 | "version": "1.18.1", 988 | "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", 989 | "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", 990 | "requires": { 991 | "eventemitter3": "^4.0.0", 992 | "follow-redirects": "^1.0.0", 993 | "requires-port": "^1.0.0" 994 | } 995 | }, 996 | "http-proxy-middleware": { 997 | "version": "0.19.2", 998 | "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.2.tgz", 999 | "integrity": "sha512-aYk1rTKqLTus23X3L96LGNCGNgWpG4cG0XoZIT1GUPhhulEHX/QalnO6Vbo+WmKWi4AL2IidjuC0wZtbpg0yhQ==", 1000 | "requires": { 1001 | "http-proxy": "^1.18.1", 1002 | "is-glob": "^4.0.0", 1003 | "lodash": "^4.17.11", 1004 | "micromatch": "^3.1.10" 1005 | } 1006 | }, 1007 | "iconv-lite": { 1008 | "version": "0.4.24", 1009 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 1010 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 1011 | "requires": { 1012 | "safer-buffer": ">= 2.1.2 < 3" 1013 | } 1014 | }, 1015 | "inherits": { 1016 | "version": "2.0.3", 1017 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 1018 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 1019 | }, 1020 | "ipaddr.js": { 1021 | "version": "1.9.1", 1022 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 1023 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 1024 | }, 1025 | "is-accessor-descriptor": { 1026 | "version": "0.1.6", 1027 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", 1028 | "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", 1029 | "requires": { 1030 | "kind-of": "^3.0.2" 1031 | }, 1032 | "dependencies": { 1033 | "kind-of": { 1034 | "version": "3.2.2", 1035 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 1036 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 1037 | "requires": { 1038 | "is-buffer": "^1.1.5" 1039 | } 1040 | } 1041 | } 1042 | }, 1043 | "is-arrayish": { 1044 | "version": "0.3.2", 1045 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", 1046 | "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" 1047 | }, 1048 | "is-buffer": { 1049 | "version": "1.1.6", 1050 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 1051 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 1052 | }, 1053 | "is-data-descriptor": { 1054 | "version": "0.1.4", 1055 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", 1056 | "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", 1057 | "requires": { 1058 | "kind-of": "^3.0.2" 1059 | }, 1060 | "dependencies": { 1061 | "kind-of": { 1062 | "version": "3.2.2", 1063 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 1064 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 1065 | "requires": { 1066 | "is-buffer": "^1.1.5" 1067 | } 1068 | } 1069 | } 1070 | }, 1071 | "is-descriptor": { 1072 | "version": "0.1.6", 1073 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", 1074 | "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", 1075 | "requires": { 1076 | "is-accessor-descriptor": "^0.1.6", 1077 | "is-data-descriptor": "^0.1.4", 1078 | "kind-of": "^5.0.0" 1079 | }, 1080 | "dependencies": { 1081 | "kind-of": { 1082 | "version": "5.1.0", 1083 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", 1084 | "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" 1085 | } 1086 | } 1087 | }, 1088 | "is-expression": { 1089 | "version": "3.0.0", 1090 | "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", 1091 | "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", 1092 | "requires": { 1093 | "acorn": "~4.0.2", 1094 | "object-assign": "^4.0.1" 1095 | }, 1096 | "dependencies": { 1097 | "acorn": { 1098 | "version": "4.0.13", 1099 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", 1100 | "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" 1101 | } 1102 | } 1103 | }, 1104 | "is-extendable": { 1105 | "version": "0.1.1", 1106 | "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", 1107 | "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" 1108 | }, 1109 | "is-extglob": { 1110 | "version": "2.1.1", 1111 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1112 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" 1113 | }, 1114 | "is-glob": { 1115 | "version": "4.0.1", 1116 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 1117 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 1118 | "requires": { 1119 | "is-extglob": "^2.1.1" 1120 | } 1121 | }, 1122 | "is-number": { 1123 | "version": "3.0.0", 1124 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", 1125 | "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", 1126 | "requires": { 1127 | "kind-of": "^3.0.2" 1128 | }, 1129 | "dependencies": { 1130 | "kind-of": { 1131 | "version": "3.2.2", 1132 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 1133 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 1134 | "requires": { 1135 | "is-buffer": "^1.1.5" 1136 | } 1137 | } 1138 | } 1139 | }, 1140 | "is-plain-object": { 1141 | "version": "2.0.4", 1142 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", 1143 | "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", 1144 | "requires": { 1145 | "isobject": "^3.0.1" 1146 | } 1147 | }, 1148 | "is-promise": { 1149 | "version": "2.2.2", 1150 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", 1151 | "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" 1152 | }, 1153 | "is-regex": { 1154 | "version": "1.1.1", 1155 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", 1156 | "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", 1157 | "requires": { 1158 | "has-symbols": "^1.0.1" 1159 | } 1160 | }, 1161 | "is-retry-allowed": { 1162 | "version": "1.2.0", 1163 | "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", 1164 | "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" 1165 | }, 1166 | "is-stream": { 1167 | "version": "2.0.0", 1168 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", 1169 | "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" 1170 | }, 1171 | "is-windows": { 1172 | "version": "1.0.2", 1173 | "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", 1174 | "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" 1175 | }, 1176 | "isarray": { 1177 | "version": "1.0.0", 1178 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 1179 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 1180 | }, 1181 | "isobject": { 1182 | "version": "3.0.1", 1183 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 1184 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 1185 | }, 1186 | "js-stringify": { 1187 | "version": "1.0.2", 1188 | "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", 1189 | "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" 1190 | }, 1191 | "jstransformer": { 1192 | "version": "1.0.0", 1193 | "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", 1194 | "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", 1195 | "requires": { 1196 | "is-promise": "^2.0.0", 1197 | "promise": "^7.0.1" 1198 | } 1199 | }, 1200 | "kind-of": { 1201 | "version": "6.0.3", 1202 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", 1203 | "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" 1204 | }, 1205 | "kuler": { 1206 | "version": "2.0.0", 1207 | "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", 1208 | "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" 1209 | }, 1210 | "lazy-cache": { 1211 | "version": "1.0.4", 1212 | "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", 1213 | "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" 1214 | }, 1215 | "lodash": { 1216 | "version": "4.17.20", 1217 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 1218 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" 1219 | }, 1220 | "logform": { 1221 | "version": "2.2.0", 1222 | "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", 1223 | "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", 1224 | "requires": { 1225 | "colors": "^1.2.1", 1226 | "fast-safe-stringify": "^2.0.4", 1227 | "fecha": "^4.2.0", 1228 | "ms": "^2.1.1", 1229 | "triple-beam": "^1.3.0" 1230 | }, 1231 | "dependencies": { 1232 | "ms": { 1233 | "version": "2.1.2", 1234 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1235 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1236 | } 1237 | } 1238 | }, 1239 | "longest": { 1240 | "version": "1.0.1", 1241 | "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", 1242 | "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" 1243 | }, 1244 | "map-cache": { 1245 | "version": "0.2.2", 1246 | "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", 1247 | "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" 1248 | }, 1249 | "map-visit": { 1250 | "version": "1.0.0", 1251 | "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", 1252 | "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", 1253 | "requires": { 1254 | "object-visit": "^1.0.0" 1255 | } 1256 | }, 1257 | "media-typer": { 1258 | "version": "0.3.0", 1259 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1260 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 1261 | }, 1262 | "merge-descriptors": { 1263 | "version": "1.0.1", 1264 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1265 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 1266 | }, 1267 | "methods": { 1268 | "version": "1.1.2", 1269 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1270 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 1271 | }, 1272 | "micromatch": { 1273 | "version": "3.1.10", 1274 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", 1275 | "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", 1276 | "requires": { 1277 | "arr-diff": "^4.0.0", 1278 | "array-unique": "^0.3.2", 1279 | "braces": "^2.3.1", 1280 | "define-property": "^2.0.2", 1281 | "extend-shallow": "^3.0.2", 1282 | "extglob": "^2.0.4", 1283 | "fragment-cache": "^0.2.1", 1284 | "kind-of": "^6.0.2", 1285 | "nanomatch": "^1.2.9", 1286 | "object.pick": "^1.3.0", 1287 | "regex-not": "^1.0.0", 1288 | "snapdragon": "^0.8.1", 1289 | "to-regex": "^3.0.2" 1290 | } 1291 | }, 1292 | "mime": { 1293 | "version": "1.6.0", 1294 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1295 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 1296 | }, 1297 | "mime-db": { 1298 | "version": "1.44.0", 1299 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", 1300 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" 1301 | }, 1302 | "mime-types": { 1303 | "version": "2.1.27", 1304 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", 1305 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", 1306 | "requires": { 1307 | "mime-db": "1.44.0" 1308 | } 1309 | }, 1310 | "mixin-deep": { 1311 | "version": "1.3.2", 1312 | "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", 1313 | "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", 1314 | "requires": { 1315 | "for-in": "^1.0.2", 1316 | "is-extendable": "^1.0.1" 1317 | }, 1318 | "dependencies": { 1319 | "is-extendable": { 1320 | "version": "1.0.1", 1321 | "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", 1322 | "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", 1323 | "requires": { 1324 | "is-plain-object": "^2.0.4" 1325 | } 1326 | } 1327 | } 1328 | }, 1329 | "morgan": { 1330 | "version": "1.10.0", 1331 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", 1332 | "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", 1333 | "requires": { 1334 | "basic-auth": "~2.0.1", 1335 | "debug": "2.6.9", 1336 | "depd": "~2.0.0", 1337 | "on-finished": "~2.3.0", 1338 | "on-headers": "~1.0.2" 1339 | }, 1340 | "dependencies": { 1341 | "debug": { 1342 | "version": "2.6.9", 1343 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1344 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1345 | "requires": { 1346 | "ms": "2.0.0" 1347 | } 1348 | }, 1349 | "depd": { 1350 | "version": "2.0.0", 1351 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 1352 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 1353 | } 1354 | } 1355 | }, 1356 | "ms": { 1357 | "version": "2.0.0", 1358 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1359 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1360 | }, 1361 | "nanomatch": { 1362 | "version": "1.2.13", 1363 | "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", 1364 | "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", 1365 | "requires": { 1366 | "arr-diff": "^4.0.0", 1367 | "array-unique": "^0.3.2", 1368 | "define-property": "^2.0.2", 1369 | "extend-shallow": "^3.0.2", 1370 | "fragment-cache": "^0.2.1", 1371 | "is-windows": "^1.0.2", 1372 | "kind-of": "^6.0.2", 1373 | "object.pick": "^1.3.0", 1374 | "regex-not": "^1.0.0", 1375 | "snapdragon": "^0.8.1", 1376 | "to-regex": "^3.0.1" 1377 | } 1378 | }, 1379 | "negotiator": { 1380 | "version": "0.6.2", 1381 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 1382 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 1383 | }, 1384 | "object-assign": { 1385 | "version": "4.1.1", 1386 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1387 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 1388 | }, 1389 | "object-copy": { 1390 | "version": "0.1.0", 1391 | "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", 1392 | "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", 1393 | "requires": { 1394 | "copy-descriptor": "^0.1.0", 1395 | "define-property": "^0.2.5", 1396 | "kind-of": "^3.0.3" 1397 | }, 1398 | "dependencies": { 1399 | "define-property": { 1400 | "version": "0.2.5", 1401 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 1402 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 1403 | "requires": { 1404 | "is-descriptor": "^0.1.0" 1405 | } 1406 | }, 1407 | "kind-of": { 1408 | "version": "3.2.2", 1409 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 1410 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 1411 | "requires": { 1412 | "is-buffer": "^1.1.5" 1413 | } 1414 | } 1415 | } 1416 | }, 1417 | "object-visit": { 1418 | "version": "1.0.1", 1419 | "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", 1420 | "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", 1421 | "requires": { 1422 | "isobject": "^3.0.0" 1423 | } 1424 | }, 1425 | "object.pick": { 1426 | "version": "1.3.0", 1427 | "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", 1428 | "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", 1429 | "requires": { 1430 | "isobject": "^3.0.1" 1431 | } 1432 | }, 1433 | "on-finished": { 1434 | "version": "2.3.0", 1435 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 1436 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 1437 | "requires": { 1438 | "ee-first": "1.1.1" 1439 | } 1440 | }, 1441 | "on-headers": { 1442 | "version": "1.0.2", 1443 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 1444 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" 1445 | }, 1446 | "one-time": { 1447 | "version": "1.0.0", 1448 | "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", 1449 | "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", 1450 | "requires": { 1451 | "fn.name": "1.x.x" 1452 | } 1453 | }, 1454 | "parseurl": { 1455 | "version": "1.3.3", 1456 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1457 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 1458 | }, 1459 | "pascalcase": { 1460 | "version": "0.1.1", 1461 | "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", 1462 | "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" 1463 | }, 1464 | "path-parse": { 1465 | "version": "1.0.6", 1466 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 1467 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" 1468 | }, 1469 | "path-to-regexp": { 1470 | "version": "0.1.7", 1471 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1472 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 1473 | }, 1474 | "posix-character-classes": { 1475 | "version": "0.1.1", 1476 | "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", 1477 | "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" 1478 | }, 1479 | "process-nextick-args": { 1480 | "version": "2.0.1", 1481 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 1482 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 1483 | }, 1484 | "promise": { 1485 | "version": "7.3.1", 1486 | "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", 1487 | "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", 1488 | "requires": { 1489 | "asap": "~2.0.3" 1490 | } 1491 | }, 1492 | "proxy-addr": { 1493 | "version": "2.0.6", 1494 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", 1495 | "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", 1496 | "requires": { 1497 | "forwarded": "~0.1.2", 1498 | "ipaddr.js": "1.9.1" 1499 | } 1500 | }, 1501 | "pug": { 1502 | "version": "2.0.4", 1503 | "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.4.tgz", 1504 | "integrity": "sha512-XhoaDlvi6NIzL49nu094R2NA6P37ijtgMDuWE+ofekDChvfKnzFal60bhSdiy8y2PBO6fmz3oMEIcfpBVRUdvw==", 1505 | "requires": { 1506 | "pug-code-gen": "^2.0.2", 1507 | "pug-filters": "^3.1.1", 1508 | "pug-lexer": "^4.1.0", 1509 | "pug-linker": "^3.0.6", 1510 | "pug-load": "^2.0.12", 1511 | "pug-parser": "^5.0.1", 1512 | "pug-runtime": "^2.0.5", 1513 | "pug-strip-comments": "^1.0.4" 1514 | } 1515 | }, 1516 | "pug-attrs": { 1517 | "version": "2.0.4", 1518 | "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.4.tgz", 1519 | "integrity": "sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ==", 1520 | "requires": { 1521 | "constantinople": "^3.0.1", 1522 | "js-stringify": "^1.0.1", 1523 | "pug-runtime": "^2.0.5" 1524 | } 1525 | }, 1526 | "pug-code-gen": { 1527 | "version": "2.0.2", 1528 | "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.2.tgz", 1529 | "integrity": "sha512-kROFWv/AHx/9CRgoGJeRSm+4mLWchbgpRzTEn8XCiwwOy6Vh0gAClS8Vh5TEJ9DBjaP8wCjS3J6HKsEsYdvaCw==", 1530 | "requires": { 1531 | "constantinople": "^3.1.2", 1532 | "doctypes": "^1.1.0", 1533 | "js-stringify": "^1.0.1", 1534 | "pug-attrs": "^2.0.4", 1535 | "pug-error": "^1.3.3", 1536 | "pug-runtime": "^2.0.5", 1537 | "void-elements": "^2.0.1", 1538 | "with": "^5.0.0" 1539 | } 1540 | }, 1541 | "pug-error": { 1542 | "version": "1.3.3", 1543 | "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.3.tgz", 1544 | "integrity": "sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ==" 1545 | }, 1546 | "pug-filters": { 1547 | "version": "3.1.1", 1548 | "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-3.1.1.tgz", 1549 | "integrity": "sha512-lFfjNyGEyVWC4BwX0WyvkoWLapI5xHSM3xZJFUhx4JM4XyyRdO8Aucc6pCygnqV2uSgJFaJWW3Ft1wCWSoQkQg==", 1550 | "requires": { 1551 | "clean-css": "^4.1.11", 1552 | "constantinople": "^3.0.1", 1553 | "jstransformer": "1.0.0", 1554 | "pug-error": "^1.3.3", 1555 | "pug-walk": "^1.1.8", 1556 | "resolve": "^1.1.6", 1557 | "uglify-js": "^2.6.1" 1558 | } 1559 | }, 1560 | "pug-lexer": { 1561 | "version": "4.1.0", 1562 | "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-4.1.0.tgz", 1563 | "integrity": "sha512-i55yzEBtjm0mlplW4LoANq7k3S8gDdfC6+LThGEvsK4FuobcKfDAwt6V4jKPH9RtiE3a2Akfg5UpafZ1OksaPA==", 1564 | "requires": { 1565 | "character-parser": "^2.1.1", 1566 | "is-expression": "^3.0.0", 1567 | "pug-error": "^1.3.3" 1568 | } 1569 | }, 1570 | "pug-linker": { 1571 | "version": "3.0.6", 1572 | "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.6.tgz", 1573 | "integrity": "sha512-bagfuHttfQOpANGy1Y6NJ+0mNb7dD2MswFG2ZKj22s8g0wVsojpRlqveEQHmgXXcfROB2RT6oqbPYr9EN2ZWzg==", 1574 | "requires": { 1575 | "pug-error": "^1.3.3", 1576 | "pug-walk": "^1.1.8" 1577 | } 1578 | }, 1579 | "pug-load": { 1580 | "version": "2.0.12", 1581 | "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.12.tgz", 1582 | "integrity": "sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg==", 1583 | "requires": { 1584 | "object-assign": "^4.1.0", 1585 | "pug-walk": "^1.1.8" 1586 | } 1587 | }, 1588 | "pug-parser": { 1589 | "version": "5.0.1", 1590 | "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-5.0.1.tgz", 1591 | "integrity": "sha512-nGHqK+w07p5/PsPIyzkTQfzlYfuqoiGjaoqHv1LjOv2ZLXmGX1O+4Vcvps+P4LhxZ3drYSljjq4b+Naid126wA==", 1592 | "requires": { 1593 | "pug-error": "^1.3.3", 1594 | "token-stream": "0.0.1" 1595 | } 1596 | }, 1597 | "pug-runtime": { 1598 | "version": "2.0.5", 1599 | "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.5.tgz", 1600 | "integrity": "sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw==" 1601 | }, 1602 | "pug-strip-comments": { 1603 | "version": "1.0.4", 1604 | "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz", 1605 | "integrity": "sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw==", 1606 | "requires": { 1607 | "pug-error": "^1.3.3" 1608 | } 1609 | }, 1610 | "pug-walk": { 1611 | "version": "1.1.8", 1612 | "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.8.tgz", 1613 | "integrity": "sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA==" 1614 | }, 1615 | "qs": { 1616 | "version": "6.7.0", 1617 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 1618 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 1619 | }, 1620 | "random-bytes": { 1621 | "version": "1.0.0", 1622 | "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", 1623 | "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" 1624 | }, 1625 | "range-parser": { 1626 | "version": "1.2.1", 1627 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1628 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 1629 | }, 1630 | "raw-body": { 1631 | "version": "2.4.0", 1632 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 1633 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 1634 | "requires": { 1635 | "bytes": "3.1.0", 1636 | "http-errors": "1.7.2", 1637 | "iconv-lite": "0.4.24", 1638 | "unpipe": "1.0.0" 1639 | } 1640 | }, 1641 | "readable-stream": { 1642 | "version": "3.6.0", 1643 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 1644 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 1645 | "requires": { 1646 | "inherits": "^2.0.3", 1647 | "string_decoder": "^1.1.1", 1648 | "util-deprecate": "^1.0.1" 1649 | } 1650 | }, 1651 | "regenerator-runtime": { 1652 | "version": "0.11.1", 1653 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", 1654 | "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" 1655 | }, 1656 | "regex-not": { 1657 | "version": "1.0.2", 1658 | "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", 1659 | "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", 1660 | "requires": { 1661 | "extend-shallow": "^3.0.2", 1662 | "safe-regex": "^1.1.0" 1663 | } 1664 | }, 1665 | "repeat-element": { 1666 | "version": "1.1.3", 1667 | "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", 1668 | "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" 1669 | }, 1670 | "repeat-string": { 1671 | "version": "1.6.1", 1672 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", 1673 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" 1674 | }, 1675 | "requires-port": { 1676 | "version": "1.0.0", 1677 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 1678 | "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" 1679 | }, 1680 | "resolve": { 1681 | "version": "1.17.0", 1682 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 1683 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 1684 | "requires": { 1685 | "path-parse": "^1.0.6" 1686 | } 1687 | }, 1688 | "resolve-url": { 1689 | "version": "0.2.1", 1690 | "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", 1691 | "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" 1692 | }, 1693 | "ret": { 1694 | "version": "0.1.15", 1695 | "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", 1696 | "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" 1697 | }, 1698 | "right-align": { 1699 | "version": "0.1.3", 1700 | "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", 1701 | "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", 1702 | "requires": { 1703 | "align-text": "^0.1.1" 1704 | } 1705 | }, 1706 | "safe-buffer": { 1707 | "version": "5.1.2", 1708 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1709 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1710 | }, 1711 | "safe-regex": { 1712 | "version": "1.1.0", 1713 | "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", 1714 | "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", 1715 | "requires": { 1716 | "ret": "~0.1.10" 1717 | } 1718 | }, 1719 | "safer-buffer": { 1720 | "version": "2.1.2", 1721 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1722 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1723 | }, 1724 | "send": { 1725 | "version": "0.17.1", 1726 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 1727 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 1728 | "requires": { 1729 | "debug": "2.6.9", 1730 | "depd": "~1.1.2", 1731 | "destroy": "~1.0.4", 1732 | "encodeurl": "~1.0.2", 1733 | "escape-html": "~1.0.3", 1734 | "etag": "~1.8.1", 1735 | "fresh": "0.5.2", 1736 | "http-errors": "~1.7.2", 1737 | "mime": "1.6.0", 1738 | "ms": "2.1.1", 1739 | "on-finished": "~2.3.0", 1740 | "range-parser": "~1.2.1", 1741 | "statuses": "~1.5.0" 1742 | }, 1743 | "dependencies": { 1744 | "debug": { 1745 | "version": "2.6.9", 1746 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1747 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1748 | "requires": { 1749 | "ms": "2.0.0" 1750 | }, 1751 | "dependencies": { 1752 | "ms": { 1753 | "version": "2.0.0", 1754 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1755 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1756 | } 1757 | } 1758 | }, 1759 | "ms": { 1760 | "version": "2.1.1", 1761 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 1762 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 1763 | } 1764 | } 1765 | }, 1766 | "serve-static": { 1767 | "version": "1.14.1", 1768 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 1769 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 1770 | "requires": { 1771 | "encodeurl": "~1.0.2", 1772 | "escape-html": "~1.0.3", 1773 | "parseurl": "~1.3.3", 1774 | "send": "0.17.1" 1775 | } 1776 | }, 1777 | "set-value": { 1778 | "version": "2.0.1", 1779 | "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", 1780 | "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", 1781 | "requires": { 1782 | "extend-shallow": "^2.0.1", 1783 | "is-extendable": "^0.1.1", 1784 | "is-plain-object": "^2.0.3", 1785 | "split-string": "^3.0.1" 1786 | }, 1787 | "dependencies": { 1788 | "extend-shallow": { 1789 | "version": "2.0.1", 1790 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 1791 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 1792 | "requires": { 1793 | "is-extendable": "^0.1.0" 1794 | } 1795 | } 1796 | } 1797 | }, 1798 | "setprototypeof": { 1799 | "version": "1.1.1", 1800 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 1801 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 1802 | }, 1803 | "simple-oauth2": { 1804 | "version": "2.5.2", 1805 | "resolved": "https://registry.npmjs.org/simple-oauth2/-/simple-oauth2-2.5.2.tgz", 1806 | "integrity": "sha512-8qjf+nHRdSUllFjjfpnonrU1oF/HNVbDle5HIbvXRYiy38C7KUvYe6w0ZZ//g4AFB6VNWuiZ80HmnycR8ZFDyQ==", 1807 | "requires": { 1808 | "@hapi/joi": "^15.1.1", 1809 | "date-fns": "^2.2.1", 1810 | "debug": "^4.1.1", 1811 | "wreck": "^14.0.2" 1812 | }, 1813 | "dependencies": { 1814 | "debug": { 1815 | "version": "4.1.1", 1816 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 1817 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 1818 | "requires": { 1819 | "ms": "^2.1.1" 1820 | } 1821 | }, 1822 | "ms": { 1823 | "version": "2.1.2", 1824 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1825 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1826 | } 1827 | } 1828 | }, 1829 | "simple-swizzle": { 1830 | "version": "0.2.2", 1831 | "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", 1832 | "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", 1833 | "requires": { 1834 | "is-arrayish": "^0.3.1" 1835 | } 1836 | }, 1837 | "snapdragon": { 1838 | "version": "0.8.2", 1839 | "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", 1840 | "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", 1841 | "requires": { 1842 | "base": "^0.11.1", 1843 | "debug": "^2.2.0", 1844 | "define-property": "^0.2.5", 1845 | "extend-shallow": "^2.0.1", 1846 | "map-cache": "^0.2.2", 1847 | "source-map": "^0.5.6", 1848 | "source-map-resolve": "^0.5.0", 1849 | "use": "^3.1.0" 1850 | }, 1851 | "dependencies": { 1852 | "debug": { 1853 | "version": "2.6.9", 1854 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1855 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1856 | "requires": { 1857 | "ms": "2.0.0" 1858 | } 1859 | }, 1860 | "define-property": { 1861 | "version": "0.2.5", 1862 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 1863 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 1864 | "requires": { 1865 | "is-descriptor": "^0.1.0" 1866 | } 1867 | }, 1868 | "extend-shallow": { 1869 | "version": "2.0.1", 1870 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 1871 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 1872 | "requires": { 1873 | "is-extendable": "^0.1.0" 1874 | } 1875 | } 1876 | } 1877 | }, 1878 | "snapdragon-node": { 1879 | "version": "2.1.1", 1880 | "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", 1881 | "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", 1882 | "requires": { 1883 | "define-property": "^1.0.0", 1884 | "isobject": "^3.0.0", 1885 | "snapdragon-util": "^3.0.1" 1886 | }, 1887 | "dependencies": { 1888 | "define-property": { 1889 | "version": "1.0.0", 1890 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", 1891 | "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", 1892 | "requires": { 1893 | "is-descriptor": "^1.0.0" 1894 | } 1895 | }, 1896 | "is-accessor-descriptor": { 1897 | "version": "1.0.0", 1898 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 1899 | "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", 1900 | "requires": { 1901 | "kind-of": "^6.0.0" 1902 | } 1903 | }, 1904 | "is-data-descriptor": { 1905 | "version": "1.0.0", 1906 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 1907 | "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", 1908 | "requires": { 1909 | "kind-of": "^6.0.0" 1910 | } 1911 | }, 1912 | "is-descriptor": { 1913 | "version": "1.0.2", 1914 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", 1915 | "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", 1916 | "requires": { 1917 | "is-accessor-descriptor": "^1.0.0", 1918 | "is-data-descriptor": "^1.0.0", 1919 | "kind-of": "^6.0.2" 1920 | } 1921 | } 1922 | } 1923 | }, 1924 | "snapdragon-util": { 1925 | "version": "3.0.1", 1926 | "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", 1927 | "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", 1928 | "requires": { 1929 | "kind-of": "^3.2.0" 1930 | }, 1931 | "dependencies": { 1932 | "kind-of": { 1933 | "version": "3.2.2", 1934 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 1935 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 1936 | "requires": { 1937 | "is-buffer": "^1.1.5" 1938 | } 1939 | } 1940 | } 1941 | }, 1942 | "source-map": { 1943 | "version": "0.5.7", 1944 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 1945 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" 1946 | }, 1947 | "source-map-resolve": { 1948 | "version": "0.5.3", 1949 | "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", 1950 | "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", 1951 | "requires": { 1952 | "atob": "^2.1.2", 1953 | "decode-uri-component": "^0.2.0", 1954 | "resolve-url": "^0.2.1", 1955 | "source-map-url": "^0.4.0", 1956 | "urix": "^0.1.0" 1957 | } 1958 | }, 1959 | "source-map-url": { 1960 | "version": "0.4.0", 1961 | "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", 1962 | "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" 1963 | }, 1964 | "split-string": { 1965 | "version": "3.1.0", 1966 | "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", 1967 | "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", 1968 | "requires": { 1969 | "extend-shallow": "^3.0.0" 1970 | } 1971 | }, 1972 | "stack-trace": { 1973 | "version": "0.0.10", 1974 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 1975 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" 1976 | }, 1977 | "static-extend": { 1978 | "version": "0.1.2", 1979 | "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", 1980 | "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", 1981 | "requires": { 1982 | "define-property": "^0.2.5", 1983 | "object-copy": "^0.1.0" 1984 | }, 1985 | "dependencies": { 1986 | "define-property": { 1987 | "version": "0.2.5", 1988 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 1989 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 1990 | "requires": { 1991 | "is-descriptor": "^0.1.0" 1992 | } 1993 | } 1994 | } 1995 | }, 1996 | "statuses": { 1997 | "version": "1.5.0", 1998 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 1999 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 2000 | }, 2001 | "string_decoder": { 2002 | "version": "1.3.0", 2003 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 2004 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 2005 | "requires": { 2006 | "safe-buffer": "~5.2.0" 2007 | }, 2008 | "dependencies": { 2009 | "safe-buffer": { 2010 | "version": "5.2.1", 2011 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 2012 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 2013 | } 2014 | } 2015 | }, 2016 | "text-hex": { 2017 | "version": "1.0.0", 2018 | "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", 2019 | "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" 2020 | }, 2021 | "to-fast-properties": { 2022 | "version": "1.0.3", 2023 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", 2024 | "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" 2025 | }, 2026 | "to-object-path": { 2027 | "version": "0.3.0", 2028 | "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", 2029 | "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", 2030 | "requires": { 2031 | "kind-of": "^3.0.2" 2032 | }, 2033 | "dependencies": { 2034 | "kind-of": { 2035 | "version": "3.2.2", 2036 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 2037 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 2038 | "requires": { 2039 | "is-buffer": "^1.1.5" 2040 | } 2041 | } 2042 | } 2043 | }, 2044 | "to-regex": { 2045 | "version": "3.0.2", 2046 | "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", 2047 | "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", 2048 | "requires": { 2049 | "define-property": "^2.0.2", 2050 | "extend-shallow": "^3.0.2", 2051 | "regex-not": "^1.0.2", 2052 | "safe-regex": "^1.1.0" 2053 | } 2054 | }, 2055 | "to-regex-range": { 2056 | "version": "2.1.1", 2057 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", 2058 | "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", 2059 | "requires": { 2060 | "is-number": "^3.0.0", 2061 | "repeat-string": "^1.6.1" 2062 | } 2063 | }, 2064 | "toidentifier": { 2065 | "version": "1.0.0", 2066 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 2067 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 2068 | }, 2069 | "token-stream": { 2070 | "version": "0.0.1", 2071 | "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", 2072 | "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" 2073 | }, 2074 | "triple-beam": { 2075 | "version": "1.3.0", 2076 | "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", 2077 | "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" 2078 | }, 2079 | "type-is": { 2080 | "version": "1.6.18", 2081 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 2082 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 2083 | "requires": { 2084 | "media-typer": "0.3.0", 2085 | "mime-types": "~2.1.24" 2086 | } 2087 | }, 2088 | "uglify-js": { 2089 | "version": "2.8.29", 2090 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", 2091 | "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", 2092 | "requires": { 2093 | "source-map": "~0.5.1", 2094 | "uglify-to-browserify": "~1.0.0", 2095 | "yargs": "~3.10.0" 2096 | } 2097 | }, 2098 | "uglify-to-browserify": { 2099 | "version": "1.0.2", 2100 | "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", 2101 | "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", 2102 | "optional": true 2103 | }, 2104 | "uid-safe": { 2105 | "version": "2.1.5", 2106 | "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", 2107 | "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", 2108 | "requires": { 2109 | "random-bytes": "~1.0.0" 2110 | } 2111 | }, 2112 | "union-value": { 2113 | "version": "1.0.1", 2114 | "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", 2115 | "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", 2116 | "requires": { 2117 | "arr-union": "^3.1.0", 2118 | "get-value": "^2.0.6", 2119 | "is-extendable": "^0.1.1", 2120 | "set-value": "^2.0.1" 2121 | } 2122 | }, 2123 | "unpipe": { 2124 | "version": "1.0.0", 2125 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 2126 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 2127 | }, 2128 | "unset-value": { 2129 | "version": "1.0.0", 2130 | "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", 2131 | "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", 2132 | "requires": { 2133 | "has-value": "^0.3.1", 2134 | "isobject": "^3.0.0" 2135 | }, 2136 | "dependencies": { 2137 | "has-value": { 2138 | "version": "0.3.1", 2139 | "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", 2140 | "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", 2141 | "requires": { 2142 | "get-value": "^2.0.3", 2143 | "has-values": "^0.1.4", 2144 | "isobject": "^2.0.0" 2145 | }, 2146 | "dependencies": { 2147 | "isobject": { 2148 | "version": "2.1.0", 2149 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", 2150 | "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", 2151 | "requires": { 2152 | "isarray": "1.0.0" 2153 | } 2154 | } 2155 | } 2156 | }, 2157 | "has-values": { 2158 | "version": "0.1.4", 2159 | "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", 2160 | "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" 2161 | } 2162 | } 2163 | }, 2164 | "urix": { 2165 | "version": "0.1.0", 2166 | "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", 2167 | "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" 2168 | }, 2169 | "use": { 2170 | "version": "3.1.1", 2171 | "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", 2172 | "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" 2173 | }, 2174 | "util-deprecate": { 2175 | "version": "1.0.2", 2176 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 2177 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 2178 | }, 2179 | "utils-merge": { 2180 | "version": "1.0.1", 2181 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 2182 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 2183 | }, 2184 | "uuid": { 2185 | "version": "3.4.0", 2186 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 2187 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" 2188 | }, 2189 | "vary": { 2190 | "version": "1.1.2", 2191 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 2192 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 2193 | }, 2194 | "void-elements": { 2195 | "version": "2.0.1", 2196 | "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", 2197 | "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" 2198 | }, 2199 | "window-size": { 2200 | "version": "0.1.0", 2201 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", 2202 | "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" 2203 | }, 2204 | "winston": { 2205 | "version": "3.3.3", 2206 | "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", 2207 | "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", 2208 | "requires": { 2209 | "@dabh/diagnostics": "^2.0.2", 2210 | "async": "^3.1.0", 2211 | "is-stream": "^2.0.0", 2212 | "logform": "^2.2.0", 2213 | "one-time": "^1.0.0", 2214 | "readable-stream": "^3.4.0", 2215 | "stack-trace": "0.0.x", 2216 | "triple-beam": "^1.3.0", 2217 | "winston-transport": "^4.4.0" 2218 | } 2219 | }, 2220 | "winston-transport": { 2221 | "version": "4.4.0", 2222 | "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", 2223 | "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", 2224 | "requires": { 2225 | "readable-stream": "^2.3.7", 2226 | "triple-beam": "^1.2.0" 2227 | }, 2228 | "dependencies": { 2229 | "readable-stream": { 2230 | "version": "2.3.7", 2231 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 2232 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 2233 | "requires": { 2234 | "core-util-is": "~1.0.0", 2235 | "inherits": "~2.0.3", 2236 | "isarray": "~1.0.0", 2237 | "process-nextick-args": "~2.0.0", 2238 | "safe-buffer": "~5.1.1", 2239 | "string_decoder": "~1.1.1", 2240 | "util-deprecate": "~1.0.1" 2241 | } 2242 | }, 2243 | "string_decoder": { 2244 | "version": "1.1.1", 2245 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 2246 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 2247 | "requires": { 2248 | "safe-buffer": "~5.1.0" 2249 | } 2250 | } 2251 | } 2252 | }, 2253 | "with": { 2254 | "version": "5.1.1", 2255 | "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", 2256 | "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", 2257 | "requires": { 2258 | "acorn": "^3.1.0", 2259 | "acorn-globals": "^3.0.0" 2260 | } 2261 | }, 2262 | "wordwrap": { 2263 | "version": "0.0.2", 2264 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", 2265 | "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" 2266 | }, 2267 | "wreck": { 2268 | "version": "14.2.0", 2269 | "resolved": "https://registry.npmjs.org/wreck/-/wreck-14.2.0.tgz", 2270 | "integrity": "sha512-NFFft3SMgqrJbXEVfYifh+QDWFxni+98/I7ut7rLbz3F0XOypluHsdo3mdEYssGSirMobM3fGlqhyikbWKDn2Q==", 2271 | "requires": { 2272 | "boom": "7.x.x", 2273 | "bourne": "1.x.x", 2274 | "hoek": "6.x.x" 2275 | } 2276 | }, 2277 | "yargs": { 2278 | "version": "3.10.0", 2279 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", 2280 | "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", 2281 | "requires": { 2282 | "camelcase": "^1.0.2", 2283 | "cliui": "^2.1.0", 2284 | "decamelize": "^1.0.0", 2285 | "window-size": "0.1.0" 2286 | } 2287 | } 2288 | } 2289 | } 2290 | -------------------------------------------------------------------------------- /terminal/gateway/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workshop-gateway", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "start": "node server.js" 7 | }, 8 | "dependencies": { 9 | "express": "^4.16.4", 10 | "express-basic-auth": "^1.1.7", 11 | "express-session": "^1.15.6", 12 | "http-proxy-middleware": "^0.19.1", 13 | "simple-oauth2": "^2.2.1", 14 | "axios": "^0.19.0", 15 | "uuid": "^3.3.2", 16 | "pug": "^2.0.3", 17 | "morgan": "^1.9.1", 18 | "winston": "^3.2.0", 19 | "axios-retry": "^3.1.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /terminal/gateway/routes/console.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var proxy = require('http-proxy-middleware'); 3 | var logger = require('../logger'); 4 | 5 | var enable_console = process.env.ENABLE_CONSOLE; 6 | 7 | var console_url = process.env.CONSOLE_URL; 8 | 9 | module.exports = function(app, prefix) { 10 | var router = express.Router(); 11 | 12 | if (enable_console != 'true') { 13 | return router; 14 | } 15 | 16 | if (console_url) { 17 | router.use(proxy(prefix, { 18 | target: console_url, 19 | ws: true, 20 | onProxyRes: function (proxyRes, req, res) { 21 | delete proxyRes.headers['x-frame-options']; 22 | } 23 | })); 24 | } 25 | 26 | return router; 27 | } 28 | -------------------------------------------------------------------------------- /terminal/gateway/routes/terminal.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var proxy = require('http-proxy-middleware'); 3 | var logger = require('../logger'); 4 | 5 | var enable_terminal = process.env.ENABLE_TERMINAL; 6 | 7 | module.exports = function(app, prefix) { 8 | // Setup proxying to terminal application. If no terminal session is 9 | // provided, redirect to session 1. This ensures user always get the 10 | // same session and not a new one each time if refresh the web browser 11 | // or access same URL from another browser window. 12 | 13 | var router = express.Router(); 14 | 15 | if (enable_terminal != 'true') { 16 | return router; 17 | } 18 | 19 | router.get('^/?$', function (req, res) { 20 | res.redirect(req.baseUrl + '/session/1'); 21 | }) 22 | 23 | router.use('/static', express.static('/opt/workshop/butterfly/static')); 24 | 25 | router.use(proxy(prefix, { 26 | target: 'http://127.0.0.1:10081', 27 | ws: true 28 | })); 29 | 30 | return router; 31 | } 32 | -------------------------------------------------------------------------------- /terminal/gateway/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var basic_auth = require('express-basic-auth') 3 | var session = require('express-session'); 4 | var proxy = require('http-proxy-middleware'); 5 | var uuid = require('uuid'); 6 | var http = require('http'); 7 | var https = require('https'); 8 | var axios = require('axios'); 9 | var path = require('path'); 10 | var url = require('url'); 11 | var fs = require('fs'); 12 | var morgan = require('morgan') 13 | var logger = require('./logger'); 14 | 15 | var enable_webdav = process.env.ENABLE_WEBDAV; 16 | 17 | // Setup the root application. Everything will actually be under a 18 | // mount point corresponding to the specific user. This is added in 19 | // each of the routes when defined. 20 | 21 | var app = express(); 22 | 23 | var uri_root_path = process.env.URI_ROOT_PATH || ''; 24 | 25 | // Add logging for request. 26 | 27 | var log_format = process.env.LOG_FORMAT || 'dev'; 28 | 29 | app.use(morgan(log_format)); 30 | 31 | // In OpenShift we are always behind a proxy, so trust the headers sent. 32 | 33 | app.set('trust proxy', true); 34 | 35 | // Short circuit WebDAV access as it handles its own authentication. 36 | 37 | if (enable_webdav == 'true') { 38 | app.use(uri_root_path + '/webdav/', proxy({ 39 | target: 'http://127.0.0.1:10084', 40 | ws: true 41 | })); 42 | } 43 | 44 | // Enable use of a client session for the user. This is used to track 45 | // whether the user has logged in when using oauth. Session will expire 46 | // after 24 hours. 47 | 48 | var handshakes = {} 49 | 50 | app.use(session({ 51 | name: 'workshop-session-id', 52 | genid: function(req) { 53 | return uuid.v4() 54 | }, 55 | secret: uuid.v4(), 56 | cookie: { 57 | path: uri_root_path, 58 | maxAge: 24*60*60*1000 59 | }, 60 | resave: false, 61 | saveUninitialized: true 62 | })); 63 | 64 | // For standalone container deployment, provide the ability to enable 65 | // authentication using HTTP Basic authentication. In this case there 66 | // will be no user object added to the client session. 67 | 68 | var auth_username = process.env.AUTH_USERNAME; 69 | var auth_password = process.env.AUTH_PASSWORD; 70 | 71 | async function install_basic_auth() { 72 | logger.info('Register basic auth handler'); 73 | 74 | app.use(basic_auth({ 75 | challenge: true, 76 | realm: 'Terminal', 77 | authorizer: function (username, password) { 78 | return username == auth_username && password == auth_password; 79 | } 80 | })); 81 | } 82 | 83 | // For OAuth, two types of authentication methods are supported. The 84 | // first is where the terminal is behind JupyterHub. In this case 85 | // JupyterHub handles primary authentication of the user using whatever 86 | // method it chooses to use. In this case the terminal still needs to do 87 | // a OAuth handshake with JupyterHub. This mode of operation is setup by 88 | // the following environment variables. 89 | 90 | var jupyterhub_user = process.env.JUPYTERHUB_USER; 91 | var jupyterhub_client_id = process.env.JUPYTERHUB_CLIENT_ID; 92 | var jupyterhub_api_url = process.env.JUPYTERHUB_API_URL; 93 | var jupyterhub_api_token = process.env.JUPYTERHUB_API_TOKEN; 94 | var jupyterhub_route = process.env.JUPYTERHUB_ROUTE 95 | 96 | // The second authentication method using OAuth is where the terminal 97 | // delegates authentication to OpenShift itself. In this case the 98 | // service accounts is used as an OAuth proxy. This mode of operation 99 | // is setup by the following environment variables. 100 | 101 | var oauth_service_account = process.env.OAUTH_SERVICE_ACCOUNT; 102 | 103 | // These functions provide details on the project the deployment is, 104 | // the service account name and the service token. These are only used 105 | // when using OpenShift OAuth and rely on the service account details 106 | // being mounted into the container. 107 | 108 | function project_name() { 109 | const account_path = '/var/run/secrets/kubernetes.io/serviceaccount'; 110 | const namespace_path = path.join(account_path, 'namespace'); 111 | 112 | return fs.readFileSync(namespace_path, 'utf8'); 113 | } 114 | 115 | function service_account_name(name) { 116 | const prefix = 'system:serviceaccount'; 117 | const namespace = project_name(); 118 | 119 | return prefix + ':' + namespace + ':' + name; 120 | } 121 | 122 | function service_account_token() { 123 | const account_path = '/var/run/secrets/kubernetes.io/serviceaccount'; 124 | const token_path = path.join(account_path, 'token'); 125 | 126 | return fs.readFileSync(token_path, 'utf8'); 127 | } 128 | 129 | // OAuth servers support a well known URL for querying properties of the 130 | // OAuth server. Unfortunately JupyterHub doesn't support this, so we 131 | // need to fake up this data later. We can use this for the case when 132 | // using OpenShift OAuth. 133 | 134 | async function get_oauth_metadata(server) { 135 | const options = { 136 | baseURL: server, 137 | httpsAgent: new https.Agent({ rejectUnauthorized: false }), 138 | responseType: 'json' 139 | }; 140 | 141 | const url = '/.well-known/oauth-authorization-server'; 142 | 143 | return (await axios.get(url, options)).data; 144 | } 145 | 146 | function setup_oauth_credentials(metadata, client_id, client_secret) { 147 | var credentials = { 148 | client: { 149 | id: client_id, 150 | secret: client_secret 151 | }, 152 | auth: { 153 | tokenHost: metadata['issuer'], 154 | authorizePath: metadata['authorization_endpoint'], 155 | tokenPath: metadata['token_endpoint'] 156 | }, 157 | options: { 158 | authorizationMethod: 'body', 159 | }, 160 | http: { 161 | rejectUnauthorized: false 162 | } 163 | }; 164 | 165 | return credentials; 166 | } 167 | 168 | // When using JupyterHub OAuth, after the user has authenticated, to 169 | // verify the user we need to make sure that they are the owner of the 170 | // instance, or that they are an admin. 171 | 172 | async function get_jupyterhub_user_details(access_token) { 173 | const options = { 174 | baseURL: jupyterhub_api_url, 175 | headers: { 'Authorization': 'Bearer ' + access_token }, 176 | responseType: 'json' 177 | }; 178 | 179 | const url = '/user'; 180 | 181 | return (await axios.get(url, options)).data; 182 | } 183 | 184 | async function verify_jupyterhub_user(access_token) { 185 | var details = await get_jupyterhub_user_details(access_token); 186 | 187 | logger.info('JupyterHub user name', {name:details.name}); 188 | 189 | if (details.admin) 190 | return details.name; 191 | 192 | if (details.name == jupyterhub_user) 193 | return details.name; 194 | 195 | logger.info('User forbidden access', {name:details.name}); 196 | } 197 | 198 | // When using OpenShift OAuth, after the user has authenticated, to 199 | // verify the user we need to check whether the user is as admin of the 200 | // project the deployment is in. This assumes the deployment is done 201 | // using a service account with admin access on the project as it needs 202 | // to be able to query rolebindings for the project. 203 | 204 | var kubernetes_service_host = process.env.KUBERNETES_SERVICE_HOST; 205 | var kubernetes_service_port = process.env.KUBERNETES_SERVICE_PORT; 206 | 207 | async function get_openshift_user_details(server, access_token) { 208 | const options = { 209 | baseURL: server, 210 | httpsAgent: new https.Agent({ rejectUnauthorized: false }), 211 | headers: { 'Authorization': 'Bearer ' + access_token }, 212 | responseType: 'json' 213 | }; 214 | 215 | const url = '/apis/user.openshift.io/v1/users/~'; 216 | 217 | var details = (await axios.get(url, options)).data; 218 | var name = details['metadata']['name']; 219 | 220 | return name; 221 | } 222 | 223 | async function get_openshift_admin_users(server) { 224 | const namespace = project_name(); 225 | const token = service_account_token(); 226 | 227 | const options = { 228 | baseURL: server, 229 | httpsAgent: new https.Agent({ rejectUnauthorized: false }), 230 | headers: { 'Authorization': 'Bearer ' + token }, 231 | responseType: 'json' 232 | }; 233 | 234 | const url = '/apis/authorization.openshift.io/v1/namespaces/' + 235 | namespace + '/rolebindings'; 236 | 237 | var details = (await axios.get(url, options)).data; 238 | 239 | var users = []; 240 | 241 | for (var i=0; i { 279 | try { 280 | var code = req.query.code; 281 | var state = req.query.state; 282 | 283 | // If we seem to have no record of the specific handshake 284 | // state, redirect back to the main page and start over. 285 | 286 | if (handshakes[state] === undefined) { 287 | return res.redirect(uri_root_path + '/'); 288 | } 289 | 290 | // This retrieves the next URL to redirect to from the session 291 | // for this particular oauth handshake. 292 | 293 | var next_url = handshakes[state]; 294 | delete handshakes[state]; 295 | 296 | // Obtain the user access token using the authorization 297 | // code. The same_origin flags is to indicate whether the 298 | // OAuth server is under the same hostname or not. This is 299 | // the case for JupyterHub OAuth. Need to treat this 300 | // differently as JupyterHub doesn't like it when the 301 | // redirect_uri has a hostname in it. Instead it wants just 302 | // the path. This is okay since are under the same origin. 303 | // That said, technically, not ensure sure the redirect_uri 304 | // is used at this point but documentation for oauth 305 | // packages has it in examples. 306 | 307 | var redirect_uri; 308 | 309 | if (!same_origin) { 310 | redirect_uri = [req.protocol, '://', req.hostname, 311 | uri_root_path, '/oauth_callback'].join(''); 312 | } else { 313 | redirect_uri = [uri_root_path, '/oauth_callback'].join(''); 314 | } 315 | 316 | var options = { 317 | redirect_uri: redirect_uri, 318 | scope: 'user:info', 319 | code: code 320 | }; 321 | 322 | logger.debug('token_options', {options:options}); 323 | 324 | var auth_result = await oauth2.authorizationCode.getToken(options); 325 | var token_result = oauth2.accessToken.create(auth_result); 326 | 327 | logger.debug('auth_result', {result:auth_result}); 328 | logger.debug('token_result', {result:token_result}); 329 | 330 | // Now we need to verify whether this user is allowed access 331 | // to the project. 332 | 333 | req.session.user = await verify_user( 334 | token_result['token']['access_token']); 335 | 336 | if (!req.session.user) { 337 | return res.status(403).json('Access forbidden'); 338 | } 339 | 340 | logger.info('User access granted', {name:req.session.user}); 341 | 342 | return res.redirect(next_url); 343 | } catch(err) { 344 | console.error('Error', err.message); 345 | return res.status(500).json('Authentication failed'); 346 | } 347 | }); 348 | } 349 | 350 | // Setup up redirection to the OAuth server authorization endpoint. 351 | 352 | function register_oauth_handshake(oauth2, same_origin) { 353 | logger.info('Register OAuth handshake'); 354 | 355 | app.get(uri_root_path + '/oauth_handshake', (req, res) => { 356 | // Stash the next URL after authentication in the user session 357 | // keyed by unique code for this oauth handshake. Use the code 358 | // as the state for oauth requests. 359 | 360 | var state = uuid.v4(); 361 | handshakes[state] = req.query.next; 362 | 363 | // Redirect to the authorization end point for the OAuth server. 364 | // The same_origin flags is to indicate whether the OAuth server 365 | // is under the same hostname or not. This is the case for 366 | // JupyterHub OAuth. Need to treat this differently as 367 | // JupyterHub doesn't like it when the redirect_uri has a 368 | // hostname in it. Instead it wants just the path. This is okay 369 | // since are under the same origin. 370 | 371 | var redirect_uri; 372 | 373 | if (!same_origin) { 374 | redirect_uri = [req.protocol, '://', req.hostname, 375 | uri_root_path, '/oauth_callback'].join(''); 376 | } else { 377 | redirect_uri = [uri_root_path, '/oauth_callback'].join(''); 378 | } 379 | 380 | const authorization_uri = oauth2.authorizationCode.authorizeURL({ 381 | redirect_uri: redirect_uri, 382 | scope: 'user:info', 383 | state: state 384 | }); 385 | 386 | logger.debug('authorization_uri', {uri:authorization_uri}); 387 | 388 | res.redirect(authorization_uri); 389 | }); 390 | 391 | app.use(function (req, res, next) { 392 | if (!req.session.user) { 393 | next_url = encodeURIComponent(req.url); 394 | res.redirect(uri_root_path + '/oauth_handshake?next=' + next_url); 395 | } 396 | else { 397 | next(); 398 | } 399 | }) 400 | } 401 | 402 | // Setup routes etc, corresponding to requirements of different possible 403 | // authentication methods used. 404 | 405 | async function install_jupyterhub_auth() { 406 | // JupyterHub OAuth server doesn't support URL for query properties 407 | // of the server, so fake up the metadata here so can create the 408 | // credentials. 409 | 410 | var issuer = jupyterhub_route; 411 | 412 | var client_id = jupyterhub_client_id; 413 | var client_secret = jupyterhub_api_token; 414 | 415 | var api_url = url.parse(jupyterhub_api_url); 416 | 417 | var metadata = { 418 | issuer: issuer, 419 | authorization_endpoint: issuer + api_url.pathname + '/oauth2/authorize', 420 | token_endpoint: issuer + api_url.pathname + '/oauth2/token' 421 | }; 422 | 423 | logger.info('OAuth server metadata', {metadata:metadata}); 424 | 425 | var credentials = setup_oauth_credentials(metadata, client_id, 426 | client_secret); 427 | 428 | logger.info('OAuth server credentials', {credentials:credentials}); 429 | 430 | var oauth2 = require('simple-oauth2').create(credentials); 431 | 432 | var same_origin = true; 433 | 434 | register_oauth_callback(oauth2, verify_jupyterhub_user, same_origin); 435 | register_oauth_handshake(oauth2, same_origin); 436 | } 437 | 438 | async function install_openshift_auth() { 439 | var server = 'https://' + kubernetes_service_host + ':' + kubernetes_service_port; 440 | 441 | var client_id = service_account_name(oauth_service_account); 442 | var client_secret = service_account_token(); 443 | 444 | var metadata = await get_oauth_metadata(server); 445 | 446 | logger.info('OAuth server metadata', {metadata:metadata}); 447 | 448 | var credentials = setup_oauth_credentials(metadata, client_id, 449 | client_secret); 450 | 451 | logger.info('OAuth server credentials', {credentials:credentials}); 452 | 453 | var oauth2 = require('simple-oauth2').create(credentials); 454 | 455 | var same_origin = false; 456 | 457 | register_oauth_callback(oauth2, function(access_token) { 458 | return verify_openshift_user(server, access_token); }, same_origin); 459 | register_oauth_handshake(oauth2, same_origin); 460 | } 461 | 462 | async function setup_access() { 463 | if (jupyterhub_client_id) { 464 | logger.info('Install JupyterHub auth support'); 465 | await install_jupyterhub_auth(); 466 | } 467 | else if (auth_username) { 468 | if (auth_username != '*') { 469 | logger.info('Install HTTP Basic auth support'); 470 | await install_basic_auth(); 471 | } 472 | else { 473 | logger.info('All authentication has been disabled'); 474 | } 475 | } 476 | else if (oauth_service_account) { 477 | logger.info('Install OpenShift auth support'); 478 | await install_openshift_auth(); 479 | } 480 | } 481 | 482 | // Setup handler for default page and routes. If no overrides of any 483 | // sort are defined then redirect to /terminal. 484 | 485 | function set_default_page() { 486 | var default_route = process.env.DEFAULT_ROUTE || '/terminal'; 487 | 488 | var default_index = path.join(__dirname, 'routes', 'index.js'); 489 | var override_index = '/opt/app-root/gateway/routes/index.js'; 490 | 491 | if (fs.existsSync(override_index)) { 492 | logger.info('Set index to', {path:override_index}); 493 | app.get('^' + uri_root_path + '/?$', require(override_index)); 494 | } 495 | else if (fs.existsSync(default_index)) { 496 | logger.info('Set index to', {path:default_index}); 497 | app.get('^' + uri_root_path + '/?$', require(default_index)); 498 | } 499 | else { 500 | logger.info('Set index to', {path:default_route}); 501 | app.get('^' + uri_root_path + '/?$', function (req, res) { 502 | res.redirect(uri_root_path + default_route); 503 | }); 504 | } 505 | } 506 | 507 | function install_routes(directory) { 508 | if (fs.existsSync(directory)) { 509 | var files = fs.readdirSync(directory); 510 | 511 | for (var i=0; i