Initial Commit

master
raj 3 years ago
commit 794573d7a0

@ -0,0 +1,54 @@
{
"name": "armintatankapi",
"version": "1.0.0",
"description": "Arminta Tank API",
"main": "index.js",
"scripts": {
"start": "./node_modules/nodemon/bin/nodemon.js ./src/index.js",
"start:dev": "nodemon ./src/index.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"@adminjs/fastify": "^3.0.1",
"@adminjs/mongoose": "^3.0.1",
"@fastify/env": "^3.0.0",
"@fastify/jwt": "^6.5.0",
"@fastify/view": "^7.1.2",
"adminjs": "^6.7.2",
"bcrypt": "^5.0.0",
"body-parser": "^1.19.0",
"boom": "^7.3.0",
"chalk": "^4.1.0",
"cors": "^2.8.5",
"csvjson": "^5.1.0",
"csvtojson": "^2.0.10",
"email-validator": "^2.0.4",
"fastify": "^3.29.4",
"fastify-auth": "^1.0.1",
"fastify-cors": "^5.2.0",
"fastify-env": "^2.2.0",
"fastify-formbody": "^5.3.0",
"fastify-jwt": "^4.2.0",
"fastify-multer": "^2.0.2",
"fastify-multipart": "^5.4.0",
"fastify-static": "^4.7.0",
"fastify-swagger": "^5.2.0",
"form-data": "^2.3.3",
"generate-password": "^1.6.0",
"gridfile": "^1.1.3",
"libphonenumber-js": "^1.9.6",
"moment": "^2.29.4",
"mongoose": "^5.11.8",
"multer": "^1.4.5-lts.1",
"multer-gridfs-storage": "^5.0.2",
"nodemon": "^2.0.20",
"nunjucks": "^3.2.3",
"point-of-view": "^5.3.0",
"raw-body": "^2.4.1",
"request": "^2.88.2",
"twilio": "^2.5.2",
"urllib": "^2.38.0",
"xml2js": "^0.4.23"
}
}

@ -0,0 +1,65 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
.btn{
width: 90px;
height: 32px;
font-size:13px;
background-color: #47AFE8;
color: #fff;
border: none;
margin: 0 2px;
border-radius: 2px;
cursor:pointer;
outline:none;
}
.txt{
width: 100px;
height: 30px;
font-size:13px;
background-color: transparent;
border: 1px solid #47AFE8;
color: #fff;
padding:0 8px;
margin: 0 2px;
border-radius: 2px;
outline:none;
}
rapi-doc{
width:100%;
}
</style>
<script type="module" src="https://unpkg.com/rapidoc/dist/rapidoc-min.js"></script>
<script>
function setApiKey(){
const docEl = document.getElementById('thedoc');
const keyInputEl = document.getElementById('key-val-input');
docEl.setAttribute('api-key-name','Authorization');
docEl.setAttribute('api-key-location','header');
docEl.setAttribute('api-key-value',keyInputEl.value);
}
</script>
</head>
<body>
<rapi-doc
spec-url="http://localhost:3000/documentation/json"
show-header = 'false'
allow-authentication ="true"
>
<!--
below html is custom html that adds an input field and a button in header
on clicking the button the 'api-key-value' is set to the value in input box
-->
<!--div slot='header' style='display:flex; margin:0 16px;'>
<input class='txt' id='key' type='text' >
<button class='btn' onclick='setApiKey()' > Set Key </button >
</div-->
</rapi-doc>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,8 @@
/*
jQuery Wiggle
Author: WonderGroup, Jordan Thomas
URL: http://labs.wondergroup.com/demos/mini-ui/index.html
License: MIT (http://en.wikipedia.org/wiki/MIT_License)
*/
jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('<div class="wiggle-wrap"></div>').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);}
if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});};

File diff suppressed because it is too large Load Diff

@ -0,0 +1,10 @@
/**
* @license
* ========================================================================
* ScrollPos-Styler v0.7.0
* https://github.com/acch/scrollpos-styler
* ========================================================================
* Copyright 2015 Achim Christ
* Licensed under MIT (https://github.com/acch/scrollpos-styler/blob/master/LICENSE)
* ======================================================================== */
var ScrollPosStyler=function(s,e){"use strict";function t(){n=e.pageYOffset;for(var s=[],t=0;i[t];++t){var a=i[t],l=a.getAttribute(m)||o,r=a.classList.contains(c);r&&n>l?s.push({element:a,addClass:f,removeClass:c}):!r&&n<=l&&s.push({element:a,addClass:c,removeClass:f})}return s}function a(s){for(var e=0;s[e];++e){var t=s[e];t.element.classList.add(t.addClass),t.element.classList.remove(t.removeClass)}l=!1}var n=0,l=!1,o=1,r="sps",i=s.getElementsByClassName(r),c="sps--abv",f="sps--blw",m="data-sps-offset",u={init:function(n){l=!0,n&&(n.spsClass&&(r=n.spsClass,i=s.getElementsByClassName(r)),o=n.scrollOffsetY||o,c=n.classAbove||c,f=n.classBelow||f,m=n.offsetTag||m);var u=t();u.length>0?e.requestAnimationFrame(function(){a(u)}):l=!1}};return s.addEventListener("DOMContentLoaded",function(){e.setTimeout(u.init,1)}),e.addEventListener("scroll",function(){if(!l){var s=t();s.length>0&&(l=!0,e.requestAnimationFrame(function(){a(s)}))}}),u}(document,window);

@ -0,0 +1,301 @@
var popupMask;
var popupDialog;
var clientId;
var oauth2KeyName;
var redirect_uri;
function handleLogin() {
var scopes = [];
var auths = window.swaggerUi.api.authSchemes || window.swaggerUi.api.securityDefinitions;
if (auths) {
var key;
var defs = auths;
for (key in defs) {
var auth = defs[key];
if (auth.type === 'oauth2' && auth.scopes) {
oauth2KeyName = key;
var scope;
if (Array.isArray(auth.scopes)) {
// 1.2 support
var i;
for (i = 0; i < auth.scopes.length; i++) {
scopes.push(auth.scopes[i]);
}
}
else {
// 2.0 support
for (scope in auth.scopes) {
scopes.push({scope: scope, description: auth.scopes[scope]});
}
}
}
}
}
popupDialog = $(
[
'<div class="modal fade show api-popup-dialog" id="credentials-modal show" tabindex="-1" role="dialog" aria-labelledby="credentials-modal-label" aria-hidden="false">',
'<div class="modal-dialog">',
'<div class="modal-content">',
'<div class="modal-header">',
'<h5 class="modal-title" id="credentials-modal-label">Select OAuth2.0 Scopes</h5>',
'<button type="button" class="close api-popup-cancel" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>',
'</div>',
'<div class="modal-body">',
'<p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.</p>',
'<div class="oauth2-details"></div>',
'<form>',
'<div class="api-popup-scopes">',
'<div class="scopes">',
'<strong>Scopes:</strong>',
'</div>',
'</div>',
'<form>',
'<p class="error-msg"></p>',
'</div>',
'<div class="modal-footer">',
'<div class="api-popup-actions">',
'<button class="api-popup-cancel btn btn-sm btn-outline-secondary text-uppercase mr-2" type="button">Cancel</button>',
'<button class="api-popup-authbtn btn btn-sm btn-primary text-uppercase" type="button">Authorize</button>',
'</div>',
'</div>',
'</div>',
'</div>',
'</div>',
'<div class="modal-backdrop fade show"></div>'].join(''));
$(document.body).append(popupDialog);
if(oauth2KeyName) {
details = defs[oauth2KeyName];
oauth_dets = popupDialog.find('.oauth2-details').empty();
str = [
'<h5>'+ oauth2KeyName +' ('+details.type+', '+details.flow+')</h5>',
'<pre><code class="hljs rounded">Authorization URL: '+details.authorizationUrl+'</br>',
'Flow: '+details.flow+'</code></pre>',
'<div class="form-group">',
'<label for="input_client_id">client_id</label>',
'<input class="form-control form-control-sm" type="input" id="input_client_id">',
'</div>',
'<h6>Scopes:</h6>'
].join('')
oauth_dets.append(str);
}
popup = popupDialog.find('.scopes').empty();
for (i = 0; i < scopes.length; i++) {
scope = scopes[i];
str = '<button type="button" class="scope btn btn-sm btm-sm btn-outline-secondary mr-1" data-toggle-scope="' + scope.scope + '"><i class="fa fa-check mr-2 d-none"></i>' + scope.scope + '</button> ';
popup.append(str);
}
popupDialog.find('scopes').click(function () {
popupMask.hide();
popupDialog.hide();
popupDialog.empty();
popupDialog = [];
});
popupDialog.find('[data-toggle-scope]').click(function () {
$(this).toggleClass('btn-outline-secondary btn-outline-success');
$(this).find('.fa-check').toggleClass('d-none d-inline');
});
popupDialog.find('button.api-popup-cancel').click(function () {
popupMask.hide();
popupDialog.hide();
popupDialog.empty();
popupDialog.remove();
popupDialog = [];
});
popupDialog.find('#input_client_id').on('change', function () {
clientId = this.value;
});
$('button.api-popup-authbtn').unbind();
popupDialog.find('button.api-popup-authbtn').click(function () {
popupMask.hide();
popupDialog.hide();
var authSchemes = window.swaggerUi.api.authSchemes;
var host = window.location;
var pathname = location.pathname.substring(0, location.pathname.lastIndexOf("/"));
var defaultRedirectUrl = host.protocol + '//' + host.host + pathname + '/assets/o2c.html';
var redirectUrl = window.oAuthRedirectUrl || defaultRedirectUrl;
var url = null;
for (var key in authSchemes) {
if (authSchemes.hasOwnProperty(key)) {
var flow = authSchemes[key].flow;
if (authSchemes[key].type === 'oauth2' && flow && (flow === 'implicit' || flow === 'accessCode')) {
var dets = authSchemes[key];
url = dets.authorizationUrl + '?response_type=' + (flow === 'implicit' ? 'token' : 'code');
window.swaggerUi.tokenName = dets.tokenName || 'access_token';
window.swaggerUi.tokenUrl = (flow === 'accessCode' ? dets.tokenUrl : null);
}
else if (authSchemes[key].grantTypes) {
// 1.2 support
var o = authSchemes[key].grantTypes;
for (var t in o) {
if (o.hasOwnProperty(t) && t === 'implicit') {
var dets = o[t];
var ep = dets.loginEndpoint.url;
url = dets.loginEndpoint.url + '?response_type=token';
window.swaggerUi.tokenName = dets.tokenName;
}
else if (o.hasOwnProperty(t) && t === 'accessCode') {
var dets = o[t];
var ep = dets.tokenRequestEndpoint.url;
url = dets.tokenRequestEndpoint.url + '?response_type=code';
window.swaggerUi.tokenName = dets.tokenName;
}
}
}
}
}
var scopes = [];
var o = $(".scopes:last .active");
for (k = 0; k < o.length; k++) {
var scope = $(o[k]).attr('data-toggle-scope');
if (scopes.indexOf(scope) === -1)
scopes.push(scope);
}
// Implicit auth recommends a state parameter.
var state = Math.random();
window.enabledScopes = scopes;
redirect_uri = redirectUrl;
url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
url += '&client_id=' + encodeURIComponent(clientId);
url += '&scope=' + encodeURIComponent(scopes.join(' '));
url += '&state=' + encodeURIComponent(state);
window.open(url);
});
popupMask.show();
popupDialog.show();
}
function handleLogout() {
for (key in window.authorizations.authz) {
window.authorizations.remove(key)
}
window.enabledScopes = null;
var oauthBtn = $('.api-ic');
oauthBtn.addClass('btn-outline-secondary');
oauthBtn.removeClass('btn-success');
oauthBtn.removeClass('btn-warning');
oauthBtn.text('oauth');
$('#input_apiKey').val('');
}
function initOAuth(opts) {
var o = (opts || {});
var errors = [];
popupMask = (o.popupMask || $('#api-common-mask'));
popupDialog = (o.popupDialog || $('.api-popup-dialog'));
if (errors.length > 0) {
console.log('auth unable initialize oauth: ' + errors);
return;
}
$('pre code').each(function (i, e) {
hljs.highlightBlock(e)
});
var oauthBtn = $('.api-ic');
oauthBtn.unbind();
oauthBtn.click(function (s) {
if ($(s.target).hasClass('btn-outline-secondary'))
handleLogin();
else {
handleLogout();
}
});
}
window.processOAuthCode = function processOAuthCode(data) {
var params = {
'client_id': clientId,
'code': data.code,
'grant_type': 'authorization_code',
'redirect_uri': redirect_uri
};
$.ajax(
{
url: window.swaggerUi.tokenUrl,
type: "POST",
data: params,
success: function (data, textStatus, jqXHR) {
onOAuthComplete(data);
},
error: function (jqXHR, textStatus, errorThrown) {
onOAuthComplete("");
}
});
};
window.onOAuthComplete = function onOAuthComplete(token) {
if (token) {
if (token.error) {
var checkbox = $('input[type=checkbox],.secured')
checkbox.each(function (pos) {
checkbox[pos].checked = false;
});
alert(token.error);
}
else {
var b = token[window.swaggerUi.tokenName];
if (b) {
// if all roles are satisfied
var o = null;
$.each($('.auth #api_information_panel'), function (k, v) {
var children = v;
if (children && children.childNodes) {
var requiredScopes = [];
$.each((children.childNodes), function (k1, v1) {
var inner = v1.innerHTML;
if (inner)
requiredScopes.push(inner);
});
var diff = [];
for (var i = 0; i < requiredScopes.length; i++) {
var s = requiredScopes[i];
if (window.enabledScopes && window.enabledScopes.indexOf(s) == -1) {
diff.push(s);
}
}
if (diff.length > 0) {
o = v.parentNode;
// sorry, not all scopes are satisfied
$(o).find('.api-ic').addClass('btn-warning');
$(o).find('.api-ic').removeClass('btn-outline-secondary');
$(o).find('.api-ic').removeClass('btn-success');
}
else {
o = v.parentNode;
// all scopes are satisfied
$(o).find('.api-ic').addClass('btn-success');
$(o).find('.api-ic').removeClass('btn-outline-secondary');
$(o).find('.api-ic').removeClass('btn-warning');
}
}
});
$('.api-ic').text('signout');
$('#input_apiKey').val(b);
window.swaggerUi.api.clientAuthorizations.add(oauth2KeyName, new SwaggerClient.ApiKeyAuthorization('Authorization', 'Bearer ' + b, 'header'));
}
}
}
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,20 @@
<script>
var qp = null;
if(window.location.hash) {
qp = location.hash.substring(1);
}
else {
qp = location.search.substring(1);
}
qp = qp ? JSON.parse('{"' + qp.replace(/&/g, '","').replace(/=/g,'":"') + '"}',
function(key, value) {
return key===""?value:decodeURIComponent(value) }
):{}
if (window.opener.swaggerUi.tokenUrl)
window.opener.processOAuthCode(qp);
else
window.opener.onOAuthComplete(qp);
window.close();
</script>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,668 @@
* {
box-sizing: border-box;
}
blockquote,
body,
button,
dd,
dl,
figure,
h1,
h2,
h3,
h4,
h5,
h6,
ol,
p,
pre,
ul {
margin: 0;
padding: 0;
}
ol,
ul {
list-style: none;
}
a {
text-decoration: none;
}
button,
select {
border: none;
outline: none;
background: none;
font-family: inherit;
}
a,
button,
input,
select,
textarea {
-webkit-tap-highlight-color: transparent;
}
:root {
overflow-x: hidden;
height: 100%;
}
body {
background: #fff;
min-height: 100%;
display: -ms-flexbox;
display: flex;
-ms-flex-direction: column;
flex-direction: column;
font-size: 62.5%;
font-family: Roboto, Open Sans, Segoe UI, sans-serif;
font-weight: 400;
font-style: normal;
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
font-feature-settings: "pnum";
font-variant-numeric: proportional-nums;
}
.globalContent {
-ms-flex-positive: 1;
flex-grow: 1;
}
@font-face {
font-family: StripeIcons;
src: url(data:application/octet-stream;base64,d09GRk9UVE8AAAZUAAoAAAAAB6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAADKAAAAx8AAAOKkWuAp0dTVUIAAAZIAAAACgAAAAoAAQAAT1MvMgAAAXAAAABJAAAAYGcdjVZjbWFwAAACvAAAAFYAAACUKEhKfWhlYWQAAAD8AAAAMAAAADYJAklYaGhlYQAAAVAAAAAgAAAAJAYoAa5obXR4AAABLAAAACQAAAAoEOAAWW1heHAAAAD0AAAABgAAAAYAClAAbmFtZQAAAbwAAAD%2FAAABuXejDuxwb3N0AAADFAAAABMAAAAg%2F7gAMgAAUAAACgAAeNpjYGRgYABifeaSpHh%2Bm68MzMwHgCIMl08yqyDo%2F95Mkcy8QC4zAxNIFAD8tAiweNpjfMAQyfiAgYEpgoGBcQmQlmFgYPgAZOtAcQZEDgCHaQVGeNpjYGRgYD7z34eBgSmCgeH%2Ff6ZIBqAICuACAHpYBNp42mNgZtzAOIGBlYGDqYDJgYGBwQNCMwYwGDEcA%2FKBUthBqHe4H4MDg4L6Imae%2Fz4MB5jPMGwBCjOC5Bi9mKYAKQUGBgAFHgteAAAAeNplkMFqwkAURU9itBVKF6XLLrLsxiGKMYH0B4IgoqjdRokajAmNUfolhX5Df7IvZhBt5zHMeffduQwDPPCFQbWM81mzyZ3uocEz95qtK0%2BTN140t2jzLk7DaotiEmk2eWSlucErH5otnvjW3OSTH82tSg8n8eaYRkVXOY4TzIaLURB2tDaPi0OSZ3Y9G09tx6lxm5erPDtVA%2BX7wT7axXm5Vmmy7ClXDfqe515CCJkQs%2BFIKk8t6KJwzhUwY8iCkVBI54%2FvvzKXruBAQk6GfZM0ZipKxdfqVpylfErlP11uKHypgL2k7iSz8qxFTSV5SU%2FIlT2gjyfl%2FgKN9EDsAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAHEEACj8QNOBhYGOyDNAYRMQFpBcZL6ov%2F%2Foaw5%2F%2F%2F%2Ff3kvH8iD2McCxExAO1kYWIE2cjCwAwAgUQwvAAB42mNgZgCD%2F1sZjBiwAAAswgHqAHjaNVFbbxNHGN2JMmtlNnIoZFFx1F2nDoTWgJLIhRQqWlRowyXiUkqE1IZLVW0dJzHYjpAhxnbYi8HXdWxsEKCIi0DdqjxVyhOKkBBS%2FdAX%2FkJfmiCe0Gz4orbjLNFo5uj79B19Z85BXGsLhxAiB7ef%2BFmZGj8XaVb9dgdn%2B5Dd02J%2F2JqFIXtpeQ5Lc6h1YzKbXcN2F%2F2qg373wZ3ly%2Bs5gpCwfpO3d8dnXwyfOheJhC9FgsovsanJ4MCuzw84sN%2BBb1Zh34ADfU7za6fq%2Fyl8Ib7K9E4Eo9HgpHLQu6aL45CB8ug6yqAbKIeyqMAhjjD1nM49596hbqQgHf2B%2Fm5xt3S8sqXlORFe%2FHuSvuD3vesUQ4eVxjgEfm08PWK5%2FoF14lBjDAJvXI0xMRS0%2BMVjbGLIbzV%2BP2y5aOC46IfAb7TzT5cFbSJwEKCc9eXifGgqtOBahN3vWy7aOS76f1zkrVNiaNw1NIpfhyBg8X%2FN428t3v2KJl6KtVqxWpXpCD2Bq5XZW3XPrWv1dMVHEmZy9pr8dhsGdQuhKt%2FTh9Mz6nTCE34Yeyy56byfUHMzqaWrEpRpHldmrpqJrosXPyV0N%2BzAsMJYKzwMwjacTmtXGe9%2B7InkrtPz3aRoaIWPSUEtGjL1wUcYFnoJXeChG7qwpmfUHkI30XsvRdMsmKZMs9TwEsjR67ik6%2Fk14hk4jVcGe4k9yMMojGDNyKiqRy1opi5phUrG7HLDnkfdxOHktZIu072wB9jFhpHReoj3UXNF3lmReb%2FC0eaMx%2BESO1NY1w2myfuMuXW7VKvJ9CQ9im9Wy3XmllpLVX0kWUzNpmW6E%2FrY8ePkjLaV%2FPCMWVTeTJidTYtyuJpuWhSOMYsuwBhMgNK0dCtxS3O7%2Fmtvy7YL9lKn7RfvbODaEerw%2BXfuPfT92WDkiopLpaJZ9pQNUy9JAlNdyjVVH6PDTDV7saB2TadSCVWQYIQeZ2F8QgTVM30zdZtFlcOVSmU1WYFXolFFeRB9Kgt8PJmMx2vJu7IwvZoOS9XRFwsLsXCylKjMyGxXrV5kXxb%2BBxsddR0AAAEAAAAAAAAAAAAA)
format("woff");
}
.container,
.container-fluid,
.container-lg,
.container-wide,
.container-xl {
margin: 0 auto;
padding: 0 20px;
width: 100%;
}
.container,
.container-lg {
max-width: 1040px;
}
.container-wide,
.container-xl {
max-width: 1160px;
}
.common-SuperTitle {
font-weight: 300;
font-size: 45px;
line-height: 60px;
color: #32325d;
letter-spacing: -.01em;
}
@media (min-width: 670px) {
.common-SuperTitle {
font-size: 50px;
line-height: 70px;
}
}
.common-IntroText {
font-weight: 400;
font-size: 21px;
line-height: 31px;
color: #525f7f;
}
@media (min-width: 670px) {
.common-IntroText {
font-size: 24px;
line-height: 36px;
}
}
.common-BodyText {
font-weight: 400;
font-size: 17px;
line-height: 26px;
color: #6b7c93;
}
.common-Link {
color: #6772e5;
font-weight: 500;
transition: color 0.1s ease;
cursor: pointer;
}
.common-Link:hover {
color: #32325d;
}
.common-Link:active {
color: #000;
}
.common-Link--arrow:after {
font: normal 16px StripeIcons;
content: "\2192";
padding-left: 5px;
}
.common-Button {
white-space: nowrap;
display: inline-block;
height: 40px;
line-height: 40px;
padding: 0 14px;
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
background: #fff;
border-radius: 4px;
font-size: 15px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.025em;
color: #6772e5;
text-decoration: none;
transition: all 0.15s ease;
}
.common-Button:hover {
color: #7795f8;
transform: translateY(-1px);
box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);
}
.common-Button:active {
color: #555abf;
background-color: #f6f9fc;
transform: translateY(1px);
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
}
.common-Button--default {
color: #fff;
background: #6772e5;
}
.common-Button--default:hover {
color: #fff;
background-color: #7795f8;
}
.common-Button--default:active {
color: #e6ebf1;
background-color: #555abf;
}
.common-Button--dark {
color: #fff;
background: #32325d;
}
.common-Button--dark:hover {
color: #fff;
background-color: #43458b;
}
.common-Button--dark:active {
color: #e6ebf1;
background-color: #32325d;
}
.common-Button--disabled {
color: #fff;
background: #aab7c4;
pointer-events: none;
}
.common-ButtonIcon {
display: inline;
margin: 0 5px 0 0;
position: relative;
}
.common-ButtonGroup {
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin: -10px;
}
.common-ButtonGroup .common-Button {
-ms-flex-negative: 0;
flex-shrink: 0;
margin: 10px;
}
/** Page-specific styles */
@keyframes spin {
0% {
transform: rotate(0deg);
}
to {
transform: rotate(1turn);
}
}
@keyframes void-animation-out {
0%,
to {
opacity: 1;
}
}
body {
overflow-x: hidden;
/* background-color: #f6f9fc; */
/* For armintatank */
background-color: #ffffff;
}
main {
position: relative;
display: block;
z-index: 1;
}
.stripes {
position: absolute;
width: 100%;
transform: skewY(-12deg);
height: 950px;
top: -350px;
background: linear-gradient(180deg, #e6ebf1 350px, rgba(230, 235, 241, 0));
}
.stripes .stripe {
position: absolute;
height: 190px;
}
.stripes .s1 {
height: 380px;
top: 0;
left: 0;
width: 24%;
background: linear-gradient(90deg, #e6ebf1, rgba(230, 235, 241, 0));
}
.stripes .s2 {
top: 380px;
left: 4%;
width: 35%;
background: linear-gradient(
90deg,
hsla(0, 0%, 100%, 0.65),
hsla(0, 0%, 100%, 0)
);
}
.stripes .s3 {
top: 380px;
right: 0;
width: 38%;
background: linear-gradient(90deg, #e4e9f0, rgba(228, 233, 240, 0));
}
main > .container-lg {
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
position: relative;
max-width: 750px;
padding: 20px 20px 110px;
}
main > .container-lg .cell {
display: -ms-flexbox;
display: flex;
-ms-flex-direction: column;
flex-direction: column;
-ms-flex-pack: center;
justify-content: center;
position: relative;
-ms-flex: auto;
flex: auto;
min-width: 100%;
min-height: 360px;
padding: 0 40px;
}
main > .container-lg .cell + .cell {
margin-top: 70px;
}
main > .container-lg .cell.intro {
padding: 0;
}
@media (min-width: 670px) {
main > .container-lg .cell.intro {
-ms-flex-align: center;
align-items: center;
text-align: center;
}
.optionList {
margin-left: 13px;
}
}
main > .container-lg .cell.intro > * {
width: 100%;
max-width: 700px;
}
main > .container-lg .cell.intro .common-IntroText {
margin-top: 10px;
}
main > .container-lg .cell.intro .common-BodyText {
margin-top: 15px;
}
main > .container-lg .cell.intro .common-ButtonGroup {
width: auto;
margin-top: 20px;
}
main > .container-lg .example {
-ms-flex-align: center;
align-items: center;
border-radius: 4px;
box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08);
padding: 80px 0px;
margin-left: -20px;
margin-right: -20px;
}
/* For armintatank , changing min-width to 460 from 670
padding from 40 to 5 */
@media (min-width: 360px) {
main > .container-lg .example {
padding: 5px;
}
}
main > .container-lg .example.submitted form,
main > .container-lg .example.submitting form {
opacity: 0;
transform: scale(0.9);
pointer-events: none;
}
main > .container-lg .example.submitted .success,
main > .container-lg .example.submitting .success {
pointer-events: all;
}
main > .container-lg .example.submitting .success .icon {
opacity: 1;
}
main > .container-lg .example.submitted .success > * {
opacity: 1;
transform: none !important;
}
main > .container-lg .example.submitted .success > :nth-child(2) {
transition-delay: 0.1s;
}
main > .container-lg .example.submitted .success > :nth-child(3) {
transition-delay: 0.2s;
}
main > .container-lg .example.submitted .success > :nth-child(4) {
transition-delay: 0.3s;
}
main > .container-lg .example.submitted .success .icon .border,
main > .container-lg .example.submitted .success .icon .checkmark {
opacity: 1;
stroke-dashoffset: 0 !important;
}
main > .container-lg .example * {
margin: 0;
padding: 0;
}
main > .container-lg .example .caption {
display: flex;
justify-content: space-between;
position: absolute;
width: 100%;
top: 100%;
left: 0;
padding: 15px 10px 0;
color: #aab7c4;
font-family: Roboto, "Open Sans", "Segoe UI", sans-serif;
font-size: 15px;
font-weight: 500;
}
main > .container-lg .example .caption * {
font-family: inherit;
font-size: inherit;
font-weight: inherit;
}
main > .container-lg .example .caption .no-charge {
color: #cfd7df;
margin-right: 10px;
}
main > .container-lg .example .caption a.source {
text-align: right;
color: inherit;
transition: color 0.1s ease-in-out;
margin-left: 10px;
}
main > .container-lg .example .caption a.source:hover {
color: #6772e5;
}
main > .container-lg .example .caption a.source:active {
color: #43458b;
}
main > .container-lg .example .caption a.source svg {
margin-right: 10px;
}
main > .container-lg .example .caption a.source svg path {
fill: currentColor;
}
main > .container-lg .example form {
position: relative;
width: 100%;
max-width: 500px;
transition-property: opacity, transform;
transition-duration: 0.35s;
transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
}
main > .container-lg .example form input::-webkit-input-placeholder {
opacity: 1;
}
main > .container-lg .example form input::-moz-placeholder {
opacity: 1;
}
main > .container-lg .example form input:-ms-input-placeholder {
opacity: 1;
}
main > .container-lg .example .error {
display: -ms-flexbox;
display: flex;
-ms-flex-pack: center;
justify-content: center;
position: absolute;
width: 100%;
top: 100%;
margin-top: 20px;
left: 0;
padding: 0 15px;
font-size: 13px !important;
opacity: 0;
transform: translateY(10px);
transition-property: opacity, transform;
transition-duration: 0.35s;
transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
}
main > .container-lg .example .error.visible {
opacity: 1;
transform: none;
}
main > .container-lg .example .error .message {
font-size: inherit;
}
main > .container-lg .example .error svg {
-ms-flex-negative: 0;
flex-shrink: 0;
margin-top: -1px;
margin-right: 10px;
}
main > .container-lg .example .success {
display: -ms-flexbox;
display: flex;
-ms-flex-direction: column;
flex-direction: column;
-ms-flex-align: center;
align-items: center;
-ms-flex-pack: center;
justify-content: center;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
padding: 10px;
text-align: center;
pointer-events: none;
overflow: hidden;
}
@media (min-width: 670px) {
main > .container-lg .example .success {
padding: 40px;
}
}
main > .container-lg .example .success > * {
transition-property: opacity, transform;
transition-duration: 0.35s;
transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
opacity: 0;
transform: translateY(50px);
}
main > .container-lg .example .success .icon {
margin: 15px 0 30px;
transform: translateY(70px) scale(0.75);
}
main > .container-lg .example .success .icon svg {
will-change: transform;
}
main > .container-lg .example .success .icon .border {
stroke-dasharray: 251;
stroke-dashoffset: 62.75;
transform-origin: 50% 50%;
transition: stroke-dashoffset 0.35s cubic-bezier(0.165, 0.84, 0.44, 1);
animation: spin 1s linear infinite;
}
main > .container-lg .example .success .icon .checkmark {
stroke-dasharray: 60;
stroke-dashoffset: 60;
transition: stroke-dashoffset 0.35s cubic-bezier(0.165, 0.84, 0.44, 1) 0.35s;
}
main > .container-lg .example .success .title {
font-size: 17px;
font-weight: 500;
margin-bottom: 8px;
}
main > .container-lg .example .success .message {
font-size: 14px;
font-weight: 400;
margin-bottom: 25px;
line-height: 1.6em;
}
main > .container-lg .example .success .message span {
font-size: inherit;
}
main > .container-lg .example .success .reset:active {
transition-duration: 0.15s;
transition-delay: 0s;
opacity: 0.65;
}
main > .container-lg .example .success .reset svg {
will-change: transform;
}
footer {
position: relative;
max-width: 750px;
padding: 50px 20px;
margin: 0 auto;
}
.optionList {
margin: 6px 0;
}
.optionList li {
display: inline-block;
margin-right: 13px;
}
.optionList a {
color: #aab7c4;
transition: color 0.1s ease-in-out;
cursor: pointer;
font-size: 15px;
line-height: 26px;
}
.optionList a.selected {
color: #6772e5;
font-weight: 600;
}
.optionList a:hover {
color: #32325d;
}
.optionList a.selected:hover {
cursor: default;
color: #6772e5;
}

@ -0,0 +1,366 @@
/* Variables */
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 14px;
-webkit-font-smoothing: antialiased;
display: flex;
justify-content: center;
align-content: center;
height: 100vh;
width: 100vw;
}
form {
width: 30vw;
min-width: 480px;
align-self: center;
box-shadow: 0px 0px 0px 0.5px rgba(50, 50, 93, 0.1),
0px 2px 5px 0px rgba(50, 50, 93, 0.1), 0px 1px 1.5px 0px rgba(0, 0, 0, 0.07);
border-radius: 7px;
padding: 10px;
}
input {
border-radius: 6px;
margin-bottom: 6px;
padding: 12px;
border: 1px solid rgba(50, 50, 93, 0.1);
height: 44px;
font-size: 16px;
width: 100%;
background: white;
}
.result-message {
line-height: 22px;
font-size: 16px;
}
.result-message a {
color: rgb(89, 111, 214);
font-weight: 600;
text-decoration: none;
}
.hidden {
display: none;
}
#card-error {
color: rgb(105, 115, 134);
text-align: left;
font-size: 13px;
line-height: 17px;
margin-top: 12px;
}
#card-element {
border-radius: 4px 4px 0 0 ;
padding: 12px;
border: 1px solid rgba(50, 50, 93, 0.1);
height: 44px;
width: 100%;
background: white;
}
#payment-request-button {
margin-bottom: 32px;
}
/* Buttons and links */
button {
background: #5469d4;
color: #ffffff;
font-family: Arial, sans-serif;
border-radius: 0 0 4px 4px;
border: 0;
padding: 12px 16px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
display: block;
transition: all 0.2s ease;
box-shadow: 0px 4px 5.5px 0px rgba(0, 0, 0, 0.07);
width: 100%;
}
button:hover {
filter: contrast(115%);
}
button:disabled {
opacity: 0.5;
cursor: default;
}
/* spinner/processing state, errors */
.spinner,
.spinner:before,
.spinner:after {
border-radius: 50%;
}
.spinner {
color: #ffffff;
font-size: 22px;
text-indent: -99999px;
margin: 0px auto;
position: relative;
width: 20px;
height: 20px;
box-shadow: inset 0 0 0 2px;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
}
.spinner:before,
.spinner:after {
position: absolute;
content: "";
}
.spinner:before {
width: 10.4px;
height: 20.4px;
background: #5469d4;
border-radius: 20.4px 0 0 20.4px;
top: -0.2px;
left: -0.2px;
-webkit-transform-origin: 10.4px 10.2px;
transform-origin: 10.4px 10.2px;
-webkit-animation: loading 2s infinite ease 1.5s;
animation: loading 2s infinite ease 1.5s;
}
.spinner:after {
width: 10.4px;
height: 10.2px;
background: #5469d4;
border-radius: 0 10.2px 10.2px 0;
top: -0.1px;
left: 10.2px;
-webkit-transform-origin: 0px 10.2px;
transform-origin: 0px 10.2px;
-webkit-animation: loading 2s infinite ease;
animation: loading 2s infinite ease;
}
@-webkit-keyframes loading {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes loading {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@media only screen and (max-width: 600px) {
form {
width: 80vw;
}
}
/* Stripe Example 2 CSS */
.example.example2 {
background-color: #fff;
}
.example.example2 * {
font-family: Source Code Pro, Consolas, Menlo, monospace;
font-size: 16px;
font-weight: 500;
}
.example.example2 .row {
display: -ms-flexbox;
display: flex;
margin: 0 5px 10px;
}
.example.example2 .field {
position: relative;
width: 100%;
height: 50px;
margin: 0 10px;
}
.example.example2 .field.half-width {
width: 50%;
}
.example.example2 .field.quarter-width {
width: calc(25% - 10px);
}
.example.example2 .baseline {
position: absolute;
width: 100%;
height: 1px;
left: 0;
bottom: 0;
background-color: #cfd7df;
transition: background-color 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.example.example2 label {
position: absolute;
width: 100%;
left: 0;
bottom: 8px;
color: #cfd7df;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
transform-origin: 0 50%;
cursor: text;
pointer-events: none;
transition-property: color, transform;
transition-duration: 0.3s;
transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
}
.example.example2 .input {
position: absolute;
width: 100%;
left: 0;
bottom: 0;
padding-bottom: 7px;
color: #32325d;
background-color: transparent;
}
.example.example2 .input::-webkit-input-placeholder {
color: transparent;
transition: color 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.example.example2 .input::-moz-placeholder {
color: transparent;
transition: color 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.example.example2 .input:-ms-input-placeholder {
color: transparent;
transition: color 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.example.example2 .input.StripeElement {
opacity: 0;
transition: opacity 0.3s cubic-bezier(0.165, 0.84, 0.44, 1);
will-change: opacity;
}
.example.example2 .input.focused,
.example.example2 .input:not(.empty) {
opacity: 1;
}
.example.example2 .input.focused::-webkit-input-placeholder,
.example.example2 .input:not(.empty)::-webkit-input-placeholder {
color: #cfd7df;
}
.example.example2 .input.focused::-moz-placeholder,
.example.example2 .input:not(.empty)::-moz-placeholder {
color: #cfd7df;
}
.example.example2 .input.focused:-ms-input-placeholder,
.example.example2 .input:not(.empty):-ms-input-placeholder {
color: #cfd7df;
}
.example.example2 .input.focused + label,
.example.example2 .input:not(.empty) + label {
color: #aab7c4;
transform: scale(0.85) translateY(-25px);
cursor: default;
}
.example.example2 .input.focused + label {
color: #24b47e;
}
.example.example2 .input.invalid + label {
color: #ffa27b;
}
.example.example2 .input.focused + label + .baseline {
background-color: #24b47e;
}
.example.example2 .input.focused.invalid + label + .baseline {
background-color: #e25950;
}
.example.example2 input, .example.example2 button {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
outline: none;
border-style: none;
}
.example.example2 input:-webkit-autofill {
-webkit-text-fill-color: #e39f48;
transition: background-color 100000000s;
-webkit-animation: 1ms void-animation-out;
}
.example.example2 .StripeElement--webkit-autofill {
background: transparent !important;
}
.example.example2 input, .example.example2 button {
-webkit-animation: 1ms void-animation-out;
}
.example.example2 button {
display: block;
width: calc(100% - 30px);
height: 40px;
margin: 40px 15px 0;
background-color: #24b47e;
border-radius: 4px;
color: #fff;
text-transform: uppercase;
font-weight: 600;
cursor: pointer;
}
.example.example2 .error svg {
margin-top: 0 !important;
}
.example.example2 .error svg .base {
fill: #e25950;
}
.example.example2 .error svg .glyph {
fill: #fff;
}
.example.example2 .error .message {
color: #e25950;
}
.example.example2 .success .icon .border {
stroke: #abe9d2;
}
.example.example2 .success .icon .checkmark {
stroke: #24b47e;
}
.example.example2 .success .title {
color: #32325d;
font-size: 16px !important;
}
.example.example2 .success .message {
color: #8898aa;
font-size: 13px !important;
}
.example.example2 .success .reset path {
fill: #24b47e;
}

@ -0,0 +1,121 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>armintatank API - v1.0.0</title>
<link href="assets/css/style.css" rel="stylesheet">
<link rel="icon" href="https://getbootstrap.com/favicon.ico">
</head>
<body data-spy="scroll" data-target="#resources_nav" data-offset="60">
<nav id="main-nav" class="navbar navbar-expand-md navbar-dark bg-dark">
<a class="navbar-brand" href="http://armintatankapi.true2air.com">armintatank API - v1.0.0</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#sticky_nav_wrapper">
<span class="navbar-toggler-icon"></span>
</button>
<!-- Keep For Reference
<div class="collapse navbar-collapse" id="swaggerTopNav">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>
</ul>
</div>
-->
<!-- <form class="form-inline ml-auto d-none d-lg-block">
<div class="input-group input-group-sm">
<input type="text" autocorrect="off" class="form-control form-control-sm" id="input_baseUrl" placeholder="http://example.com/api">
<div class="input-group-append">
<button data-add-scope id="update-url" class="btn btn-secondary" type="button">Explore</button>
</div>
</div>
</form> -->
</nav>
<main role="main" class="container-fluid">
<div id="swagger-ui-container" class="row"></div>
</main>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>window.jQuery || document.write('<script src="assets/lib/jquery-3.2.1.min.js"><\/script>')</script>
<!-- Dependencies -->
<script src="assets/lib/jquery.wiggle.min.js" type="text/javascript"></script>
<script src="assets/lib/handlebars-2.0.0.js" type="text/javascript"></script>
<script src="assets/lib/underscore-min.js" type="text/javascript"></script>
<script src="assets/lib/backbone-min.js" type="text/javascript"></script>
<script src="assets/swagger-ui.js" type="text/javascript"></script>
<script src="assets/lib/marked.js" type="text/javascript"></script>
<script src="assets/lib/bootstrap.min.js" type="text/javascript"></script>
<script src="assets/lib/highlight.pack.js" type="text/javascript"></script>
<script src="assets/lib/scrollPosStyler.min.js" type="text/javascript"></script>
<script src="assets/lib/fontawesome-all.min.js" type="text/javascript"></script>
<script src="assets/lib/swagger-oauth.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
window.swaggerUi = new SwaggerUi({
url: 'http://armintatank.true2air.com:3000/api-docs/armintatank.json', // Change to your json
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch', 'head', 'options'],
onComplete: function (swaggerApi, swaggerUi) {
// if(typeof initOAuth == "function") {
// initOAuth()
// }
$('pre code').each(function (i, e) {
hljs.highlightBlock(e)
});
ScrollPosStyler.init({ scrollOffsetY: 54 });
// Scroll To Item
var ancloc = window.location.hash;
if(ancloc){
document.location.href=ancloc;
}
// Set Key by default
// addApiKeyAuthorization();
},
onFailure: function (data) {
console.log("Unable to Load SwaggerUI");
},
docExpansion: "none",
sorter: "alpha"
});
window.swaggerUi.load();
// function addApiKeyAuthorization() {
// key = 'fbc44ac8-e03f-4611-847a-6d5a72bbe76b';
// var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization('x-api-key', key, 'header');
// window.swaggerUi.api.clientAuthorizations.add('x-api-key', apiKeyAuth);
// $('#input_apiKey_x-api-key').val(key)
// }
function addApiKeyAuthorization(){
var key = encodeURIComponent( $('#input_apiKey')[0].value );
if(key && key.trim() != "") {
var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization( "Authorization", "Bearer " + key, "header" );
window.swaggerUi.api.clientAuthorizations.add( "bearer", apiKeyAuth );
$('#input_apiKey_x-api-key').val(key)
alert( "Set bearer token: " + key );
}
}
// Optional if including Explore form
$('#update-url').click(function(){
window.swaggerUi.updateSwaggerUi({
url: $('#input_baseUrl').val()
});
});
});
</script>
</body>
</html>

@ -0,0 +1,18 @@
// Simple localization
const isStripeDev = window.location.hostname === 'stripe.dev';
const localeIndex = isStripeDev ? 2 : 1;
window.__exampleLocale = window.location.pathname.split('/')[localeIndex] || 'en';
const urlPrefix = isStripeDev ? '/elements-examples/' : '/';
document.querySelectorAll('.optionList a').forEach(function(langNode) {
const langValue = langNode.getAttribute('data-lang');
const langUrl = langValue === 'en' ? urlPrefix : (urlPrefix + langValue + '/');
if (langUrl === window.location.pathname || langUrl === window.location.pathname + '/') {
langNode.className += ' selected';
langNode.parentNode.setAttribute('aria-selected', 'true');
} else {
langNode.setAttribute('href', langUrl);
langNode.parentNode.setAttribute('aria-selected', 'false');
}
});

@ -0,0 +1,71 @@
// This file establishes conenction to the mongodb. Connection is used by the applicaiton and initialized
// when it loads during starup . This is registered in the root level index.js
// require mongoose module
const fp = require("fastify-plugin");
const db = require("mongoose");
db.set("useFindAndModify", false);
// require clak module to give colors to console text.
var chalk = require("chalk");
const boom = require("boom");
// Define colors for db messages on cosole.
var connected = chalk.bold.cyan;
var error = chalk.bold.yellow;
var disconnected = chalk.bold.red;
var termination = chalk.bold.magenta;
// TODO
// Need to read database url from a environment variable.
// const databaseURL = "mongodb://armintatankdbuser:armintatank1@35.207.198.4:27017/arminta-tank-db;
const databaseURLNew = "mongodb://35.207.198.4:27017/arminta-tank-db";
// const databaseURLNew = "mongodb://127.0.0.1:27017/arminta-tank-db";
// Next line not used , or no need to pass default db in the mongo connection url.
const defaultDatabase = "arminta-tank-db";
//export this function and imported by server.js
async function dbConnection() {
try {
db.connect(databaseURLNew, { user: "armintatankdbuser", pass: "armintatank1" , useUnifiedTopology: true, });
// db.connect(databaseURLNew)
db.connection.on("connected", function () {
console.log(
connected("Mongoose default connection is open to ", databaseURLNew)
);
});
db.connection.on("error", function (err) {
console.log(
error("Mongoose default connection has occured " + err + " error")
);
});
db.connection.on("disconnected", function () {
console.log(disconnected("Mongoose default connection is disconnected"));
});
process.on("SIGINT", function () {
db.connection.close(function () {
console.log(
termination(
"Mongoose default connection is disconnected due to application termination"
)
);
process.exit(0);
});
});
} catch (err) {
throw boom.boomify(err);
}
}
module.exports = fp(dbConnection, { fastify: ">=1.0.0" });

@ -0,0 +1,34 @@
exports.options = {
routePrefix: "/documentation",
exposeRoute: true,
swagger: {
info: {
title: "Arminta Tank API's",
description:
"These are the API's that support armintatank operations including user self service rentals , staff assisted rentals , customer support operations and management dashboards ",
version: "1.0.0",
},
externalDocs: {
url: "https://swagger.io",
description: "Find more info here",
},
// We have to change this beacause this is running on local
// we have to run on this on swagger
// host: "localhost:3000", //Devlopemnt host on lcoal
host: "localhost:3000", //Production for swagger
schemes: ["http"],
consumes: ["application/json"],
produces: ["application/json"],
securityDefinitions: {
basicAuth: {
type: "apiKey",
name: "Authorization",
in: 'header'
},
},
uiConfig: {
docExpansion: 'full',
deepLinking: false
},
},
};

@ -0,0 +1,316 @@
// const fastify = require("fastify")({
// logger: true,
// });
const bcrypt = require("bcrypt");
const saltRounds = 10;
const libphonenumberjs = require("libphonenumber-js");
// External Dependancies
// offers http-friendly error objects.
const boom = require("boom");
// Get Data Models
const User = require("../models/User");
const customJwtAuth = require("../customAuthJwt");
const fastify = require("fastify")({
logger: true,
});
//function to encrypt password.
//used bcrypt module.
async function bcryptPassword(password) {
encryptedPwd = bcrypt.hash(password, saltRounds);
return encryptedPwd;
}
//function to decrypt password return user object .
//used bcrypt module.
async function bcryptComparePassword(pwd, encpassword) {
isSame = bcrypt.compare(pwd, encpassword);
return isSame;
}
// Get current users
exports.getCurrentUser = async (req, reply) => {
try {
const users = await await User.findOne({ username: req.user.username });
return users;
} catch (err) {
throw boom.boomify(err);
}
};
// Get all users
exports.getUsers = async (req, reply) => {
const limit = parseInt(req.query.limit) || 100;
const page = parseInt(req.query.page) || 1;
const startindex = (page - 1) * limit;
try {
await User.find()
.limit(limit)
.skip(startindex)
.exec()
.then((docs) => {
reply.send({ status_code: 200, data: docs, count: docs.length });
})
.catch((err) => {
console.log(err);
reply.send({ error: err });
});
} catch (err) {
throw boom.boomify(err);
}
};
// Get single user by ID
exports.getSingleUser = async (req, reply) => {
try {
const username = req.params.username;
const user = await User.findOne({ username: username });
return user;
} catch (err) {
throw boom.boomify(err);
}
};
// Edit user info by userId
exports.editUserInfo = async (req, body) => {
try {
const { userId } = req.params;
const userInfo = await User.findById(userId);
const updateData = req.body;
console.log(updateData.firstName);
if (updateData.firstName) userInfo.profile.firstName = updateData.firstName;
if (updateData.lastName) userInfo.profile.lastName = updateData.lastName;
if (updateData.phone) userInfo.profile.contactNumber = updateData.phone;
if (updateData.address1) userInfo.profile.address1 = updateData.address1;
if (updateData.address2) userInfo.profile.address2 = updateData.address2;
if (updateData.city) userInfo.profile.city = updateData.city;
if (updateData.state) userInfo.profile.state = updateData.state;
if (updateData.country) userInfo.profile.country = updateData.country;
if (updateData.zip) userInfo.profile.zip = updateData.zip;
if (updateData.phone) userInfo.phone = updateData.phone;
if (updateData.email) userInfo.emails[0].email = updateData.email;
if (updateData.role) userInfo.profile.role = updateData.role;
const user = await userInfo.save();
return user;
} catch (err) {
throw boom.boomify(err);
}
};
exports.editCuurentUserInfo = async (req, reply) => {
try {
const { username } = req.params;
const userInfo = await User.findOne({ username: username.toString() });
const updateData = req.body;
if (updateData.firstName) userInfo.profile.firstName = updateData.firstName;
if (updateData.lastName) userInfo.profile.lastName = updateData.lastName;
if (updateData.phone) userInfo.profile.contactNumber = updateData.phone;
if (updateData.address1) userInfo.profile.address1 = updateData.address1;
if (updateData.address2) userInfo.profile.address2 = updateData.address2;
if (updateData.city) userInfo.profile.city = updateData.city;
if (updateData.state) userInfo.profile.state = updateData.state;
if (updateData.country) userInfo.profile.country = updateData.country;
if (updateData.zip) userInfo.profile.zip = updateData.zip;
// if (updateData.phone) userInfo.phone = updateData.phone;
if (updateData.email) userInfo.emails[0].email = updateData.email;
if (updateData.role) userInfo.profile.role = updateData.role;
if (updateData.phone) {
const phoneNumber = libphonenumberjs.parsePhoneNumber(updateData.phone);
if (phoneNumber) {
// access returned collection
if (!phoneNumber.isValid()) {
error = {
armintatankdata: {
error: true,
code: 10002,
message:
"10002 - Phone # " +
updateData.phone +
" is not a valid phone number",
},
};
req.body.regError = error;
reply.status(406).send(error);
}
}
}
if (userInfo.phone == updateData.phone) {
console.log("IF++++++++++++++=");
userInfo.phone = updateData.phone;
userInfo.phoneVerified = true;
} else {
console.log("Ilse++++++++++++++=");
userInfo.phone = updateData.phone;
userInfo.phoneVerified = false;
}
const user = await userInfo.save();
return user;
} catch (err) {
throw boom.boomify(err);
}
};
// Add a new user
// Function accepts username, password , encrypts password and saves it in the database.
exports.addUser = async (req, reply) => {
try {
// console.log("This is the reply in the handler after the validations", reply);
userData = {
username: req.body.username,
emails: req.body.emails,
password: req.body.password,
phone: req.body.phone,
profile: {
firstName: req.body.firstname,
lastName: req.body.lastname,
contactNumber: req.body.phone,
country: req.body.country,
state: req.body.state,
city: req.body.city,
address1: req.body.address1,
address2: req.body.address2,
zip: req.body.zip,
notes: req.body.notes,
},
};
var user = new User(userData);
//password is not at the top level in the collection.
userpass = req.body.password;
// If fields are sent via form encoding , capture the fields and assign them to the user Object.
checkFormEncoding = isUserFormUrlEncoded(req);
if (checkFormEncoding.isUserFormUrlEncoded) {
usertobeInserted = checkFormEncoding.user;
console.log("thsi true url string");
user.username = usertobeInserted.username;
user.firstName = usertobeInserted.firstName;
user.lastName = usertobeInserted.lastName;
user.lastName = usertobeInserted.lastName;
user.phone = usertobeInserted.phone;
user.emails = usertobeInserted.emails;
user.passsword = usertobeInserted.password;
}
console.log("---------checkurl ecnoded string-----------------------");
// Store hash in your password DB.
hash = await bcryptPassword(userpass);
if (hash) {
user.services.password.bcrypt = hash;
if (req.body.role) {
user.profile.role = req.body.role;
console.log("******************************************************");
console.log(user);
} else {
// override and make the user role as "user" by default
role = ["user"];
user.profile.role = role;
}
insertedUser = await user.save();
console.log(insertedUser);
if (insertedUser) {
// Prepare user object and wrap it inside the armintatankdata
var retUser = {
armintatankdata: {
username: insertedUser.username,
phone: insertedUser.phone,
emails: [
{
email: insertedUser.emails[0].email,
},
],
profile: insertedUser.profile,
},
status_code: 200,
};
return retUser;
}
}
} catch (err) {
throw boom.boomify(err);
}
};
// Login a user
// Accepts a user , password , and checks in the system to see if user exists , and password is valid
// returns a user object so that jwt token can be created and sent back to the client
exports.loginUser = async (req) => {
try {
const username = req.body.username;
const password = req.body.password;
const user = await User.findOne({ username: username });
// compare users password with what is supplied
if (user) {
isSame = await bcryptComparePassword(
password,
user.services.password.bcrypt
);
// if password supplied matches return object
if (isSame) {
return { same: true, user: user };
} else {
return { same: false };
}
} else {
return { same: false };
}
} catch (err) {
throw boom.boomify(err);
}
};
// Update an existing user
exports.updateUser = async (req, reply) => {
try {
const id = req.params.id;
const user = req.body;
const { ...updateData } = user;
const update = await User.findByIdAndUpdate(id, updateData, { new: true });
return update;
} catch (err) {
throw boom.boomify(err);
}
};
// Delete a user
exports.deleteUser = async (req, reply) => {
try {
const id = req.params.id;
const user = await User.findByIdAndRemove(id);
return user;
} catch (err) {
throw boom.boomify(err);
}
};
//Added the getphone user and delphone user apis for testing purposes
exports.getPhoneUser = async (req, reply) => {
try {
console.log(" requesting the api getPhoneUser , and passing the phone ");
const phone = req.body.phone;
const user = await User.findOne({ phone: phone });
return user;
} catch (err) {
throw boom.boomify(err);
}
};
exports.delPhoneUser = async (req, reply) => {
try {
const phone = req.body.phone;
console.log("deleting users wiht the phone ....", phone);
const user = await User.deleteOne({ phone: phone });
return user;
} catch (err) {
throw boom.boomify(err);
}
};

@ -0,0 +1,51 @@
// Have custom Auth Tokens based on the user roles/privileges
// operatorAuthenticate role is required for all updates , deletes and create calls for non user specific actions.
const fastifyJwt = require("fastify-jwt");
const fp = require("fastify-plugin");
async function customJwtAuth(fastify, opts, next) {
fastify.register(fastifyJwt, {
secret: "asecretthatsverylongandimportedfromanenvfile",
});
fastify.decorate("authenticate", async function (request, reply) {
try {
// to whatever you want, read the token from cookies for example..
// const token = request.headers.authorization
// override the request.headers.authorization to prepend with Bearer as fastifiy verify expects token
// in the form - "Bearer tokenvalue"
let token = request.headers.authorization;
// console.log("Received Token is ... \n");
token = "Bearer " + token;
// console.log(token);
request.headers.authorization = token;
// console.log(request.headers.authorization, "hello");
await request.jwtVerify();
} catch (err) {
reply.send(err);
}
});
fastify.decorate("operatorAuthenticate", async function (request, reply) {
try {
// to whatever you want, read the token from cookies for example..
// override the request.headers.authorization to prepend with Bearer as fastifiy verify expects token
// in the form - "Bearer tokenvalue"
let token = request.headers.authorization;
token = "Bearer " + token;
request.headers.authorization = token;
decodedtoken = await request.jwtVerify();
console.log("decodedtoken is ************************", decodedtoken);
userRoles = decodedtoken.roles;
if (userRoles.indexOf("operator") === -1) {
reply.send({
error: {
message: "You Do not have permission to execute this action",
},
});
}
} catch (err) {
reply.send(err);
}
});
}
module.exports = fp(customJwtAuth, { fastify: ">=1.0.0" });

@ -0,0 +1,17 @@
const fastify = require("fastify");
const fp = require("fastify-plugin");
const boom = require("boom");
async function decodeToken(request) {
let token = request.headers.authorization;
request.headers.authorization = token;
console.log("------------------");
// data = await request.jwtVerify()
await request.jwtVerify(token, (err, decoded) => {
if (err) fastify.log.error(err);
console.log("username : " + decoded.username);
});
}
module.exports = fp(decodeToken, { fastify: ">=1.0.0" });

@ -0,0 +1,803 @@
const User = require("../models/User");
const Message = require("../models/Message");
const generator = require("generate-password");
const bcrypt = require("bcrypt");
const saltRounds = 10;
// External Dependancies
// offers http-friendly error objects.
const boom = require("boom");
var twilio = require("twilio");
// var twilioAccountSid = "ACb743d0f1aa003ecae7548f69796ba26e"; // Your Account SID from www.twilio.com/console
// var twilioAuthToken = "2b1d785f485b07cfd5052c023871bd8d"; // Your Auth Token from www.twilio.com/console
var twilioAccountSid = "ACd29a8d6cf5c99b57bb7f3ce61cad4511"; // Your Account SID from www.twilio.com/console - armintatank
var twilioAuthToken = "7710db3aea89b94027155e6ae774b688"; // Your Auth Token from www.twilio.com/console - armintatank
const libphonenumberjs = require("libphonenumber-js");
const emailValidator = require("email-validator");
//function to encrypt password.
//used bcrypt module.
async function bcryptPassword(password) {
encryptedPwd = bcrypt.hash(password, saltRounds);
return encryptedPwd;
}
isUserFormUrlEncoded = (req) => {
var isUserFormUrlEncoded = false;
console.log("check is user encoe url funtion");
// This iterates through the req headers object.
// could not access req.headers.content-type due to the hyphen in the content-type key.
// console.log(`${key}: ${value}`);
for (const [key, value] of Object.entries(req.headers)) {
if (`${key}` === "content-type") {
if (`${value}` == "application/x-www-form-urlencoded") {
// console.log( "data supplied is with content type," , `${value}`)
// set isUserFormUrlEncoded value to true
isUserFormUrlEncoded = true;
// create user object with form variables . Password is used from the request object directly.
user = {
username: req.body.username,
phone: req.body.phone,
address: req.body.address,
password: req.body.password,
emails: [
{
email: req.body.email,
},
],
profile: {
firstName: req.body.firstName,
lastName: req.body.lastName,
},
};
return { isUserFormUrlEncoded: isUserFormUrlEncoded, user: user };
} else {
return { isUserFormUrlEncoded: false, user: "" };
}
}
}
};
const sendTextToPhone = async (phone, text) => {
var accountSid = twilioAccountSid; // Your Account SID from www.twilio.com/console
var authToken = twilioAuthToken; // Your Auth Token from www.twilio.com/console
var client = new twilio(accountSid, authToken);
console.log(accountSid, authToken, client);
// Message format to allow app to autopickup sms message and
// verify the code.
// Be no longer than 140 bytes
// Begin with the prefix <#>
// Contain a one-time code that the client sends back to your server to complete the verification flow
// End with an 11-character hash string that identifies your app - this is hardcoded as FA+9qCX9VSu as a reminder.
return await client.messages.create({
body: text,
to: phone, // Text this number
from: "+16193135693", // From a valid Twilio number
});
};
// Function to check if user exists in the system
exports.verifyUser = async (req, reply) => {
try {
var user = new User(req.body);
// Handle if the user data is supplied via a url encoded form
// capture fields if data is sent via form instead of json encoding
checkFormEncoding = isUserFormUrlEncoded(req);
if (checkFormEncoding.isUserFormUrlEncoded) {
usertobeInserted = checkFormEncoding.user;
user.username = usertobeInserted.username;
user.firstName = usertobeInserted.firstName;
user.lastName = usertobeInserted.lastName;
user.phone = usertobeInserted.phone;
user.emails = usertobeInserted.emails;
}
username = user.username;
userpass = req.body.password;
// check if user exists in the system. If user exists , display message that
// username is not available
userExists = await User.findOne({ username: username });
if (userExists) {
// return user exists message
error = {
armintatankdata: {
error: true,
code: 10001,
message:
"10001 - Username " +
userExists.username +
" is not available. please use a different username",
},
};
req.body.regError = error;
reply.send(error);
}
} catch (err) {
throw boom.boomify(err);
}
};
exports.validatePhoneFormat = async (req, reply) => {
try {
var user = new User(req.body);
// check if user supplied phone is of the right format.
// Handle if the user data is supplied via a url encoded form
// capture fields if data is sent via form instead of json encoding
checkFormEncoding = isUserFormUrlEncoded(req);
console.log(checkFormEncoding);
if (checkFormEncoding.isUserFormUrlEncoded) {
usertobeInserted = checkFormEncoding.user;
user.username = usertobeInserted.username;
user.firstName = usertobeInserted.firstName;
user.lastName = usertobeInserted.lastName;
user.phone = usertobeInserted.phone;
user.emails = usertobeInserted.emails;
}
if (user) {
const phoneNumber = libphonenumberjs.parsePhoneNumber(user.phone);
if (phoneNumber) {
// access returned collection
if (!phoneNumber.isValid()) {
error = {
armintatankdata: {
error: true,
code: 10002,
message:
"10002 - Phone # " +
user.phone +
" is not a valid phone number",
},
};
req.body.regError = error;
reply.status(406).send(error);
}
}
}
} catch (err) {
throw boom.boomify(err);
}
};
exports.validateEmailFormat = async (req, reply) => {
try {
var user = new User(req.body);
// Handle if the user data is supplied via a url encoded form
// capture fields if data is sent via form instead of json encoding
checkFormEncoding = isUserFormUrlEncoded(req);
if (checkFormEncoding.isUserFormUrlEncoded) {
usertobeInserted = checkFormEncoding.user;
user.username = usertobeInserted.username;
user.firstName = usertobeInserted.firstName;
user.lastName = usertobeInserted.lastName;
user.phone = usertobeInserted.phone;
user.emails = usertobeInserted.emails;
}
useremail = await user.emails[0].email;
// check if user supplied email is of the right format.
if (user) {
const isValidEmail = emailValidator.validate(useremail.trim());
if (!isValidEmail) {
// Return email invalid format message
error = {
armintatankdata: {
error: true,
code: 10003,
message:
"10003 - Email " + user.emails[0].email + " is not a valid email",
},
};
req.body.regError = error;
reply.send(error);
}
}
} catch (err) {
throw boom.boomify(err);
}
};
// Check if all the required fields are supplied by the user
exports.fieldCheck = async (req, reply) => {
try {
userData = {
username: req.body.username,
emails: req.body.emails,
password: req.body.password,
services: { password: {bcrypt: req.body.password} },
phone: req.body.phone,
profile: {
firstName: req.body.firstName,
lastName: req.body.lastName,
contactNumber: req.body.phone,
country: req.body.country,
state: req.body.state,
city: req.body.city,
address1: req.body.address1,
address2: req.body.address2,
zip: req.body.zip,
notes: req.body.notes,
},
};
var user = new User(userData);
//password is not at the top level in the collection.
password = req.body.password;
// capture fields if data is sent via form instead of json encoding
checkFormEncoding = isUserFormUrlEncoded(req);
if (checkFormEncoding.isUserFormUrlEncoded) {
usertobeInserted = checkFormEncoding.user;
user.username = usertobeInserted.username;
user.firstName = usertobeInserted.firstName;
user.lastName = usertobeInserted.lastName;
user.phone = usertobeInserted.phone;
user.emails = usertobeInserted.emails;
password = usertobeInserted.password;
}
console.log("User to be inserted is ", user.username,password,user.phone,user.profile);
// check if all rerquired fields are passed.
if (
!(
user.username &&
password &&
user.phone &&
// user.profile.firstName &&
// user.profile.lastName &&
// user.profile.address1 &&
user.emails[0].email
)
) {
console.log(
user.username,
password,
user.phone,
// user.profile.firstName,
// user.profile.lastName,
user.emails[0].email
);
// Required Fields are missing
suppliedvalues =
user.username +
" ," +
password +
" ," +
user.phone +
" ," +
user.firstName +
" ," +
user.lastName +
" ," +
user.emails[0].email;
error = {
armintatankdata: {
error: true,
code: 10004,
message:
"10004 - username, password, phone , firstname , lastname email city country state address1 and zip are required fields. Supplied values are " +
suppliedvalues,
},
};
req.body.regError = error;
reply.send(error);
}
} catch (err) {
throw boom.boomify(err);
}
};
// This methond supports verifying a users phone.
exports.verifyPhone = async (req, reply) => {
console.log("-------------------------------------------------");
try {
phone = req.body.phone;
phoneVerificationCode = req.body.phoneVerificationCode;
// check if user exists in the system. If user exists , display message that
// username is not available
console.log(
"this is the phone and verification code",
phone,
phoneVerificationCode
);
userExists = await User.findOne({
phone: phone,
phoneVerified: false,
phoneVerificationCode: phoneVerificationCode,
});
console.log(userExists);
if (userExists) {
// update the phoneVerified flag to true.
const filter = {
phone: phone,
phoneVerificationCode: phoneVerificationCode,
};
const update = { phoneVerified: true };
const doc = await User.findOneAndUpdate(filter, update);
updatedUser = await User.findOne({ phone: phone });
if (updatedUser.phoneVerified) {
reply.send('{"armintatankdata":{"error":false,"verified": true}}');
} else {
error = {
armintatankdata: {
error: true,
code: 10005,
message: "10005 - Verification code entered cannot be validated.",
},
};
req.body.regError = error;
reply.send(error);
}
} else {
error = {
armintatankdata: {
error: true,
code: 10005,
message: "10005 - Verification code entered cannot be validated.",
},
};
req.body.regError = error;
reply.send(error);
}
} catch (err) {
throw boom.boomify(err);
}
};
exports.sendPhoneVerificationCode = async (req, reply) => {
// Setting a regError in request so a user is not sent a verificaiton code if registration is not successful
// checkign if regError property is available in the req body, if it exists , do not send the message
// if it does not exisit , it impplies registraiton is succesful , as no pre handlers have set this reqError
// during registraiton validations.
if (!("regError" in req.body)) {
// var user = new User(req.body);
// // Handle if the user data is supplied via a url encoded form
// // capture fields if data is sent via form instead of json encoding
// checkFormEncoding = isUserFormUrlEncoded(req);
// if (checkFormEncoding.isUserFormUrlEncoded) {
// usertobeInserted = checkFormEncoding.user;
// user.username = usertobeInserted.username;
// user.firstName = usertobeInserted.firstName;
// user.lastName = usertobeInserted.lastName;
// user.phone = usertobeInserted.phone;
// user.emails = usertobeInserted.emails;
// }
phone = req.body.phone;
var accountSid = twilioAccountSid; // Your Account SID from www.twilio.com/console
var authToken = twilioAuthToken; // Your Auth Token from www.twilio.com/console
var client = new twilio(accountSid, authToken);
var code = Math.floor(100000 + Math.random() * 900000);
console.log("resending code", code);
// Message format to allow app to autopickup sms message and
// verify the code.
// Be no longer than 140 bytes
// Begin with the prefix <#>
// Contain a one-time code that the client sends back to your server to complete the verification flow
// End with an 11-character hash string that identifies your app - this is hardcoded as FA+9qCX9VSu as a reminder.
var created = await client.messages.create({
// body: "<#> Your verification code is " + code + " - from armintatank " + "FA+9qCX9VSu",
body: "Your verification code is " + code + " - from armintatank ",
to: phone, // Text this number
from: "+16193135693", // From a valid Twilio number
})
// ;
// if (created.errcode == null) {
// try {
// const msg = new Message(created);
// msg.save();
// const filter = { phone: phone };
// const update = { $set: { phoneVerificationCode: code } };
// var u = await User.updateOne(filter, update);
// if (u) {
// reply.send({
// armintatankdata: { error: false, message: "Message Successfully sent" },
// });
// }
// } catch (err) {
// throw err;
// }
// } else {
// reply.send({ armintatankda: { error: true, message: "Message not sent" } });
// }
// .then(function (message) {
// try {
// const msg = new Message(message);
// msg.save();
// } catch (err) {
// throw err;
// }
// })
.then(async function () {
// While testing ensure that there are no multiple users with the same phone number ,
// generally we are assumign each user will have a unique phone number.
const filter = { phone: phone };
const update = {'$set':{ 'phoneVerificationCode': code }};
await User.updateOne(filter, update);
});
}
};
exports.forgotPassword = async (req, reply) => {
try {
var user = new User(req.body);
// Handle if the user data is supplied via a url encoded form
// capture fields if data is sent via form instead of json encoding
checkFormEncoding = isUserFormUrlEncoded(req);
if (checkFormEncoding.isUserFormUrlEncoded) {
usertobeInserted = checkFormEncoding.user;
user.username = usertobeInserted.username;
user.firstName = usertobeInserted.firstName;
user.lastName = usertobeInserted.lastName;
user.phone = usertobeInserted.phone;
user.emails = usertobeInserted.emails;
}
// check if user exists in the system. If user exists , display message that
// username is not available
userExists = await User.findOne({
phone: user.phone,
});
if (userExists) {
// Create a code and send it via sms so user can validate it and create a password
// var accountSid = twilioAccountSid; // Your Account SID from www.twilio.com/console
// var authToken = twilioAuthToken; // Your Auth Token from www.twilio.com/console
// var client = new twilio(accountSid, authToken);
const code = Math.floor(100000 + Math.random() * 900000);
console.log(code, typeof code);
// converting the code to a string type as hash generation was throwing error
codestr = "";
codestr = code.toString();
hash = await bcryptPassword(codestr);
// update the phoneVerified flag to true.
const filter = {
phone: userExists.phone,
};
const update = {
$set: {
passwordResetCode: code,
"services.password.bcrypt": hash,
oneTimePasswordSetFlag: true,
},
};
console.log(update);
const doc = await User.updateOne(filter, update);
console.log(doc);
updatedUser = await User.findOne({ phone: userExists.phone });
if (updatedUser.oneTimePasswordSetFlag) {
req.body.passwordResetCode = code;
reply.send('{"armintatankdata":{"error":false,"forgotPassword": true}}');
} else {
error = {
armintatankdata: {
error: true,
code: 10007,
message: "10007 - Unable to reset password",
},
};
req.body.regError = error;
reply.send(error);
}
} else {
error = {
armintatankdata: {
error: true,
code: 10006,
message: "10006 - Please check the phone number you entered..",
},
};
req.body.regError = error;
reply.send(error);
}
} catch (err) {
throw boom.boomify(err);
}
};
exports.sendPasswordResetCode = async (req) => {
// Setting a regError in request so a user is not sent a verificaiton code if registration is not successful
// checkign if regError property is available in the req body, if it exists , do not send the message
// if it does not exisit , it impplies registraiton is succesful , as no pre handlers have set this reqError
// during registraiton validations.
if (!("regError" in req.body)) {
var user = req.body;
// // Handle if the user data is supplied via a url encoded form
// checkFormEncoding = isUserFormUrlEncoded(req);
// if (checkFormEncoding.isUserFormUrlEncoded) {
// user = checkFormEncoding.user;
// }
phone = user.phone;
console.log(
"user for which password reset code needs to be sent is ",
user
);
// var accountSid = twilioAccountSid; // Your Account SID from www.twilio.com/console
// var authToken = twilioAuthToken; // Your Auth Token from www.twilio.com/console
var client = new twilio(twilioAccountSid, twilioAuthToken);
// var code = Math.floor(100000 + Math.random() * 900000);
code = user.passwordResetCode.toString();
// Message format to allow app to autopickup sms message and
// verify the code.
// Be no longer than 140 bytes
// Begin with the prefix <#>
// Contain a one-time code that the client sends back to your server to complete the verification flow
// End with an 11-character hash string that identifies your app - this is hardcoded as FA+9qCX9VSu as a reminder.
var created = client.messages
.create({
// body: "<#> Your verification code from armintatank is " + code,
body: "Your verification code is " + code + " - from armintatank ",
to: phone, // Text this number
from: "+16193135693", // From a valid Twilio number
})
.then(function (message) {
try {
const msg = new Message(message);
msg.save();
} catch (err) {
throw err;
}
})
// .then(async function () {
// // While testing ensure that there are no multiple users with the same phone number ,
// // generally we are assumign each user will have a unique phone number . Only updated while phone verficiation code is sent
// const filter = { phone: phone };
// const update = { phoneVerificationCode: code };
// await User.findOneAndUpdate(filter, update);
// });
}
};
exports.resetPassword = async (req, reply) => {
try {
console.log(" in reset Password method");
var user = req.body;
// Handle if the user data is supplied via a url encoded form
checkFormEncoding = isUserFormUrlEncoded(req);
if (checkFormEncoding.isUserFormUrlEncoded) {
user = checkFormEncoding.user;
}
phone = user.phone;
resetPasswordCode = user.resetPasswordCode;
newPassword = user.newPassword;
hash = await bcryptPassword(newPassword);
console.log(user);
// check if user exists in the system. If user exists , display message that
// username is not available
userExists = await User.findOne({
phone: phone,
passwordResetCode: resetPasswordCode,
});
console.log(userExists);
if (userExists) {
// update the phoneVerified flag to true.
const filter = {
phone: phone,
passwordResetCode: resetPasswordCode,
};
console.log(filter);
const update = {
$set: {
"services.password.bcrypt": hash,
oneTimePasswordSetFlag: false,
},
};
console.log(update);
const doc = await User.updateOne(filter, update);
// updatedUser = await User.findOne({ phone: phone });
if (doc) {
reply.send('{"armintatankdata":{"error":false,"passwordReset": true}}');
} else {
error = {
armintatankdata: {
error: true,
code: 10007,
message: "10007 - Password Reset code entered cannot be validated.",
},
};
req.body.regError = error;
reply.send(error);
}
} else {
error = {
armintatankdata: {
error: true,
code: 10008,
message: "10008 - Either Phone or Temporary Code is Invalid.",
},
};
req.body.regError = error;
reply.send(error);
}
} catch (err) {
throw boom.boomify(err);
}
};
exports.resetPasswordFromAdmin = async (req, reply) => {
try {
var { userId } = req.body;
// check if user exists in the system. If user exists , display message that
// username is not available
userExists = await User.findById(userId);
if (userExists) {
const newPassword = generator.generate({
length: 10,
numbers: true,
});
const hash = await bcryptPassword(newPassword);
userExists.services.password.bcrypt = hash;
userExists.oneTimePasswordSetFlag = true;
const doc = await userExists.save();
await sendTextToPhone(
userExists.phone,
`Your new password is ${newPassword} - from armintatank `
);
if (doc) {
reply.send('{"armintatankdata":{"error":false,"passwordReset": true}}');
} else {
error = {
armintatankdata: {
error: true,
code: 10007,
message: "10007 - Can't set new password",
},
};
req.body.regError = error;
reply.send(error);
}
} else {
error = {
armintatankdata: {
error: true,
code: 10008,
message: "10008 - The user is not found",
},
};
req.body.regError = error;
reply.send(error);
}
} catch (err) {
throw boom.boomify(err);
}
};
exports.resetPhoneVerificationCode = async (req, reply) => {
try {
console.log(" in resetPhoneVerificationCode method");
var user = req.body;
// Handle if the user data is supplied via a url encoded form
checkFormEncoding = isUserFormUrlEncoded(req);
if (checkFormEncoding.isUserFormUrlEncoded) {
user = checkFormEncoding.user;
}
phone = user.phone;
userExists = await User.findOne({
phone: phone,
});
if (userExists) {
// update the phoneVerified flag to true.
const filter = {
phone: phone,
passwordResetCode: resetPasswordCode,
};
console.log(filter);
const update = {
$set: {
"services.password.bcrypt": hash,
oneTimePasswordSetFlag: false,
},
};
console.log(update);
const doc = await User.updateOne(filter, update);
// updatedUser = await User.findOne({ phone: phone });
if (doc) {
reply.send('{"armintatankdata":{"error":false,"passwordReset": true}}');
} else {
error = {
armintatankdata: {
error: true,
code: 10007,
message: "10007 - Password Reset code entered cannot be validated.",
},
};
req.body.regError = error;
reply.send(error);
}
} else {
error = {
armintatankdata: {
error: true,
code: 10008,
message: "10008 - Either Phone or Temporary Code is Invalid.",
},
};
req.body.regError = error;
reply.send(error);
}
} catch (err) {
throw boom.boomify(err);
}
};
exports.sendMessageNotification = async (req, reply) => {
try {
var { userId } = req.body;
userExists = await User.findById(userId);
if (userExists) {
const doc = await sendTextToPhone(
userExists.phone,
`${req.body.message} `
);
if (doc.errcode == null) {
return reply.send({
armintatankdata: { error: false, message: "Message Successfully sent" },
});
} else {
error = {
armintatankdata: {
error: true,
code: 10007,
message: "10007 - Can't set new password",
},
};
req.body.regError = error;
reply.send(error);
}
} else {
error = {
armintatankdata: {
error: true,
code: 10008,
message: "10008 - The user is not found",
},
};
req.body.regError = error;
reply.send(error);
}
} catch (err) {
throw boom.boomify(err);
}
};

@ -0,0 +1,261 @@
const AdminJSFastify = require('@adminjs/fastify')
const AdminJS = require('adminjs')
const userController = require("./controllers/userController");
const cors = require("cors");
const swagger = require("./config/swagger");
const rawBody = require("raw-body");
const uuidv4 = require("uuid").v4;
const Fastify = require("fastify")({
logger: true,
//disableRequestLogging: true,
genReqId(req) {
// you get access to the req here if you need it - must be a synchronous function
return uuidv4();
},
});
const now = () => Date.now();
const fastifyEnv = require("fastify-env");
const schema = {
type: "object",
required: ["PORT"],
properties: {
PORT: {
type: "string",
default: 3000,
},
APIVERSION: {
type: "string",
default: "1.0.0",
},
},
};
const options = {
confKey: "config", // optional, default: 'config'
schema: schema,
// data: data // optional, default: process.env
};
Fastify.register(fastifyEnv, options).ready((err) => {
if (err) console.error(err);
console.log(Fastify.config.PORT); // or fastify[options.confKey]
// output: { PORT: 3000 }
Fastify.decorate("conf", {
port: Fastify.config.PORT,
APIVERSION: Fastify.config.APIVERSION,
});
});
const apiversion = "1.0.0";
const path = require("path");
// Using static content for swagger documentation. Generated swagger UI is not user friendly.
Fastify.register(require("fastify-static"), {
root: path.join(__dirname, "api-docs"),
prefix: "/api-docs", // optional: default '/'
});
Fastify.register(require("fastify-swagger"), swagger.options);
const customJwtAuth = require("./customAuthJwt");
Fastify.register(customJwtAuth);
//login route - accept user credentials and send a token with role . "user" role is required to use the app.
// support login using application/x-www-form-urlencoded so users can login via a web form in addition to api
Fastify.register(require("fastify-formbody"));
Fastify.register((fastify, opts, done) => {
Fastify.addContentTypeParser(
"application/json",
{ parseAs: "buffer" },
function (_req, body, done) {
try {
done(null, body);
} catch (error) {
error.statusCode = 400;
done(error, undefined);
}
}
);
done(null);
});
Fastify.register(require("point-of-view"), {
engine: {
nunjucks: require("nunjucks"),
},
root: path.join(__dirname, "views"),
includeViewExtension: true,
});
// * This is for user login *
Fastify.post("/api/login", {
schema: {
description: "This is for Login User",
tags: ["Login"],
summary: "This is for User Login",
body: {
type: "object",
required: ["username", "password"],
properties: {
username: { type: "string" },
password: { type: "string" },
},
},
},
async handler(req, reply) {
loginObject = await userController.loginUser(req);
if (loginObject.same) {
const phoneVerified = loginObject.user.phoneVerified;
const oneTimePasswordSetFlag = loginObject.user.oneTimePasswordSetFlag;
console.log(
"oneTimePasswordSetFlag is ......",
oneTimePasswordSetFlag,
typeof oneTimePasswordSetFlag,
typeof phoneVerified
);
if (!phoneVerified) {
reply.send({
armintatankdata: {
error: false,
phoneVerified: false,
phone: loginObject.user.phone,
oneTimePasswordSetFlag: oneTimePasswordSetFlag,
message: "Please Verify your phone number",
},
});
} else if (oneTimePasswordSetFlag) {
reply.send({
armintatankdata: {
error: false,
phoneVerified: phoneVerified,
phone: loginObject.user.phone,
oneTimePasswordSetFlag: true,
message: "Password must be reset",
},
});
} else {
const token = fastify.jwt.sign(
{
username: loginObject.user.username,
userId: loginObject.user._id,
roles: loginObject.user.profile.role,
},
//expiresIn: expressed in seconds or a string describing a time span zeit/ms. Eg: 60, "2 days", "10h", "7d".
//A numeric value is interpreted as a seconds count. If you use a string be sure you provide the time units (days, hours, etc),
//otherwise milliseconds unit is used by default ("120" is equal to "120ms").
{ expiresIn: "12h" }
);
var arr = loginObject.user.profile.role;
var arrayToString = JSON.stringify(Object.assign({}, arr)); // convert array to string
var stringToJsonObject = JSON.parse(arrayToString); // convert string to json object
// console.log({
// username: loginObject.user.username,
// roles: loginObject.user.profile.role,
// rolesasobj: stringToJsonObject,
// });
// console.log("sending token \n");
// console.log(token);
reply.send({
armintatankdata: {
error: false,
apiversion: fastify.config.APIVERSION,
access_token: token,
username: loginObject.user.username,
phoneVerified: loginObject.user.phoneVerified,
oneTimePasswordSetFlag: loginObject.user.oneTimePasswordSetFlag,
type: loginObject.user.profile.role,
typeasobj: stringToJsonObject,
},
});
}
} else {
error = {
armintatankdata: {
error: true,
code: 400,
message: "Invalid UserId , Password supplied",
},
};
reply.send(error);
}
},
});
Fastify.get("/api/reset_token/:username", {
async handler(req, reply) {
try {
const get_user = await userController.getSingleUser(req);
const token = fastify.jwt.sign(
{
username: get_user.username,
userId: get_user._id,
roles: get_user.profile.role,
},
{ expiresIn: "12h" }
);
reply.send({ access_token: token, username: get_user.username });
} catch (err) {
console.log(err);
error = {
armintatankdata: {
error: true,
code: 400,
message: "Reset Token failed",
},
};
reply.status(401).send(error);
}
},
});
Fastify.get("/testtemp", (req, reply) => {
reply.view("layouts/main", {});
});
//fastify-auth plugin is required so we can define routes in seperate files and verify jwt supplied in preHandlers for each request.
const multer = require("fastify-multer");
Fastify.register(require("fastify-auth"));
const dbConnection = require("./config/config");
Fastify.register(dbConnection);
Fastify.register(multer.contentParser);
const { Schema } = require("mongoose");
// fastify.register(dbConnection);
Fastify.register(require("./routes/usersRoute"));
// Testing route allows for retrieving a user by phone so one can see what is the phone verification code sent for a given user's phone
// Also allows deletion of a user with a given phone number
Fastify.register(require("./routes/forTestingRoute"));
const start = async () => {
const app = Fastify
const admin = new AdminJS({
databases: [],
rootPath: '/admin'
})
try {
// await AdminJSFastify.buildRouter(
// admin,
// app,
// )
await app.listen(3000, "0.0.0.0");
Fastify.log.info(`listening on ${Fastify.server.address().port}`);
Fastify.log.info(`server listening on ${Fastify.config}`);
} catch (err) {
Fastify.log.error(err);
process.exit(1);
}
};
start();

@ -0,0 +1,40 @@
const mongoose = require("mongoose");
const dbConnection = require("../config/config.js");
mongoose.connection = dbConnection;
// require clak module to give colors to console text.
var chalk = require("chalk");
// Define colors for db messages on cosole.
const Schema = mongoose.Schema;
const messageSchema = new mongoose.Schema(
{
body: String,
numSegments: String,
direction: String,
from: String,
to: String,
dateUpdated: Date,
price: String,
errorMessage: String,
uri: String,
accountSid: String,
numMedia: String,
status: String,
messagingServiceSid: String,
sid: String,
dateSent: Date,
dateCreated: Date,
errorCode: String,
priceUnit: String,
apiVersion: String,
subresourceUris: {
media: String,
},
},
{ versionKey: false }
);
module.exports = mongoose.model("Message", messageSchema);

@ -0,0 +1,79 @@
const mongoose = require("mongoose");
// const dbConnection =require('../config/config.js');
// mongoose.connection = dbConnection;
// const { schema } = require('./Asset');
const Schema = mongoose.Schema;
const ObjectId = Schema.Types.ObjectId;
// Store a random password reset code
const code = Math.floor(100000 + Math.random() * 900000);
const RoleSchema = new Schema({ name: String });
const userSchema = new mongoose.Schema(
{
username: { type: String, unique: true, trim: true },
phone: { type: String, unique: true, trim: true },
address: String,
phoneVerified: { type: Boolean, default: false },
phoneVerificationCode: { type: Number, default: 11111 },
passwordResetCode: { type: Number, default: code },
oneTimePasswordSetFlag: { type: Boolean, default: false },
emails: [{ email: String, verified: { type: Boolean, default: false } }],
services: { password: { bcrypt: String } },
profile: {
role: [{ type: String, default: "user" }],
firstName: { type: String, default: null },
lastName: { type: String, default: null },
contactNumber: { type: String, default: null },
address1: { type: String, default: null },
address2: { type: String, default: null },
city: { type: String, default: null },
state: { type: String, default: null },
country: { type: String, default: null },
zip: { type: String, default: null },
notes: { type: String, default: null },
},
stripeCustomerId: String,
stripePaymentIntentId: String,
stripeSubscriptionId: String,
stripeSubscriptionStatus: { type: Boolean, default: false },
stripePaymentStatus: { type: Boolean, default: false },
stripePlanId: String,
stripeAmountReceived: Number,
stripeSubscripedOn: { type: Date },
stripeCancelledOn: { type: Date },
stripeData: Object,
notes: String,
currentGPS: {
// It's important to define type within type field, because
// mongoose use "type" to identify field's object type.
gpsType: { type: String, default: "Point" },
// Default value is needed. Mongoose pass an empty array to
// array type by default, but it will fail MongoDB's pre-save
// validation.
coordinates: { type: [Number], default: [0, 0] },
},
isActive: Boolean,
tenantId: ObjectId,
createdAt: {
type: Date,
default: function () {
return Date.now();
},
},
createdBy: ObjectId,
updatedAt: {
type: Date,
default: function () {
return Date.now();
},
},
updatedBy: ObjectId,
},
{ versionKey: false }
);
module.exports = mongoose.model("User", userSchema);

@ -0,0 +1,62 @@
const fastify = require("fastify");
const userController = require("../controllers/userController");
module.exports = function (fastify, opts, next) {
fastify.route({
method: "POST",
url: "/api/forTesting/getphoneuser",
schema: {
tags: ["ForTesting"],
description:
"This is get the user in the system for a given phone number",
summary:
"Pass the phone in the format +11234567890 ( us format with the country code )",
body: {
type: "object",
required: ["phone"],
properties: {
phone: { type: "string" },
},
},
security: [
{
basicAuth: [],
},
],
},
//preHandler: fastify.auth([fastify.authenticate]),
handler: userController.getPhoneUser,
});
fastify.route({
method: "POST",
url: "/api/forTesting/delphoneuser",
schema: {
tags: ["ForTesting"],
description:
"This is delete a user in the system for a given phone number",
summary:
"Pass the phone in the format +11234567890 ( us format with the country code )",
body: {
type: "object",
required: ["phone"],
properties: {
phone: { type: "string" },
},
},
security: [
{
basicAuth: [],
},
],
},
// Add authentication after testing
//preHandler: fastify.auth([fastify.authenticate]),
handler: userController.delPhoneUser,
});
// Login for a user is in the main index.js file.
// fastify-jwt used to create the token was throwing exceptions and requierd
// it be called before the route is loaded.
next();
};

@ -0,0 +1,369 @@
const fastify = require("fastify");
const userController = require("../controllers/userController");
const validationHandler = require("../handlers/userHandler");
module.exports = function (fastify, opts, next) {
fastify.get("/api/users", {
schema: {
tags: ["User"],
description: "This is for Get All Users",
summary: "This is for to Get All Users",
security: [
{
basicAuth: [],
},
],
},
preHandler: fastify.auth([fastify.authenticate]),
handler: userController.getUsers,
});
fastify.route({
method: "POST",
url: "/api/users/:username",
schema: {
description: "To Get user by username",
tags: ["User"],
summary: "This is for Get a Single User by Username",
params: {
type: "object",
properties: {
username: {
type: "string",
description: "username",
},
},
},
security: [
{
basicAuth: [],
},
],
},
preHandler: fastify.auth([fastify.authenticate]),
handler: userController.getSingleUser,
});
fastify.route({
method: "POST",
url: "/api/currentUser",
schema: {
tags: ["User"],
description: "This is for Get Current User by User Name by Post Body",
summary: "This is for Get a Current User.",
body: {
type: "object",
required: ["username"],
properties: {
username: { type: "string" },
},
},
security: [
{
basicAuth: [],
},
],
},
preHandler: fastify.auth([fastify.authenticate]),
handler: userController.getCurrentUser,
// onSend: (request, reply, done) => {
// // fire&forget
// request.log.info("#########################################");
// request.log.info(reply);
// done()
// },
});
fastify.route({
method: "PUT",
url: "/api/user/:userId",
schema: {
tags: ["User"],
summary: "This is for update user",
params: {
type: "object",
properties: {
userId: {
type: "string",
description: "user id",
},
},
},
body: {
type: "object",
// required: ['phone'],
properties: {
phone: { type: "string" },
firstName: { type: "string" },
lastName: { type: "string" },
address1: { type: "string" },
address2: { type: "string" },
city: { type: "string" },
state: { type: "string" },
country: { type: "string" },
zip: { type: "string" },
},
},
security: [
{
basicAuth: [],
},
],
},
preHandler: [
fastify.auth([fastify.operatorAuthenticate]),
validationHandler.validatePhoneFormat,
],
// preHandler: fastify.auth([fastify.authenticate]),
handler: userController.editUserInfo,
});
fastify.route({
method: "POST",
url: "/api/users",
schema: {
tags: ["User"],
description: "This is for cretae New user",
summary: "This is for Create New User.",
body: {
type: "object",
properties: {
username: { type: "string" },
password: { type: "string" },
emails: {
type: "array",
maxItems: 2,
items: {
type: "object",
properties: {
email: { type: "string", default: null },
},
},
},
firstName: { type: "string", default: null },
lastName: { type: "string", default: null },
phone: { type: "string" },
address1: { type: "string", default: null },
address2: { type: "string", default: null },
city: { type: "string", default: null },
state: { type: "string", default: null },
zip: { type: "string", default: null },
country: { type: "string", default: null },
notes: { type: "string", default: null },
},
},
security: [
{
basicAuth: [],
},
],
},
preHandler: [
validationHandler.fieldCheck,
validationHandler.verifyUser,
// validationHandler.validatePhoneFormat,
validationHandler.validateEmailFormat,
],
handler: userController.addUser,
// onResponse: (request, reply) => {
// validationHandler.sendPhoneVerificationCode(request, reply);
// },
//onResponse: validationHandler.sendPhoneVerificationCode,
});
fastify.route({
method: "POST",
url: "/api/phone",
schema: {
tags: ["User"],
description: "This is for verify User Phone",
summary: "This is to Verify User Phone.",
body: {
type: "object",
required: ["phone"],
properties: {
phoneVerificationCode: { type: "string" },
phone: { type: "string" },
},
},
security: [
{
basicAuth: [],
},
],
},
// preHandler: fastify.auth([fastify.authenticate]),
handler: validationHandler.verifyPhone,
});
fastify.route({
method: "POST",
url: "/api/forgotpassword",
schema: {
tags: ["User"],
description: "This is for forget password for the User.",
summary: "This is for forget User Password.",
body: {
type: "object",
required: ["phone"],
properties: {
phone: { type: "string" },
},
},
security: [
{
basicAuth: [],
},
],
},
// preHandler: [validationHandler.],
handler: validationHandler.forgotPassword,
onResponse: (request, reply) => {
validationHandler.sendPasswordResetCode(request, reply);
},
});
fastify.route({
method: "POST",
url: "/api/resetpassword",
schema: {
tags: ["User"],
description: "This is for Reset User Password.",
summary: "This is for Reset User Password.",
body: {
type: "object",
required: ["phone", "resetPasswordCode", "newPassword"],
properties: {
phone: { type: "string" },
resetPasswordCode: { type: "string" },
newPassword: { type: "string" },
},
},
security: [
{
basicAuth: [],
},
],
},
// preHandler: [validationHandler.],
handler: validationHandler.resetPassword,
// onResponse: (request,reply) => {validationHandler.resetPassword(request,reply)}
});
fastify.route({
method: "POST",
url: "/api/resetPasswordFromAdmin",
schema: {
tags: ["User"],
description: "This is for Reset Password for Admin.",
summary: "This is for Reset Password for Admin.",
body: {
type: "object",
properties: {
userId: { type: "string" },
},
},
security: [
{
basicAuth: [],
},
],
},
// preHandler: [validationHandler.],
handler: validationHandler.resetPasswordFromAdmin,
// onResponse: (request,reply) => {validationHandler.resetPassword(request,reply)}
});
fastify.route({
method: "POST",
url: "/api/resendphoneverificationcode",
schema: {
tags: ["User"],
description: "This is for Reset phone Verification Code.",
summary: "This is for Reset phone verification Code.",
body: {
type: "object",
required: ["phone"],
properties: {
phone: { type: "string" },
},
},
security: [
{
basicAuth: [],
},
],
},
// preHandler: [validationHandler.],
handler: validationHandler.sendPhoneVerificationCode,
// onResponse: (request,reply) => {validationHandler.sendPhoneVerificationCode(request,reply)}
});
fastify.route({
method: "POST",
url: "/api/users/send_message",
schema: {
tags: ["User"],
description: "This is for Send Message.",
summary: "This is for Send Message.",
body: {
type: "object",
required: ["userId"],
properties: {
userId: { type: "string" },
},
},
security: [
{
basicAuth: [],
},
],
},
handler: validationHandler.sendMessageNotification,
});
fastify.route({
method: "PUT",
url: "/api/update/currentUser/:username",
schema: {
tags: ["User"],
summary: "This is for update current user",
params: {
type: "object",
properties: {
username: {
type: "string",
description: "username",
},
},
},
body: {
type: "object",
properties: {
phone: { type: "string" },
firstName: { type: "string" },
lastName: { type: "string" },
address1: { type: "string" },
address2: { type: "string" },
city: { type: "string" },
state: { type: "string" },
country: { type: "string" },
zip: { type: "string" },
},
},
security: [
{
basicAuth: [],
},
],
},
preHandler: [fastify.auth([fastify.authenticate])],
handler: userController.editCuurentUserInfo,
});
// Login for a user is in the main index.js file.
// fastify-jwt used to create the token was throwing exceptions and requierd
// it be called before the route is loaded.
next();
};
Loading…
Cancel
Save