├── .babelrc ├── .circleci └── config.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── build.sh ├── client ├── client.js ├── components │ ├── FnAppForm.vue │ ├── FnNotification.vue │ ├── FnRouteForm.vue │ ├── FnRunFunction.vue │ ├── FnSidebar.vue │ └── FnWelcomeSection.vue ├── css │ ├── app.css │ ├── fonts.css │ ├── main.css │ ├── sidebar.css │ └── variables.css ├── fonts │ ├── Open_Sans-italic-400.css │ ├── Open_Sans-italic-400.eot │ ├── Open_Sans-italic-400.svg │ ├── Open_Sans-italic-400.ttf │ ├── Open_Sans-italic-400.woff │ ├── Open_Sans-italic-400.woff2 │ ├── Open_Sans-normal-300.eot │ ├── Open_Sans-normal-300.svg │ ├── Open_Sans-normal-300.ttf │ ├── Open_Sans-normal-300.woff │ ├── Open_Sans-normal-300.woff2 │ ├── Open_Sans-normal-400.eot │ ├── Open_Sans-normal-400.svg │ ├── Open_Sans-normal-400.ttf │ ├── Open_Sans-normal-400.woff │ ├── Open_Sans-normal-400.woff2 │ ├── Open_Sans-normal-700.eot │ ├── Open_Sans-normal-700.svg │ ├── Open_Sans-normal-700.ttf │ ├── Open_Sans-normal-700.woff │ ├── Open_Sans-normal-700.woff2 │ └── fonts.css ├── lib │ ├── VueBootstrapModal.vue │ └── helpers.js └── pages │ ├── AppPage.vue │ └── IndexPage.vue ├── docs └── screenshots │ ├── add_route.png │ ├── apps.png │ ├── routes.png │ └── run.png ├── package-lock.json ├── package.json ├── public ├── android-icon-144x144.png ├── android-icon-192x192.png ├── android-icon-36x36.png ├── android-icon-48x48.png ├── android-icon-72x72.png ├── android-icon-96x96.png ├── apple-icon-114x114.png ├── apple-icon-120x120.png ├── apple-icon-144x144.png ├── apple-icon-152x152.png ├── apple-icon-180x180.png ├── apple-icon-57x57.png ├── apple-icon-60x60.png ├── apple-icon-72x72.png ├── apple-icon-76x76.png ├── apple-icon-precomposed.png ├── apple-icon.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon-96x96.png ├── favicon.ico ├── fonts │ ├── Open_Sans-italic-400.css │ ├── Open_Sans-italic-400.eot │ ├── Open_Sans-italic-400.svg │ ├── Open_Sans-italic-400.ttf │ ├── Open_Sans-italic-400.woff │ ├── Open_Sans-italic-400.woff2 │ ├── Open_Sans-normal-300.eot │ ├── Open_Sans-normal-300.svg │ ├── Open_Sans-normal-300.ttf │ ├── Open_Sans-normal-300.woff │ ├── Open_Sans-normal-300.woff2 │ ├── Open_Sans-normal-400.eot │ ├── Open_Sans-normal-400.svg │ ├── Open_Sans-normal-400.ttf │ ├── Open_Sans-normal-400.woff │ ├── Open_Sans-normal-400.woff2 │ ├── Open_Sans-normal-700.eot │ ├── Open_Sans-normal-700.svg │ ├── Open_Sans-normal-700.ttf │ ├── Open_Sans-normal-700.woff │ ├── Open_Sans-normal-700.woff2 │ └── fonts.css ├── images │ ├── icons │ │ ├── github-logo.svg │ │ ├── ic_close_24px.svg │ │ ├── more_vert.svg │ │ ├── plus.svg │ │ └── wrench.svg │ ├── ironfunctions-logo.svg │ └── titan-logo.svg ├── index.html ├── manifest.json ├── ms-icon-144x144.png ├── ms-icon-150x150.png ├── ms-icon-310x310.png └── ms-icon-70x70.png ├── release.sh ├── server.js ├── server ├── controllers │ ├── apps.js │ ├── info.js │ └── routes.js ├── helpers │ └── app-helpers.js ├── router.js └── server.js ├── stylelint.config.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "plugins": ["transform-runtime"], 4 | "comments": false 5 | } 6 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | machine: true 5 | working_directory: ~/go/src/github.com/functions-ui 6 | environment: 7 | - GOPATH=/home/circleci/go 8 | - GOVERSION=1.8.3 9 | - OS=linux 10 | - ARCH=amd64 11 | steps: 12 | - checkout 13 | # update Docker 14 | - run: | 15 | docker version 16 | sudo service docker stop 17 | curl -fsSL https://get.docker.com/ | sudo sh 18 | - run: docker version 19 | - deploy: 20 | command: | 21 | if [ "${CIRCLE_BRANCH}" == "master" ]; then 22 | git config --global user.email "circleci@iron.io" 23 | git config --global user.name "CircleCI" 24 | docker login -u $DOCKER_USER -p $DOCKER_PASS 25 | ./release.sh 26 | git branch --set-upstream-to=origin/${CIRCLE_BRANCH} ${CIRCLE_BRANCH} 27 | fi 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /build 3 | /public/build 4 | /data 5 | npm-debug.log 6 | .DS_Store -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mhart/alpine-node:6.3 2 | MAINTAINER iron.io 3 | 4 | RUN mkdir /app 5 | WORKDIR /app 6 | 7 | # Install app dependencies 8 | RUN npm install webpack -g 9 | COPY package.json /app 10 | RUN npm install 11 | 12 | # Bundle app source 13 | COPY . /app 14 | 15 | # Build assets 16 | RUN webpack 17 | 18 | ENV NODE_ENV production 19 | ENV PORT 4000 20 | EXPOSE 4000 21 | 22 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 Iron.io 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UI for [IronFunctions](https://github.com/iron-io/functions) 2 | 3 | # Usage 4 | 5 | Just run the container. 6 | `API_URL` is IronFunctions API URL 7 | __NOTE: latest version is broken, please use 0.0.2__ 8 | 9 | ``` 10 | docker run --rm -it --link functions:api -p 4000:4000 -e "API_URL=http://api:8080" iron/functions-ui:0.0.2 11 | ``` 12 | 13 | # Screenshots 14 | 15 | ![ScreenShot routes](https://raw.githubusercontent.com/iron-io/functions-ui/master/docs/screenshots/routes.png) 16 | ![ScreenShot run command](https://raw.githubusercontent.com/iron-io/functions-ui/master/docs/screenshots/run.png) 17 | 18 | # Development 19 | 20 | 1) Install dependencies: 21 | ``` 22 | npm install && npm install -g webpack 23 | ``` 24 | 25 | 2) Start Functions API 26 | ``` 27 | docker run --rm -it --name functions --privileged -v $PWD/data:/app/data -p 8080:8080 iron/functions 28 | ``` 29 | 30 | 3) Start web server: 31 | ``` 32 | PORT=4000 API_URL=http://localhost:8080 npm start 33 | ``` 34 | 35 | * `PORT` - port to run UI on. Optional, 4000 by default 36 | * `API_URL` - Functions API URL. Required 37 | 38 | 4) Launch automatic asset recompilation: 39 | ``` 40 | webpack --watch 41 | ``` 42 | 43 | Example: 44 | ``` 45 | # Launch Functions API 46 | docker run --rm -it --name functions --privileged -v $PWD/data:/app/data -p 8080:8080 iron/functions 47 | 48 | # Launch FunctionsUI (use `docker-machine ip` instead of localhost if needed - mac only) 49 | API_URL=http://localhost:8080 npm start 50 | 51 | # Launch launch automatic asset recompilation: 52 | webpack --watch 53 | 54 | # Open http://localhost:4000/ in browser 55 | ``` 56 | 57 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | set -ex 2 | 3 | docker build -t iron/functions-ui:latest . 4 | -------------------------------------------------------------------------------- /client/client.js: -------------------------------------------------------------------------------- 1 | require("./css/app.css"); 2 | 3 | require('expose?$!expose?jQuery!jquery'); 4 | require("bootstrap/dist/js/bootstrap.min"); 5 | 6 | import _ from 'lodash/core'; 7 | 8 | import Vue from 'vue'; 9 | import VueRouter from 'vue-router'; 10 | Vue.use(VueRouter); 11 | 12 | import IndexPage from './pages/IndexPage.vue'; 13 | import AppPage from './pages/AppPage.vue'; 14 | 15 | import FnSidebar from './components/FnSidebar.vue'; 16 | import FnNotification from './components/FnNotification.vue'; 17 | import { defaultErrorHandler } from './lib/helpers'; 18 | 19 | export const eventBus = new Vue(); 20 | 21 | const router = new VueRouter({ 22 | routes: [ 23 | { path: '/', component: IndexPage }, 24 | { path: '/app/:appname', component: AppPage } 25 | ] 26 | }); 27 | 28 | new Vue({ 29 | router: router, 30 | data: { 31 | apps: null 32 | }, 33 | components: { 34 | IndexPage, 35 | FnSidebar, 36 | FnNotification 37 | }, 38 | methods: { 39 | loadApps: function(){ 40 | var t = this; 41 | $.ajax({ 42 | url: '/api/apps', 43 | dataType: 'json', 44 | success: (apps) => t.apps = apps, 45 | error: defaultErrorHandler 46 | }) 47 | } 48 | }, 49 | created: function(){ 50 | this.loadApps(); 51 | eventBus.$on('AppAdded', (app) => { 52 | this.loadApps() 53 | }); 54 | eventBus.$on('AppUpdated', (app) => { 55 | this.loadApps() 56 | }); 57 | eventBus.$on('AppDeleted', (app) => { 58 | this.loadApps() 59 | }); 60 | } 61 | }).$mount('#app') 62 | 63 | 64 | //console.log("client initialized"); 65 | -------------------------------------------------------------------------------- /client/components/FnAppForm.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 121 | 122 | 132 | -------------------------------------------------------------------------------- /client/components/FnNotification.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 45 | 46 | 72 | -------------------------------------------------------------------------------- /client/components/FnRouteForm.vue: -------------------------------------------------------------------------------- 1 | 118 | 119 | 223 | 224 | 234 | -------------------------------------------------------------------------------- /client/components/FnRunFunction.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 127 | 128 | -------------------------------------------------------------------------------- /client/components/FnSidebar.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 46 | 47 | 50 | -------------------------------------------------------------------------------- /client/components/FnWelcomeSection.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 46 | -------------------------------------------------------------------------------- /client/css/app.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | @import "./fonts.css"; 3 | @import "./variables.css"; 4 | @import "normalize-css/normalize.css"; 5 | @import "font-awesome/css/font-awesome.css"; 6 | @import "bootstrap/dist/css/bootstrap.min.css"; 7 | @import "./sidebar.css"; 8 | @import "./main.css"; 9 | 10 | body { 11 | font-family: 'Open Sans', Helvetica, Arial, sans-serif; 12 | } 13 | -------------------------------------------------------------------------------- /client/css/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Open Sans'; 3 | font-style: normal; 4 | font-weight: 300; 5 | src: url(../fonts/Open_Sans-normal-300.woff) format('woff'); 6 | unicode-range: U+0-10FFFF; 7 | } 8 | 9 | @font-face { 10 | font-family: 'Open Sans'; 11 | font-style: normal; 12 | font-weight: 400; 13 | src: url(../fonts/Open_Sans-normal-400.woff) format('woff'); 14 | unicode-range: U+0-10FFFF; 15 | } 16 | 17 | @font-face { 18 | font-family: 'Open Sans'; 19 | font-style: normal; 20 | font-weight: 700; 21 | src: url(../fonts/Open_Sans-normal-700.woff) format('woff'); 22 | unicode-range: U+0-10FFFF; 23 | } 24 | 25 | @font-face { 26 | font-family: 'Open Sans'; 27 | font-style: italic; 28 | font-weight: 400; 29 | src: url(../fonts/Open_Sans-italic-400.woff) format('woff'); 30 | unicode-range: U+0-10FFFF; 31 | } 32 | 33 | 34 | @font-face { 35 | font-family: "OpenSans-Italic"; 36 | src: url("../fonts/Open_Sans-italic-400.eot"); 37 | src: url("../fonts/Open_Sans-italic-400.eot?#iefix") format("embedded-opentype"), 38 | url("../fonts/Open_Sans-italic-400.woff2") format("woff2"), 39 | url("../fonts/Open_Sans-italic-400.woff") format("woff"), 40 | url("../fonts/Open_Sans-italic-400.ttf") format("ttf"), 41 | url("../fonts/Open_Sans-italic-400.svg#OpenSans-Italic") format("svg"); 42 | font-style: italic; 43 | font-weight: 300; 44 | } 45 | 46 | @font-face { 47 | font-family: "OpenSans-Light"; 48 | src: url("../fonts/Open_Sans-normal-300.eot"); 49 | src: url("../fonts/Open_Sans-normal-300.eot?#iefix") format("embedded-opentype"), 50 | url("../fonts/Open_Sans-normal-300.woff2") format("woff2"), 51 | url("../fonts/Open_Sans-normal-300.woff") format("woff"), 52 | url("../fonts/Open_Sans-normal-300.ttf") format("ttf"), 53 | url("../fonts/Open_Sans-normal-300.svg#OpenSans-Light") format("svg"); 54 | font-style: normal; 55 | font-weight: 300; 56 | } 57 | 58 | @font-face { 59 | font-family: "OpenSans"; 60 | src: url("../fonts/Open_Sans-normal-400.eot"); 61 | src: url("../fonts/Open_Sans-normal-400.eot?#iefix") format("embedded-opentype"), 62 | url("../fonts/Open_Sans-normal-400.woff2") format("woff2"), 63 | url("../fonts/Open_Sans-normal-400.woff") format("woff"), 64 | url("../fonts/Open_Sans-normal-400.ttf") format("ttf"), 65 | url("../fonts/Open_Sans-normal-400.svg#OpenSans") format("svg"); 66 | font-style: normal; 67 | font-weight: 300; 68 | } 69 | 70 | @font-face { 71 | font-family: "OpenSans-Bold"; 72 | src: url("../fonts/Open_Sans-normal-700.eot"); 73 | src: url("../fonts/Open_Sans-normal-700.eot?#iefix") format("embedded-opentype"), 74 | url("../fonts/Open_Sans-normal-700.woff2") format("woff2"), 75 | url("../fonts/Open_Sans-normal-700.woff") format("woff"), 76 | url("../fonts/Open_Sans-normal-700.ttf") format("ttf"), 77 | url("../fonts/Open_Sans-normal-700.svg#OpenSans-Bold") format("svg"); 78 | font-style: normal; 79 | font-weight: 700; 80 | } 81 | -------------------------------------------------------------------------------- /client/css/main.css: -------------------------------------------------------------------------------- 1 | .main { 2 | padding: 20px; 3 | 4 | & .page-header { 5 | margin-top: 0; 6 | } 7 | 8 | & .dropdown-menu { 9 | border-radius: 1px; 10 | & > li > a { 11 | padding: 5px 10px; 12 | } 13 | } 14 | 15 | & .toolbar { 16 | & .btn-group { 17 | white-space: nowrap; 18 | display: flex; 19 | 20 | & .dropdown-toggle { 21 | border-left: 1px solid #DDD; 22 | } 23 | } 24 | & .btn { 25 | border: none; 26 | } 27 | & .fa { 28 | font-size: 1em; 29 | margin-right: 5px; 30 | } 31 | } 32 | 33 | & a.text-danger, 34 | & .text-danger, 35 | & a.text-danger:hover, 36 | & .text-danger:hover { 37 | color: #E74C3C; 38 | } 39 | } 40 | 41 | .table { 42 | & thead.transparent { 43 | opacity: .1; 44 | pointer-events: none; 45 | } 46 | & tbody tr .no-matches { 47 | text-align: center; 48 | width: 100%; 49 | z-index: 1; 50 | font-size: 30px; 51 | opacity: 1; 52 | color: #B8B8B8; 53 | margin: 40px 0; 54 | line-height: 1.5em; 55 | padding: 30px 0; 56 | } 57 | } 58 | 59 | @media (min-width: 768px) { 60 | .main { 61 | padding-right: 40px; 62 | padding-left: 40px; 63 | 64 | & .modal .modal-title { 65 | text-align: center; 66 | } 67 | 68 | & .modal-content { 69 | box-shadow: 0 5px 10px rgba(0,0,0,.25); 70 | border: none; 71 | border-radius: 2px; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /client/css/sidebar.css: -------------------------------------------------------------------------------- 1 | /* Hide for mobile, show later */ 2 | .sidebar { 3 | display: none; 4 | color: white; 5 | background: #2E2E2E; 6 | 7 | & .navbar-brand { 8 | float: none; 9 | display: block; 10 | padding: 0; 11 | margin-bottom: 30px; 12 | & svg { 13 | width: 100%; 14 | } 15 | } 16 | 17 | & h4 { 18 | margin-bottom: 30px; 19 | } 20 | 21 | & p { 22 | font-size: 12px; 23 | color: rgba(255,255,255,.35); 24 | border-bottom: 1px solid rgba(255,255,255,.1); 25 | padding-bottom: 20px; 26 | } 27 | 28 | & .welcome-section .fa { 29 | font-size: 30px; 30 | vertical-align: middle; 31 | margin-right: 10px; 32 | width: 25px; 33 | text-align: center; 34 | margin-left: -35px; 35 | } 36 | } 37 | 38 | @media (min-width: 768px) { 39 | .sidebar { 40 | position: fixed; 41 | top: 0; 42 | bottom: 0; 43 | left: 0; 44 | z-index: 1000; 45 | display: block; 46 | padding: 20px; 47 | overflow-x: hidden; 48 | 49 | /* Scrollable contents if viewport is shorter than content. */ 50 | overflow-y: auto; 51 | 52 | /* Sidebar navigation */ 53 | & .nav-sidebar { 54 | margin-right: -21px; /* 20px padding + 1px border */ 55 | margin-bottom: 20px; 56 | margin-left: -20px; 57 | 58 | & > li > a { 59 | padding: 10px 10px 10px 20px; 60 | color: rgba(255,255,255,.75); 61 | } 62 | & > li > a:hover, 63 | & > li > a:focus, 64 | & > li > a:active:hover { 65 | color: white; 66 | background-color: rgba(#7E38FF, .25); 67 | } 68 | & > .active > a, 69 | & > .active > a:hover, 70 | & > .active > a:focus { 71 | color: white; 72 | background-color: #7E38FF; 73 | } 74 | } 75 | 76 | & .welcome-section .nav-sidebar > li > a { 77 | padding: 10px 5px 10px 55px; 78 | } 79 | } 80 | } 81 | 82 | .navbar-inverse { 83 | background-color: #2E2E2E; 84 | border: none; 85 | display: block; 86 | 87 | & .navbar-brand { 88 | padding: 13px 0 0 23px; 89 | 90 | & svg { 91 | width: 100px; 92 | } 93 | } 94 | } 95 | 96 | @media (min-width: 768px) { 97 | .navbar-inverse { 98 | display: none; 99 | } 100 | } 101 | 102 | #navbar .nav { 103 | & .fa { 104 | font-size: 30px; 105 | vertical-align: middle; 106 | margin-right: 10px; 107 | width: 25px; 108 | text-align: center; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /client/css/variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --logoYellow: #FFF200; 3 | 4 | /* --textColor: #66758B; */ 5 | --textColor: rgba(58,68,82,.87); 6 | --text2ndColor: rgba(58,68,82,.5); 7 | --disabledColor: rgba(58,68,82,.3); 8 | --dividerColor: rgba(58,68,82,.1); 9 | 10 | --darkBgColor: #222D32; 11 | --textOnDColor: white; 12 | --text2ndOnDColor: rgba(#D8DFE2,.7); 13 | --textDisabledOnDColor: rgba(#D8DFE2,.3); 14 | --greyColor: #D2D6DE; 15 | 16 | --primaryColor: #0072A5; 17 | --accentColor: #FCB104; 18 | --warnColor: var(--redColor); 19 | 20 | --blueColor: #378CBE; 21 | --yellowColor: #F59D00; 22 | --redColor: #DF4A32; 23 | --greenColor: #00A757; 24 | 25 | --orangeColor: #FF8500; 26 | --maroonColor: #DA135F; 27 | --tealColor: #2DCCCD; 28 | --purpleColor: #605AAA; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /client/fonts/Open_Sans-italic-400.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "OpenSans-Italic"; 3 | src: url("fonts/Open_Sans-italic-400.eot"); 4 | src: url("fonts/Open_Sans-italic-400.eot?#iefix") format("embedded-opentype"), 5 | url("fonts/Open_Sans-italic-400.woff2") format("woff2"), 6 | url("fonts/Open_Sans-italic-400.woff") format("woff"), 7 | url("fonts/Open_Sans-italic-400.ttf") format("ttf"), 8 | url("fonts/Open_Sans-italic-400.svg#OpenSans-Italic") format("svg"); 9 | font-style: italic; 10 | font-weight: 300; 11 | } 12 | 13 | @font-face { 14 | font-family: "OpenSans-Light"; 15 | src: url("fonts/Open_Sans-normal-300.eot"); 16 | src: url("fonts/Open_Sans-normal-300.eot?#iefix") format("embedded-opentype"), 17 | url("fonts/Open_Sans-normal-300.woff2") format("woff2"), 18 | url("fonts/Open_Sans-normal-300.woff") format("woff"), 19 | url("fonts/Open_Sans-normal-300.ttf") format("ttf"), 20 | url("fonts/Open_Sans-normal-300.svg#OpenSans-Light") format("svg"); 21 | font-style: normal; 22 | font-weight: 300; 23 | } 24 | 25 | @font-face { 26 | font-family: "OpenSans"; 27 | src: url("fonts/Open_Sans-normal-400.eot"); 28 | src: url("fonts/Open_Sans-normal-400.eot?#iefix") format("embedded-opentype"), 29 | url("fonts/Open_Sans-normal-400.woff2") format("woff2"), 30 | url("fonts/Open_Sans-normal-400.woff") format("woff"), 31 | url("fonts/Open_Sans-normal-400.ttf") format("ttf"), 32 | url("fonts/Open_Sans-normal-400.svg#OpenSans") format("svg"); 33 | font-style: normal; 34 | font-weight: 300; 35 | } 36 | 37 | @font-face { 38 | font-family: "OpenSans-Bold"; 39 | src: url("fonts/Open_Sans-normal-700.eot"); 40 | src: url("fonts/Open_Sans-normal-700.eot?#iefix") format("embedded-opentype"), 41 | url("fonts/Open_Sans-normal-700.woff2") format("woff2"), 42 | url("fonts/Open_Sans-normal-700.woff") format("woff"), 43 | url("fonts/Open_Sans-normal-700.ttf") format("ttf"), 44 | url("fonts/Open_Sans-normal-700.svg#OpenSans-Bold") format("svg"); 45 | font-style: normal; 46 | font-weight: 700; 47 | } 48 | -------------------------------------------------------------------------------- /client/fonts/Open_Sans-italic-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-italic-400.eot -------------------------------------------------------------------------------- /client/fonts/Open_Sans-italic-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-italic-400.ttf -------------------------------------------------------------------------------- /client/fonts/Open_Sans-italic-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-italic-400.woff -------------------------------------------------------------------------------- /client/fonts/Open_Sans-italic-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-italic-400.woff2 -------------------------------------------------------------------------------- /client/fonts/Open_Sans-normal-300.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-normal-300.eot -------------------------------------------------------------------------------- /client/fonts/Open_Sans-normal-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-normal-300.ttf -------------------------------------------------------------------------------- /client/fonts/Open_Sans-normal-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-normal-300.woff -------------------------------------------------------------------------------- /client/fonts/Open_Sans-normal-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-normal-300.woff2 -------------------------------------------------------------------------------- /client/fonts/Open_Sans-normal-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-normal-400.eot -------------------------------------------------------------------------------- /client/fonts/Open_Sans-normal-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-normal-400.ttf -------------------------------------------------------------------------------- /client/fonts/Open_Sans-normal-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-normal-400.woff -------------------------------------------------------------------------------- /client/fonts/Open_Sans-normal-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-normal-400.woff2 -------------------------------------------------------------------------------- /client/fonts/Open_Sans-normal-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-normal-700.eot -------------------------------------------------------------------------------- /client/fonts/Open_Sans-normal-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-normal-700.ttf -------------------------------------------------------------------------------- /client/fonts/Open_Sans-normal-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-normal-700.woff -------------------------------------------------------------------------------- /client/fonts/Open_Sans-normal-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/client/fonts/Open_Sans-normal-700.woff2 -------------------------------------------------------------------------------- /client/fonts/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Open Sans'; 3 | font-style: normal; 4 | font-weight: 300; 5 | src: url(Open_Sans-normal-300.woff) format('woff'); 6 | unicode-range: U+0-10FFFF; 7 | } 8 | 9 | @font-face { 10 | font-family: 'Open Sans'; 11 | font-style: normal; 12 | font-weight: 400; 13 | src: url(Open_Sans-normal-400.woff) format('woff'); 14 | unicode-range: U+0-10FFFF; 15 | } 16 | 17 | @font-face { 18 | font-family: 'Open Sans'; 19 | font-style: normal; 20 | font-weight: 700; 21 | src: url(Open_Sans-normal-700.woff) format('woff'); 22 | unicode-range: U+0-10FFFF; 23 | } 24 | 25 | @font-face { 26 | font-family: 'Open Sans'; 27 | font-style: italic; 28 | font-weight: 400; 29 | src: url(Open_Sans-italic-400.woff) format('woff'); 30 | unicode-range: U+0-10FFFF; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /client/lib/VueBootstrapModal.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 156 | 157 | -------------------------------------------------------------------------------- /client/lib/helpers.js: -------------------------------------------------------------------------------- 1 | import { eventBus } from '../client'; 2 | 3 | 4 | 5 | export const defaultErrorHandler = function(jqXHR){ 6 | var text = "Something went terribly wrong (Status Code: " + jqXHR.status + ")" 7 | try { 8 | text = jqXHR.responseJSON.msg 9 | } catch (_err) {} 10 | eventBus.$emit('NotificationError', text); 11 | } 12 | 13 | 14 | // lines is array in format [{key: "", value: ""}] 15 | // config is key-value hash 16 | export const configToLines = function(config){ 17 | config = config || {}; 18 | var lines = []; 19 | var k; 20 | for (k in config) { 21 | lines.push({key: k, value: config[k]}) 22 | }; 23 | // Always show at least one empty line 24 | if (lines.length == 0) { 25 | lines.push({key: "", value: ""}) 26 | } 27 | return lines; 28 | } 29 | 30 | export const linesToConfig = function(lines){ 31 | var config = {}; 32 | for (var k = 0, i = 0, len = lines.length; i < len; k = ++i) { 33 | var v = lines[k]; 34 | if (v.key){ 35 | config[v.key] = v.value; 36 | } 37 | } 38 | return config; 39 | } 40 | 41 | export const headersToLines = function(headers){ 42 | headers = headers || {}; 43 | var lines = []; 44 | var k; 45 | for (k in headers) { 46 | lines.push({key: k, value: headers[k][0]}) 47 | }; 48 | // Always show at least one empty line 49 | if (lines.length == 0) { 50 | lines.push({key: "", value: ""}) 51 | } 52 | return lines; 53 | } 54 | 55 | export const linesToHeaders = function(lines){ 56 | var headers = {}; 57 | for (var k = 0, i = 0, len = lines.length; i < len; k = ++i) { 58 | var v = lines[k]; 59 | if (v.key){ 60 | headers[v.key] = [v.value]; 61 | } 62 | } 63 | return headers; 64 | } 65 | 66 | export const getApiUrl = function(cb, errCb){ 67 | var errCb = errCb || null; 68 | $.ajax({ 69 | url: '/api/info/api-url', 70 | method: 'GET', 71 | contentType: "application/json", 72 | dataType: 'json', 73 | success: (res) => { 74 | cb(res.url) 75 | }, 76 | error: function(jqXHR, textStatus, errorThrown){ 77 | if (errCb){errCb(jqXHR, textStatus, errorThrown)} 78 | } 79 | }) 80 | } -------------------------------------------------------------------------------- /client/pages/AppPage.vue: -------------------------------------------------------------------------------- 1 | 88 | 89 | 193 | 194 | 197 | -------------------------------------------------------------------------------- /client/pages/IndexPage.vue: -------------------------------------------------------------------------------- 1 | 77 | 78 | 114 | 115 | 118 | -------------------------------------------------------------------------------- /docs/screenshots/add_route.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/docs/screenshots/add_route.png -------------------------------------------------------------------------------- /docs/screenshots/apps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/docs/screenshots/apps.png -------------------------------------------------------------------------------- /docs/screenshots/routes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/docs/screenshots/routes.png -------------------------------------------------------------------------------- /docs/screenshots/run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/docs/screenshots/run.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "FunctionsUI", 3 | "description": "Functions UI", 4 | "version": "0.0.5", 5 | "private": true, 6 | "scripts": { 7 | "start": "node server" 8 | }, 9 | "dependencies": { 10 | "body-parser": "^1.15.2", 11 | "exports-loader": "^0.6.3", 12 | "express": "^4.16.3", 13 | "file-loader": "^0.8.5", 14 | "imports-loader": "^0.6.5", 15 | "jsonwebtoken": "^8.1.1", 16 | "lodash": "^4.17.11", 17 | "request": "^2.88.0", 18 | "resolve-url-loader": "^1.4.3", 19 | "url-loader": "^1.1.1", 20 | "vue": "^2.1.6", 21 | "vue-router": "^2.1.1" 22 | }, 23 | "devDependencies": { 24 | "autoprefixer": "^6.3.7", 25 | "babel-core": "^6.26.3", 26 | "babel-eslint": "^7.0.0", 27 | "babel-loader": "^6.0.0", 28 | "babel-plugin-transform-runtime": "^6.0.0", 29 | "babel-preset-es2015": "^6.0.0", 30 | "babel-preset-stage-2": "^6.0.0", 31 | "babel-register": "^6.0.0", 32 | "bootstrap": "^3.3.7", 33 | "css-loader": "^0.23.1", 34 | "cssnano": "^3.7.1", 35 | "expose-loader": "^0.7.1", 36 | "extract-text-webpack-plugin": "^1.0.1", 37 | "font-awesome": "^4.7.0", 38 | "jquery": "^3.1.0", 39 | "modernizr": "^3.6.0", 40 | "modernizr-webpack-plugin": "^1.0.7", 41 | "normalize-css": "^2.3.1", 42 | "normalize.css": "^4.2.0", 43 | "postcss-cssnext": "^2.7.0", 44 | "postcss-font-magician": "^1.4.0", 45 | "postcss-fontpath": "^0.3.0", 46 | "postcss-hexrgba": "^0.2.0", 47 | "postcss-import": "^8.1.2", 48 | "postcss-loader": "^0.9.1", 49 | "postcss-url": "^7.3.0", 50 | "style-loader": "^0.13.0", 51 | "stylelint": "^7.1.0", 52 | "stylelint-loader": "^6.2.0", 53 | "vue-loader": "^10.0.2", 54 | "vue-template-compiler": "^2.0.0", 55 | "webpack": "^1.14.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /public/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/android-icon-144x144.png -------------------------------------------------------------------------------- /public/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/android-icon-192x192.png -------------------------------------------------------------------------------- /public/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/android-icon-36x36.png -------------------------------------------------------------------------------- /public/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/android-icon-48x48.png -------------------------------------------------------------------------------- /public/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/android-icon-72x72.png -------------------------------------------------------------------------------- /public/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/android-icon-96x96.png -------------------------------------------------------------------------------- /public/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/apple-icon-114x114.png -------------------------------------------------------------------------------- /public/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/apple-icon-120x120.png -------------------------------------------------------------------------------- /public/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/apple-icon-144x144.png -------------------------------------------------------------------------------- /public/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/apple-icon-152x152.png -------------------------------------------------------------------------------- /public/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/apple-icon-180x180.png -------------------------------------------------------------------------------- /public/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/apple-icon-57x57.png -------------------------------------------------------------------------------- /public/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/apple-icon-60x60.png -------------------------------------------------------------------------------- /public/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/apple-icon-72x72.png -------------------------------------------------------------------------------- /public/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/apple-icon-76x76.png -------------------------------------------------------------------------------- /public/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/apple-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/apple-icon.png -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/favicon-96x96.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/favicon.ico -------------------------------------------------------------------------------- /public/fonts/Open_Sans-italic-400.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "OpenSans-Italic"; 3 | src: url("fonts/Open_Sans-italic-400.eot"); 4 | src: url("fonts/Open_Sans-italic-400.eot?#iefix") format("embedded-opentype"), 5 | url("fonts/Open_Sans-italic-400.woff2") format("woff2"), 6 | url("fonts/Open_Sans-italic-400.woff") format("woff"), 7 | url("fonts/Open_Sans-italic-400.ttf") format("ttf"), 8 | url("fonts/Open_Sans-italic-400.svg#OpenSans-Italic") format("svg"); 9 | font-style: italic; 10 | font-weight: 300; 11 | } 12 | 13 | @font-face { 14 | font-family: "OpenSans-Light"; 15 | src: url("fonts/Open_Sans-normal-300.eot"); 16 | src: url("fonts/Open_Sans-normal-300.eot?#iefix") format("embedded-opentype"), 17 | url("fonts/Open_Sans-normal-300.woff2") format("woff2"), 18 | url("fonts/Open_Sans-normal-300.woff") format("woff"), 19 | url("fonts/Open_Sans-normal-300.ttf") format("ttf"), 20 | url("fonts/Open_Sans-normal-300.svg#OpenSans-Light") format("svg"); 21 | font-style: normal; 22 | font-weight: 300; 23 | } 24 | 25 | @font-face { 26 | font-family: "OpenSans"; 27 | src: url("fonts/Open_Sans-normal-400.eot"); 28 | src: url("fonts/Open_Sans-normal-400.eot?#iefix") format("embedded-opentype"), 29 | url("fonts/Open_Sans-normal-400.woff2") format("woff2"), 30 | url("fonts/Open_Sans-normal-400.woff") format("woff"), 31 | url("fonts/Open_Sans-normal-400.ttf") format("ttf"), 32 | url("fonts/Open_Sans-normal-400.svg#OpenSans") format("svg"); 33 | font-style: normal; 34 | font-weight: 300; 35 | } 36 | 37 | @font-face { 38 | font-family: "OpenSans-Bold"; 39 | src: url("fonts/Open_Sans-normal-700.eot"); 40 | src: url("fonts/Open_Sans-normal-700.eot?#iefix") format("embedded-opentype"), 41 | url("fonts/Open_Sans-normal-700.woff2") format("woff2"), 42 | url("fonts/Open_Sans-normal-700.woff") format("woff"), 43 | url("fonts/Open_Sans-normal-700.ttf") format("ttf"), 44 | url("fonts/Open_Sans-normal-700.svg#OpenSans-Bold") format("svg"); 45 | font-style: normal; 46 | font-weight: 700; 47 | } 48 | -------------------------------------------------------------------------------- /public/fonts/Open_Sans-italic-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-italic-400.eot -------------------------------------------------------------------------------- /public/fonts/Open_Sans-italic-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-italic-400.ttf -------------------------------------------------------------------------------- /public/fonts/Open_Sans-italic-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-italic-400.woff -------------------------------------------------------------------------------- /public/fonts/Open_Sans-italic-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-italic-400.woff2 -------------------------------------------------------------------------------- /public/fonts/Open_Sans-normal-300.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-normal-300.eot -------------------------------------------------------------------------------- /public/fonts/Open_Sans-normal-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-normal-300.ttf -------------------------------------------------------------------------------- /public/fonts/Open_Sans-normal-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-normal-300.woff -------------------------------------------------------------------------------- /public/fonts/Open_Sans-normal-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-normal-300.woff2 -------------------------------------------------------------------------------- /public/fonts/Open_Sans-normal-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-normal-400.eot -------------------------------------------------------------------------------- /public/fonts/Open_Sans-normal-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-normal-400.ttf -------------------------------------------------------------------------------- /public/fonts/Open_Sans-normal-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-normal-400.woff -------------------------------------------------------------------------------- /public/fonts/Open_Sans-normal-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-normal-400.woff2 -------------------------------------------------------------------------------- /public/fonts/Open_Sans-normal-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-normal-700.eot -------------------------------------------------------------------------------- /public/fonts/Open_Sans-normal-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-normal-700.ttf -------------------------------------------------------------------------------- /public/fonts/Open_Sans-normal-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-normal-700.woff -------------------------------------------------------------------------------- /public/fonts/Open_Sans-normal-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/fonts/Open_Sans-normal-700.woff2 -------------------------------------------------------------------------------- /public/fonts/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Open Sans'; 3 | font-style: normal; 4 | font-weight: 300; 5 | src: url(Open_Sans-normal-300.woff) format('woff'); 6 | unicode-range: U+0-10FFFF; 7 | } 8 | 9 | @font-face { 10 | font-family: 'Open Sans'; 11 | font-style: normal; 12 | font-weight: 400; 13 | src: url(Open_Sans-normal-400.woff) format('woff'); 14 | unicode-range: U+0-10FFFF; 15 | } 16 | 17 | @font-face { 18 | font-family: 'Open Sans'; 19 | font-style: normal; 20 | font-weight: 700; 21 | src: url(Open_Sans-normal-700.woff) format('woff'); 22 | unicode-range: U+0-10FFFF; 23 | } 24 | 25 | @font-face { 26 | font-family: 'Open Sans'; 27 | font-style: italic; 28 | font-weight: 400; 29 | src: url(Open_Sans-italic-400.woff) format('woff'); 30 | unicode-range: U+0-10FFFF; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /public/images/icons/github-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/images/icons/ic_close_24px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/images/icons/more_vert.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/images/icons/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/images/icons/wrench.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/images/ironfunctions-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/images/titan-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Functions UI 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 | 34 | 35 | 78 | 79 |
80 | 81 | 82 |
83 | 86 |
87 |
88 | 89 | 90 | 91 | 92 |
93 |
94 |
95 | 96 | 97 | 98 |
99 |
100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "icons": [ 4 | { 5 | "src": "\/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /public/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/ms-icon-144x144.png -------------------------------------------------------------------------------- /public/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/ms-icon-150x150.png -------------------------------------------------------------------------------- /public/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/ms-icon-310x310.png -------------------------------------------------------------------------------- /public/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/public/ms-icon-70x70.png -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | # ensure working dir is clean 6 | git status 7 | if [[ -z $(git status -s) ]] 8 | then 9 | echo "tree is clean" 10 | else 11 | echo "tree is dirty, please commit changes before running this" 12 | exit 1 13 | fi 14 | 15 | image="iron/functions-ui" 16 | 17 | git pull 18 | 19 | version_file="package.json" 20 | if [ -z $(grep -m1 -Eo "[0-9]+\.[0-9]+\.[0-9]+" $version_file) ]; then 21 | echo "did not find semantic version in $version_file" 22 | exit 1 23 | fi 24 | # https://github.com/treeder/dockers/tree/master/bump 25 | docker run --rm -it -v $PWD:/app -w /app treeder/bump --filename package.json patch 26 | version=$(grep -m1 -Eo "[0-9]+\.[0-9]+\.[0-9]+" $version_file) 27 | echo "Version: $version" 28 | 29 | ./build.sh 30 | 31 | tag="$version" 32 | git add -u 33 | git commit -m "functions-ui: $version [skip ci]" 34 | git tag -f -a $tag -m "functions-ui: $version" 35 | git push 36 | git push origin $tag 37 | 38 | docker tag $image:latest $image:$version 39 | docker push $image:$version 40 | docker push $image:latest 41 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var url = require('url'); 4 | var bodyParser = require('body-parser'); 5 | 6 | var helpers = require('./server/helpers/app-helpers.js'); 7 | 8 | var app = express(); 9 | 10 | var isProduction = process.env.NODE_ENV === 'production'; 11 | var apiUrl = url.parse(process.env.API_URL); 12 | if (!apiUrl || !apiUrl.hostname) { 13 | console.log("API URL not set. Please specify Functions API URL via environment variable, e.g. API_URL=http://localhost:8080 npm start"); 14 | process.exit(1); 15 | } 16 | 17 | app.set('api-url', apiUrl); 18 | var port = process.env.PORT || 4000; 19 | var publicPath = path.resolve(__dirname, 'public'); 20 | 21 | app.use(express.static(publicPath)); 22 | app.use(bodyParser.json()); 23 | 24 | app.use(require('./server/router.js')); 25 | 26 | app.disable('etag'); 27 | 28 | app.listen(port, function () { 29 | console.log('Using API url: ' + apiUrl.host); 30 | console.log('Server running on port ' + port); 31 | }); -------------------------------------------------------------------------------- /server/controllers/apps.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var helpers = require('../helpers/app-helpers.js'); 4 | 5 | router.get('/', function(req, res) { 6 | successcb = function(data){ 7 | res.json(data.apps); 8 | } 9 | 10 | helpers.getApiEndpoint(req, "/v1/apps", {}, successcb, helpers.standardErrorcb(res)) 11 | }); 12 | 13 | router.get('/:app', function(req, res) { 14 | successcb = function(data){ 15 | res.json(data.app); 16 | } 17 | 18 | helpers.getApiEndpoint(req, "/v1/apps/" + encodeURIComponent(req.params.app), {}, successcb, helpers.standardErrorcb(res)) 19 | }); 20 | 21 | // Create New App 22 | router.post('/', function(req, res) { 23 | successcb = function(data){ 24 | res.json(data); 25 | } 26 | var data = req.body; 27 | 28 | helpers.postApiEndpoint(req, "/v1/apps", {}, {app: data}, successcb, helpers.standardErrorcb(res)); 29 | }); 30 | 31 | // Update App 32 | router.patch('/:app', function(req, res) { 33 | successcb = function(data){ 34 | res.json(data); 35 | } 36 | 37 | var data = req.body; 38 | delete data.name; 39 | 40 | helpers.execApiEndpoint('PATCH', req, "/v1/apps/" + encodeURIComponent(req.params.app) , {}, {app: data}, successcb, helpers.standardErrorcb(res)); 41 | }); 42 | 43 | // Delete App 44 | router.delete('/:app', function(req, res) { 45 | successcb = function(data){ 46 | res.json(data); 47 | } 48 | 49 | helpers.execApiEndpoint('DELETE', req, "/v1/apps/" + encodeURIComponent(req.params.app) , {}, {}, successcb, helpers.standardErrorcb(res)); 50 | }); 51 | 52 | 53 | module.exports = router; 54 | -------------------------------------------------------------------------------- /server/controllers/info.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var url = require('url'); 3 | var router = express.Router(); 4 | var helpers = require('../helpers/app-helpers.js'); 5 | 6 | router.get('/api-url', function(req, res) { 7 | var apiUrl = req.app.get('api-url'); 8 | res.json({url: url.format(apiUrl)}); 9 | }); 10 | 11 | module.exports = router; -------------------------------------------------------------------------------- /server/controllers/routes.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var jwt = require('jsonwebtoken'); 4 | var helpers = require('../helpers/app-helpers.js'); 5 | 6 | router.get('/:app/routes', function(req, res) { 7 | var successcb = function(data){ 8 | res.json(data.routes); 9 | } 10 | 11 | helpers.getApiEndpoint(req, "/v1/apps/" + encodeURIComponent(req.params.app) + "/routes", {}, successcb, helpers.standardErrorcb(res)) 12 | }); 13 | 14 | // Create New Route 15 | router.post('/:app/routes', function(req, res) { 16 | var successcb = function(data){ 17 | res.json(data); 18 | } 19 | 20 | helpers.postApiEndpoint(req, "/v1/apps/" + encodeURIComponent(req.params.app) + "/routes", {}, {route: req.body}, successcb, helpers.standardErrorcb(res)); 21 | }); 22 | 23 | // Update Route 24 | router.patch('/:app/routes/:route', function(req, res) { 25 | var successcb = function(data){ 26 | res.json(data); 27 | } 28 | var data = req.body; 29 | delete data.path; 30 | 31 | helpers.execApiEndpoint('PATCH', req, "/v1/apps/" + encodeURIComponent(req.params.app)+ "/routes/" + encodeURIComponent(req.params.route), {}, {route: data}, successcb, helpers.standardErrorcb(res)); 32 | }); 33 | 34 | // Delete Route 35 | router.delete('/:app/routes/:route', function(req, res) { 36 | var successcb = function(data){ 37 | res.json(data); 38 | } 39 | 40 | helpers.execApiEndpoint('DELETE', req, "/v1/apps/" + encodeURIComponent(req.params.app)+ "/routes/" + encodeURIComponent(req.params.route) , {}, {}, successcb, helpers.standardErrorcb(res)); 41 | }); 42 | 43 | // Run Route 44 | router.post('/:app/routes/:route/run', function(req, res) { 45 | var successcb = function(data){ 46 | res.json({output: data}); 47 | }; 48 | var errcb = function(status, err){ 49 | console.log("Error. Api responded with ", status, err); 50 | var text = "Something went terribly wrong (Status Code: " + status + ") "; 51 | if (err){ 52 | try { 53 | var parsed = JSON.parse(err); 54 | if (parsed && parsed.error && parsed.error.message){ 55 | text = parsed.error.message; 56 | } 57 | if (parsed.request_id){ 58 | text += "\n request_id: " + parsed.request_id; 59 | }; 60 | } catch (e) { 61 | } 62 | } 63 | res.status(400).json({msg: text}); 64 | } 65 | var data = req.body.payload; 66 | if (req.headers['x-jwt-key']) { 67 | var token = jwt.sign(data, req.headers['x-jwt-key']); 68 | var authHeader = {'Authorization': 'Bearer ' + token }; 69 | } 70 | else { 71 | var authHeader = {}; 72 | } 73 | var path = "/r/" + encodeURIComponent(req.params.app)+ "/" + encodeURIComponent(req.params.route); 74 | helpers.execApiEndpointRaw('POST', req, path, {}, data, successcb, errcb, authHeader); 75 | 76 | }); 77 | 78 | 79 | module.exports = router; 80 | -------------------------------------------------------------------------------- /server/helpers/app-helpers.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var url = require('url'); 3 | var request = require('request'); 4 | var jwt = require('jsonwebtoken'); 5 | 6 | exports.extend = function(target) { 7 | var sources = [].slice.call(arguments, 1); 8 | sources.forEach(function (source) { 9 | for (var prop in source) { 10 | target[prop] = source[prop]; 11 | } 12 | }); 13 | return target; 14 | } 15 | 16 | exports.apiFullUrl = function(req, path) { 17 | var apiUrl = req.app.get('api-url'); 18 | var httpurl = url.format(apiUrl) + path.replace(/^\//, ""); 19 | return httpurl; 20 | } 21 | 22 | exports.getApiEndpoint = function(req, path, params, successcb, errorcb) { 23 | var url = exports.apiFullUrl(req, path); 24 | var headers = {} 25 | if (process.env.JWT_AUTH_KEY) { 26 | var token = jwt.sign(params, process.env.JWT_AUTH_KEY); 27 | headers['Authorization'] = 'Bearer ' + token; 28 | } 29 | console.log("GET " + url + ", params: ", params, " headers: ", headers); 30 | var options = { 31 | url: url, 32 | qs: params, 33 | headers: headers 34 | } 35 | request(options, function(error, response, body){exports.requrestCb(successcb, errorcb, error, response, body)}); 36 | } 37 | 38 | exports.postApiEndpoint = function(req, path, params, postfields, successcb, errorcb) { 39 | exports.execApiEndpoint('POST', req, path, params, postfields, successcb, errorcb, {}); 40 | } 41 | 42 | exports.execApiEndpoint = function(method, req, path, params, postfields, successcb, errorcb, headers) { 43 | if(headers == null) { 44 | headers = {}; 45 | } 46 | if (process.env.JWT_AUTH_KEY) { 47 | var token = jwt.sign(params, process.env.JWT_AUTH_KEY); 48 | headers['Authorization'] = 'Bearer ' + token; 49 | } 50 | 51 | var options = { 52 | uri: exports.apiFullUrl(req, path), 53 | method: method, 54 | json: postfields, 55 | headers: headers 56 | }; 57 | 58 | console.log(options.method + " " + options.uri + ", params: ", options.json); 59 | 60 | request(options, function(error, response, body){exports.requrestCb(successcb, errorcb, error, response, body)}); 61 | } 62 | 63 | exports.execApiEndpointRaw = function(method, req, path, params, postfields, successcb, errorcb, headers) { 64 | var options = { 65 | uri: exports.apiFullUrl(req, path), 66 | method: method, 67 | body: postfields, 68 | headers: headers 69 | }; 70 | 71 | console.log(options.method + " " + options.uri + ", params: ", options.body); 72 | 73 | request(options, function(error, response, body){exports.requrestCbRaw(successcb, errorcb, error, response, body)}); 74 | } 75 | 76 | // expects response as json 77 | exports.requrestCb = function (successcb, errorcb, error, response, body) { 78 | var parsed; 79 | if (!error && response.statusCode >= 200 && response.statusCode < 300) { 80 | try { 81 | if (typeof body == "string"){ 82 | parsed = JSON.parse(body); 83 | } else { 84 | parsed = body; 85 | } 86 | } catch (e) { 87 | console.warn("Can not parse json:", body, e); 88 | }; 89 | if (parsed){ 90 | successcb(parsed); 91 | } else { 92 | errorcb(response.statusCode, "Can not parse api response"); 93 | }; 94 | } else { 95 | var message; 96 | try { 97 | if (typeof body == "string"){ 98 | parsed = JSON.parse(body); 99 | } else { 100 | parsed = body; 101 | } 102 | if (parsed && parsed.error && parsed.error.message){ 103 | message = parsed.error.message; 104 | } 105 | } catch (e) { 106 | message = "Can not parse api response"; 107 | } 108 | message = message || "An error ocurred." 109 | var status = response ? response.statusCode : error.code; 110 | console.warn("[ERR] " + status + " | " + message); 111 | errorcb(status, message); 112 | } 113 | } 114 | 115 | // expects response as plain text 116 | exports.requrestCbRaw = function (successcb, errorcb, error, response, body) { 117 | if (!error && response.statusCode >= 200 && response.statusCode < 300) { 118 | successcb(body); 119 | } else { 120 | var status = response ? response.statusCode : error.code; 121 | errorcb(status, body); 122 | } 123 | } 124 | 125 | exports.standardErrorcb = function(res){ 126 | return function(status, err){ 127 | console.log("Error. Api responded with ", status, err); 128 | var text = "Something went terribly wrong (Status Code: " + status + ") "; 129 | if (err){ 130 | text = "Error: " + err; 131 | } 132 | res.status(400).json({msg: text}); 133 | } 134 | } 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /server/router.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | router.use('/api/apps', require('./controllers/apps.js')) 5 | router.use('/api/apps', require('./controllers/routes.js')) 6 | router.use('/api/info', require('./controllers/info.js')) 7 | 8 | module.exports = router; -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iron-io/functions-ui/3898929278367260357658368bc6ae2e21a2a0a7/server/server.js -------------------------------------------------------------------------------- /stylelint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "rules": { 3 | "at-rule-empty-line-before": [ "always", { 4 | except: [ "blockless-group", "first-nested" ], 5 | ignore: ["after-comment"], 6 | } ], 7 | "at-rule-name-case": "lower", 8 | "at-rule-name-space-after": "always-single-line", 9 | "at-rule-semicolon-newline-after": "always", 10 | "block-closing-brace-newline-after": "always", 11 | "block-closing-brace-newline-before": "always-multi-line", 12 | "block-closing-brace-space-before": "always-single-line", 13 | "block-no-empty": true, 14 | "block-opening-brace-newline-after": "always-multi-line", 15 | "block-opening-brace-space-after": "always-single-line", 16 | "block-opening-brace-space-before": "always", 17 | "color-hex-case": "upper", 18 | "color-hex-length": "short", 19 | "color-no-invalid-hex": true, 20 | "comment-empty-line-before": [ "always", { 21 | except: ["first-nested"], 22 | ignore: ["stylelint-commands"], 23 | } ], 24 | "comment-whitespace-inside": "always", 25 | "declaration-bang-space-after": "never", 26 | "declaration-bang-space-before": "always", 27 | "declaration-block-no-ignored-properties": true, 28 | "declaration-block-no-shorthand-property-overrides": true, 29 | "declaration-block-semicolon-newline-after": "always-multi-line", 30 | "declaration-block-semicolon-space-after": "always-single-line", 31 | "declaration-block-semicolon-space-before": "never", 32 | "declaration-block-single-line-max-declarations": 1, 33 | "declaration-block-trailing-semicolon": "always", 34 | "declaration-colon-newline-after": "always-multi-line", 35 | // "declaration-colon-space-after": "always", 36 | "declaration-colon-space-before": "never", 37 | "function-calc-no-unspaced-operator": true, 38 | "function-comma-newline-after": "always-multi-line", 39 | "function-comma-space-after": "never", 40 | "function-comma-space-before": "never", 41 | "function-linear-gradient-no-nonstandard-direction": true, 42 | "function-max-empty-lines": 0, 43 | "function-name-case": "lower", 44 | "function-parentheses-newline-inside": "always-multi-line", 45 | "function-parentheses-space-inside": "never-single-line", 46 | "function-whitespace-after": "always", 47 | "indentation": 2, 48 | "keyframe-declaration-no-important": true, 49 | "length-zero-no-unit": true, 50 | "max-empty-lines": 1, 51 | "media-feature-colon-space-after": "always", 52 | "media-feature-colon-space-before": "never", 53 | "media-feature-no-missing-punctuation": true, 54 | "media-feature-range-operator-space-after": "always", 55 | "media-feature-range-operator-space-before": "always", 56 | "media-query-list-comma-newline-after": "always-multi-line", 57 | "media-query-list-comma-space-after": "always-single-line", 58 | "media-query-list-comma-space-before": "never", 59 | //"media-feature-parentheses-space-inside": "never", 60 | "no-eol-whitespace": true, 61 | "no-extra-semicolons": true, 62 | "no-invalid-double-slash-comments": true, 63 | //"no-missing-end-of-source-newline": true, 64 | "number-leading-zero": "never", 65 | "number-no-trailing-zeros": true, 66 | "property-case": "lower", 67 | "rule-non-nested-empty-line-before": [ "always-multi-line", { 68 | ignore: ["after-comment"], 69 | } ], 70 | "selector-attribute-brackets-space-inside": "never", 71 | "selector-attribute-operator-space-after": "never", 72 | "selector-attribute-operator-space-before": "never", 73 | "selector-combinator-space-after": "always", 74 | "selector-combinator-space-before": "always", 75 | "selector-list-comma-newline-after": "always-multi-line", 76 | "selector-list-comma-space-before": "never", 77 | "selector-max-empty-lines": 0, 78 | "selector-pseudo-class-case": "lower", 79 | "selector-pseudo-class-no-unknown": true, 80 | "selector-pseudo-class-parentheses-space-inside": "never", 81 | "selector-pseudo-element-case": "lower", 82 | "selector-pseudo-element-colon-notation": "double", 83 | "selector-pseudo-element-no-unknown": true, 84 | "selector-type-case": "lower", 85 | // "selector-type-no-unknown": true, 86 | "shorthand-property-no-redundant-values": true, 87 | "string-no-newline": true, 88 | "unit-case": "lower", 89 | "unit-no-unknown": true, 90 | "value-list-comma-newline-after": "always-multi-line", 91 | "value-list-comma-space-after": "always-single-line", 92 | "value-list-comma-space-before": "never", 93 | }, 94 | } 95 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const fs = require('fs'); 4 | const extractTextPlugin = require("extract-text-webpack-plugin"); 5 | 6 | // style 7 | const postcssImport = require('postcss-import'); 8 | const postcssURL = require('postcss-url'); 9 | const cssnext = require('postcss-cssnext'); 10 | const cssnano = require('cssnano'); 11 | const fontMagician = require('postcss-font-magician'); 12 | 13 | 14 | var nodeModules = {}; 15 | fs.readdirSync('node_modules') 16 | .filter(function(x) { 17 | return ['.bin'].indexOf(x) === -1; 18 | }) 19 | .forEach(function(mod) { 20 | nodeModules[mod] = 'commonjs ' + mod; 21 | }); 22 | 23 | module.exports = [ 24 | { 25 | name: 'server', 26 | entry: './server/server.js', 27 | target: 'node', 28 | output: { 29 | path: path.join(__dirname, 'build'), 30 | filename: 'backend.js' 31 | }, 32 | externals: nodeModules 33 | }, 34 | { 35 | name: 'client', 36 | entry: [ 37 | 'jquery/', 38 | './client/client.js', 39 | ], 40 | // target: 'web', // by default 41 | output: { 42 | path: path.join(__dirname, 'public', 'build'), 43 | filename: 'app.js', 44 | }, 45 | resolve: { 46 | extensions: ['', '.js', '.vue', '.json'], 47 | fallback: [path.join(__dirname, './node_modules')], 48 | 49 | alias: { 50 | 'vue$': path.join(__dirname, './node_modules/vue/dist/vue.common.js'), 51 | 'vue-router$': path.join(__dirname, './node_modules/vue-router/dist/vue-router.common.js'), 52 | } 53 | }, 54 | module: { 55 | preLoaders: [ 56 | { test: /\.css$/, loader: 'stylelint' } 57 | ], 58 | loaders: [ 59 | // { 60 | // test:/bootstrap-sass[\/\\]assets[\/\\]javascripts[\/\\]/, 61 | // loader: 'imports?jQuery=jquery' 62 | // }, 63 | { 64 | test: /\.vue$/, 65 | loader: 'vue', 66 | options: { 67 | // vue-loader options go here 68 | } 69 | }, 70 | 71 | // Extract css files 72 | { 73 | test: /\.css$/, 74 | loader: extractTextPlugin.extract("style-loader", "css-loader!postcss") 75 | }, 76 | 77 | // ES2015 78 | { 79 | test: /\.js$/, 80 | loader: 'babel', 81 | exclude: /node_modules/ 82 | }, 83 | 84 | { 85 | test: /\.woff2?(\?v=[0-9]\.[0-9]\.[0-9])?$/, 86 | loader: "url?limit=10000" 87 | }, 88 | 89 | { 90 | test: /\.(ttf|eot|svg)(\?[\s\S]+)?$/, 91 | loader: 'file' 92 | } 93 | ] 94 | }, 95 | plugins: [ 96 | new extractTextPlugin("app.css", { 97 | allChunks: true 98 | }) 99 | ], 100 | stylelint: { 101 | configFile: path.join(__dirname, './stylelint.config.js'), 102 | configOverrides: { 103 | rules: { 104 | // Your rule overrides here 105 | } 106 | } 107 | }, 108 | postcss: [ 109 | // inline @import need to merge vars 110 | postcssImport(), 111 | fontMagician({ 112 | hosted: path.join(__dirname, './public/fonts/Roboto') 113 | }), 114 | postcssURL(), 115 | require('postcss-hexrgba')(), 116 | cssnext() 117 | ] 118 | } 119 | ]; 120 | --------------------------------------------------------------------------------