diff --git a/CHANGELOG.md b/CHANGELOG.md index 348b4502..a2ad79db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.0.15] - 2023-08-09 + +### Added + +- Installation video to documentation +- add fnGetNumberedName as local function + +### Fixed + +- Table expression issue fixed + ## [4.0.14] - 2023-08-03 ### Added @@ -559,7 +570,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow change password for current local user - Start tracking versions -[Unreleased]: https://github.com/ansibleguy76/ansibleforms/compare/4.0.14...HEAD +[Unreleased]: https://github.com/ansibleguy76/ansibleforms/compare/4.0.15...HEAD + +[4.0.15]: https://github.com/ansibleguy76/ansibleforms/compare/4.0.14...4.0.15 [4.0.14]: https://github.com/ansibleguy76/ansibleforms/compare/4.0.13...4.0.14 diff --git a/app_versions.gradle b/app_versions.gradle index a3d1b02c..7fb326b0 100644 --- a/app_versions.gradle +++ b/app_versions.gradle @@ -1,2 +1,2 @@ -ext.version_code = 40014 -ext.version_name = "4.0.14" +ext.version_code = 40015 +ext.version_name = "4.0.15" diff --git a/client/package.json b/client/package.json index 0ac3f2a4..eb0e8167 100644 --- a/client/package.json +++ b/client/package.json @@ -1,12 +1,12 @@ { "name": "ansible_forms_vue", - "version": "4.0.14", + "version": "4.0.15", "private": true, "scripts": { "serve": "vue-cli-service serve", "start": "nodemon --exec 'vue-cli-service serve --mode development'", "remove": "rm -rdf ./../server/views || exit 0", - "copy": "cp -R dist ../server/views", + "copy": "cp -R dist ../server/views && cp ./../docs/_data/help.yaml ../server", "bundle": "npm run build && npm run remove && npm run copy", "build": "vue-cli-service build", "lint": "vue-cli-service lint" diff --git a/client/src/components/Form.vue b/client/src/components/Form.vue index da3a0bf8..a52972ab 100644 --- a/client/src/components/Form.vue +++ b/client/src/components/Form.vue @@ -904,6 +904,7 @@ // instead of taking the default value, see if it needs to be evaluated // allowing dynamic defaults getDefaultValue(fieldname,value){ + if(value!=undefined){ var _value = this.replacePlaceholderInString(value).value @@ -911,7 +912,7 @@ if(this.fieldOptions[fieldname].evalDefault){ var r=undefined try{ - r=eval(_value) + r=Helpers.evalSandbox(_value) return r }catch(e){ console.log(`Error evaluating default value : ${e}`) @@ -1301,100 +1302,6 @@ //---------------------------------------------------------------- startDynamicFieldsLoop() { // this.$toast("Start eval") - function matchRuleShort(str, rule) { - var escapeRegex = (str) => str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); // eslint-disable-line - return new RegExp("^" + rule.split("*").map(escapeRegex).join(".*") + "$").test(str); // eslint-disable-line - } - - function compareProps(x1,x2,p){ - for(let i=0;i<p.length;i++){ - const x=p[i] - - if(!matchRuleShort(x1[x],x2[x])){ - return false - } - } - return true - } - - function comparePropsRegex(x1,x2,p){ - for(let i=0;i<p.length;i++){ - const x=p[i] - - if(!x1[x].match(x2[x])){ - return false - } - } - return true - } - - function dynamicSort(property) { - var sortOrder = 1; - if(property[0] === "-") { - sortOrder = -1; - property = property.substr(1); - } - return function (a,b) { - /* next line works with strings and numbers, - * and you may want to customize it to your needs - */ - var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0; - return result * sortOrder; - } - } - - function dynamicSortMultiple() { - /* - * save the arguments object as it will be overwritten - * note that arguments object is an array-like object - * consisting of the names of the properties to sort by - */ - var props = arguments; - return function (obj1, obj2) { - var i = 0, result = 0, numberOfProperties = props.length; - /* try getting a different result from 0 (equal) - * as long as we have extra properties to compare - */ - while(result === 0 && i < numberOfProperties) { - result = dynamicSort(props[i])(obj1, obj2); - i++; - } - return result; - } - } - - class fnArray extends Array { - sortBy(...args) { - return this.sort(dynamicSortMultiple(...args)); - } - distinctBy(...args) { - return this.filter((a, i) => this.findIndex((s) => compareProps(a,s,args)) === i) - } - filterBy(...args) { - let props=Object.keys(args[0]) - return this.filter((x)=>{ - return compareProps(x,args[0],props) - }) - } - regexBy(...args) { - let props=Object.keys(args[0]) - return this.filter((x)=>{ - return comparePropsRegex(x,args[0],props) - }) - } - selectAttr(...args) { - let props=Object.keys(args[0]) - - return this.map((x)=>{ - let o = {} - for(let i=0;i<props.length;i++){ - o[props[i]]=x[args[0][props[i]]] - } - return o - }) - } - } - // console.log("invoking field expressions and queries") var ref=this; // a reference to 'this' ref.watchdog=0 // a counter how many times we retry to find a value @@ -1435,18 +1342,18 @@ // check if direct object attempt // console.log(placeholderCheck.value) if(placeholderCheck.value.at(0)=="{" && placeholderCheck.value.at(-1)=="}"){ - result=eval(`Object.assign(${placeholderCheck.value})`) + result=Helpers.evalSandbox(`Object.assign(${placeholderCheck.value})`) }else{ - result=eval(placeholderCheck.value) + result=Helpers.evalSandbox(placeholderCheck.value) } if(item.type=="expression" || item.type=="html") Vue.set(ref.form, item.name, result); if((item.type=="query")||(item.type=="enum")) Vue.set(ref.queryresults, item.name, [].concat(result)); // table is special. if external data is passed. we take that instead of results. - if(item.type=="table" && !ref.defaults(item.name)){ + if(item.type=="table" && !ref.defaults[item.name]){ Vue.set(ref.form, item.name, [].concat(result)); } - if(item.type=="table" && ref.defaults(item.name)){ + if(item.type=="table" && ref.defaults[item.name]){ Vue.set(ref.form, item.name, [].concat(ref.defaults[item.name])); } if(placeholderCheck.hasPlaceholders){ // if placeholders were found we set this a variable dynamic field. diff --git a/client/src/lib/Helpers.js b/client/src/lib/Helpers.js index 1aa2da1c..6087d91b 100644 --- a/client/src/lib/Helpers.js +++ b/client/src/lib/Helpers.js @@ -23,7 +23,161 @@ var Helpers = { } else{ return `$(${match})` // return original } + }, + evalSandbox(expression){ + // local autonumbering + function fnGetNumberedName(names,pattern,value,fillgap=false){ + var nr=null + var nrsequence + var regex + var nrs + var re=new RegExp("[^\#]*(\#+)[^\#]*") // eslint-disable-line + var patternmatch=re.exec(pattern) + if(!names || !Array.isArray(names)){ + console.log("fnGetNumberedName, No input or no array") + return value + } + if(patternmatch && patternmatch.length==2){ + nrsequence=patternmatch[1] + regex="^" + pattern.replace(nrsequence,"([0-9]{"+nrsequence.length+"})") + "$" + nrs=names.map((item)=>{ + var regexp=new RegExp(regex,"g"); + var matches=regexp.exec(item) + if(matches && matches.length==2){ + return parseInt(matches[1]) + }else{ + null + } + }).filter((item)=>(item)) + var gaps=nrs.reduce(function(acc, cur, ind, arr) { + var diff = cur - arr[ind-1]; + if (diff > 1) { + var i = 1; + while (i < diff) { + acc.push(arr[ind-1]+i); + i++; + } + } + return acc; + }, []); + var max=(nrs.length>0)?Math.max(...nrs):null + var gap=(gaps.length>0)?Math.min(...gaps):null + if(max){ + nr=max+1 + } + if(fillgap && gap){ + nr=gap + } + if(nr){ + var tmp = pattern.replace(nrsequence,nr.toString().padStart(nrsequence.length,"0")) + return tmp + }else{ + console.log("fnGetNumberedName, no pattern matches found in the list") + return value + } + }else{ + console.log("fnGetNumberedName, no pattern found, use ### for numbers") + return value + } + } + function matchRuleShort(str, rule) { + var escapeRegex = (str) => str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); // eslint-disable-line + return new RegExp("^" + rule.split("*").map(escapeRegex).join(".*") + "$").test(str); // eslint-disable-line + } + + function compareProps(x1,x2,p){ + for(let i=0;i<p.length;i++){ + const x=p[i] + + if(!matchRuleShort(x1[x],x2[x])){ + return false + } + } + return true + } + + function comparePropsRegex(x1,x2,p){ + for(let i=0;i<p.length;i++){ + const x=p[i] + + if(!x1[x].match(x2[x])){ + return false + } + } + return true + } + + function dynamicSort(property) { + var sortOrder = 1; + if(property[0] === "-") { + sortOrder = -1; + property = property.substr(1); + } + return function (a,b) { + /* next line works with strings and numbers, + * and you may want to customize it to your needs + */ + var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0; + return result * sortOrder; + } + } + + function dynamicSortMultiple() { + /* + * save the arguments object as it will be overwritten + * note that arguments object is an array-like object + * consisting of the names of the properties to sort by + */ + var props = arguments; + return function (obj1, obj2) { + var i = 0, result = 0, numberOfProperties = props.length; + /* try getting a different result from 0 (equal) + * as long as we have extra properties to compare + */ + while(result === 0 && i < numberOfProperties) { + result = dynamicSort(props[i])(obj1, obj2); + i++; + } + return result; + } + } + + + class fnArray extends Array { + sortBy(...args) { + return this.sort(dynamicSortMultiple(...args)); + } + distinctBy(...args) { + return this.filter((a, i) => this.findIndex((s) => compareProps(a,s,args)) === i) + } + filterBy(...args) { + let props=Object.keys(args[0]) + return this.filter((x)=>{ + return compareProps(x,args[0],props) + }) + } + regexBy(...args) { + let props=Object.keys(args[0]) + return this.filter((x)=>{ + return comparePropsRegex(x,args[0],props) + }) + } + selectAttr(...args) { + let props=Object.keys(args[0]) + + return this.map((x)=>{ + let o = {} + for(let i=0;i<props.length;i++){ + o[props[i]]=x[args[0][props[i]]] + } + return o + }) + } + } + return eval(expression) } + + }; export default Helpers diff --git a/docs/_config.yml b/docs/_config.yml index 8faf1de5..9d9db24e 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -32,25 +32,35 @@ doks: item_icon: brackets-curly - item_name: FAQ item_url: '/faq' - item_icon: square-question - github_url: https://github.com/ansibleguy76 + item_icon: square-question + social: + - item_icon: github + item_name: Ansible Forms on Github + item_url: https://github.com/ansibleguy76 + - item_icon: docker + item_name: Ansible Forms on Docker Hub + item_url: https://hub.docker.com/r/ansibleguy/ansibleforms + - item_icon: youtube + item_name: Video's about ansible and Ansible Forms + item_url: https://www.youtube.com/@ansibleguy76/videos footer: content: logo: text: image: true copyright: Copyright © 2023. - AnsibleForms <br>All rights reserved. - social_list: - #- network_name: facebook - # profile_url: '#' - #- network_name: twitter - # profile_url: '#' - #- network_name: instagram - # profile_url: '#' - #- network_name: youtube - # profile_url: '#' + social: + - item_icon: github + item_name: Ansible Forms on Github + item_url: https://github.com/ansibleguy76 + - item_icon: docker + item_name: Ansible Forms on Docker Hub + item_url: https://hub.docker.com/r/ansibleguy/ansibleforms + - item_icon: youtube + item_name: Video's about ansible and Ansible Forms + item_url: https://www.youtube.com/@ansibleguy76/videos google_analytics: - tracking_code: # Add your Google Analytics tracking code to activate Google Analytics + tracking_code: G-P2KE4SYQ47 comments: disqus_forum_shortname: # Add your disqus forum shortname to activate comments diff --git a/docs/doks-theme/_includes/google-analytics.html b/docs/doks-theme/_includes/google-analytics.html index d8521845..b71ffe9c 100644 --- a/docs/doks-theme/_includes/google-analytics.html +++ b/docs/doks-theme/_includes/google-analytics.html @@ -1,11 +1,11 @@ {% if jekyll.environment == "production" and site.doks.google_analytics.tracking_code %} +<!-- Google tag (gtag.js) --> +<script async src="https://www.googletagmanager.com/gtag/js?id={{ site.doks.google_analytics.tracking_code }}"></script> <script> - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); - ga('create', '{{ site.doks.google_analytics.tracking_code }}', 'auto'); - ga('send', 'pageview'); + gtag('config', '{{ site.doks.google_analytics.tracking_code }}'); </script> {% endif %} diff --git a/docs/doks-theme/_includes/site-footer.html b/docs/doks-theme/_includes/site-footer.html index ef551b6a..c0fbec0a 100644 --- a/docs/doks-theme/_includes/site-footer.html +++ b/docs/doks-theme/_includes/site-footer.html @@ -1,4 +1,4 @@ -{% if site.doks.footer.content.logo.image or site.doks.footer.content.logo.text or site.doks.footer.content.copyright or site.doks.footer.social_list %} +{% if site.doks.footer.content.logo.image or site.doks.footer.content.logo.text or site.doks.footer.content.copyright or site.doks.footer.social %} <footer class="site-footer"> <div class="container"> <div class="row"> @@ -15,13 +15,13 @@ <p class="site-footer__copyright">{{ site.doks.footer.content.copyright }}</p> {% endif %} </div><!-- /.col --> - {% if site.doks.footer.social_list %} + {% if site.doks.footer.social %} <div class="col-sm-6 align-right"> <ul class="social-list"> - {% for item in site.doks.footer.social_list %} + {% for item in site.doks.footer.social %} <li> - <a href="{{ item.profile_url }}" target="_blank" class="social-list__item social-list__item--{{ item.network_name }}"> - <i class="icon icon--{{ item.network_name }}"></i> + <a href="{{ item.item_url }}" target="_blank" class="social-list__item social-list__item--{{ item.item_icon }}"> + <i class="fab fa-2x fa-{{ item.item_icon }}"></i> </a> </li> {% endfor %} diff --git a/docs/doks-theme/_includes/site-header.html b/docs/doks-theme/_includes/site-header.html index 37622e05..145be7b5 100644 --- a/docs/doks-theme/_includes/site-header.html +++ b/docs/doks-theme/_includes/site-header.html @@ -25,7 +25,9 @@ {% for item in site.doks.header.nav %} <li><a href="{% if jekyll.environment == 'production' %}{{ site.doks.baseurl }}{% endif %}{{ item.item_url }}" {% if item.item_url==url %}class="active"{% endif %}>{{ item.item_name }}</a></li> {% endfor %} - <li><a href="{{ site.doks.header.github_url }}" target="_blank"><i class="fab fa-github fa-lg"></i></a></li> + {% for item in site.doks.header.social %} + <li><a href="{{ item.item_url }}" title="{{ item.item_name }}" target="_blank"><i class="fab fa-{{ item.item_icon }} fa-lg"></i></a></li> + {% endfor %} </ul><!-- /.site-header__nav --> <button class="offcanvas-toggle visible-xs"> <span></span> diff --git a/docs/doks-theme/_sass/components/_social-list.scss b/docs/doks-theme/_sass/components/_social-list.scss index 19f56b26..cdde84b8 100644 --- a/docs/doks-theme/_sass/components/_social-list.scss +++ b/docs/doks-theme/_sass/components/_social-list.scss @@ -41,6 +41,10 @@ color: #205081; } + &.social-list__item--docker { + color: #339def; + } + &.social-list__item--codepen { color: #494949; } diff --git a/docs/doks-theme/_sass/custom/_custom.scss b/docs/doks-theme/_sass/custom/_custom.scss index 66bd6800..38eed94d 100644 --- a/docs/doks-theme/_sass/custom/_custom.scss +++ b/docs/doks-theme/_sass/custom/_custom.scss @@ -196,6 +196,13 @@ div.highlighter-rouge { .mt-2{ margin-top:0.4em; } +.ml-2{ + margin-left:0.4em; +} + +.m-0{ + margin:0; +} table td{ diff --git a/docs/doks-theme/assets/files/docker and ansibleforms.pdf b/docs/doks-theme/assets/files/docker and ansibleforms.pdf new file mode 100644 index 00000000..d5bfb8f7 Binary files /dev/null and b/docs/doks-theme/assets/files/docker and ansibleforms.pdf differ diff --git a/docs/forms.md b/docs/forms.md index b1169082..8b5913f5 100644 --- a/docs/forms.md +++ b/docs/forms.md @@ -9,7 +9,7 @@ title: First time use and Forms.yaml description: | Configuring AnsibleForms is done using 1 or more yaml files.<br> The master yaml file is <code>forms.yaml</code> and is the heart of your forms.<br><br> - This file is so important, this is likely the most important part of this help documentation to get you started. + This file is so important, this is likely the most important part of this help documentation to get you started. # Micro navigation micro_nav: true @@ -35,6 +35,10 @@ page_nav: {{ formsyaml.description }} +<a href="https://www.youtube.com/watch?v=lIhYZ9Et5Ic" class="btn btn--dark btn--rounded btn--w-icon"> + <span class="icon"><i class="fat fa-video"></i></span> <span class="ml-2">VIDEO : Create you first form</span> +</a> + The file below is a minimal sample forms.yaml file to start with. If has only the required `default` category, the required `admin` and `public` roles and a very simple sample form with 1 text field. diff --git a/docs/index.md b/docs/index.md index 558d19e5..8d7a55bc 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,6 +13,10 @@ buttons: content: AnsibleForms on Github url: 'https://github.com/ansibleguy76/ansibleforms' external_url: true + - icon: youtube + content: AnsibleForms on Youtube + url: 'https://www.youtube.com/@ansibleguy76/videos' + external_url: true # Author box author: diff --git a/docs/installation.md b/docs/installation.md index 9e1e2375..f65cf197 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -40,7 +40,7 @@ The recommended way to install AnsibleForms is using `Docker Compose`, which is <div class="callout callout--danger"> <p><strong>Note</strong> Using docker and docker-compose for the first time, requires some basic linux skills and some knowledge about containers</p> - <p><a href="#todo">Download this document</a> to get you kick-started with containers and Docker</p> + <p><a href="/doks-theme/assets/files/docker and ansibleforms.pdf">Download this document</a> to get you kick-started with containers and Docker</p> </div> ## Prerequisites @@ -50,60 +50,75 @@ The recommended way to install AnsibleForms is using `Docker Compose`, which is * **Install Docker** : You need to have a container environment, and in this example we use Docker * **Install Docker Compose** : To spin-up AnsibleForms and MySql with docker, using a few simple configuration-files, we need Docker Compose -<div class="callout callout--info"> - <p><strong>Note</strong> The steps below are also explained on Github. - <br><a class="btn btn-md" href="https://github.com/ansibleguy76/ansibleforms-docker">Read on Github</a></p> -</div> - <div class="callout callout--warning"> - <p><strong>Linux Flavour</strong> The examples below are for Redhat/CentOs, use apt-get or other package managers for your flavour of linux.</p> + <p><strong>Linux Flavour</strong> The examples below are for Redhat/CentOs and Ubuntu/Debian, use apt-get or other package managers for your flavour of linux.</p> </div> - +<a href="https://www.youtube.com/watch?v=IHGIggmtTuA" class="btn btn--dark btn--rounded btn--w-icon"> + <span class="icon"><i class="fat fa-video"></i></span> <span class="ml-2"> VIDEO How to install AnsibleForms</span> +</a> ## Choose a location to install ```bash -mkdir /srv/apps +sudo mkdir /srv/apps cd /srv/apps ``` ## Clone the docker-compose project ```bash -yum install -y git -git init -git clone https://github.com/ansibleguy76/ansibleforms-docker.git +# centos +sudo yum install -y git + +# ubuntu +sudo apt-get install -y git + +sudo git init +sudo git clone https://github.com/ansibleguy76/ansibleforms-docker.git cd ansibleforms-docker ``` -## Set permissions +## Set proper permissions ```bash # write access will be needed on the datafolder -chmod -R 664 ./data +sudo chmod -R 664 ./data # the mysql init folder needs execute rights -chmod -R +x ./data/mysql/init/ +sudo chmod -R +x ./data/mysql/init/ ``` -## Install Docker +## Install Docker and docker-compose + +[Docker installation manuals](https://docs.docker.com/engine/install) ```bash +# centos yum install -y docker-ce docker-ce-cli containerd.io docker-compose +# ubuntu +sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-compose + # the below is to ensure dns works properly inside the dockerimages -mkdir -p /etc/docker -echo "{\"dns-opts\":[\"ndots:15\"]}" > /etc/docker/daemon.json +sudo mkdir -p /etc/docker +echo "{\"dns-opts\":[\"ndots:15\"]}" | sudo tee /etc/docker/daemon.json # start docker permanently as a service -systemctl start docker -systemctl enable docker +sudo systemctl start docker +sudo systemctl enable docker ``` ## Customize -Feel free to look at the variables in the `.env` file and `docker-compose.yaml` file. +Feel free to look at the variables in the `.env` file and `docker-compose.yaml` file. +[Learn more about the environment variables](/customization) + +## Start docker-compose project + +```bash +sudo docker-compose up -d +``` ## Test the application @@ -215,22 +230,22 @@ The server app (express.js) will cover authentication, background database conne ```bash # remove nodejs if needed -yum remove -y nodejs +sudo yum remove -y nodejs # get repro -yum install -y gcc-c++ make +sudo yum install -y gcc-c++ make curl -sL https://rpm.nodesource.com/setup_14.x | sudo -E bash - # install nodejs -yum install -y nodejs +sudo yum install -y nodejs # create holder folder (can be custom) -mkdir /srv/apps +sudo mkdir /srv/apps cd /srv/apps # grab the code from github -yum install -y git -git init -git clone https://github.com/ansibleguy76/ansibleforms.git +sudo yum install -y git +sud ogit init +sudo git clone https://github.com/ansibleguy76/ansibleforms.git # enter the app project cd ansibleforms @@ -244,11 +259,11 @@ First we install all nodejs dependencies for both client & server ```bash cd server -npm install +sudo npm install cd .. cd client -npm install +sudo npm install cd .. ``` @@ -258,12 +273,12 @@ This application comes with an `.env.example` file that you must copy to `.env.d ```bash cd client -cp .env.example .env.development +sudo cp .env.example .env.development cd .. cd server -cp .env.example .env.development -cp ./persistent/forms.yaml.example ./persistent/forms.yaml +sudo cp .env.example .env.development +sudo cp ./persistent/forms.yaml.example ./persistent/forms.yaml ``` ## Modify the .env.development (or .env.production) to your needs @@ -297,7 +312,7 @@ When you test a vue2 application (client application), it typically spins up a t ```bash cd client -npm run start +sudo npm run start ``` #### Run compiled in development @@ -308,7 +323,7 @@ First we compile the client code, and bundle it in the server code ```bash cd client -npm run bundle +sudo npm run bundle ``` Then we run the server code in development mode. `npm run dev` will also copy the `.env.development` file into the `./dist` folder, so make sure it's there ! @@ -316,7 +331,7 @@ Then we run the server code in development mode. `npm run dev` will also copy t ```bash cd .. cd server -npm run dev +sudo npm run dev ``` ### Run in production with PM2 @@ -324,14 +339,14 @@ npm run dev Running the application in the commandline, makes it fragile when something goes wrong. We need an environment where the nodejs application can run when logged of, where it can be monitored and even restarted in case of a crash. That's were PM2 comes in. (https://pm2.keymetrics.io/) ```bash -npm install -g pm2 +sudo npm install -g pm2 ``` We again compile the client code and bundle it in the server code ```bash cd client -npm run bundle +sudo npm run bundle ``` We now compile the server code, but don't start it. @@ -339,20 +354,20 @@ We now compile the server code, but don't start it. ```bash cd .. cd server -npm run build +sudo npm run build ``` Then we copy a production ready environment file. (change it to fit your production environment) ```bash -cp .env.example ./dist/.env.production +sudo cp .env.example ./dist/.env.production ``` Then we start it in PM2. ```bash cd dist -pm2 start ecosystem.config.js --env production +sudo pm2 start ecosystem.config.js --env production ``` Once started diff --git a/server/config/app.config.js b/server/config/app.config.js index 16715d60..557a2cff 100644 --- a/server/config/app.config.js +++ b/server/config/app.config.js @@ -6,7 +6,7 @@ var app_config = { showDesigner: process.env.SHOW_DESIGNER ?? true, formsPath: process.env.FORMS_PATH || path.resolve(__dirname + "/../persistent/forms.yaml"), lockPath: process.env.LOCK_PATH || path.resolve(__dirname + "/../persistent/ansibleForms.lock"), - helpPath: path.resolve(__dirname + "/../../docs/_data/help.yaml"), + helpPath: path.resolve(__dirname + "/../help.yaml"), encryptionSecret: ((process.env.ENCRYPTION_SECRET + "vOVH6sdmpNWjRRIqCc7rdxs01lwHzfr3").substring(0,32)) || "vOVH6sdmpNWjRRIqCc7rdxs01lwHzfr3", homePath: process.env.HOME_PATH || require('os').homedir(), repoPath: process.env.REPO_PATH || path.resolve(__dirname + "/../persistent/repositories"), diff --git a/server/package.json b/server/package.json index c6304018..432c7951 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "ansible_forms", - "version": "4.0.14", + "version": "4.0.15", "repository": { "type": "git", "url": "git://github.com/ansibleguy76/ansibleforms.git" diff --git a/server/src/swagger.json b/server/src/swagger.json index de471b1c..7020459c 100644 --- a/server/src/swagger.json +++ b/server/src/swagger.json @@ -2,7 +2,7 @@ "swagger": "2.0", "info": { "description": "This is the swagger interface for AnsibleForms.\r\nUse the `/auth/login` api with basic authentication to obtain a JWT token.\r\nThen use the access token, prefixed with the word '**Bearer**' to use all other api's.\r\nNote that the access token is limited in time. You can then either login again and get a new set of tokens or use the `/token` api and the refresh token to obtain a new set (preferred).", - "version": "4.0.14", + "version": "4.0.15", "title": "AnsibleForms", "contact": { "email": "info@ansibleforms.com"