-
\ No newline at end of file
diff --git a/Development/client/src/app/tools/upload/upload.component.css b/Development/client/src/app/tools/upload/upload.component.css
deleted file mode 100644
index e69de29..0000000
diff --git a/Development/client/src/assets/layout/css/layout-green.min.css b/Development/client/src/assets/layout/css/layout-green.min.css
deleted file mode 100644
index 65e14cb..0000000
--- a/Development/client/src/assets/layout/css/layout-green.min.css
+++ /dev/null
@@ -1 +0,0 @@
-@font-face{font-family:"Roboto";font-style:normal;font-weight:300;src:url("../fonts/roboto-v15-latin-300.eot");src:local("Roboto Light"),local("Roboto-Light"),url("../fonts/roboto-v15-latin-300.eot?#iefix") format("embedded-opentype"),url("../fonts/roboto-v15-latin-300.woff2") format("woff2"),url("../fonts/roboto-v15-latin-300.woff") format("woff"),url("../fonts/roboto-v15-latin-300.ttf") format("truetype"),url("../fonts/roboto-v15-latin-300.svg#Roboto") format("svg")}@font-face{font-family:"Roboto";font-style:normal;font-weight:400;src:url("../fonts/roboto-v15-latin-regular.eot");src:local("Roboto"),local("Roboto-Regular"),url("../fonts/roboto-v15-latin-regular.eot#iefix") format("embedded-opentype"),url("../fonts/roboto-v15-latin-regular.woff2") format("woff2"),url("../fonts/roboto-v15-latin-regular.woff") format("woff"),url("../fonts/roboto-v15-latin-regular.ttf") format("truetype"),url("../fonts/roboto-v15-latin-regular.svg#Roboto") format("svg")}@font-face{font-family:"Roboto";font-style:normal;font-weight:700;src:url("../fonts/roboto-v15-latin-700.eot");src:local("Roboto Bold"),local("Roboto-Bold"),url("../fonts/roboto-v15-latin-700.eot#iefix") format("embedded-opentype"),url("../fonts/roboto-v15-latin-700.woff2") format("woff2"),url("../fonts/roboto-v15-latin-700.woff") format("woff"),url("../fonts/roboto-v15-latin-700.ttf") format("truetype"),url("../fonts/roboto-v15-latin-700.svg#Roboto") format("svg")}@font-face{font-family:"Material Icons";font-style:normal;font-weight:400;src:url("../fonts/MaterialIcons-Regular.eot");src:local("Material Icons"),local("MaterialIcons-Regular"),url("../fonts/MaterialIcons-Regular.woff2") format("woff2"),url("../fonts/MaterialIcons-Regular.woff") format("woff"),url("../fonts/MaterialIcons-Regular.ttf") format("truetype")}.clearfix:after{content:" ";display:block;clear:both}*[hidden]{display:none}.card{box-shadow:0 1px 3px 0 rgba(0,0,0,0.2),0 1px 1px 0 rgba(0,0,0,0.14),0 2px 1px -1px rgba(0,0,0,0.12);-webkit-box-shadow:0 1px 3px 0 rgba(0,0,0,0.2),0 1px 1px 0 rgba(0,0,0,0.14),0 2px 1px -1px rgba(0,0,0,0.12);-moz-box-shadow:0 1px 3px 0 rgba(0,0,0,0.2),0 1px 1px 0 rgba(0,0,0,0.14),0 2px 1px -1px rgba(0,0,0,0.12);-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;background:#fff;padding:1em;margin-bottom:1em;box-sizing:border-box}.card.card-w-title{padding-bottom:2em}.card h1{font-size:1.5em;font-weight:400;margin:1em 0}.card h1:first-child{margin-top:.667em}.card h2{font-size:1.375em;font-weight:400}.card h3{font-size:1.25em;font-weight:400}.card h4{font-size:1.125em;font-weight:400}.nopad{padding:0}.nopad .ui-panel-content{padding:0}@-webkit-keyframes fadeInDown{from{opacity:0;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDown{from{opacity:0;transform:translate3d(0,-20px,0)}to{opacity:1;transform:none}}@-webkit-keyframes fadeOutUp{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}}@keyframes fadeOutUp{from{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}}@keyframes rippleOn{0%{opacity:.5}100%{opacity:0;transform:scale(13,13)}}@keyframes rippleOff{0%{opacity:.5}100%{opacity:0;transform:scale(13,13)}}@-webkit-keyframes spin{from{-webkit-transform:rotate(0)}to{-webkit-transform:rotate(359deg)}}@keyframes spin{from{transform:rotate(0)}to{transform:rotate(359deg)}}.ui-icon-spin{-webkit-animation-name:spin;animation-name:spin}.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}.fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}.ui-shadow-1{-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.12),0 1px 2px rgba(0,0,0,0.24);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.12),0 1px 2px rgba(0,0,0,0.24);box-shadow:0 1px 3px rgba(0,0,0,0.12),0 1px 2px rgba(0,0,0,0.24)}.ui-shadow-2{-webkit-box-shadow:0 3px 6px rgba(0,0,0,0.16),0 3px 6px rgba(0,0,0,0.23);-moz-box-shadow:0 3px 6px rgba(0,0,0,0.16),0 3px 6px rgba(0,0,0,0.23);box-shadow:0 3px 6px rgba(0,0,0,0.16),0 3px 6px rgba(0,0,0,0.23)}.ui-shadow-3{-webkit-box-shadow:0 10px 20px rgba(0,0,0,0.19),0 6px 6px rgba(0,0,0,0.23);-moz-box-shadow:0 10px 20px rgba(0,0,0,0.19),0 6px 6px rgba(0,0,0,0.23);box-shadow:0 10px 20px rgba(0,0,0,0.19),0 6px 6px rgba(0,0,0,0.23)}.ui-shadow-4{-webkit-box-shadow:0 14px 28px rgba(0,0,0,0.25),0 10px 10px rgba(0,0,0,0.22);-moz-box-shadow:0 14px 28px rgba(0,0,0,0.25),0 10px 10px rgba(0,0,0,0.22);box-shadow:0 14px 28px rgba(0,0,0,0.25),0 10px 10px rgba(0,0,0,0.22)}.ui-shadow-5{-webkit-box-shadow:0 19px 38px rgba(0,0,0,0.3),0 15px 12px rgba(0,0,0,0.22);-moz-box-shadow:0 19px 38px rgba(0,0,0,0.3),0 15px 12px rgba(0,0,0,0.22);box-shadow:0 19px 38px rgba(0,0,0,0.3),0 15px 12px rgba(0,0,0,0.22)}.ui-g{-ms-flex-wrap:wrap}.ui-g.form-group>div{padding:1em}.ui-g.form-group-m>div{padding:1em}.ripplelink{text-decoration:none;position:relative;overflow:hidden;-webkit-transition:all .2s ease;-moz-transition:all .2s ease;-o-transition:all .2s ease;transition:all .2s ease;z-index:0}.ink{display:block;position:absolute;background:rgba(255,255,255,0.4);border-radius:100%;-webkit-transform:scale(0);-moz-transform:scale(0);-o-transform:scale(0);transform:scale(0)}.ripple-animate{-webkit-animation:ripple .65s linear;-moz-animation:ripple .65s linear;-ms-animation:ripple .65s linear;-o-animation:ripple .65s linear;animation:ripple .65s linear}@-webkit-keyframes ripple{100%{opacity:0;-webkit-transform:scale(2.5)}}@-moz-keyframes ripple{100%{opacity:0;-moz-transform:scale(2.5)}}@-o-keyframes ripple{100%{opacity:0;-o-transform:scale(2.5)}}@keyframes ripple{100%{opacity:0;transform:scale(2.5)}}@keyframes rippleOn{0%{opacity:.5}100%{opacity:0;transform:scale(13,13)}}@keyframes rippleOff{0%{opacity:.5}100%{opacity:0;transform:scale(13,13)}}.splash-screen{width:100%;min-height:100%;background-color:#4caf50;position:absolute}.splash-loader-container{text-align:center;position:absolute;top:50%;left:50%;margin-left:-32px;margin-top:-32px}.splash-loader{animation:rotator 1.4s linear infinite}@keyframes rotator{0%{transform:rotate(0)}100%{transform:rotate(270deg)}}.splash-path{stroke-dasharray:187;stroke-dashoffset:0;transform-origin:center;animation:dash 1.4s ease-in-out infinite,colors 5.6s ease-in-out infinite}@keyframes colors{0%{stroke:#4285f4}25%{stroke:#de3e35}50%{stroke:#f7c223}75%{stroke:#1b9a59}100%{stroke:#4285f4}}@keyframes dash{0%{stroke-dashoffset:187}50%{stroke-dashoffset:46.75;transform:rotate(135deg)}100%{stroke-dashoffset:187;transform:rotate(450deg)}}.dashboard .overview{padding:0 !important;min-height:140px;position:relative;margin-bottom:0 !important}.dashboard .overview .overview-content{padding:16px}.dashboard .overview .overview-content .overview-title{font-size:18px}.dashboard .overview .overview-content .overview-badge{float:right;color:#757575}.dashboard .overview .overview-content .overview-detail{display:block;font-size:24px;margin-top:5px}.dashboard .overview .overview-footer{position:absolute;bottom:0;width:100%}.dashboard .overview .overview-footer img{display:block}.dashboard .colorbox{padding:0 !important;text-align:center;overflow:hidden;margin-bottom:0 !important}.dashboard .colorbox i{font-size:48px;margin-top:10px;color:#fff}.dashboard .colorbox .colorbox-name{font-size:20px;display:inline-block;width:100%;margin:4px 0 10px 0;color:#fff}.dashboard .colorbox .colorbox-count{color:#fff;font-size:36px}.dashboard .colorbox .colorbox-count{font-weight:bold}.dashboard .colorbox.colorbox-1{background-color:#4caf50}.dashboard .colorbox.colorbox-1 div:first-child{background-color:#2e7d32}.dashboard .colorbox.colorbox-2{background-color:#03a9f4}.dashboard .colorbox.colorbox-2 div:first-child{background-color:#0277bd}.dashboard .colorbox.colorbox-3{background-color:#673ab7}.dashboard .colorbox.colorbox-3 div:first-child{background-color:#4527a0}.dashboard .colorbox.colorbox-4{background-color:#009688}.dashboard .colorbox.colorbox-4 div:first-child{background-color:#00695c}.dashboard .task-list{overflow:hidden}.dashboard .task-list>.ui-panel{min-height:340px}.dashboard .task-list .ui-panel-content{padding:10px 0 !important}.dashboard .task-list ul{list-style-type:none;margin:0;padding:0}.dashboard .task-list ul li{padding:.625em .875em;border-bottom:1px solid #dbdbdb}.dashboard .task-list ul li:first-child{margin-top:10px}.dashboard .task-list ul .ui-chkbox{vertical-align:middle;margin-right:5px}.dashboard .task-list ul .task-name{vertical-align:middle}.dashboard .task-list ul i{color:#757575;float:right}.dashboard .contact-form{overflow:hidden}.dashboard .contact-form .ui-panel{min-height:340px}.dashboard .contact-form .ui-g-12{padding:16px 10px}.dashboard .contact-form .ui-button{margin-top:20px}.dashboard .contacts{overflow:hidden}.dashboard .contacts>.ui-panel{min-height:340px}.dashboard .contacts .ui-panel-content{padding:15px 0 10px 0 !important}.dashboard .contacts ul{list-style-type:none;padding:0;margin:0}.dashboard .contacts ul li{border-bottom:1px solid #d8d8d8}.dashboard .contacts ul li a{padding:9px;width:100%;box-sizing:border-box;text-decoration:none;position:relative;display:block;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-moz-transition:background-color .2s;-o-transition:background-color .2s;-webkit-transition:background-color .2s;transition:background-color .2s}.dashboard .contacts ul li a .name{position:absolute;right:10px;top:10px;font-size:18px;color:#212121}.dashboard .contacts ul li a .email{position:absolute;right:10px;top:30px;font-size:14px;color:#757575}.dashboard .contacts ul li a:hover{cursor:pointer;background-color:#e8e8e8}.dashboard .contacts ul li:last-child{border:0}.dashboard .activity-list{list-style-type:none;padding:0;margin:0}.dashboard .activity-list li{border-bottom:1px solid #bdbdbd;padding:15px 0 9px 9px}.dashboard .activity-list li .count{font-size:24px;color:#fff;background-color:#03a9f4;font-weight:bold;display:inline-block;padding:5px;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px}.dashboard .activity-list li:first-child{border-top:1px solid #bdbdbd}.dashboard .activity-list li:last-child{border:0}.dashboard .activity-list li .ui-g-6:first-child{font-size:18px;padding-left:0}.dashboard .activity-list li .ui-g-6:last-child{text-align:right;color:#757575}.dashboard .timeline{height:100%;box-sizing:border-box}.dashboard .timeline>.ui-g .ui-g-3{font-size:14px;position:relative;border-right:1px solid #bdbdbd}.dashboard .timeline>.ui-g .ui-g-3 i{background-color:#fff;font-size:36px;position:absolute;top:0;right:-18px}.dashboard .timeline>.ui-g .ui-g-9{padding-left:1.5em}.dashboard .timeline>.ui-g .ui-g-9 .event-text{color:#757575;font-size:14px;display:block;padding-bottom:20px}.dashboard .timeline>.ui-g .ui-g-9 .event-content img{width:100%}.dashboard>div>.ui-panel{box-shadow:0 1px 3px 0 rgba(0,0,0,0.2),0 1px 1px 0 rgba(0,0,0,0.14),0 2px 1px -1px rgba(0,0,0,0.12);-webkit-box-shadow:0 1px 3px 0 rgba(0,0,0,0.2),0 1px 1px 0 rgba(0,0,0,0.14),0 2px 1px -1px rgba(0,0,0,0.12);-moz-box-shadow:0 1px 3px 0 rgba(0,0,0,0.2),0 1px 1px 0 rgba(0,0,0,0.14),0 2px 1px -1px rgba(0,0,0,0.12)}.layout-rightpanel .layout-rightpanel-header{background:url("../images/dashboard/sidebar-image.jpg") no-repeat;background-size:cover;height:118px;padding:20px 14px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.layout-rightpanel .layout-rightpanel-header .weather-day,.layout-rightpanel .layout-rightpanel-header .weather-date{color:#fff;left:14px;font-size:18px;font-weight:700;padding-bottom:4px}.layout-rightpanel .layout-rightpanel-content{padding:14px}.layout-rightpanel .layout-rightpanel-content h1{font-size:18px;margin:0 0 4px 0}.layout-rightpanel .layout-rightpanel-content h2{font-size:16px;margin:0;color:#757575;font-weight:normal}.layout-rightpanel .layout-rightpanel-content .weather-today{text-align:center;margin-top:28px}.layout-rightpanel .layout-rightpanel-content .weather-today .weather-today-value{font-size:36px;vertical-align:middle;margin-right:14px}.layout-rightpanel .layout-rightpanel-content .weather-today img{vertical-align:middle}.layout-rightpanel .layout-rightpanel-content .weekly-weather{list-style-type:none;margin:28px 0 0 0;padding:0}.layout-rightpanel .layout-rightpanel-content .weekly-weather li{padding:8px 14px;border-bottom:1px solid #d8dae2;position:relative}.layout-rightpanel .layout-rightpanel-content .weekly-weather li .weekly-weather-value{position:absolute;right:40px}.layout-rightpanel .layout-rightpanel-content .weekly-weather li img{width:24px;position:absolute;right:0;top:4px}.login-body{padding:1px;background:url("../images/login/login.png") top left no-repeat #f7f7f7;background-size:100% auto;height:auto}.login-panel{text-align:center;width:350px;min-height:440px;padding:50px 20px;margin:100px auto 0 auto}.login-panel .ui-g .ui-g-12{padding:25px 40px}.login-panel .ui-g .ui-g-12 .ui-button{margin-bottom:20px}.login-panel .ui-button:hover{background-color:#2e7d32}.login-panel .ui-button:focus{outline:0 none;background-color:#6ec071}.login-panel .ui-button.secondary:hover{background-color:#4527a0}.login-panel .ui-button.secondary:focus{outline:0 none;background-color:#fff06e}.login-footer{position:absolute;bottom:10px;font-size:16px;width:100%;text-align:center;color:#757575}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.login-body{background:url("../images/login/login2x.png") top left no-repeat #f7f7f7;background-size:100% auto}}@media(max-width:1024px){.login-panel{text-align:center;min-height:440px;margin:100px auto 0 auto}}@media(max-width:640px){.login-panel{text-align:center;width:300px;min-height:440px;padding:40px 20px;margin:75px auto 0 auto}.login-panel .ui-g .ui-g-12{padding:20px 20px}.login-panel .ui-g .ui-g-12 .ui-button{margin-top:30px}}.exception-body{background-color:#f7f7f7;height:auto}.exception-body .exception-type{width:100%;height:50%;padding:100px 100px 0 100px;box-sizing:border-box;text-align:center}.exception-body .exception-panel{text-align:center;width:350px;padding:35px;margin:-10% auto 0 auto;z-index:100}.exception-body .exception-panel i{font-size:72px}.exception-body .exception-panel h1{font-size:36px;line-height:36px;color:#757575}.exception-body .exception-panel .exception-detail{margin:20px 0 100px 0;color:#757575}.exception-body .ui-button{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}.exception-body .ui-button:hover{background-color:#4527a0}.exception-body.error-page .exception-type{background-color:#e62a10}.exception-body.error-page .exception-type img{width:100%}.exception-body.error-page .exception-panel i{color:#f79a84}.exception-body.notfound-page .exception-type{background-color:#3f51b5}.exception-body.notfound-page .exception-type img{width:54%}.exception-body.notfound-page .exception-panel i{color:#9fa8da}.exception-body.accessdenied-page .exception-type{background-color:#e91e63}.exception-body.accessdenied-page .exception-type img{width:50%}.exception-body.accessdenied-page .exception-panel i{color:#f48fb1}@media(max-width:1024px){.exception-body .exception-panel{margin-top:-50px}}@media(max-width:640px){.exception-body .exception-panel{width:250px;margin-top:-15px}}.landing-wrapper .ui-button{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px}.landing-wrapper .ui-button:hover{background-color:#4527a0}.landing-wrapper #header{width:100%;min-height:400px;background:url("../images/landing/landing-header.png") top left no-repeat #f7f7f7;background-size:100% auto}.landing-wrapper #header .header-top{width:960px;margin:0 auto;padding:30px 0}.landing-wrapper #header .header-top .logo{display:inline-block;vertical-align:middle;width:200px;height:30px;background:url("../images/logo.png") top left no-repeat}.landing-wrapper #header .header-top #menu{float:right;list-style:none;margin:0;padding:0}.landing-wrapper #header .header-top #menu li{float:left;display:block;margin-left:30px}.landing-wrapper #header .header-top #menu li a{color:#fff}.landing-wrapper #header .header-top #menu li i{display:none}.landing-wrapper #header .header-top #menu.lmenu-active{display:block}.landing-wrapper #header .header-top #menu-button{height:36px;margin-top:-2px;float:right;color:#fff;display:none}.landing-wrapper #header .header-top #menu-button i{font-size:36px}.landing-wrapper #header .header-content{width:960px;margin:0 auto;text-align:center}.landing-wrapper #header .header-content h1{margin:75px 0 50px 0;font-weight:400;color:#fff;line-height:36px}.landing-wrapper #features{width:960px;margin:0 auto;padding:50px 0;text-align:center}.landing-wrapper #features h2{font-weight:400;line-height:28px}.landing-wrapper #features h3{font-weight:400}.landing-wrapper #features p{color:#757575}.landing-wrapper #features .ui-g-12{padding:2em .5em}.landing-wrapper #features .feature-icon{display:inline-block;-moz-border-radius:50%;-webkit-border-radius:50%;border-radius:50%;background-color:#f4f8fc;box-sizing:border-box;width:100px;height:100px;text-align:center;-moz-transition:background-color .3s;-o-transition:background-color .3s;-webkit-transition:background-color .3s;transition:background-color .3s}.landing-wrapper #features .feature-icon i{margin-top:30px;font-size:36px}.landing-wrapper #features .feature-icon:hover{background-color:#e91e63}.landing-wrapper #features .feature-icon:hover i{color:#fff}.landing-wrapper #promotion{background:url("../images/landing/promotion.png") top left no-repeat;background-size:100% auto}.landing-wrapper #promotion .ui-lg-8{padding:150px 0 0 150px}.landing-wrapper #promotion .ui-lg-8 h1{font-weight:48px;color:#fff;font-weight:400}.landing-wrapper #promotion .ui-lg-4{margin:-50px 0 -50px 0}.landing-wrapper #promotion .ui-lg-4 .card{-webkit-box-shadow:0 0 27px 4.5px rgba(13,36,62,0.1);-moz-box-shadow:0 0 27px 4.5px rgba(13,36,62,0.1);box-shadow:0 0 27px 4.5px rgba(13,36,62,0.1);margin-bottom:20px}.landing-wrapper #promotion .ui-lg-4 .card h3{font-weight:400}.landing-wrapper #promotion .ui-lg-4 .card p{color:#757575}.landing-wrapper #promotion .ui-lg-4 .card:last-child{margin-bottom:0}.landing-wrapper #pricing{width:960px;margin:0 auto;padding:50px 0;text-align:center}.landing-wrapper #pricing h2{font-weight:400}.landing-wrapper #pricing p{color:#757575}.landing-wrapper #pricing .pricing-box .card{height:100%;padding:0}.landing-wrapper #pricing .pricing-box .pricing-header{padding:40px 0;color:#fff}.landing-wrapper #pricing .pricing-box .pricing-header span{display:block;line-height:48px}.landing-wrapper #pricing .pricing-box .pricing-header span.name{font-weight:300;font-size:24px}.landing-wrapper #pricing .pricing-box .pricing-header span.fee{font-size:48px;font-weight:700}.landing-wrapper #pricing .pricing-box .pricing-header span.type{font-weight:300;font-size:16px}.landing-wrapper #pricing .pricing-box .pricing-content ul{margin:0;padding:30px 20px;list-style-type:none}.landing-wrapper #pricing .pricing-box .pricing-content ul li{font-size:18px;text-align:left;padding:10px 14px}.landing-wrapper #pricing .pricing-box .pricing-content ul li i{margin-right:20px;vertical-align:middle}.landing-wrapper #pricing .pricing-box .pricing-content ul li span{vertical-align:middle}.landing-wrapper #pricing .pricing-box.pricing-basic .pricing-header{background-color:#3f51b5}.landing-wrapper #pricing .pricing-box.pricing-basic i{color:#3f51b5}.landing-wrapper #pricing .pricing-box.pricing-standard .pricing-header{background-color:#e91e63}.landing-wrapper #pricing .pricing-box.pricing-standard i{color:#e91e63}.landing-wrapper #pricing .pricing-box.pricing-professional .pricing-header{background-color:#607d8b}.landing-wrapper #pricing .pricing-box.pricing-professional i{color:#607d8b}.landing-wrapper #video{background-color:#f7f7f7;min-width:400px}.landing-wrapper #video .video-content{width:960px;margin:0 auto;padding:50px 0;text-align:center}.landing-wrapper #video .video-content h2{font-weight:400}.landing-wrapper #video .video-content p{color:#757575}.landing-wrapper .footer{background-color:#f7f7f7;border-top:1px solid #ddd}.landing-wrapper .footer .footer-content{width:960px;margin:0 auto;padding:30px 0 50px 0}.landing-wrapper .footer .footer-content ul{float:right;list-style-type:none}.landing-wrapper .footer .footer-content ul li a{color:#757575;-moz-transition:color .3s;-o-transition:color .3s;-webkit-transition:color .3s;transition:color .3s}.landing-wrapper .footer .footer-content ul li a:hover{color:#212121}@media(max-width:1024px){.landing-wrapper #header{min-height:200px;background-size:cover}.landing-wrapper #header .header-top{z-index:100;position:fixed;top:0;background:#424242;background-size:100% auto;padding:30px;width:100%;box-sizing:border-box;-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,0.26);-moz-box-shadow:0 2px 5px 0 rgba(0,0,0,0.26);box-shadow:0 2px 5px 0 rgba(0,0,0,0.26)}.landing-wrapper #header .header-top #menu-button{display:inline-block}.landing-wrapper #header .header-top #menu{z-index:100;position:fixed;top:86px;right:30px;float:none;display:none;margin:0;padding:0;width:225px;list-style:none;background-color:#fff;-webkit-box-shadow:0 6px 20px 0 rgba(0,0,0,0.19),0 8px 17px 0 rgba(0,0,0,0.2);-moz-box-shadow:0 6px 20px 0 rgba(0,0,0,0.19),0 8px 17px 0 rgba(0,0,0,0.2);box-shadow:0 6px 20px 0 rgba(0,0,0,0.19),0 8px 17px 0 rgba(0,0,0,0.2);-webkit-animation-duration:.5s;-moz-animation-duration:.5s;animation-duration:.5s}.landing-wrapper #header .header-top #menu li{float:none;margin-left:0}.landing-wrapper #header .header-top #menu li a{font-size:16px;display:block;padding:10px 16px;color:#212121;width:100%;box-sizing:border-box;-moz-transition:background-color .3s;-o-transition:background-color .3s;-webkit-transition:background-color .3s;transition:background-color .3s;overflow:hidden}.landing-wrapper #header .header-top #menu li a i{color:#757575;display:inline-block;vertical-align:middle;margin-right:12px;font-size:24px}.landing-wrapper #header .header-top #menu li a:hover{background-color:#e8e8e8}.landing-wrapper #header .header-top #menu li a span{display:inline-block;vertical-align:middle}.landing-wrapper #header .header-content{width:100%;padding:100px 30px 60px 30px;box-sizing:border-box}.landing-wrapper #header .header-content h1{margin:75px 0 50px 0;font-weight:400}.landing-wrapper #features,.landing-wrapper #promotion,.landing-wrapper #pricing,.landing-wrapper #video,.landing-wrapper .footer .footer-content{width:100%;padding-right:30px;padding-left:30px;box-sizing:border-box}.landing-wrapper #promotion .ui-lg-8{padding:100px 0 30px;text-align:center}.landing-wrapper #promotion .ui-lg-8 h1{margin-top:-30px;font-weight:48px;color:#fff;font-weight:400}.landing-wrapper #video .video-content{width:100%}.landing-wrapper #video .video-content .video-container iframe{width:350px;height:220px}.landing-wrapper .footer .footer-content{text-align:center}.landing-wrapper .footer .footer-content ul{float:none;margin:0;padding:0}}@media(-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){.landing-wrapper .header .header-top .logo{background:url("../images/logo2x.png") top left no-repeat;background-size:200px 30px}}.help-wrapper .card{background-color:#f3f5f7}.help-wrapper .card.help-wrapper-card{padding:0}.help-wrapper .card.help-wrapper-card .help-header{position:relative}.help-wrapper .card.help-wrapper-card .help-header h1{color:#fff;font-size:28px;position:absolute;top:40%;left:40px;letter-spacing:.25px}.help-wrapper .card.help-wrapper-card .help-header .search{bottom:-20px;position:absolute;height:50px;background-color:#fafafa;box-shadow:0 1px 3px 0 rgba(0,0,0,0.2);left:40px;right:40px}.help-wrapper .card.help-wrapper-card .help-header .search span{width:100%}.help-wrapper .card.help-wrapper-card .help-header .search span input{border:0;position:relative;width:100%;padding:10px 40px;height:50px;font-size:16px;color:rgba(0,0,0,0.87)}.help-wrapper .card.help-wrapper-card .help-header .search i{position:absolute;bottom:12px;left:12px;z-index:1;color:rgba(0,0,0,0.54);cursor:pointer}.help-wrapper .card.help-wrapper-card .help-content{padding:20px 0}.help-wrapper .card.help-wrapper-card .help-content .card{margin:20px 40px;background-color:#fafafa;padding:5px 20px}.help-wrapper .card.help-wrapper-card .help-content .card .main-accordion{background-color:#f3f5f7}.help-wrapper .card.help-wrapper-card .help-content .card .main-accordion .ui-accordion-header>a{border:0;background-color:#fafafa;color:#212121;position:relative}.help-wrapper .card.help-wrapper-card .help-content .card .main-accordion .ui-accordion-header>a .accordion-title{padding-left:45px}.help-wrapper .card.help-wrapper-card .help-content .card .main-accordion .ui-accordion-header>a .accordion-title h1{margin:0;margin-top:8px}.help-wrapper .card.help-wrapper-card .help-content .card .main-accordion .ui-accordion-header>a i{color:#ffeb3b;position:absolute;bottom:28px;left:4px;z-index:1;font-size:50px}.help-wrapper .card.help-wrapper-card .help-content .card .main-accordion .ui-accordion-header>a .ui-accordion-toggle-icon{position:absolute;top:50%;margin-top:-10px;color:#212121;right:30px;left:auto}.help-wrapper .card.help-wrapper-card .help-content .card .main-accordion .ui-accordion-content{border:0;box-shadow:none;background-color:#fafafa}.help-wrapper .card.help-wrapper-card .help-content .card .main-accordion .ui-accordion-content .questions .sub-accordion .ui-accordion-header>a{border:0;background-color:#eaeaea;color:#212121;padding:20px;border-radius:2px;border:solid 1px #e0e0e0;font-size:16px;letter-spacing:.12px;color:#212121;margin-bottom:10px}.help-wrapper .card.help-wrapper-card .help-content .card .main-accordion .ui-accordion-content .questions .sub-accordion .ui-accordion-header>a .ui-accordion-toggle-icon{color:#212121;right:15px;left:auto}.help-wrapper .card.help-wrapper-card .help-content .card .main-accordion .ui-accordion-content .questions .sub-accordion .ui-accordion-header>a:hover{background-color:#4caf50;color:#fff}.help-wrapper .card.help-wrapper-card .help-content .card .main-accordion .ui-accordion-content .questions .sub-accordion .ui-accordion-header>a:hover .ui-accordion-toggle-icon{color:#fff}.help-wrapper .card.help-wrapper-card .help-content .card .main-accordion .ui-accordion-content .questions .sub-accordion .ui-accordion-content{padding-bottom:24px;line-height:1.5}@media(max-width:640px){.help-wrapper .card.help-wrapper-card{padding:0}.help-wrapper .card.help-wrapper-card .help-header{line-height:1.5}.help-wrapper .card.help-wrapper-card .help-header img{height:130px}.help-wrapper .card.help-wrapper-card .help-header h1{top:0}.help-wrapper .card.help-wrapper-card .help-header .search{left:10px;right:10px}.help-wrapper .card.help-wrapper-card .help-content .card{margin:10px;padding:0 5px}.help-wrapper .card.help-wrapper-card .help-content .card .main-accordion .ui-accordion-header>a .ui-accordion-toggle-icon{right:5px}.help-wrapper .card.help-wrapper-card .help-content .card .main-accordion .ui-accordion-content .questions .sub-accordion .ui-accordion-header>a .ui-accordion-toggle-icon{right:2px}}.invoice-wrapper .invoice-header{margin-bottom:30px}.invoice-wrapper .invoice-header .title{margin-top:40px;font-size:28px;font-weight:900;color:#212121}.invoice-wrapper .invoice-header .logo-adress{text-align:right}.invoice-wrapper .card.invoice-table{padding:0;margin-bottom:42px;width:100%}.invoice-wrapper .card.invoice-table h2,.invoice-wrapper .card.invoice-table p{margin:0}.invoice-wrapper .card.invoice-table .table-header{padding:3px 5px;border-radius:2px;background-color:#e0e0e0;text-align:right}.invoice-wrapper .card.invoice-table .table-header h2{font-size:12px;font-weight:700;color:rgba(0,0,0,0.6)}.invoice-wrapper .card.invoice-table .table-content-row{padding:3px 5px;font-size:14px;font-weight:500;color:#212121;text-align:right}.invoice-wrapper .card.invoice-table .table-content-row h2{font-size:12px;font-weight:500;color:rgba(0,0,0,0.6)}.invoice-wrapper .card.invoice-table .row-title{text-align:left}.invoice-wrapper .card.invoice-table .total{color:#ffeb3b}.invoice-wrapper .card.invoice-table.billto-table .table-header{text-align:left}.invoice-wrapper .card.invoice-table.billto-table .table-content-row{text-align:left}.invoice-wrapper .card.invoice-table.bank-table{margin-right:25px}.invoice-wrapper .table-g-6{padding:0}@media(max-width:1024px){.invoice-wrapper .card.invoice-table.bank-table{margin-right:0}}@media(max-width:640px){.invoice-wrapper .logo-adress img{width:135px}.invoice-wrapper .invoice-table .table-content-row{font-size:12px}}@media print{body *{visibility:hidden}#invoice-content *{visibility:visible}#invoice-content{position:absolute;left:0;top:0}#invoice-content .card{box-shadow:none}#invoice-content .card.invoice-table{margin-bottom:10px;background-color:transparent}}.wizard-body{height:100vh;background:url("../../layout/images/extensions/background@2x.jpg") center;background-size:cover;background-repeat:no-repeat;background-attachment:fixed}.wizard-body .wizard-wrapper{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:100%}.wizard-body .wizard-wrapper .wizard-topbar{background-color:#3949ab;z-index:1000;-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,0.26);-moz-box-shadow:0 2px 5px 0 rgba(0,0,0,0.26);box-shadow:0 2px 5px 0 rgba(0,0,0,0.26);height:75px;padding:0 10%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-preferred-size:75px;-webkit-flex-basis:75px;flex-basis:75px;-ms-flex-positive:0;-webkit-flex-grow:0;flex-grow:0;-ms-flex-negative:0;-webkit-flex-shrink:0;flex-shrink:0}.wizard-body .wizard-wrapper .wizard-topbar .logo{display:inline-block;vertical-align:middle;width:200px;height:30px;background:url("../../layout/images/logo.png") top left no-repeat}.wizard-body .wizard-wrapper .wizard-topbar .profile{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-direction:row;flex-direction:row}.wizard-body .wizard-wrapper .wizard-topbar .profile .profile-text{margin-right:15px;text-align:right}.wizard-body .wizard-wrapper .wizard-topbar .profile .profile-text h1{font-size:16px;color:#fff;margin:0}.wizard-body .wizard-wrapper .wizard-topbar .profile .profile-text p{font-size:16px;opacity:.6;margin:0;color:rgba(255,255,255,0.7)}.wizard-body .wizard-wrapper .wizard-topbar .profile .profile-image{display:inline-block;vertical-align:middle;width:40px}.wizard-body .wizard-wrapper .wizard-content{height:calc(100% - 75px);min-height:600px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.wizard-body .wizard-wrapper .wizard-content .wizard-card{background-color:#fafafa;box-shadow:0 1px 3px 0 rgba(0,0,0,0.2),0 2px 1px -1px rgba(0,0,0,0.12),0 1px 1px 0 rgba(0,0,0,0.14);height:550px;width:54.33%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-ms-flex-positive:0;-webkit-flex-grow:0;flex-grow:0;-ms-flex-negative:0;-webkit-flex-shrink:0;flex-shrink:0}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-header{width:100%;background-color:#3f51b5;box-shadow:0 3px 3px 0 rgba(0,0,0,0.2);position:relative}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-header .tab{background-color:#3f51b5;text-align:center;cursor:pointer}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-header .tab i{width:20px;opacity:.38;color:#fff}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-header .tab .title{color:#fff;opacity:.38;font-size:16px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-header .tab.selected-tab{transition-duration:.6s}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-header .tab.selected-tab i{opacity:1}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-header .tab.selected-tab .title{opacity:1}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-header .tab-bar{position:absolute;bottom:0;left:0;height:2px;transition:.5s cubic-bezier(0.35,0,0.25,1);background-color:#fff;visibility:visible}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content{padding:30px;display:none;overflow:auto;height:100%}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content>.ui-g{height:100%;width:100%}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content h1{font-size:12px;color:rgba(0,0,0,0.6);letter-spacing:2px;margin:0}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.active-content{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-positive:1;-webkit-flex-grow:1;flex-grow:1}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.register .forms .ui-inputgroup{background-color:#f4f4f4;margin-top:25px;padding-top:20px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.register .forms .ui-inputgroup input{width:100%;padding-bottom:15px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.register .forms .ui-inputgroup i{margin-bottom:15px;margin-left:4px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.register .forms .ui-dropdown{background-color:#f4f4f4;margin-top:25px;padding-top:23px;width:100%}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.register .forms .ui-dropdown .ui-dropdown-label{padding-bottom:12px;padding-left:10px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.register .forms .ui-dropdown .ui-dropdown-trigger{top:22px;right:10px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.register .forms .calendar{margin-top:14px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.register .forms .calendar .ui-calendar{width:100%;position:relative}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.register .forms .calendar .ui-calendar input{padding-top:32px;padding-bottom:12px;padding-left:15px;width:100%;background-color:#f4f4f4}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.register .forms .calendar .ui-calendar button{top:20px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.register .continue-button.ui-button{width:100%;margin-top:25px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.tier .card{min-height:400px;padding:0;position:relative}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.tier .card .card-header{color:#fff;font-size:18px;padding:15px 10px;background-color:#3f51b5}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.tier .card .card-header h1{color:#fff;font-size:24px;display:inline}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.tier .card .card-content{font-size:14px;padding:10px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.tier .card .card-content i{color:#3f51b5}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.tier .card .card-content .card-row{height:40px;width:100%}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.tier .card .card-content .tier-button-wrapper{position:absolute;bottom:15px;right:10px;left:0;width:auto}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.tier .card .card-content .tier-button-wrapper .tier-button.ui-button{width:100%}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.tier .card.pro .card-header{background-color:#e91e63}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.tier .card.pro .card-content i{color:#e91e63}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.tier .card.pro .card-content .tier-button.ui-button{background-color:#e91e63}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.tier .card.pro-plus .card-header{background-color:#607d8b}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.tier .card.pro-plus .card-content i{color:#607d8b}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.tier .card.pro-plus .card-content .tier-button.ui-button{background-color:#607d8b}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment{padding:0}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .payment-info{padding:70px 35px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .payment-info .md-inputfield-box{background-color:#f4f4f4}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .payment-info .md-inputfield-box input{width:100%;padding-bottom:15px;background-color:transparent}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .payment-info .ui-chkbox-label{font-size:14px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .payment-info #customPanel{width:100%}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .payment-info .check-info{margin-top:10px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .order-info{padding:15px;background-color:#e0e0e0;border-left:solid 1px #bdbdbd;font-size:14px;color:#757575}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .order-info .order-basic,.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .order-info .order-pro,.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .order-info .order-pro-plus,.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .order-info .order-default{display:none}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .order-info .selected-order{display:block}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .order-info h1{margin-top:15px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .order-info .price{font-weight:700;text-align:right}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .order-info .total{border-top:1px solid #bdbdbd;padding:15px 0;margin-top:30px}.wizard-body .wizard-wrapper .wizard-content .wizard-card .wizard-card-content.payment .order-info .buy-button.ui-button{width:100%;margin:68px 0}@media(max-width:1024px){.wizard-body .wizard-wrapper .wizard-content .wizard-card{width:90%}}@media(max-width:640px){.wizard-body .wizard-wrapper .wizard-topbar{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:150px;padding:0 5%;-ms-flex-pack:distribute;justify-content:space-around;-ms-flex-preferred-size:150px;-webkit-flex-basis:150px;flex-basis:150px}.wizard-body .wizard-wrapper .wizard-topbar .logo{-ms-flex-item-align:start;align-self:flex-start}.wizard-body .wizard-wrapper .wizard-topbar .profile{-ms-flex-item-align:end;align-self:flex-end}.wizard-body .wizard-wrapper .wizard-content{height:calc(100% - 150px)}}html{height:100%}body{font-family:Roboto,"Helvetica Neue",sans-serif;font-size:16px;line-height:1.5em;color:#212121;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;padding:0;margin:0;background-color:#f7f7f7;min-height:100%}body a{text-decoration:none}.layout-mask{position:fixed;width:100%;height:100%;background-color:#424242;top:0;left:0;z-index:999999997;opacity:.7;filter:alpha(opacity=70)}.layout-container .topbar{position:fixed;z-index:100;width:100%;height:75px;background-color:#4caf50;box-shadow:0 2px 5px 0 rgba(0,0,0,0.26);-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,0.26);-moz-box-shadow:0 2px 5px 0 rgba(0,0,0,0.26)}.layout-container .topbar .logo{display:inline-block;vertical-align:middle;width:200px;height:30px;background:url("../images/logo2x.png") top left no-repeat;background-size:200px 30px}.layout-container .topbar .topbar-left{box-sizing:border-box;padding:20px;height:75px;width:250px;background-color:#2e7d32;float:left;box-shadow:3px 0 6px rgba(0,0,0,0.3);-webkit-box-shadow:3px 0 6px rgba(0,0,0,0.3);-moz-box-shadow:3px 0 6px rgba(0,0,0,0.3)}.layout-container .topbar .topbar-right{padding:15px 15px 15px 0;position:relative;width:calc(100% - 250px);float:right}.layout-container .topbar .topbar-right #menu-button{color:#212121;display:inline-block;vertical-align:middle;height:36px;margin-right:10px;position:relative;left:-16px;top:3px;background-color:#ffeb3b;-moz-border-radius:50%;-webkit-border-radius:50%;border-radius:50%;-moz-transition:all .3s;-o-transition:all .3s;-webkit-transition:all .3s;transition:all .3s;-webkit-box-shadow:0 3px 10px rgba(0,0,0,0.23),0 3px 10px rgba(0,0,0,0.16);-moz-box-shadow:0 3px 10px rgba(0,0,0,0.23),0 3px 10px rgba(0,0,0,0.16);box-shadow:0 3px 10px rgba(0,0,0,0.23),0 3px 10px rgba(0,0,0,0.16)}.layout-container .topbar .topbar-right #menu-button:hover{-webkit-transform:scale(1.2);-moz-transform:scale(1.2);-o-transform:scale(1.2);-ms-transform:scale(1.2);transform:scale(1.2)}.layout-container .topbar .topbar-right #menu-button i{font-family:"Material Icons";font-weight:normal;font-style:normal;font-size:1.5em;display:inline-block;width:1em;height:1em;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;text-indent:0;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga";-moz-transition:all .3s;-o-transition:all .3s;-webkit-transition:all .3s;transition:all .3s;font-size:36px}.layout-container .topbar .topbar-right #menu-button i:before{content:"chevron_left"}.layout-container .topbar .topbar-right #topbar-menu-button,.layout-container .topbar .topbar-right #rightpanel-menu-button{display:none;color:#fff;vertical-align:middle;height:36px;margin-top:4px;float:right;-moz-transition:all .3s;-o-transition:all .3s;-webkit-transition:all .3s;transition:all .3s}.layout-container .topbar .topbar-right #topbar-menu-button i,.layout-container .topbar .topbar-right #rightpanel-menu-button i{-moz-transition:color .3s;-o-transition:color .3s;-webkit-transition:color .3s;transition:color .3s;font-size:36px}.layout-container .topbar .topbar-right #rightpanel-menu-button{display:block}.layout-container .topbar .topbar-right #rightpanel-menu-button:hover{color:#e8e8e8}.layout-container .topbar .topbar-right .topbar-items .search-item input{position:relative;top:-10px;font-size:16px;background-color:transparent;background-image:linear-gradient(to bottom,#fff,#fff),linear-gradient(to bottom,#a3d7a5,#a3d7a5);border-width:0;padding:2px;color:#fff}.layout-container .topbar .topbar-right .topbar-items .search-item input:focus{outline:0 none}.layout-container .topbar .topbar-right .topbar-items .search-item input:focus ~ label{top:-5px;font-size:12px}.layout-container .topbar .topbar-right .topbar-items .search-item input.ui-state-filled ~ label{display:none}.layout-container .topbar .topbar-right .topbar-items .search-item label{color:#fff;top:8px}.layout-container .layout-menu{overflow:auto;position:fixed;width:250px;z-index:99;top:75px;height:100%;background-color:#fff;box-shadow:3px 0 6px rgba(0,0,0,0.3);-webkit-box-shadow:3px 0 6px rgba(0,0,0,0.3);-moz-box-shadow:3px 0 6px rgba(0,0,0,0.3);-moz-transition:margin-left .3s;-o-transition:margin-left .3s;-webkit-transition:margin-left .3s;transition:margin-left .3s}.layout-container .layout-menu .profile{box-sizing:border-box;padding-top:2em;width:250px;height:145px;text-align:center;background:url("../images/profile-bg.png") top left no-repeat;background-size:250px 145px;box-shadow:0 2px 5px 0 rgba(0,0,0,0.16);-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,0.16);-moz-box-shadow:0 2px 5px 0 rgba(0,0,0,0.16)}.layout-container .layout-menu .profile .profile-image{width:60px;height:60px;margin:0 auto 5px auto;display:block}.layout-container .layout-menu .profile .profile-name{display:inline-block;color:#212121;vertical-align:middle;font-size:1em}.layout-container .layout-menu .profile i{color:#212121;vertical-align:middle;-moz-transition:transform .3s;-o-transition:transform .3s;-webkit-transition:transform .3s;transition:transform .3s}.layout-container .layout-menu .profile.profile-expanded i{-webkit-transform:rotate(-180deg);-moz-transform:rotate(-180deg);-o-transform:rotate(-180deg);-ms-transform:rotate(-180deg);transform:rotate(-180deg)}.layout-container .layout-menu .profile-menu{border-bottom:1px solid #d6d5d5;overflow:hidden}.layout-container .layout-menu .profile-menu li:first-child{margin-top:1em}.layout-container .layout-menu .profile-menu li:last-child{margin-bottom:1em}.layout-container .layout-menu.layout-menu-dark{background-color:#424242}.layout-container .layout-menu.layout-menu-dark .profile{background-image:url("../images/profile-bg-dark.png")}.layout-container .layout-menu.layout-menu-dark .profile .profile-name{color:#fff}.layout-container .layout-menu.layout-menu-dark .profile i{color:#fff}.layout-container .layout-menu.layout-menu-dark .profile-menu{border-bottom:1px solid #545454}.layout-container .layout-menu.layout-menu-dark ul.ultima-menu li a{color:#fff}.layout-container .layout-menu.layout-menu-dark ul.ultima-menu li a i{color:#fff}.layout-container .layout-menu.layout-menu-dark ul.ultima-menu li a:hover{background-color:#676767}.layout-container .layout-menu.layout-menu-dark ul.ultima-menu li a.active-menuitem-routerlink{color:#ffeb3b}.layout-container .layout-menu.layout-menu-dark ul.ultima-menu li a.active-menuitem-routerlink i{color:#ffeb3b}.layout-container .layout-menu.layout-menu-dark ul.ultima-menu li a.active-menuitem-routerlink:hover{color:#fff}.layout-container .layout-menu.layout-menu-dark ul.ultima-menu li a.active-menuitem-routerlink:hover>i{color:#fff}.layout-container .layout-menu.layout-menu-dark ul.ultima-menu li.active-menuitem>a{color:#212121;background-color:#ffeb3b}.layout-container .layout-menu.layout-menu-dark ul.ultima-menu li.active-menuitem>a.active-menuitem-routerlink{color:#212121;background-color:#ffeb3b}.layout-container .layout-menu.layout-menu-dark ul.ultima-menu li.active-menuitem>a.active-menuitem-routerlink i{color:#212121}.layout-container .layout-menu .menuitem-badge{position:absolute;right:3.5em;top:.75em;display:inline-block;width:1em;height:1em;margin-right:.5em;text-align:center;background-color:#ffeb3b;color:#212121;font-size:14px;font-weight:700;line-height:1em;-moz-border-radius:50%;-webkit-border-radius:50%;border-radius:50%}.layout-container .layout-menu .layout-menu-tooltip{display:none;padding:0 5px;position:absolute;left:76px;top:6px;z-index:101;line-height:1}.layout-container .layout-menu .layout-menu-tooltip .layout-menu-tooltip-text{padding:6px 8px;font-weight:700;background-color:#353535;color:#fff;min-width:75px;white-space:nowrap;text-align:center;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,0.175);-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);-moz-box-shadow:0 6px 12px rgba(0,0,0,0.175)}.layout-container .layout-menu .layout-menu-tooltip .layout-menu-tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid;top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#353535}.layout-container.menu-layout-overlay .layout-menu{margin-left:-250px}.layout-container.menu-layout-overlay .layout-main{margin-left:0}.layout-container.menu-layout-overlay.layout-menu-overlay-active .layout-menu{z-index:999999999;margin-left:0}.layout-container.menu-layout-overlay.layout-menu-overlay-active .layout-mask{display:block}.layout-container.menu-layout-overlay.layout-menu-overlay-active .topbar .topbar-right #menu-button i{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}.layout-container.menu-layout-overlay .topbar{z-index:999999998}.layout-container.menu-layout-overlay .topbar .topbar-right #menu-button i{font-size:36px !important;-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-o-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.layout-container .layout-main{padding:75px 0 0 0;-moz-transition:margin-left .3s;-o-transition:margin-left .3s;-webkit-transition:margin-left .3s;transition:margin-left .3s}.layout-container .layout-main .layout-content{padding:16px}.layout-container .layout-mask{display:none}.layout-container .layout-breadcrumb{background-color:#fff;box-shadow:inset 0 -2px 4px 0 rgba(0,0,0,0.14);-webkit-box-shadow:inset 0 -2px 4px 0 rgba(0,0,0,0.14);-moz-box-shadow:inset 0 -2px 4px 0 rgba(0,0,0,0.14);min-height:42px}.layout-container .layout-breadcrumb:before,.layout-container .layout-breadcrumb:after{content:"";display:table}.layout-container .layout-breadcrumb:after{clear:both}.layout-container .layout-breadcrumb ul{margin:8px 0 0 0;padding:0 0 0 20px;list-style:none;color:#757575;display:inline-block}.layout-container .layout-breadcrumb ul li{display:inline-block;vertical-align:top;color:#757575}.layout-container .layout-breadcrumb ul li:nth-child(even){font-size:20px}.layout-container .layout-breadcrumb ul li:first-child(even){color:#4caf50}.layout-container .layout-breadcrumb ul li a{color:#757575}.layout-container .layout-breadcrumb .layout-breadcrumb-options{float:right;padding:0 20px 0 0;height:100%}.layout-container .layout-breadcrumb .layout-breadcrumb-options a{color:#757575;display:inline-block;width:42px;height:42px;line-height:42px;text-align:center;-moz-transition:background-color .3s;-o-transition:background-color .3s;-webkit-transition:background-color .3s;transition:background-color .3s}.layout-container .layout-breadcrumb .layout-breadcrumb-options a:hover{background-color:#e8e8e8}.layout-container .layout-breadcrumb .layout-breadcrumb-options a i{line-height:inherit}.layout-container .ultima-menu{margin:0;padding:0;list-style:none;width:268px}.layout-container .ultima-menu.ultima-main-menu{margin-top:16px;padding-bottom:120px}.layout-container .ultima-menu li a{font-size:1em;display:block;padding:.5em 2.5em .5em 1em;color:#212121;width:100%;box-sizing:border-box;-moz-transition:background-color .3s;-o-transition:background-color .3s;-webkit-transition:background-color .3s;transition:background-color .3s;overflow:hidden}.layout-container .ultima-menu li a i{color:#757575}.layout-container .ultima-menu li a i:first-child{display:inline-block;vertical-align:middle;margin-right:.5em;font-size:1.5em}.layout-container .ultima-menu li a i:last-child{float:right;font-size:20px;margin-top:.15em;margin-right:-0.15em;-moz-transition:transform .3s;-o-transition:transform .3s;-webkit-transition:transform .3s;transition:transform .3s}.layout-container .ultima-menu li a:hover{background-color:#e8e8e8}.layout-container .ultima-menu li a span{display:inline-block;vertical-align:middle}.layout-container .ultima-menu li a.active-menuitem-routerlink{color:#4caf50}.layout-container .ultima-menu li a.active-menuitem-routerlink>i{color:#4caf50}.layout-container .ultima-menu li a.active-menuitem-routerlink:hover{color:#212121}.layout-container .ultima-menu li a.active-menuitem-routerlink:hover>i{color:#757575}.layout-container .ultima-menu li.active-menuitem>a{color:#4caf50;background-color:#e8e8e8}.layout-container .ultima-menu li.active-menuitem>a i{color:#4caf50}.layout-container .ultima-menu li.active-menuitem>a i:last-child{-webkit-transform:rotate(-180deg);-moz-transform:rotate(-180deg);-o-transform:rotate(-180deg);-ms-transform:rotate(-180deg);transform:rotate(-180deg)}.layout-container .ultima-menu li ul{padding:0;margin:0;list-style:none;overflow:hidden}.layout-container .ultima-menu li ul li a{padding:.5em 2.5em .5em 2em}.layout-container .ultima-menu li ul li a>span{font-size:15px}.layout-container .ultima-menu li ul li a i:first-child{display:inline-block;vertical-align:middle;margin-right:.6em;font-size:1.25em}.layout-container .ultima-menu li ul li ul li a{padding-left:3em}.layout-container .ultima-menu li ul li ul ul li a{padding-left:4em}.layout-container .ultima-menu li ul li ul ul ul li a{padding-left:5em}.layout-container .ultima-menu li ul li ul ul ul ul li a{padding-left:6em}.layout-container .ultima-menu li.red-badge>a .menuitem-badge{background-color:#f44336;color:#fff}.layout-container .ultima-menu li.purple-badge>a .menuitem-badge{background-color:#4527a0;color:#fff}.layout-container .ultima-menu li.teal-badge>a .menuitem-badge{background-color:#00695c;color:#fff}.layout-container .footer{padding:.5em}.layout-container .footer .footer-text-left{float:left}.layout-container .footer .footer-text-right{color:#757575;float:right}.layout-container .footer .footer-text-right span{vertical-align:middle;display:inline-block}.layout-container .layout-rightpanel{position:fixed;top:75px;height:100%;right:-240px;width:240px;z-index:100;overflow:auto;background-color:#fff;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-moz-transition:right .3s;-o-transition:right .3s;-webkit-transition:right .3s;transition:right .3s;box-shadow:0 2px 10px 0 rgba(0,0,0,0.3);-webkit-box-shadow:0 2px 10px 0 rgba(0,0,0,0.3);-moz-box-shadow:0 2px 10px 0 rgba(0,0,0,0.3)}.layout-container .layout-rightpanel.layout-rightpanel-active{right:0;-webkit-transition-timing-function:cubic-bezier(0.86,0,0.07,1);transition-timing-function:cubic-bezier(0.86,0,0.07,1)}.layout-container .layout-rightpanel .layout-rightpanel-content{padding:14px;padding-bottom:120px}.ajax-loader{font-size:2em;color:#ffeb3b}@media(min-width:1025px){.layout-container .topbar-items{float:right;margin:0;padding:5px 0 0 0;list-style-type:none}.layout-container .topbar-items>li{float:right;position:relative;margin-left:8px}.layout-container .topbar-items>li>a{position:relative;display:block}.layout-container .topbar-items>li>a .topbar-item-name{display:none}.layout-container .topbar-items>li>a .topbar-badge{position:absolute;right:-5px;top:-5px;background-color:#ffeb3b;color:#212121;-moz-border-radius:50%;-webkit-border-radius:50%;border-radius:50%;padding:2px 4px;display:block;font-size:12px;line-height:12px}.layout-container .topbar-items>li .topbar-icon{font-size:36px;color:#fff;-moz-transition:color .3s;-o-transition:color .3s;-webkit-transition:color .3s;transition:color .3s}.layout-container .topbar-items>li .topbar-icon:hover{color:#e8e8e8}.layout-container .topbar-items>li.profile-item .profile-image{width:36px;height:36px}.layout-container .topbar-items>li>ul{position:absolute;top:55px;right:5px;display:none;width:250px;background-color:#fff;-webkit-box-shadow:0 6px 20px 0 rgba(0,0,0,0.19),0 8px 17px 0 rgba(0,0,0,0.2);-moz-box-shadow:0 6px 20px 0 rgba(0,0,0,0.19),0 8px 17px 0 rgba(0,0,0,0.2);box-shadow:0 6px 20px 0 rgba(0,0,0,0.19),0 8px 17px 0 rgba(0,0,0,0.2);-webkit-animation-duration:.3s;-moz-animation-duration:.3s;animation-duration:.3s}.layout-container .topbar-items>li.active-top-menu>ul{display:block}.layout-container .topbar-items>li .topbar-message img{display:inline-block;vertical-align:middle;margin-right:12px}.layout-container.menu-layout-static .layout-menu{margin-left:0}.layout-container.menu-layout-static .layout-main{margin-left:250px}.layout-container.menu-layout-static.layout-menu-static-inactive .topbar .topbar-right #menu-button i{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-o-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.layout-container.menu-layout-static.layout-menu-static-inactive .layout-menu{margin-left:-250px}.layout-container.menu-layout-static.layout-menu-static-inactive .layout-main{margin-left:0}.layout-container.menu-layout-static .layout-mask{display:none}.layout-container.menu-layout-horizontal .topbar{box-shadow:none;-webkit-box-shadow:none;-moz-box-shadow:none}.layout-container.menu-layout-horizontal .topbar .topbar-left{background-color:#4caf50;box-shadow:none;-webkit-box-shadow:none;-moz-box-shadow:none}.layout-container.menu-layout-horizontal .topbar .topbar-right #menu-button{display:none}.layout-container.menu-layout-horizontal .layout-menu{overflow:visible;position:fixed;width:100%;top:75px;height:auto;background-color:#2e7d32;box-shadow:0 2px 5px 0 rgba(0,0,0,0.26);-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,0.26);-moz-box-shadow:0 2px 5px 0 rgba(0,0,0,0.26)}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu{width:100%}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu.ultima-main-menu{margin-top:0;padding-bottom:0}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li{float:left;position:relative}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>a{padding:.5em 1em;color:#fff}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>a i{color:#fff}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>a:hover{background-color:#e8e8e8;color:#212121}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>a:hover i{color:#212121}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>a.active-menuitem-routerlink{color:#ffeb3b}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>a.active-menuitem-routerlink>i{color:#ffeb3b}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>a.active-menuitem-routerlink:hover{color:#212121}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>a.active-menuitem-routerlink:hover i{color:#212121}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>ul{position:absolute;top:41px;left:0;width:250px;background-color:#fff;-webkit-box-shadow:0 6px 20px 0 rgba(0,0,0,0.19),0 8px 17px 0 rgba(0,0,0,0.2);-moz-box-shadow:0 6px 20px 0 rgba(0,0,0,0.19),0 8px 17px 0 rgba(0,0,0,0.2);box-shadow:0 6px 20px 0 rgba(0,0,0,0.19),0 8px 17px 0 rgba(0,0,0,0.2)}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>ul li a{padding:10px 16px}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>ul li ul{position:static}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>ul li ul li a{padding-left:32px}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>ul li ul ul li a{padding-left:48px}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>ul li ul ul ul li a{padding-left:64px}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>ul li ul ul ul ul li a{padding-left:80px}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>ul li ul ul ul ul ul li a{padding-left:96px}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>ul li.active-menuitem>a{color:#4caf50;background-color:#e8e8e8}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>ul li.active-menuitem>ul{display:block}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li.active-menuitem>a{color:#212121;background-color:#ffeb3b}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li.active-menuitem>a i{color:#212121}.layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li.active-menuitem>ul{display:block}.layout-container.menu-layout-horizontal .layout-menu.layout-menu-dark{background-color:#424242}.layout-container.menu-layout-horizontal .layout-menu.layout-menu-dark ul.ultima-menu li a{color:#fff}.layout-container.menu-layout-horizontal .layout-menu.layout-menu-dark ul.ultima-menu li a:hover{background-color:#676767;color:#fff}.layout-container.menu-layout-horizontal .layout-menu.layout-menu-dark ul.ultima-menu li a:hover i{color:#fff}.layout-container.menu-layout-horizontal .layout-menu.layout-menu-dark ul.ultima-menu li a.active-menuitem-routerlink{color:#ffeb3b}.layout-container.menu-layout-horizontal .layout-menu.layout-menu-dark ul.ultima-menu li a.active-menuitem-routerlink i{color:#ffeb3b}.layout-container.menu-layout-horizontal .layout-menu.layout-menu-dark ul.ultima-menu li a.active-menuitem-routerlink:hover{color:#fff}.layout-container.menu-layout-horizontal .layout-menu.layout-menu-dark ul.ultima-menu li a.active-menuitem-routerlink:hover i{color:#fff}.layout-container.menu-layout-horizontal .layout-menu.layout-menu-dark ul.ultima-menu li.active-menuitem>a{color:#212121;background-color:#ffeb3b}.layout-container.menu-layout-horizontal .layout-menu.layout-menu-dark ul.ultima-menu li ul{background-color:#424242}.layout-container.menu-layout-horizontal .layout-menu .menuitem-badge{left:32px;top:7px}.layout-container.menu-layout-horizontal .layout-menu .active-menuitem .menuitem-badge{background-color:#fff;color:#212121}.layout-container.menu-layout-horizontal .layout-main{padding-top:116px;margin-left:0}.layout-container.menu-layout-horizontal .layout-mask{display:none}.layout-container.menu-layout-slim .topbar{left:75px;width:calc(100% - 75px)}.layout-container.menu-layout-slim .topbar .topbar-left{background:transparent;box-shadow:none;-webkit-box-shadow:none;-moz-box-shadow:none}.layout-container.menu-layout-slim .topbar .topbar-right #menu-button{display:none}.layout-container.menu-layout-slim .layout-menu{width:75px;overflow:visible;z-index:100;top:0}.layout-container.menu-layout-slim .layout-menu .profile{width:100%;height:74px;padding-top:15px}.layout-container.menu-layout-slim .layout-menu .profile>a .profile-image{width:45px;height:45px}.layout-container.menu-layout-slim .layout-menu .profile>a .profile-name,.layout-container.menu-layout-slim .layout-menu .profile>a i{display:none}.layout-container.menu-layout-slim .layout-menu .ultima-menu{padding:0;width:100%}.layout-container.menu-layout-slim .layout-menu .ultima-menu>li{position:relative}.layout-container.menu-layout-slim .layout-menu .ultima-menu>li>a{text-align:center;padding-left:0;padding-right:0;padding-top:.5em;padding-bottom:.5em}.layout-container.menu-layout-slim .layout-menu .ultima-menu>li>a i:first-child{font-size:1.75em;margin-right:0}.layout-container.menu-layout-slim .layout-menu .ultima-menu>li>a span,.layout-container.menu-layout-slim .layout-menu .ultima-menu>li>a .submenu-icon{display:none}.layout-container.menu-layout-slim .layout-menu .ultima-menu>li>a:hover+.layout-menu-tooltip{display:block}.layout-container.menu-layout-slim .layout-menu .ultima-menu>li>ul{background-color:#fff;position:absolute;top:0;left:75px;min-width:200px;box-shadow:0 2px 5px 0 rgba(0,0,0,0.26);-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,0.26);-moz-box-shadow:0 2px 5px 0 rgba(0,0,0,0.26)}.layout-container.menu-layout-slim .layout-menu .ultima-menu>li>ul li a{padding:.5em 1em .5em 2em;padding-left:16px}.layout-container.menu-layout-slim .layout-menu .ultima-menu>li>ul li ul li a{padding-left:32px}.layout-container.menu-layout-slim .layout-menu .ultima-menu>li>ul li ul ul li a{padding-left:48px}.layout-container.menu-layout-slim .layout-menu .ultima-menu>li>ul li ul ul ul li a{padding-left:64px}.layout-container.menu-layout-slim .layout-menu .ultima-menu>li>ul li ul ul ul ul li a{padding:80px}.layout-container.menu-layout-slim .layout-menu .ultima-menu>li>ul li ul ul ul ul ul li a{padding:96px}.layout-container.menu-layout-slim .layout-menu .ultima-menu>li.active-menuitem>a:hover+.layout-menu-tooltip{display:none}.layout-container.menu-layout-slim .layout-menu.layout-menu-dark .ultima-menu>li>ul{background-color:#424242}.layout-container.menu-layout-slim .layout-main{margin-left:75px}.layout-container.menu-layout-slim .layout-footer{margin-left:75px}}@media(max-width:1024px){.layout-container.menu-layout-static .topbar .topbar-right #menu-button i{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-o-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.layout-container.menu-layout-static .layout-menu{margin-left:-265px}.layout-container.menu-layout-static .layout-main{margin-left:0}.layout-container.menu-layout-static.layout-menu-static-active .layout-menu{margin-left:0;z-index:999999999}.layout-container.menu-layout-static.layout-menu-static-active .topbar{z-index:999999998}.layout-container.menu-layout-static.layout-menu-static-active .topbar .topbar-right #menu-button i{-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0)}.layout-container.menu-layout-static.layout-menu-static-active .layout-main{margin-left:0}.layout-container.menu-layout-static.layout-menu-static-active .layout-mask{display:block}.layout-container .topbar .topbar-right #topbar-menu-button{display:block}.layout-container .topbar .topbar-right .topbar-items{position:absolute;top:75px;right:15px;width:275px;display:none;background-color:#fff;-webkit-box-shadow:0 6px 20px 0 rgba(0,0,0,0.19),0 8px 17px 0 rgba(0,0,0,0.2);-moz-box-shadow:0 6px 20px 0 rgba(0,0,0,0.19),0 8px 17px 0 rgba(0,0,0,0.2);box-shadow:0 6px 20px 0 rgba(0,0,0,0.19),0 8px 17px 0 rgba(0,0,0,0.2);-webkit-animation-duration:.3s;-moz-animation-duration:.3s;animation-duration:.3s;list-style-type:none;margin:0;padding:0}.layout-container .topbar .topbar-right .topbar-items>li>a{width:100%;display:block;box-sizing:border-box;font-size:16px;padding:16px 16px;color:#212121;position:relative}.layout-container .topbar .topbar-right .topbar-items>li>a i{display:inline-block;vertical-align:middle;margin-right:12px;font-size:24px}.layout-container .topbar .topbar-right .topbar-items>li>a:hover{background-color:#e8e8e8}.layout-container .topbar .topbar-right .topbar-items>li>a .topbar-item-name{display:inline-block;vertical-align:middle}.layout-container .topbar .topbar-right .topbar-items>li>a .topbar-badge{position:absolute;left:30px;top:10px;background-color:#ffeb3b;color:#212121;-moz-border-radius:50%;-webkit-border-radius:50%;border-radius:50%;padding:2px 4px;display:block;font-size:12px;line-height:12px}.layout-container .topbar .topbar-right .topbar-items>li>ul{display:none}.layout-container .topbar .topbar-right .topbar-items>li>ul li a span,.layout-container .topbar .topbar-right .topbar-items>li>ul li a img,.layout-container .topbar .topbar-right .topbar-items>li>ul li a i{display:inline-block;vertical-align:middle}.layout-container .topbar .topbar-right .topbar-items>li.active-top-menu>a{color:#4caf50}.layout-container .topbar .topbar-right .topbar-items>li.active-top-menu>ul{display:block}.layout-container .topbar .topbar-right .topbar-items>li.active-top-menu>ul li a{padding-left:32px}.layout-container .topbar .topbar-right .topbar-items>li.search-item input{background-image:linear-gradient(to bottom,#4caf50,#4caf50),linear-gradient(to bottom,#b4c7b5,#b4c7b5)}.layout-container .topbar .topbar-right .topbar-items>li.search-item{text-align:center;width:100%;display:block;box-sizing:border-box;font-size:16px;padding:16px 16px;position:relative}.layout-container .topbar .topbar-right .topbar-items>li.search-item input{top:0;width:100%;box-sizing:border-box;padding-right:16px;border-color:#bdbdbd;color:#212121}.layout-container .topbar .topbar-right .topbar-items>li.search-item input:focus{border-color:#bdbdbd}.layout-container .topbar .topbar-right .topbar-items>li.search-item input:focus ~ label,.layout-container .topbar .topbar-right .topbar-items>li.search-item input.ui-state-filled ~ label{top:-20px;color:#4caf50}.layout-container .topbar .topbar-right .topbar-items>li.search-item label{top:1px;color:#212121}.layout-container .topbar .topbar-right .topbar-items>li.search-item i{position:absolute;right:5px;top:-2px}.layout-container .topbar .topbar-right .topbar-items>li.profile-item .profile-image{display:inline-block;vertical-align:middle;width:24px;height:24px;background:url("../images/avatar.png") top left no-repeat;background-size:24px 24px;margin-right:14px}.layout-container .topbar .topbar-right .topbar-items>li.profile-item span{vertical-align:middle;display:inline-block}.layout-container .topbar .topbar-right .topbar-items.topbar-items-visible{display:block}}@media(max-width:385px){.layout-container .topbar .topbar-right #topbar-menu-button{position:absolute;height:1.5em;right:24px;top:1.375em}.layout-container .topbar .topbar-right #topbar-menu-button i{font-size:1.5em}.layout-container .topbar .topbar-right #rightpanel-menu-button{position:absolute;height:1.5em;right:8px;top:1.375em}.layout-container .topbar .topbar-right #rightpanel-menu-button i{font-size:1.5em}.layout-container .topbar .topbar-right #menu-button{margin-right:0}}.layout-config{z-index:1000002;position:fixed;padding:0;top:75px;display:block;right:0;width:550px;z-index:996;height:calc(100% - 60px);transform:translate3d(550px,0,0);-moz-transition:transform .3s;-o-transition:transform .3s;-webkit-transition:transform .3s;transition:transform .3s;background-color:#fff}.layout-config.layout-config-active{transform:translate3d(0,0,0)}.layout-config.layout-config-active .layout-config-content .layout-config-button i{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-ms-transform:rotate(360deg);transform:rotate(360deg)}.layout-config .panel-items{display:flex;justify-content:flex-start;align-items:center;flex-wrap:wrap}.layout-config .panel-items .panel-item{margin-right:1em;margin-bottom:1em;text-align:center}.layout-config .layout-config-content{position:relative;height:100%}.layout-config .layout-config-content>form{height:100%}.layout-config .layout-config-content .layout-config-button{display:block;position:absolute;width:52px;height:52px;line-height:52px;background-color:#fafafa;text-align:center;top:230px;left:-51px;z-index:-1;cursor:pointer;color:#4caf50;-moz-border-radius-topleft:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-moz-transition:background-color .3s;-o-transition:background-color .3s;-webkit-transition:background-color .3s;transition:background-color .3s;box-shadow:0 7px 8px -4px rgba(0,0,0,0.2),0 5px 22px 4px rgba(0,0,0,0.12),0 12px 17px 2px rgba(0,0,0,0.14)}.layout-config .layout-config-content .layout-config-button i{font-size:32px;line-height:inherit;cursor:pointer;height:100%;-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0);-moz-transition:transform 1s;-o-transition:transform 1s;-webkit-transition:transform 1s;transition:transform 1s}.layout-config .layout-config-content .layout-config-button:hover{color:#80c883}.layout-config .layout-config-close{position:absolute;width:25px;height:25px;line-height:25px;text-align:center;right:32px;top:10px;z-index:999;background-color:#e0284f;-moz-border-radius:50%;-webkit-border-radius:50%;border-radius:50%;-moz-transition:background-color .3s;-o-transition:background-color .3s;-webkit-transition:background-color .3s;transition:background-color .3s}.layout-config .layout-config-close i{color:#fff;line-height:inherit;font-size:16px;font-weight:bold}.layout-config .layout-config-close:hover{background-color:#d44d69}.layout-config .p-col{text-align:center}.layout-config .ui-tabview{border:0 none;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:100%;box-shadow:0 2px 10px 0 rgba(0,0,0,0.24);-webkit-box-shadow:0 2px 10px 0 rgba(0,0,0,0.24);-moz-box-shadow:0 2px 10px 0 rgba(0,0,0,0.24);background-color:#fff}.layout-config .ui-tabview.ui-tabview-top>.ui-tabview-nav{display:flex}.layout-config .ui-tabview.ui-tabview-top>.ui-tabview-nav li{vertical-align:bottom;top:auto;margin:0;background-color:transparent;border:0 none;border-radius:0;border-bottom:3px solid transparent;outline:0;cursor:pointer}.layout-config .ui-tabview.ui-tabview-top>.ui-tabview-nav li:not(.ui-state-active):not(.ui-state-disabled):hover{border-color:#a3d7a5;background-color:#eaf6eb;-moz-transition:background-color .3s;-o-transition:background-color .3s;-webkit-transition:background-color .3s;transition:background-color .3s}.layout-config .ui-tabview.ui-tabview-top>.ui-tabview-nav li:not(.ui-state-active):not(.ui-state-disabled):hover>a{color:#1b1c1e}.layout-config .ui-tabview.ui-tabview-top>.ui-tabview-nav li.ui-state-active{border:0;border-bottom:3px solid #4caf50;background-color:#c7e7c8;-moz-transition:background-color .3s;-o-transition:background-color .3s;-webkit-transition:background-color .3s;transition:background-color .3s}.layout-config .ui-tabview.ui-tabview-top>.ui-tabview-nav li.ui-state-active>a{color:#1b1c1e;cursor:pointer}.layout-config .ui-tabview.ui-tabview-top>.ui-tabview-nav li.ui-state-active:hover{background-color:#eaf6eb;-moz-transition:background-color .3s;-o-transition:background-color .3s;-webkit-transition:background-color .3s;transition:background-color .3s}.layout-config .ui-tabview.ui-tabview-top>.ui-tabview-nav li>a{color:#1b1c1e;padding:13px 15px 10px;font-weight:bold}.layout-config .ui-tabview .ui-tabview-panels{padding:1em 0;height:100%;overflow:auto;border-width:1px 0 0 0;color:#1b1c1e;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.layout-config .ui-tabview .ui-tabview-panels .ui-tabview-panel{padding:2em}.layout-config .ui-tabview .ui-tabview-panels img{max-height:100px;box-shadow:0 1px 3px rgba(0,0,0,0.12),0 1px 2px rgba(0,0,0,0.24)}.layout-config .ui-tabview .ui-tabview-panels a{display:flex;width:auto;height:auto;position:relative;overflow:hidden;justify-content:center;align-items:center;-moz-transition:transform .3s;-o-transition:transform .3s;-webkit-transition:transform .3s;transition:transform .3s;box-shadow:0 1px 3px rgba(0,0,0,0.12),0 1px 2px rgba(0,0,0,0.24)}.layout-config .ui-tabview .ui-tabview-panels a:hover{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-o-transform:scale(1.1);-ms-transform:scale(1.1);transform:scale(1.1)}.layout-config .ui-tabview .ui-tabview-panels a i{font-size:32px;color:#4caf50;position:absolute;top:50%;left:50%;margin-top:-20px;margin-left:-20px;-moz-border-radius:50%;-webkit-border-radius:50%;border-radius:50%;background-color:#fff;width:40px;line-height:40px;height:40px;font-weight:bold;box-shadow:0 1px 3px rgba(0,0,0,0.12),0 1px 2px rgba(0,0,0,0.24)}.layout-config .ui-tabview .ui-tabview-panels a.layout-config-option{width:auto;display:flex;justify-content:center;align-items:center;height:auto;overflow:hidden;text-align:center}.layout-config .ui-tabview .ui-tabview-panels a.layout-config-option:hover{-webkit-transform:scale(1.1);-moz-transform:scale(1.1);-o-transform:scale(1.1);-ms-transform:scale(1.1);transform:scale(1.1)}.layout-config .ui-tabview .ui-tabview-panels a.layout-config-layout-option img{height:87px;width:109px}.layout-config .ui-tabview .ui-tabview-panels a.layout-config-layout-option i{color:#fff;position:absolute}.layout-config .ui-tabview .ui-tabview-panels h1{font-size:21px;font-weight:600px;margin:0;margin-bottom:10px}.layout-config .ui-tabview .ui-tabview-panels span{color:#000;font-size:13px;font-weight:500;display:block;margin-top:6px;margin-bottom:15px}.layout-config .ui-tabview .ui-tabview-panels .ui-state-disabled{display:flex;width:auto;height:auto;position:relative;overflow:hidden;justify-content:center;align-items:center}.layout-config .ui-tabview .ui-tabview-panels .ui-state-disabled:hover{-webkit-transform:scale(1);-moz-transform:scale(1);-o-transform:scale(1);-ms-transform:scale(1);transform:scale(1)}.layout-config .ui-tabview .ui-tabview-panels .ui-state-disabled i{font-size:48px;color:#4caf50;background-color:transparent;box-shadow:none;position:absolute}.layout-config p{line-height:1.5;margin-top:0;color:#757575}.blocked-scroll-config{overflow:hidden}.layout-rtl .layout-config{direction:rtl;right:auto;left:0;width:550px;transform:translate3d(-550px,0,0)}.layout-rtl .layout-config.layout-config-active{transform:translate3d(0,0,0)}.layout-rtl .layout-config .layout-config-button{left:auto;right:-51px}.layout-rtl .layout-config .layout-config-close{right:auto;left:7px}@media screen and (max-width:1024px){.layout-config{transform:translate3d(100%,0,0)}.layout-config.layout-config-active{width:100%;transform:translate3d(0,0,0)}.layout-config .layout-config-button{left:auto;right:-52px}.layout-config .layout-config-close{right:10px}}body .layout-wrapper.layout-compact{font-size:14px;line-height:18px}body .layout-wrapper.layout-compact .layout-container .ultima-menu li a i:last-child{font-size:18px}body .layout-wrapper.layout-compact .layout-container .ultima-menu li ul li a span{font-size:14px}body .layout-wrapper.layout-compact .layout-container .layout-breadcrumb ul li{vertical-align:middle}body .layout-wrapper.layout-compact .ui-radiobutton .ui-radiobutton-box{width:18px;height:18px}@media(min-width:1025px){.layout-wrapper.layout-compact .layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>ul{top:35px}.layout-wrapper.layout-compact .layout-container.menu-layout-horizontal .layout-menu ul.ultima-menu>li>ul li span{font-size:14px}.layout-wrapper.layout-compact .layout-container.menu-layout-horizontal .layout-main{padding-top:110px}}
diff --git a/Development/maintainer/environment.env b/Development/maintainer/environment.env
deleted file mode 100644
index 590adbc..0000000
--- a/Development/maintainer/environment.env
+++ /dev/null
@@ -1,2 +0,0 @@
-DEBUG = agm:*,maintainer:*
-
diff --git a/Development/satloc/environment.env b/Development/satloc/environment.env
deleted file mode 100644
index 69bfe54..0000000
--- a/Development/satloc/environment.env
+++ /dev/null
@@ -1,19 +0,0 @@
-DB_NAME=agmission
-DB_USR=agm
-DB_PWD=Agm2017_dev
-DB_HOSTS=127.0.0.1:27017
-DB_REPLSET=rs0
-
-DEBUG=agm:*
-
-QUEUE_PORT=5672
-QUEUE_HOST=localhost
-
-QUEUE_USR=agm
-QUEUE_PWD=Ag@Rabbit2019
-QUEUE_NAME_JOBS=satloc_logs
-QUEUE_VHOST='/'
-QUEUE_HEARTBEAT=580
-
-SATLOC_USERNME=vendor@agnav.com
-SATLOC_PASSWORD=a{AFA4aZ
\ No newline at end of file
diff --git a/Development/satloc/error.log b/Development/satloc/error.log
deleted file mode 100644
index ef6352e..0000000
--- a/Development/satloc/error.log
+++ /dev/null
@@ -1,61 +0,0 @@
-[2025-04-16T14:27:10.730Z] Error: Error
-Message: ENOENT: no such file or directory, open '/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/job_files/5001.job'
-Stack: Error: ENOENT: no such file or directory, open '/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/job_files/5001.job'
- at Object.openSync (node:fs:590:3)
- at Object.readFileSync (node:fs:458:35)
- at Object.convertJobFileToArea (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/satloc-util.js:211:37)
- at main (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:18:29)
- at /home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:139:9
- at Object. (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:140:3)
- at Module._compile (node:internal/modules/cjs/loader:1198:14)
- at Object.Module._extensions..js (node:internal/modules/cjs/loader:1252:10)
- at Module.load (node:internal/modules/cjs/loader:1076:32)
- at Function.Module._load (node:internal/modules/cjs/loader:911:12)
-[2025-04-16T14:27:18.061Z] Error: Error
-Message: ENOENT: no such file or directory, open '/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/job_files/5001.job'
-Stack: Error: ENOENT: no such file or directory, open '/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/job_files/5001.job'
- at Object.openSync (node:fs:590:3)
- at Object.readFileSync (node:fs:458:35)
- at Object.convertJobFileToArea (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/satloc-util.js:211:37)
- at main (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:18:29)
- at /home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:139:9
- at Object. (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:140:3)
- at Module._compile (node:internal/modules/cjs/loader:1198:14)
- at Object.Module._extensions..js (node:internal/modules/cjs/loader:1252:10)
- at Module.load (node:internal/modules/cjs/loader:1076:32)
- at Function.Module._load (node:internal/modules/cjs/loader:911:12)
-[2025-04-16T14:30:19.316Z] Error: TypeError
-Message: Cannot read properties of undefined (reading 'replace')
-Stack: TypeError: Cannot read properties of undefined (reading 'replace')
- at testSatlocApi (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:61:56)
- at processTicksAndRejections (node:internal/process/task_queues:96:5)
- at async main (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:21:5)
- at async /home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:140:3
-[2025-04-16T14:37:48.206Z] Error: TypeError
-Message: Cannot read properties of undefined (reading 'replace')
-Stack: TypeError: Cannot read properties of undefined (reading 'replace')
- at testSatlocApi (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:61:56)
- at processTicksAndRejections (node:internal/process/task_queues:96:5)
- at async main (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:21:5)
- at async /home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:140:3
-[2025-04-16T14:49:00.133Z] Error: TypeError
-Message: Cannot read properties of undefined (reading 'replace')
-Stack: TypeError: Cannot read properties of undefined (reading 'replace')
- at testSatlocApi (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:61:56)
- at processTicksAndRejections (node:internal/process/task_queues:96:5)
- at async main (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:21:5)
- at async /home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:140:3
-[2025-04-16T15:27:45.505Z] Error: ReferenceError
-Message: debug is not defined
-Stack: ReferenceError: debug is not defined
- at Object.writeSatlocLogData (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/satloc-util.js:448:3)
- at async testSatlocApi (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:65:5)
- at async main (/home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:21:5)
- at async /home/justin/VSCodeProjects/AgMission/branches/satloc-integration/satloc/index.js:140:3
-[2025-10-03T20:01:27.747Z] Error: TypeError
-Message: Cannot read properties of undefined (reading 'replace')
-Stack: TypeError: Cannot read properties of undefined (reading 'replace')
- at testSatlocApi (/home/trung/work/AgMission/branches/satloc-resume/satloc/index.js:61:56)
- at processTicksAndRejections (node:internal/process/task_queues:96:5)
- at async main (/home/trung/work/AgMission/branches/satloc-resume/satloc/index.js:21:5)
- at async /home/trung/work/AgMission/branches/satloc-resume/satloc/index.js:180:3
diff --git a/Development/satloc/tests/Liqud_IF2_G4.log b/Development/satloc/tests/Liqud_IF2_G4.log
deleted file mode 100644
index 243e4b0..0000000
Binary files a/Development/satloc/tests/Liqud_IF2_G4.log and /dev/null differ
diff --git a/Development/satloc/tests/Liquid_IF2_Falcon.log b/Development/satloc/tests/Liquid_IF2_Falcon.log
deleted file mode 100644
index b58e49a..0000000
Binary files a/Development/satloc/tests/Liquid_IF2_Falcon.log and /dev/null differ
diff --git a/Development/server/.npmrc b/Development/server/.npmrc
deleted file mode 100644
index 4fd0219..0000000
--- a/Development/server/.npmrc
+++ /dev/null
@@ -1 +0,0 @@
-engine-strict=true
\ No newline at end of file
diff --git a/Development/server/.tmp/export_69e28118a9673b08101159b1.csv b/Development/server/.tmp/export_69e28118a9673b08101159b1.csv
deleted file mode 100644
index 2cedcc4..0000000
--- a/Development/server/.tmp/export_69e28118a9673b08101159b1.csv
+++ /dev/null
@@ -1 +0,0 @@
-jobId,orderNumber,jobName,sessionId,fileName,pilotName,timestampUtc,gpsTime,lat,lon,utmX,utmY,alt_m,groundSpeed_ms,heading,crossTrackError_m,lockedLine,hdop,satsInView,correctionId,waasId,sprayStat,flowRateApplied_Lmin,flowRateRequired_Lmin,appRateRequired_Lha,appRateApplied_Lha,swathWidth_m,boomPressure_psi,sprayOnLag_s,sprayOffLag_s,pulsesPerLitre,windSpeed_ms,windDir_deg,temp_c,humidity_pct
diff --git a/Development/server/.tmp/export_69e28122a9673b08101159bc.csv b/Development/server/.tmp/export_69e28122a9673b08101159bc.csv
deleted file mode 100644
index 2cedcc4..0000000
--- a/Development/server/.tmp/export_69e28122a9673b08101159bc.csv
+++ /dev/null
@@ -1 +0,0 @@
-jobId,orderNumber,jobName,sessionId,fileName,pilotName,timestampUtc,gpsTime,lat,lon,utmX,utmY,alt_m,groundSpeed_ms,heading,crossTrackError_m,lockedLine,hdop,satsInView,correctionId,waasId,sprayStat,flowRateApplied_Lmin,flowRateRequired_Lmin,appRateRequired_Lha,appRateApplied_Lha,swathWidth_m,boomPressure_psi,sprayOnLag_s,sprayOffLag_s,pulsesPerLitre,windSpeed_ms,windDir_deg,temp_c,humidity_pct
diff --git a/Development/server/helpers/web_util.js b/Development/server/helpers/web_util.js
deleted file mode 100644
index 4c76a1f..0000000
--- a/Development/server/helpers/web_util.js
+++ /dev/null
@@ -1,56 +0,0 @@
-'use strict';
-
-const puppeteer = require('puppeteer'),
- { AppInputError } = require('./app_error'),
- debug = require('debug')('agm:web-util');
-
-/**
- * Take screenshot of a screen using a headless webdriver
- * @param {*} params screenshot options { url: url, type: 'jpeg', 'png' (default), quality: 1-100 (75% default, jpeg only), width: number, height: number, path: path to save the output image }
- */
-async function webShot(params, ops = { logTime: false, timeout: 30000 }) {
- const logTime = !!(ops && ops.logTime);
- if (logTime) console.time('webShot');
- const type = params.type || 'png';
- const quality = params.quality || 75;
- const width = params.width || 800;
- const height = params.height || 600;
-
- if (!params || !params.url || !params.path) AppInputError.throw();
-
- let browser;
- try {
- browser = await puppeteer.launch({
- headless: 'new',
- // headless: false,
- slowMo: 250,
- args: ['--incognito'],
- ignoreHTTPSErrors: true,
- ignoreDefaultArgs: ['--disable-dev-shm-usage'],
- defaultViewport: { width: width, height: height },
- fullPage: true
- });
-
- const pages = await browser.pages();
- const page = pages.length ? pages[0] : await browser.newPage();
- await page.goto(params.url);
- // const selector = 'div.gm-style-cc a';
- // await page.waitForFunction(selector => !!document.querySelector(selector), { timeout: 10000 }, selector);
- // Generic wait condition when tiles all finished loading, the page set loaded can add some delay to make sure they all loaded visually perfect
- await page.waitForFunction('window.loaded == true', { timeout: ops.timeout });
-
- const shotOps = { type: type, clip: { x: 0, y: 0, width: width, height: height }, path: params.path };
- if (type == 'jpeg') shotOps['quality'] = quality;
- await page.screenshot(shotOps);
- } catch (err) {
- debug("input:", params);
- throw err;
- } finally {
- if (browser) await browser.close();
- if (logTime) console.timeEnd('webShot');
- }
-}
-
-module.exports = {
- webShot,
-}
\ No newline at end of file
diff --git a/Development/server/scripts/sub-migration/custList-May12_25-Volusia copy.json b/Development/server/scripts/sub-migration/custList-May12_25-Volusia copy.json
deleted file mode 100644
index e4c9d8e..0000000
--- a/Development/server/scripts/sub-migration/custList-May12_25-Volusia copy.json
+++ /dev/null
@@ -1,10 +0,0 @@
-[
- {
- "username": "vcmosquito@volusia.org",
- "package": "ESS-2",
- "trackingQty": 3,
- "startDate": "26/03/2025",
- "endDate": "26/03/2026",
- "taxable": "N"
- }
-]
\ No newline at end of file
diff --git a/Development/shared/db-util/mongo-client.js b/Development/shared/db-util/mongo-client.js
deleted file mode 100644
index ea36110..0000000
--- a/Development/shared/db-util/mongo-client.js
+++ /dev/null
@@ -1,21 +0,0 @@
-
-const MongoClient = require('mongodb').MongoClient
-
-const PROD_URI = "mongodb://agm:Agm2017@localhost:27017/agmission";
-
-async function connect(url) {
- let _url;
- if (!url) _url = PROD_URI;
-
- return await MongoClient.connect(_url, {
- family: 4,
- useNewUrlParser: true,
- useUnifiedTopology: true,
- bufferMaxEntries: 0,
- keepAlive: true
- });
-}
-
-module.exports = {
- connect
-}
diff --git a/Development/track-server/helpers/env.js b/Development/track-server/helpers/env.js
deleted file mode 100644
index 8864f14..0000000
--- a/Development/track-server/helpers/env.js
+++ /dev/null
@@ -1,27 +0,0 @@
-module.exports = {
- PORT: process.env.PORT,
- // SSE stream's heartbear time (secs). The interval that the server will send 'p' heartbeat to the browser to keep the connection alive
- HEARTBEAT: Number(process.env.HEARTBEAT),
- // SSE stream's reconnection time (secs). If the connection to the server is lost, the browser will wait for the specified time before attempting to reconnect
- RETRY_INT: Number(process.env.RETRY_INT),
-
- SSL_KEY: process.env.SSL_KEY,
- SSL_CERT: process.env.SSL_CERT,
-
- // DB connection info
- DB_HOSTS: process.env.DB_HOSTS,
- DB_NAME: process.env.DB_NAME,
- DB_USR: process.env.DB_USR,
- DB_PWD: process.env.DB_PWD,
- DB_REPLSET: process.env.DB_REPLSET,
-
- // RabbitMq connection info
- QUEUE_PORT: process.env.QUEUE_PORT,
- QUEUE_HOST: process.env.QUEUE_HOST,
- QUEUE_USR: process.env.QUEUE_USR,
- QUEUE_PWD: process.env.QUEUE_PWD,
- QUEUE_VHOST: process.env.QUEUE_VHOST,
- QUEUE_NAME_GDATA: process.env.QUEUE_NAME_GDATA,
- QUEUE_HEARTBEAT: process.env.QUEUE_HEARTBEAT,
-
-}
\ No newline at end of file
diff --git a/Documents/ARCHITECTURE.md b/Documents/ARCHITECTURE.md
deleted file mode 100644
index 335303d..0000000
--- a/Documents/ARCHITECTURE.md
+++ /dev/null
@@ -1,818 +0,0 @@
-# AgMission SaaS Platform Software Architecture
-
-**Version:** 3.2.x
-**Last updated:** April 2026
-**Author:** AgNav Engineering
-
----
-
-## Table of Contents
-
-- [AgMission SaaS Platform Software Architecture](#agmission-saas-platform-software-architecture)
- - [Table of Contents](#table-of-contents)
- - [1 Product Overview](#1-product-overview)
- - [2 System Components](#2-system-components)
- - [3 High-Level Architecture](#3-high-level-architecture)
- - [4 Component Details](#4-component-details)
- - [4-1 Web Client Angular SPA](#4-1-web-client-angular-spa)
- - [Module structure](#module-structure)
- - [State management NgRx](#state-management-ngrx)
- - [Key features](#key-features)
- - [4-2 API Server](#4-2-api-server)
- - [Request lifecycle](#request-lifecycle)
- - [Route groups](#route-groups)
- - [Key server-side helpers](#key-server-side-helpers)
- - [4-3 GPS Server](#4-3-gps-server)
- - [How it works](#how-it-works)
- - [4-4 Track Server](#4-4-track-server)
- - [4-5 SatLoc Integration Service](#4-5-satloc-integration-service)
- - [4-6 Background Workers](#4-6-background-workers)
- - [Worker overview](#worker-overview)
- - [Job Worker](#job-worker)
- - [Invoice Worker](#invoice-worker)
- - [Partner Sync Worker](#partner-sync-worker)
- - [Partner Data Polling Worker](#partner-data-polling-worker)
- - [Cleanup Worker](#cleanup-worker)
- - [4-7 Maintainer Service](#4-7-maintainer-service)
- - [5 Data Architecture](#5-data-architecture)
- - [MongoDB collections](#mongodb-collections)
- - [User type hierarchy](#user-type-hierarchy)
- - [6 Message Queue Architecture](#6-message-queue-architecture)
- - [7 Partner Integration Architecture](#7-partner-integration-architecture)
- - [Partner credential model](#partner-credential-model)
- - [8 Billing and Subscription Architecture](#8-billing-and-subscription-architecture)
- - [9 Authentication and Authorization](#9-authentication-and-authorization)
- - [JWT authentication](#jwt-authentication)
- - [API key authentication Public Export API](#api-key-authentication-public-export-api)
- - [Authorization model](#authorization-model)
- - [10 Infrastructure and Deployment](#10-infrastructure-and-deployment)
- - [Production server topology](#production-server-topology)
- - [PM2 managed processes](#pm2-managed-processes)
- - [Deployment flow](#deployment-flow)
- - [11 Key Data Flows](#11-key-data-flows)
- - [Job creation and assignment](#job-creation-and-assignment)
- - [Real-time GPS tracking](#real-time-gps-tracking)
- - [12 Repository Layout](#12-repository-layout)
-
----
-
-## 1 Product Overview
-
-AgMission is a cloud-based SaaS platform for **precision aerial agriculture** management. It serves agricultural aviation operators (applicators), their clients (growers/farmers), and integrated partners with console navigation aid systems (such as AgNav—Platinum and Titanium—and SatLoc).
-
-The platform manages the complete lifecycle of an aerial application job:
-
-1. **Mission planning** — create jobs, define treatment areas, assign pilots and jobs to aircraft
-2. **Job distribution** — push assigned jobs to guidance console systems, in aircraft, on the field
-3. **Real-time tracking** — live GPS monitoring of aircraft during operations
-4. **Post-flight processing** — parse binary GPS/spray logs from console systems, compute coverage statistics
-5. **Reporting and billing** — generate application reports, manage invoices, and handle SaaS subscriptions
-
-**Supported languages:** English, Portuguese (pt), Spanish (es)
-
----
-
-## 2 System Components
-
-| Component | Technology | Purpose |
-|---|---|---|
-| Web Client | Angular 9.1.13, NgRx 9, Leaflet 1.9, StimulSoftJS 2020.3.2 | Browser-based management UI and report viewer |
-| API Server | Node.js 16.20.2 LTS, Express 4, Mongoose 6 | Core REST API, business logic |
-| GPS Server | Node.js, TCP sockets | Receives GPS data from AgNav (Platinum, Titanium) and RAP devices |
-| Track Server | Node.js, Express, SSE | Real-time GPS tracking feed to browser |
-| SatLoc Service | Node.js | Partner integration with SatLoc Cloud |
-| Job Worker | Node.js, RabbitMQ | Async processing of uploaded job files |
-| Invoice Worker | Node.js, Cron | Job Invoicing—manages applicator invoices for jobs performed |
-| Partner Sync Worker | Node.js, RabbitMQ | Partner job upload and log processing |
-| Partner Polling Worker | Node.js, Cron | Polls partner systems for new flight data |
-| Cleanup Worker | Node.js, Cron | Purges soft-deleted data |
-| Maintainer | Node.js, Cron | Scheduled database maintenance |
-
----
-
-## 3 High-Level Architecture
-
-```mermaid
-graph TD
- subgraph "Field Devices"
- AGN["AgNav Guidance System"]
- RAP["RAP Guidance System"]
- SLC["SatLoc Cloud Partner"]
- end
-
- subgraph "Browser"
- UI["Angular SPA (Web Client)"]
- end
-
- subgraph "AgMission Backend (agnav.com server)"
- NGINX["Nginx Reverse Proxy"]
- API["API Server Node.js / Express"]
- GPS["GPS Server TCP :6080 / :6082"]
- TRK["Track Server HTTP/SSE"]
- MNT["Maintainer Cron Service"]
- JW["Job Worker"]
- IW["Invoice Worker"]
- PSW["Partner Sync Worker"]
- PPW["Partner Polling Worker"]
- CW["Cleanup Worker"]
- end
-
- subgraph "Data Layer"
- MDB["MongoDB Replica Set"]
- RBT["RabbitMQ Message Broker"]
- RDS["Redis Cache"]
- end
-
- subgraph "External Services"
- STR["Stripe Billing"]
- SLC2["SatLoc Cloud API satloccloudfc.com"]
- SMTP["Email / SMTP"]
- end
-
- AGN -- "TCP :6080" --> GPS
- RAP -- "TCP :6082" --> GPS
- UI -- "HTTPS" --> NGINX
- NGINX -- "/" --> API
- NGINX -- "/track" --> TRK
- GPS -- "AMQP gdata queue" --> RBT
- RBT -- "consume gdata" --> TRK
- TRK -- "SSE" --> UI
- API -- "AMQP jobs / partner_tasks" --> RBT
- RBT -- "consume jobs" --> JW
- RBT -- "consume partner_tasks" --> PSW
- API --> MDB
- JW --> MDB
- IW --> MDB
- PSW --> MDB
- PPW --> MDB
- CW --> MDB
- MNT --> MDB
- API --> RDS
- JW --> RDS
- API --> STR
- PSW -- "REST" --> SLC2
- PPW -- "REST / cron" --> SLC2
- PPW -- "AMQP partner_tasks" --> RBT
-```
-
----
-
-## 4 Component Details
-
-### 4-1 Web Client Angular SPA
-
-**Location:** `trunk/Development/client/`
-**Version:** 2.6.15
-
-The frontend is a single-page application built with Angular 9 and served by Nginx. It communicates with the API Server over HTTPS and receives real-time GPS updates from the Track Server via Server-Sent Events (SSE).
-
-#### Module structure
-
-```mermaid
-graph TD
- ROOT["AppModule (app.module.ts)"]
- ROOT --> AUTH["AuthModule Login, signup, password reset"]
- ROOT --> DASH["DashboardModule Overview metrics"]
- ROOT --> JOBS["JobModule Mission management Map editing, file upload"]
- ROOT --> TRACK["TrackModule Live GPS tracking map"]
- ROOT --> CUST["CustomerModule Client / grower management"]
- ROOT --> BILL["BillingModule Subscription and invoices"]
- ROOT --> INV["InvoicesModule Invoice listing and detail"]
- ROOT --> PART["PartnersModule Partner system users"]
- ROOT --> ADM["AdminModule Platform admin tools"]
- ROOT --> SET["SettingsModule User and account settings"]
- ROOT --> REP["ReportComponent PDF report viewer"]
- ROOT --> SIGN["SignupModule Self-service subscription signup"]
-```
-
-#### State management NgRx
-
-The app uses NgRx (Redux pattern) for global state:
-
-- **Store** — single source of truth for session, entities, and UI state
-- **Effects** — side-effects (HTTP calls) triggered by dispatched actions
-- **Entities** — normalized collections (jobs, customers, pilots, vehicles, etc.)
-- **Reducers** — pure state transitions
-
-#### Key features
-
-| Feature | Description |
-|---|---|
-| Job map editor | Leaflet map for defining treatment areas, waypoints, and obstacles |
-| Live tracking | Real-time aircraft position overlay via SSE |
-| Data playback | Replay completed flight paths from application logs |
-| Invoicing | Create, send, and track invoices |
-| Subscription management | Self-serve plan selection, trial, and upgrade; manage billing information; integrate with Stripe |
-| Multi-language | English / Portuguese / Spanish via Angular i18n |
-| Partner customers | View and manage SatLoc partner-linked customer accounts; support Satloc console systems, G4, Falcon in automated workflows |
-
----
-
-### 4-2 API Server
-
-**Location:** `trunk/Development/server/`
-**Entry point:** `server.js`
-**Port:** `AGM_PORT` (default 7000 in production)
-
-The central Express application. All browser requests pass through Nginx which proxies to this server. It handles authentication, all business logic REST endpoints, file uploads, Stripe webhooks, and report generation.
-
-#### Request lifecycle
-
-```mermaid
-sequenceDiagram
- participant C as Client (Browser)
- participant N as Nginx
- participant S as API Server
- participant MW as Middlewares
- participant R as Routes
- participant CTL as Controller
- participant DB as MongoDB
-
- C->>N: HTTPS request
- N->>S: HTTP proxy (X-Forwarded-*)
- S->>MW: Rate limiter
- MW->>MW: JWT checkUser
- MW->>R: Router dispatch
- R->>CTL: Handler function
- CTL->>DB: Mongoose queries
- DB-->>CTL: Documents
- CTL-->>C: JSON response
-```
-
-#### Route groups
-
-| Route prefix | Controller | Description |
-|---|---|---|
-| `/api/users` | user.js | User account CRUD |
-| `/api/customers` | customer.js | Applicator / client management |
-| `/api/jobs` | job.js | Mission lifecycle |
-| `/api/upload` | upload_job.js | Job file upload (ZIP/KML/SHP) |
-| `/api/pilots` | pilot.js | Pilot management |
-| `/api/vehicles` | vehicle.js | Aircraft management |
-| `/api/billing` | billing.js | Stripe subscription billing |
-| `/api/subscription` | subscription.js | Subscription plans |
-| `/api/invoices` | invoice.js | Invoice management |
-| `/api/invoice_settings` | invoice_settings.js | Invoice templates |
-| `/api/partners` | partner.js | Partner org and system users |
-| `/api/dlq/:queue` | dlq.js | Dead Letter Queue management |
-| `/api/export` | export.js | Data export (CSV/IIF) |
-| `/api/v1` | api_pub.js | Public data export API (API-key auth) |
-| `/api/health` | health.js | Health check endpoint |
-| `/stripe_webhooks` | subscription_webhooks.js | Stripe event webhook |
-
-#### Key server-side helpers
-
-| Helper | Purpose |
-|---|---|
-| `helpers/constants.js` | Frozen enums (UserTypes, AppStatus, etc.) |
-| `helpers/env.js` | Typed environment variable access |
-| `helpers/subscription_util.js` | Stripe SDK wrappers |
-| `helpers/job_util.js` | Job state machine logic |
-| `helpers/geo_util.js` | Geospatial calculations (turf.js) |
-| `helpers/satloc_log_parser.js` | Binary SatLoc log file parser |
-| `helpers/satloc_application_processor.js` | Spray statistics from parsed logs |
-| `helpers/mailer.js` | Transactional email |
-| `helpers/logger.js` | Pino structured logging |
-
----
-
-### 4-3 GPS Server
-
-**Location:** `trunk/Development/gps-server/`
-**Entry point:** `gps-server.js`
-**Ports:** TCP 6080 (AgNav), TCP 6082 (RAP)
-
-A low-level TCP socket server that receives binary GPS telemetry from field console navigation aid systems (AgNav, RAP) in real-time.
-
-#### How it works
-
-```mermaid
-sequenceDiagram
- participant D as AgNav / RAP Console System
- participant G as GPS Server (TCP)
- participant MDB as MongoDB
- participant RBT as RabbitMQ
-
- D->>G: TCP binary packet
- G->>G: Parse (AgNavParser / RAPParser)
- G->>MDB: Upsert location + location_cache
- G->>RBT: Publish to "gdata" queue
-```
-
-Two protocol variants are configured via `PROTOCOL` env var:
-
-| Protocol | Port | Device type |
-|---|---|---|
-| `AGNAV` | 6080 | AgNav console system (Platinum, Titanium) |
-| `RAP` | 6082 | RAP binary protocol (external tracking devices) |
-
-The GPS Server writes every position to MongoDB (`locations` + `location_cache` collections) and simultaneously publishes to a RabbitMQ queue `gdata`, which the Track Server consumes for live SSE streaming.
-
----
-
-### 4-4 Track Server
-
-**Location:** `trunk/Development/track-server/`
-**Entry point:** `track-server.js`
-**Protocol:** HTTP/2 (via `spdy`) + Server-Sent Events
-
-The Track Server bridges the real-time GPS queue to browser clients using SSE channels. Each client subscribes to one or more vehicle channels; the server pushes position updates as they arrive from RabbitMQ.
-
-```mermaid
-sequenceDiagram
- participant RBT as RabbitMQ gdata
- participant TS as Track Server
- participant BR as Browser (SSE client)
-
- RBT-->>TS: GPS data message
- TS->>TS: setVehGpsData(data)
- TS->>BR: SSE event (vehicle position)
-```
-
-Authentication is JWT-based — clients obtain a short-lived track token from the API Server, then connect to Track Server with it.
-
----
-
-### 4-5 SatLoc Integration Service
-
-**Location:** `trunk/Development/satloc/`
-**Purpose:** Batch job of importing completed flight logs from the SatLoc Cloud partner system.
-
-SatLoc is an aerial guidance hardware vendor. When an applicator uses SatLoc hardware, their flight logs are stored in SatLoc's cloud. This service:
-
-1. Authenticates with SatLoc Cloud (`satloccloudfc.com`)
-2. Retrieves aircraft log metadata
-3. Downloads binary log files
-4. Parses the proprietary binary format
-5. Creates `ApplicationDetail` records in AgMission's database
-
-The core C# parsing logic (`frmMain.cs`, `APIObjects.cs`) is the original reference implementation; the production path is the Node.js port in `satloc-api.js` and the server-side `helpers/satloc_log_parser.js`.
-
----
-
-### 4-6 Background Workers
-
-All workers are independent Node.js processes managed by PM2. They communicate via RabbitMQ queues and share the same MongoDB instance as the API Server.
-
-#### Worker overview
-
-```mermaid
-graph LR
- API["API Server"] -- "publish jobs" --> JQ["jobs queue (RabbitMQ)"]
- API -- "publish partner_tasks" --> PQ["partner_tasks queue (RabbitMQ)"]
- JQ --> JW["Job Worker job_worker.js"]
- PQ --> PSW["Partner Sync Worker partner_sync_worker.js"]
- PPW["Partner Polling Worker (cron every 15 min)"] -- "publish partner_tasks" --> PQ
- PPW -- "REST poll" --> SLCAPI["SatLoc Cloud API"]
- IW["Invoice Worker (cron every 1 min)"] --> MDB["MongoDB"]
- CW["Cleanup Worker (cron weekly)"] --> MDB
- JW --> MDB
- PSW --> MDB
- PSW -- "REST" --> SLCAPI
-```
-
-#### Job Worker
-
-Consumes the `jobs` RabbitMQ queue. Triggered when a user uploads a job file via the web UI.
-
-Responsibilities:
-- Extract ZIP archives containing job data files
-- Parse AgNav binary (`.agn`), KML, and Shapefile formats
-- Calculate sprayed area, coverage geometry, application statistics
-- Create `Application`, `ApplicationFile`, and `ApplicationDetail` records
-- Use Redis for deduplication and temporary state
-
-#### Invoice Worker
-
-Cron-driven (every minute). Manages Job Invoicing—the lifecycle of applicator invoices for jobs performed on behalf of clients. Handles invoice state transitions and late-payment notifications.
-
-**Note:** Job Invoicing is separate from SaaS subscription management (which is handled by Stripe webhooks in the API Server).
-
-```mermaid
-stateDiagram-v2
- direction LR
- [*] --> Draft : invoice created
- Draft --> Open : openDate reached
- Open --> Overdue : dueDate passed
- Open --> Paid : payment received
- Overdue --> Paid : late payment
- Paid --> [*]
-```
-
-#### Partner Sync Worker
-
-Consumes the `partner_tasks` queue. Handles two task types:
-
-| Task type | Action |
-|---|---|
-| `UPLOAD_PARTNER_JOB` | Pushes a job (waypoints, boundaries) to SatLoc Cloud for the assigned aircraft |
-| `PROCESS_PARTNER_LOG` | Parses a downloaded SatLoc binary log file and creates `ApplicationDetail` records |
-
-#### Partner Data Polling Worker
-
-Cron-driven (every 15 min in production, every 1 min in development).
-
-```mermaid
-graph TD
- A["Cron trigger"] --> B["Find JobAssigns status = UPLOADED"]
- B --> C["Group by partner + customer"]
- C --> D["Call SatLoc: GetAircraftLogs"]
- D --> E{"New log files?"}
- E -- No --> F["Done"]
- E -- Yes --> G["Download log file to local storage"]
- G --> H["Create PartnerLogTracker PENDING → DOWNLOADED"]
- H --> I["Enqueue PROCESS_PARTNER_LOG to partner_tasks queue"]
-```
-
-#### Cleanup Worker
-
-Weekly cron job. Hard-deletes records that have been soft-deleted (`markedDelete: true`) and older than a retention period. Applies to customers, jobs, pilots, vehicles, and related entities.
-
----
-
-### 4-7 Maintainer Service
-
-**Location:** `trunk/Development/maintainer/`
-**Entry point:** `index.js`
-
-A lightweight cron-based utility for database maintenance tasks not suitable for the main server. Scheduled tasks:
-
-| Task | Schedule (prod) | Description |
-|---|---|---|
-| `cleanMarkedDeleteData` | Weekly (Sunday 01:00 UTC) | Remove soft-deleted customer records |
-
-Connects to MongoDB using the same models as the API Server (shared `model/` layer).
-
----
-
-## 5 Data Architecture
-
-### MongoDB collections
-
-```mermaid
-erDiagram
- USER {
- ObjectId _id
- string kind
- string email
- string name
- string userType
- boolean active
- ObjectId parent
- }
- CUSTOMER {
- ObjectId _id
- string name
- ObjectId byPuid
- }
- JOB {
- number _id
- string name
- ObjectId customer
- ObjectId byPuid
- number status
- Date startDate
- }
- JOB_ASSIGN {
- ObjectId _id
- number job
- ObjectId user
- number status
- string extJobId
- }
- APPLICATION {
- ObjectId _id
- number jobId
- string fileName
- Date startDateTime
- Date endDateTime
- number totalSprayed
- }
- APPLICATION_DETAIL {
- ObjectId _id
- ObjectId appId
- number lat
- number lon
- number rate
- Date gdt
- }
- SUBSCRIPTION {
- ObjectId _id
- ObjectId byPuid
- string stripeSubId
- string planKey
- string status
- }
- INVOICE {
- ObjectId _id
- ObjectId byPuid
- number status
- Date openDate
- Date dueDate
- }
- PARTNER_LOG_TRACKER {
- ObjectId _id
- string status
- ObjectId jobAssignId
- string localFilePath
- }
-
- USER ||--o{ JOB : "creates (byPuid)"
- CUSTOMER ||--o{ JOB : "associated"
- JOB ||--o{ JOB_ASSIGN : "assigned to"
- JOB_ASSIGN }o--|| USER : "pilot/device"
- JOB ||--o{ APPLICATION : "contains"
- APPLICATION ||--o{ APPLICATION_DETAIL : "detail records"
- USER ||--o{ SUBSCRIPTION : "holds"
- USER ||--o{ INVOICE : "receives"
- JOB_ASSIGN ||--o{ PARTNER_LOG_TRACKER : "tracks"
-```
-
-### User type hierarchy
-
-The `User` model uses a Mongoose discriminator pattern to represent multiple actor types from one collection:
-
-| `userType` code | Kind | Description |
-|---|---|---|
-| `0` | ADMIN | Platform administrator |
-| `1` | APP | Applicator (main operator account) |
-| `2` | APP_ADM | Applicator admin |
-| `3` | CLIENT | Client / grower (read-only) |
-| `4` | OFFICER | Field officer |
-| `5` | PILOT | Pilot |
-| `6` | INSPECTOR | Inspector |
-| `9` | DEVICE | Aircraft / guidance unit |
-| `20` | PARTNER | Partner organization (e.g., SatLoc) |
-| `21` | PARTNER_SYSTEM_USER | Customer account in partner system |
-
----
-
-## 6 Message Queue Architecture
-
-RabbitMQ is used for all async work. Queue names are auto-prefixed with `dev_` in non-production environments.
-
-```mermaid
-graph LR
- subgraph "Producers"
- API["API Server"]
- PPW["Partner Polling Worker"]
- GPS["GPS Server"]
- end
-
- subgraph "Queues (RabbitMQ)"
- JQ["jobs"]
- PQ["partner_tasks"]
- PDLQ["partner_tasks_failed (DLQ)"]
- GQ["gdata"]
- end
-
- subgraph "Consumers"
- JW["Job Worker"]
- PSW["Partner Sync Worker"]
- TRK["Track Server"]
- end
-
- API --> JQ
- API --> PQ
- PPW --> PQ
- GPS --> GQ
- JQ --> JW
- PQ --> PSW
- PSW -- "on max retries" --> PDLQ
- GQ --> TRK
-```
-
-The Dead Letter Queue (`partner_tasks_failed`) is managed through the `/api/dlq/:queueName/*` API endpoints, which provide list, retry, and purge operations.
-
----
-
-## 7 Partner Integration Architecture
-
-```mermaid
-sequenceDiagram
- participant UI as Web Client
- participant API as API Server
- participant PSW as Partner Sync Worker
- participant PPW as Partner Polling Worker
- participant SLC as SatLoc Cloud
-
- UI->>API: Assign job to SatLoc device
- API->>API: Create JobAssign (status=NEW)
- API->>PSW: Enqueue UPLOAD_PARTNER_JOB
- PSW->>SLC: POST /api/Satloc/UploadJobData
- SLC-->>PSW: extJobId
- PSW->>API: Update JobAssign (status=UPLOADED, extJobId)
-
- Note over PPW: Cron: every 15 min
- PPW->>API: Find JobAssigns status=UPLOADED
- PPW->>SLC: GET /api/Satloc/GetAircraftLogs
- SLC-->>PPW: Log list
- PPW->>SLC: GET /api/Satloc/GetAircraftLogData
- SLC-->>PPW: Binary log file
- PPW->>PPW: Store file locally (SATLOC_STORAGE_PATH)
- PPW->>API: Create PartnerLogTracker (DOWNLOADED)
- PPW->>PSW: Enqueue PROCESS_PARTNER_LOG
- PSW->>PSW: Parse binary log (SatLocLogParser)
- PSW->>API: Create ApplicationDetail records
-```
-
-### Partner credential model
-
-Each customer that uses a SatLoc device has a dedicated `PartnerSystemUser` record (userType=21) that stores their SatLoc `companyId`, `partnerUserId`, and API key. This isolates customer data within the partner system.
-
----
-
-## 8 Billing and Subscription Architecture
-
-```mermaid
-sequenceDiagram
- participant UI as Web Client
- participant API as API Server
- participant STR as Stripe
-
- UI->>API: Select subscription plan
- API->>STR: Create SetupIntent or Subscription
- STR-->>API: Client secret
- UI->>STR: Confirm card (3DS if needed)
- STR->>API: POST /stripe_webhooks (subscription events)
- API->>API: Update Subscription record
-```
-
-Note: SaaS subscription management (tiers, billing cycles) is handled through Stripe webhooks. Job Invoicing (applicators invoicing clients for jobs performed) is a separate function managed by the Invoice Worker.
-
-**SaaS Subscription tiers** (mapped to Stripe price IDs via env vars):
-
-| Tier | Env key prefix | Description |
-|---|---|---|
-| Essential | `ESS_1` … `ESS_5` | Entry-level operator plans |
-| Enterprise | `ENT_1` … `ENT_4` | High-volume operator plans |
-| Add-on | `ADDON_1` | Additional features, refer to Live Tracking service |
-
----
-
-## 9 Authentication and Authorization
-
-### JWT authentication
-
-All API Server endpoints (except signup and Stripe webhooks) require a JWT Bearer token:
-
-```
-Authorization: Bearer
-```
-
-Tokens are issued on login and carry `userType`, `byPuid` (applicator ID), and `userId`. The `checkUser` middleware validates the token and attaches the user to `req.user`.
-
-### API key authentication Public Export API
-
-The `/api/v1/` public export endpoints use an `X-API-Key` header instead of JWT. Keys are bcrypt-hashed and stored in the `ApiKey` collection. Each key is scoped to a specific applicator (`byPuid`), so data access is automatically isolated.
-
-### Authorization model
-
-```
-ADMIN — full platform access
-APP — manages own organization (pilots, vehicles, jobs, customers)
-APP_ADM — same as APP within parent organization
-CLIENT — read-only access to own job results
-PILOT — limited: download job assignments
-OFFICER — field oversight
-INSPECTOR — read-only job inspection
-PARTNER — manages partner organization
-PARTNER_SYSTEM_USER — customer credentials for a specific partner
-```
-
----
-
-## 10 Infrastructure and Deployment
-
-### Production server topology
-
-```mermaid
-graph TD
- INT["Internet"] --> NX["Nginx SSL termination port 443"]
- NX -- "/ → :7000" --> API["agmission-prod (PM2)"]
- NX -- "/track → :4200" --> TRK["track_server (PM2)"]
- API --> MDB["MongoDB Replica Set rs0"]
- API --> RBT["RabbitMQ"]
- API --> RDS["Redis"]
- GPS1["gps_server-agnav TCP :6080 (PM2)"] --> MDB
- GPS1 --> RBT
- GPS2["gps_server-rap TCP :6082 (PM2)"] --> MDB
- GPS2 --> RBT
- RBT --> JW["job_worker (PM2)"]
- RBT --> PSW["partner_sync_worker (PM2)"]
- PPW["partner_data_polling_worker (PM2)"] --> MDB
- PPW --> RBT
- IW["invoice_worker (PM2)"] --> MDB
- CW["cleanup_worker (PM2)"] --> MDB
-```
-
-### PM2 managed processes
-
-| PM2 name | Entry point | Description |
-|---|---|---|
-| `agmission-prod` | `server.js` | Main API server |
-| `track_server` | `track-server.js` | Live tracking |
-| `gps_server-agnav` | `gps-server.js` | AgNav TCP receiver |
-| `gps_server-rap` | `gps-server.js` | RAP TCP receiver |
-| `job_worker` / `job-importer` | `workers/job_worker.js` | Job file processing |
-| `invoice_worker` | `workers/invoice_worker.js` | Invoice automation |
-| `cleanup_worker` | `workers/cleanup_worker.js` | Soft-delete cleanup |
-| `partner_sync_worker` | `workers/partner_sync_worker.js` | Partner job/log sync |
-| `partner_data_polling_worker` | `workers/partner_data_polling_worker.js` | Partner log polling |
-
-### Deployment flow
-
-Deployments are performed using `trunk/Others/scripts/deploy/agm-deploy.sh`. See [DEPLOYMENT.md](DEPLOYMENT.md) for full instructions.
-
-```mermaid
-graph LR
- DEV["Local dev machine (SVN trunk or branch)"] -- "rsync over SSH agm-deploy.sh" --> PROD["Production server agmission-1.agnav.com:22222"]
- PROD --> PM2["pm2 reload agmission-prod"]
-```
-
----
-
-## 11 Key Data Flows
-
-### Job creation and assignment
-
-```mermaid
-graph TD
- A["Applicator creates job in Web UI"] --> B["POST /api/jobs"]
- B --> C["Job record created in MongoDB"]
- C --> D["Upload job file (ZIP / KML / SHP)"]
- D --> E["POST /api/upload"]
- E --> F["File stored on disk job-uploads/"]
- F --> G["Job message published to 'jobs' queue"]
- G --> H["Job Worker consumes message"]
- H --> I["Unzip and parse files"]
- I --> J["Calculate spray statistics"]
- J --> K["Create Application + ApplicationDetail records"]
- K --> L["Applicator assigns job to pilot / device / partner"]
- L --> M{"Partner?"}
- M -- "No (internal)" --> N["JobAssign created status=NEW"]
- M -- "Yes (SatLoc)" --> O["UPLOAD_PARTNER_JOB enqueued"]
- O --> P["Partner Sync Worker uploads to SatLoc Cloud"]
- P --> Q["JobAssign status=UPLOADED + extJobId stored"]
-```
-
-### Real-time GPS tracking
-
-```mermaid
-graph LR
- HW["AgNav Device"] -- "binary TCP" --> GPS["GPS Server"]
- GPS --> MDB["MongoDB locations"]
- GPS --> RBT["RabbitMQ gdata"]
- RBT --> TRK["Track Server"]
- TRK -- "SSE push" --> UI["Browser Map"]
-```
-
----
-
-## 12 Repository Layout
-
-```
-AgMission/
-├── trunk/
-│ ├── Development/ ← Active source code (see below)
-│ ├── Documents/ ← Architecture, requirements, design docs
-│ └── Others/
-│ ├── configs/ ← PM2 JSON configs for deployment target
-│ └── scripts/ ← Operations scripts
-│ ├── deploy/ ← Deployment automation (agm-deploy.sh)
-│ ├── backup_agm.sh ← MongoDB backup + rsync to NAS
-│ └── start_pm2_apps.sh
-│
-├── branches/ ← Feature branches (SVN layout)
-│ ├── subscription-invoicing/
-│ ├── subscription-signup/
-│ ├── job-invoicing/
-│ ├── data-export-api/
-│ └── satloc-resume/
-│
-└── tags/ ← Release snapshots
- └── release-3.2.1/
-
-trunk/Development/
-├── client/ Angular SPA
-├── server/ Express API server + workers
-│ ├── controllers/
-│ ├── helpers/
-│ ├── middlewares/
-│ ├── model/
-│ ├── routes/
-│ ├── services/
-│ └── workers/
-├── gps-server/ TCP GPS receiver
-├── track-server/ SSE live tracking
-├── satloc/ SatLoc integration (batch import)
-├── maintainer/ Scheduled DB maintenance
-├── shared/ Shared DB utilities and models
-└── libs/ Bundled third-party libs (shapefile, etc.)
-```
-
----
-
-*For deployment instructions see [DEPLOYMENT.md](DEPLOYMENT.md).*
-*For the server-side REST API reference see [`server/docs/API_SPECIFICATION.md`](../Development/server/docs/API_SPECIFICATION.md).*
-*For partner integration details see [`server/docs/PARTNER_INTEGRATION_ARCHITECTURE.md`](../Development/server/docs/PARTNER_INTEGRATION_ARCHITECTURE.md).*
diff --git a/Documents/AgMission Test Cases-18042019.xlsx b/Documents/AgMission Test Cases-18042019.xlsx
deleted file mode 100644
index 84e294a..0000000
Binary files a/Documents/AgMission Test Cases-18042019.xlsx and /dev/null differ
diff --git a/Documents/AgMission Training.docx b/Documents/AgMission Training.docx
deleted file mode 100644
index fdc0d92..0000000
Binary files a/Documents/AgMission Training.docx and /dev/null differ
diff --git a/Documents/AgMission Training.pptx b/Documents/AgMission Training.pptx
deleted file mode 100644
index 32f51ea..0000000
Binary files a/Documents/AgMission Training.pptx and /dev/null differ
diff --git a/Documents/AgMission-User Manual.doc b/Documents/AgMission-User Manual.doc
deleted file mode 100644
index 55dac73..0000000
Binary files a/Documents/AgMission-User Manual.doc and /dev/null differ
diff --git a/Documents/AgMission-User Manual.pdf b/Documents/AgMission-User Manual.pdf
deleted file mode 100644
index 61b1ab9..0000000
Binary files a/Documents/AgMission-User Manual.pdf and /dev/null differ
diff --git a/Documents/AgMission-User-Manual-Spanish.docx b/Documents/AgMission-User-Manual-Spanish.docx
deleted file mode 100644
index d8d50a1..0000000
Binary files a/Documents/AgMission-User-Manual-Spanish.docx and /dev/null differ
diff --git a/Documents/DEPLOYMENT.md b/Documents/DEPLOYMENT.md
deleted file mode 100644
index 00934e1..0000000
--- a/Documents/DEPLOYMENT.md
+++ /dev/null
@@ -1,372 +0,0 @@
-# AgMission Deployment Guide
-
-**Last updated:** April 2026
-
----
-
-## Table of Contents
-
-- [1 Prerequisites](#1-prerequisites)
-- [2 Repository and Directory Layout](#2-repository-and-directory-layout)
-- [3 Deployment Script Reference](#3-deployment-script-reference)
-- [4 Configuration](#4-configuration)
-- [5 Deployment Modes](#5-deployment-modes)
-- [6 Step-by-Step Production Deployment](#6-step-by-step-production-deployment)
-- [7 PM2 Process Management](#7-pm2-process-management)
-- [8 Backup and Recovery](#8-backup-and-recovery)
-- [9 Environment Variables Reference](#9-environment-variables-reference)
-
----
-
-## 1 Prerequisites
-
-**Developer machine (deploying from):**
-
-- SSH access to the production server on port `22222`
-- `rsync` installed
-- SVN working copy at `~/work/AgMission`
-- AGN private libraries at `~/work/@agn`
-- Angular CLI for frontend builds (`npm install -g @angular/cli`)
-
-**Production server:**
-
-- Node.js 16.20.2 LTS
-- PM2 (global): `npm install -g pm2`
-- MongoDB 4.4.x replica set (`rs0`)
-- RabbitMQ 3.10.7+
-- Redis
-- Nginx 1.10.x+
-
----
-
-## 2 Repository and Directory Layout
-
-### Local developer machine
-
-```
-~/work/AgMission/
-├── trunk/Development/ ← main line source
-├── branches// ← feature branches
-└── tags/release-x.y.z/ ← release snapshots
-
-~/work/@agn/ ← private AGN Node.js libraries
-```
-
-### Remote production server
-
-```
-/home/agm/apps/
-├── agmission/ ← API server (server/)
-├── client/dist/ ← Angular build output
-├── gps-server/
-├── track-server/
-├── satloc/
-├── maintainer/
-├── pm2-apps/ ← PM2 JSON configs
-└── @agn/ ← private libs (rsync'd from ~/work/@agn)
-```
-
----
-
-## 3 Deployment Script Reference
-
-**Script:** `trunk/Others/scripts/deploy/agm-deploy.sh`
-
-### Usage
-
-```bash
-./agm-deploy.sh [run_mode] [branch_name] [fe_mode]
-```
-
-| Argument | Values | Default | Description |
-|---|---|---|---|
-| `run_mode` | `0`, `1`, `2` | `0` | Deployment scope (see table below) |
-| `branch_name` | `trunk`, `main`, or branch name | `subscription-invoicing` | Source to deploy from |
-| `fe_mode` | `run`, `dry-run` | `run` | Frontend deploy behaviour (mode 2 only) |
-
-| `run_mode` | Backend | Frontend | Use case |
-|---|---|---|---|
-| `0` (or empty) | Dry run | Dry run | Verify what would be synced |
-| `1` | Deploy | Skipped | Backend-only update |
-| `2` | Deploy | Deploy | Full production deployment |
-
-### Configuration file recommended
-
-```bash
-# 1. Copy template
-cp trunk/Others/scripts/deploy/agm-deploy.conf.template ~/.agm-deploy.conf
-
-# 2. Edit values
-nano ~/.agm-deploy.conf
-
-# 3. Use it
-source ~/.agm-deploy.conf && ./agm-deploy.sh 1 trunk
-```
-
-### Environment variable overrides
-
-| Variable | Default | Description |
-|---|---|---|
-| `AGM_BASE_DIR` | `~/work/AgMission` | Local AgMission SVN root |
-| `AGN_LIBS_DIR` | `~/work/@agn` | Local AGN private libraries |
-| `AGM_DEST_HOST` | `agm@agmission-1.agnav.com` | SSH target (`user@host`) |
-| `AGM_DEST_PORT` | `22222` | SSH port |
-| `AGM_DEST_PATH` | `/home/agm/apps` | Remote base directory |
-
----
-
-## 4 Configuration
-
-### Excluded files
-
-`trunk/Others/scripts/deploy/excludes.txt` lists paths excluded from `rsync`. These typically include:
-
-- `node_modules/`
-- `.env` / `environment*.env` (never overwrite production secrets)
-- `.tmp/`, `job-unzip/`, `job-uploads/`
-- Log files and rlog crash reports
-
-### Production environment files
-
-Environment files are **not** deployed by the script. They must be maintained manually on the production server. See [9 Environment Variables Reference](#9-environment-variables-reference) for key variables.
-
----
-
-## 5 Deployment Modes
-
-### Dry run mode 0
-
-```bash
-./agm-deploy.sh 0 trunk
-```
-
-Shows all files that would be synced without touching the server. Always run this first when deploying after a significant code change.
-
-### Backend only mode 1
-
-```bash
-./agm-deploy.sh 1 trunk
-```
-
-Deploys:
-- `server/` → `/home/agm/apps/agmission/`
-- `gps-server/` → `/home/agm/apps/gps-server/`
-- `track-server/` → `/home/agm/apps/track-server/`
-- `shared/` → `/home/agm/apps/shared/`
-- `@agn` libraries → `/home/agm/apps/@agn/`
-
-After sync, run `pm2 reload` on the server (see [7 PM2 Process Management](#7-pm2-process-management)).
-
-### Full deployment mode 2
-
-```bash
-./agm-deploy.sh 2 trunk
-```
-
-Deploys everything in mode 1, plus the Angular frontend `client/dist/` to the Nginx webroot.
-
-**Before running mode 2**, build the frontend locally:
-
-```bash
-cd trunk/Development/client
-npm install
-npm run build-prod # builds all locales: en, pt, es
-```
-
----
-
-## 6 Step-by-Step Production Deployment
-
-```mermaid
-graph TD
- A["1. Update SVN working copy svn update trunk/"] --> B["2. Run tests cd server && npm test"]
- B --> C["3. Build frontend cd client && npm run build-prod"]
- C --> D["4. Dry run ./agm-deploy.sh 0 trunk"]
- D --> E{"Review OK?"}
- E -- No --> F["Fix issues"]
- F --> D
- E -- Yes --> G["5. Deploy backend ./agm-deploy.sh 1 trunk"]
- G --> H["6. SSH to server ssh agm@agmission-1.agnav.com -p 22222"]
- H --> I["7. Install dependencies cd /home/agm/apps/agmission && npm install --production"]
- I --> J["8. Reload PM2 pm2 reload agmission-prod"]
- J --> K["9. Check logs pm2 logs agmission-prod --lines 50"]
- K --> L{"Errors?"}
- L -- Yes --> M["Rollback: pm2 reload with previous snapshot"]
- L -- No --> N["10. Deploy frontend (if needed) ./agm-deploy.sh 2 trunk"]
-```
-
-### Post-deployment checklist
-
-- [ ] `pm2 status` — all processes show `online`
-- [ ] `pm2 logs agmission-prod --lines 20` — no crash errors
-- [ ] Health check endpoint: `curl https://agmission-1.agnav.com/api/health`
-- [ ] Login to the web UI and confirm dashboard loads
-- [ ] If partner workers were updated: `pm2 reload partner_sync_worker partner_data_polling_worker`
-- [ ] If worker code changed: check `pm2 logs job_worker --lines 20`
-
----
-
-## 7 PM2 Process Management
-
-### Start all services first time
-
-```bash
-# SSH into server
-ssh agm@agmission-1.agnav.com -p 22222
-
-# Start all configured apps
-/home/agm/apps/pm2-apps/start_pm2_apps.sh
-```
-
-### Reload after deployment
-
-```bash
-# Reload individual app (zero-downtime)
-pm2 reload agmission-prod
-
-# Reload all at once
-pm2 reload all
-
-# Check status
-pm2 status
-pm2 logs agmission-prod --lines 30
-```
-
-### Process list
-
-```bash
-pm2 list
-```
-
-Expected processes:
-
-| Name | Status |
-|---|---|
-| `agmission-prod` | online |
-| `track_server` | online |
-| `gps_server-agnav` | online |
-| `gps_server-rap` | online |
-| `job_worker` | online |
-| `invoice_worker` | online |
-| `cleanup_worker` | online |
-| `partner_sync_worker` | online |
-| `partner_data_polling_worker` | online |
-
-### Restart a single worker
-
-```bash
-pm2 restart job_worker
-pm2 restart partner_sync_worker
-pm2 restart invoice_worker
-```
-
-### Save current PM2 process list
-
-```bash
-pm2 save
-```
-
----
-
-## 8 Backup and Recovery
-
-### Automated backup
-
-`trunk/Others/scripts/backup_agm.sh` runs via cron. It:
-
-1. Dumps MongoDB to a gzip archive (`mongodump --gzip`)
-2. Retains 11 days of database archives
-3. Syncs archives to NAS via `rsync` (`rsync://rsync@data.agnav.com/agm/`)
-4. Syncs uploaded job files separately
-5. Syncs rsync log files
-
-### Manual MongoDB backup
-
-```bash
-mongodump \
- --archive=/home/agm/backups/manual_$(date +%Y%m%d).gz \
- --gzip \
- --db agmission \
- --username agm \
- --authenticationDatabase agmission
-```
-
-### Restore from backup
-
-```bash
-mongorestore \
- --archive=/home/agm/backups/agmdb_YYYYMMDD.gz \
- --gzip \
- --db agmission \
- --username agm \
- --authenticationDatabase agmission \
- --drop
-```
-
----
-
-## 9 Environment Variables Reference
-
-Environment files live on the production server at `/home/agm/apps/agmission/environment.env` and are **not** managed by the deployment script.
-
-### Core server
-
-| Variable | Description |
-|---|---|
-| `AGM_PORT` | HTTP port the API server listens on (e.g., `7000`) |
-| `PRODUCTION` | `true` in production |
-| `DB_USR` / `DB_PWD` / `DB_NAME` | MongoDB credentials |
-| `DB_HOSTS` | MongoDB host list (replica set) |
-| `DB_REPLSET` | Replica set name (`rs0`) |
-| `REDIS_PWD` | Redis password |
-| `JWT_SECRET` | JWT signing secret |
-| `MAX_SESSION_SECS` | Session token lifetime (default: 28800) |
-
-### Stripe billing
-
-| Variable | Description |
-|---|---|
-| `STRIPE_SECRET_KEY` | Stripe secret key |
-| `STRIPE_PUBLISHABLE_KEY` | Stripe publishable key |
-| `STRIPE_API_VERSION` | Stripe API version |
-| `STRIPE_WH_SEC` | Stripe webhook signing secret |
-| `ESS_1` … `ESS_5` | Stripe price IDs for Essential plans |
-| `ENT_1` … `ENT_4` | Stripe price IDs for Enterprise plans |
-| `ADDON_1` | Stripe price ID for add-on |
-
-### File storage
-
-| Variable | Description |
-|---|---|
-| `UPLOAD_DIR` | Job upload directory |
-| `UNZIP_DIR` | Temp extraction directory |
-| `INV_UPLOAD_DIR` | Invoice image upload directory |
-| `INV_IMG_VIR_DIR` | Virtual URL path for invoice images |
-| `SATLOC_STORAGE_PATH` | Local storage for downloaded SatLoc log files |
-
-### RabbitMQ
-
-| Variable | Description |
-|---|---|
-| `QUEUE_HOST` | RabbitMQ host |
-| `QUEUE_PORT` | RabbitMQ port (default: 5672) |
-| `QUEUE_USR` / `QUEUE_PWD` | RabbitMQ credentials |
-| `QUEUE_VHOST` | Virtual host |
-| `QUEUE_NAME_JOBS` | Job processing queue name |
-| `QUEUE_NAME_GDATA` | GPS data queue name |
-| `QUEUE_NAME_PARTNER` | Partner tasks queue name |
-
-### Partner integration
-
-| Variable | Description |
-|---|---|
-| `SATLOC_API_ENDPOINT` | SatLoc Cloud base URL |
-| `SATLOC_API_TIMEOUT` | HTTP request timeout (ms) |
-| `PARTNER_SYNC_INTERVAL` | Partner sync interval (ms) |
-| `PARTNER_HEALTH_CHECK_INTERVAL` | Health check interval (ms) |
-| `PARTNER_MAX_RETRIES` | Max retry attempts before DLQ |
-| `DLQ_CHECK_INTERVAL` | DLQ monitoring interval (ms) |
-
----
-
-*See [ARCHITECTURE.md](ARCHITECTURE.md) for full system design documentation.*
diff --git a/Documents/Requirements/Data-Export-API-Customer-Summary.md b/Documents/Requirements/Data-Export-API-Customer-Summary.md
deleted file mode 100644
index ce2761f..0000000
--- a/Documents/Requirements/Data-Export-API-Customer-Summary.md
+++ /dev/null
@@ -1,251 +0,0 @@
-# Data Export API — Proposal Summary
-
-**Date:** April 7, 2026
-**Prepared by:** AgMission Team
-**For:** Customer Technical & Integration Team
-
----
-
-## 1. Overview
-
-Based on your requirements, we have analysed the current AgMission platform and designed an export API that will allow your team to pull mission data directly into your data infrastructure — data warehouse, Power BI, and ArcGIS — once per day or on demand.
-
-The data available through this API is the same data currently displayed in the AgMission web application's **Data Playback** and **Report** screens.
-
----
-
-## 2. What We Will Deliver
-
-### 2.1 API Key Authentication
-
-All API access will be secured using an **API Key** as requested. Keys will be managed from within the AgMission platform:
-
-- Your applicator account can create and revoke keys independently (self-service)
-- AgMission platform administrators can also manage keys on your behalf
-- Each key is shown only once at creation and is stored securely
-- All requests must include the key in the request header (`X-API-Key`)
-
----
-
-### 2.2 Job List Screen — Enhanced Filtering
-
-The existing **Job List** screen in the AgMission web application will have its filter controls improved so users can quickly narrow the mission list before opening or exporting a specific job.
-
-**Filter controls that will be available:**
-
-| Filter | Description |
-|---|---|
-| Client | Dropdown selection |
-| Id N° | Search by mission ID |
-| Order Number | Full or partial match |
-| Mission Name | Partial match |
-| Start Date | From date (inclusive) |
-| End Date | To date (inclusive) |
-| Status | Mission status (e.g. Sprayed, Completed, etc.) |
-
----
-
-### 2.3 Mission Session Summary
-
-For each mission, one record per **application session** (uploaded data file). This covers the **Coverage with application information** requirement.
-
-Each session record includes:
-
-**Session identification & timing**
-
-| Field | Description |
-|---|---|
-| Session ID | Unique session identifier |
-| File Name | Original data file name |
-| Start / End Date & Time | UTC |
-| Total Flight Time | seconds |
-| Total Spray Time | seconds |
-| Total Turn Time | seconds |
-
-**Application results**
-
-| Field | Description |
-|---|---|
-| Total Sprayed Area | Hectares actually covered |
-| Mapped Area | Planned area size from job definition (ha) |
-| Over-Spray % | `(Sprayed − Mapped) / Mapped × 100` |
-| Material Sprayed | Total volume or mass applied (litres or kg, metric) |
-| Material Type | Liquid (wet) or dry granular |
-| Target Application Rate | Planned rate from mission file |
-| Application Rate Unit | e.g. L/ha, Kg/ha |
-| Flow Controller | Equipment type used |
-| Average Spray Speed | Average ground speed during active spraying (m/s) |
-| Spray Zone Name | Named area or zone (Ag-NAV missions only) |
-| Spray Zone Area | Area size of the named zone (Ag-NAV only) |
-| Auto Spray On / Off Lag | Spray system timing offsets (seconds) |
-| Pulses per Litre | Flow calibration value (liquid Ag-NAV only) |
-
-**Confirmed / adjusted values**
-
-If the applicator has opened the **Report Settings** screen and confirmed the values, those confirmed values are returned. If they have **not** done so, the API automatically falls back to values calculated directly from the uploaded application data files — so this group is always populated.
-
-A `reportConfirmed` flag indicates which case applies.
-
-| Field | If report confirmed | If not confirmed (fallback) |
-|---|---|---|
-| Report Confirmed | `true` | `false` |
-| Area Size | Applicator-confirmed value (ha) | Calculated from planned spray area polygons |
-| Spray Coverage | Applicator-confirmed value (ha) | Sum of sprayed area across all sessions from data files |
-| Application Rate | Applicator-confirmed value | Target application rate from the data file |
-| Estimated Spray Volume | Coverage × Application Rate | Same formula using fallback values |
-| Actual Spray Volume | Manually entered by applicator (if provided) | Not available |
-| Effective Volume | Actual volume if entered, otherwise Estimated | Estimated Spray Volume (fallback) |
-
-**Weather information** *(returned when manually entered by applicator in the absence of sensor data)*
-
-| Field | Unit |
-|---|---|
-| Wind Speed | knots |
-| Wind Direction | Compass bearing (e.g. NE, SW) |
-| Temperature | °C |
-| Humidity | % |
-
-**Pilot traceability**
-
-| Field | Description |
-|---|---|
-| Pilot Name (assigned) | Pilot assigned to the mission |
-| Pilot Name (from file) | Pilot name as recorded in the data file itself *(may differ if pilot changed in the field)* |
-| Pilot ID | Stable unique identifier |
-| Aircraft Tail Number | Registration number |
-| Aircraft Name | |
-| Assignment Date | Date the applicator officially assigned this pilot |
-
----
-
-### 2.4 Raw GPS Trace Records — Applied Flow
-
-The per-point GPS and application data that feeds the playback screen, returned as paginated records (up to 2,000 per request). This covers the **Applied Flow** requirement.
-
-Each record corresponds to one GPS reading logged by the aircraft during the mission.
-
-**GPS Data**
-
-| Field | Unit | Description |
-|---|---|---|
-| Timestamp UTC | ISO 8601 | Local GPS time converted to UTC |
-| GPS Time | seconds | Raw GPS epoch seconds |
-| Latitude | decimal degrees | WGS84 |
-| Longitude | decimal degrees | WGS84 |
-| UTM X / Y | meters | UTM coordinates |
-| Altitude | meters | Above sea level |
-| Ground Speed | m/s | |
-| Heading | degrees | Direction of travel |
-| Cross-Track Error | meters | Deviation from planned line |
-| Locked Line | integer | Spray line number (Ag-NAV only) |
-| HDOP | — | GPS horizontal accuracy |
-| Satellites in View | count | |
-| Correction ID | — | GPS differential correction source |
-| WAAS ID | — | When applicable |
-| Spray Status | 0 / 1 | 0 = spray off, 1 = spray on |
-
-> **Time interval filtering:** When requesting raw trace records or exporting, a `interval` parameter can be specified (e.g. 0.2 s, 0.4 s, 1 s, 5 s, 10 s) to return only one record per interval rather than every logged GPS point. This significantly reduces payload size for overview queries and large-batch exports.
-
-**Application Info**
-
-| Field | Unit | Description |
-|---|---|---|
-| Flow Rate Applied | L/min | Actual flow rate measured |
-| Flow Rate Required | L/min | Target flow rate |
-| Application Rate Required | L/ha or Kg/ha | Target rate per point |
-| Application Rate Applied | L/ha | Calculated from measured flow, speed, and swath |
-| Swath Width | meters | |
-| Boom Pressure | psi | Liquid Ag-NAV only |
-| Auto Spray On Lag | seconds | Spray-on activation delay (session constant, repeated per record) |
-| Auto Spray Off Lag | seconds | Spray-off deactivation delay (session constant, repeated per record) |
-| Pulses per Litre | count | Flow meter calibration value — liquid Ag-NAV only (session constant) |
-| RPM values | array | Up to 10 channels; semantics depend on material type (liquid vs. dry) |
-
-**Meteorological (MET)**
-
-| Field | Unit | Description |
-|---|---|---|
-| Wind Speed | m/s | Measured in-flight |
-| Wind Direction | degrees | |
-| Temperature | °C | |
-| Humidity | % | |
-
-> **Note:** All values are in metric units. Date/times are UTC. Coordinates are in WGS84 (EPSG:4326), which is numerically equivalent to SIRGAS 2000 used in Brazil.
-
----
-
-### 2.5 Bulk Export (CSV / GeoJSON Download)
-
-For the daily 17:00 Brasília batch pull and ArcGIS imports, a **file-based export** endpoint is available:
-
-1. **Request export** — call the export endpoint for a mission; the system generates the file asynchronously
-2. **Poll for status** — check whether the file is ready (typically seconds to a few minutes for large missions)
-3. **Download** — retrieve the file via the returned download link
-
-**Available formats:** CSV (all raw trace data, one row per GPS record) and GeoJSON.
-
-CSV files include job and session header columns repeated on every row for easy import directly into Power BI or a data warehouse without needing a separate join step.
-
----
-
-### 2.6 Spray-Area Boundary Polygons *(Pending confirmation — see Section 4)*
-
-An optional endpoint to retrieve the **planned field boundary polygons** as GeoJSON, for direct import as ArcGIS layers.
-
----
-
-## 3. Data Units & Standards
-
-| Aspect | Standard used |
-|---|---|
-| All measurements | Metric (ha, m/s, L/min, L/ha, Kg/ha, °C, meters) |
-| Dates & times | ISO 8601 UTC strings |
-| Coordinates | WGS84 decimal degrees (EPSG:4326 / numerically equivalent to SIRGAS 2000) |
-| Volumes | Litres for liquid, Kg for dry granular |
-| Speeds | m/s in raw records; km/h can be derived by consumer (× 3.6) |
-
----
-
-## 4. Pending Confirmation — One Open Item
-
-**Spray-area boundary polygons for ArcGIS**
-
-> Do you need the **planned field boundary polygons** (the area outlines drawn in the mission) exported as GeoJSON geometry, in addition to the numeric coverage statistics (hectares)?
->
-> - If **yes** — we will provide a dedicated endpoint returning the boundaries as GeoJSON features, suitable for direct layer import into ArcGIS.
-> - If **no** — the numeric coverage and session summary fields are sufficient and no additional endpoint is needed.
->
-> *This is the only remaining item before implementation can begin.*
-
----
-
-## 5. Estimated Delivery Timeline
-
-| Team size | Estimated duration | Target delivery |
-|---|---|---|
-| 1 developer | ~5 weeks | ~mid-May 2026 |
-| 2 developers | ~3 weeks | ~end of April 2026 |
-
-Includes development, testing, sandbox environment setup, and a review cycle.
-
-A **sandbox environment with sample missions** will be made available for your team to validate the API responses before production go-live.
-
----
-
-## 6. Summary of Requirements Confirmed
-
-| Requirement | Status |
-|---|---|
-| API Key authentication | ✅ Confirmed — self-service key management for both the applicator account and AgMission admin |
-| Applied Flow data (per GPS point) | ✅ Confirmed — all fields from GPS Data and Applic Info tabs included |
-| Coverage with application info | ✅ Confirmed — session summaries include both raw aggregates and applicator-confirmed values |
-| Pilot Traceability | ✅ Confirmed — pilot name, ID, tail number, assignment date, and in-file pilot name all included |
-| Mission filtering (Client, Order No., Name, Dates) | ✅ Confirmed — UI enhancement to the existing Job List screen |
-| Pull delivery (not push) | ✅ Confirmed — pull endpoints + async file download |
-| Raw data (not calculated metrics) | ✅ Confirmed — all raw sensor values returned; calculated `appRateApplied` also included as it appears in the UI |
-| Power BI integration | ✅ Supported — cursor-paginated JSON for incremental refresh |
-| ArcGIS integration | ✅ Supported — GeoJSON export + boundary polygons (pending confirmation) |
-| Data warehouse / bulk load | ✅ Supported — CSV export endpoint |
-| Consumption once daily at 17:00 Brasília | ✅ Supported — async export endpoint designed for this pattern |
-| Sandbox environment | ✅ Will be provided before production go-live |
-| Spray-area boundary polygons | ⏳ **Pending your confirmation (Section 4)** |
diff --git a/Documents/Requirements/Data-Export-API.md b/Documents/Requirements/Data-Export-API.md
deleted file mode 100644
index a2f684d..0000000
--- a/Documents/Requirements/Data-Export-API.md
+++ /dev/null
@@ -1,391 +0,0 @@
-# Data Export API — Requirements & Solution Definition
-
-**Date:** April 7, 2026
-**Status:** In Progress — Questions Q-A through Q-D resolved
-**Source documents:** Customer API requirements (Data Export API + API AGNAV sections)
-
----
-
-## 1. Background
-
-The customer (a grower/client) requires a data extraction API to pull mission data from the AgMission platform into their internal data infrastructure (data warehouse, Power BI, ArcGIS). The same data is already calculated and displayed in the web UI via the **Data Playback** function in `job-map-edit.component`.
-
-Two functional areas are requested:
-
-1. **Application data export** — expose the playback-computed data (GPS trace + application metrics) through a REST API.
-2. **Job List screen filtering (UI enhancement)** — improve the filter controls on the existing Job List screen so users can search/narrow missions by client, order number, name, date range, and status.
-
----
-
-## 2. Customer Requirements Summary
-
-| Category | Requirement |
-|---|---|
-| Data needed | Applied Flow, Coverage (with application info, not real-time only), Pilot Traceability |
-| Mission filter fields | UI enhancement on the existing Job List screen: Client / ID No. / Order No. / Name / Start Date / End Date |
-| Delivery method | Pull from API (not push) |
-| Authentication | API Key |
-| Compliance | None |
-| Data type | Raw data (no pre-calculated aggregates required beyond what is already stored) |
-| Consumption | Once per day at 17:00 Brasília time (UTC−3) |
-| Target platforms | Data warehouse, Power BI, ArcGIS |
-| Sandbox | Requested once API is ready |
-
----
-
-## 3. Proposed API Features
-
-### 3.1 API Key Authentication (Prerequisite)
-
-No API key mechanism exists in the current codebase — the server currently uses JWT Bearer tokens only (see `middlewares/app_validator.js`).
-
-**Design:**
-
-- New `ApiKey` Mongoose model with fields: `owner` (ObjectId ref to applicator/`byPuid`), `name` (string label), `keyHash` (bcrypt-hashed), `active` (boolean), `createdAt`, `lastUsedAt`, `managedBy` (enum: `customer` | `admin`)
-- New Express middleware (parallel to `checkUser`) validating `X-API-Key` header against hashed keys
-- Key resolves to an applicator `byPuid`, so all existing ownership-scoping logic continues to work unchanged
-- Management: both the **master applicator account** (self-service via web UI) and the **AgMission platform admin** can create/revoke keys
-- All public API routes mounted under `/api/v1/` prefix with API-key middleware only
-
----
-
-### 3.2 UI Enhancement 1 — Job List Screen Filtering
-
-**Type:** Frontend (web UI) change only — not an API endpoint.
-
-The existing Job List screen (`job-list` component) already shows columns for Client, Id N°, Order N°, Name, Start Date, End Date, and Status. The customer requirement is to **add or improve the interactive filter controls** on this screen so users can narrow the list easily.
-
-**Required filter controls:**
-
-| Filter | Behaviour |
-|---|---|
-| Client | Dropdown — filter by client name |
-| Id N° | Text search — partial or exact match |
-| Order N° | Text search — partial or exact match |
-| Name | Text search — partial match (case-insensitive) |
-| Start Date | Date picker — show jobs from this date |
-| End Date | Date picker — show jobs up to this date |
-| Status | Dropdown — All / Sprayed / Completed / etc. |
-
-**Backend note:** The existing `searchJobs_post` / `getJobs_get` aggregation pipeline already supports most of these filters. This step primarily wires up the UI controls and ensures `orderNumber` and date-range parameters are accepted by the backend query.
-
----
-
-### 3.3 Feature 2A — Session Summary per Job
-
-**Endpoint:** `GET /api/v1/jobs/:jobId/sessions`
-
-Returns one record per uploaded application file (one "session" = one `App` + its `AppFile` children). All values are already stored — no traversal of `AppDetail` needed.
-
-**Response fields per session:**
-
-| Field | Source model → field | Notes |
-|---|---|---|
-| `sessionId` | `App._id` | |
-| `fileName` | `App.fileName` | |
-| `startDateTime` | `App.startDateTime` | ISO 8601 UTC |
-| `endDateTime` | `App.endDateTime` | ISO 8601 UTC |
-| `totalFlightTime_s` | `App.totalFlightTime` | seconds |
-| `totalSprayTime_s` | `App.totalSprayTime` | seconds |
-| `totalTurnTime_s` | `App.totalTurnTime` | seconds |
-| `totalSprayed_ha` | `App.totalSprayed` | hectares |
-| `totalSprayMat` | `App.totalSprayMat` | L or Kg (metric) |
-| `totalSprayMatUnit` | `App.totalSprayMatUnit` | 3=L/ha, 4=Kg/ha |
-| `pilotName` | `AppFile.meta.operator` or `Job.operator.name` | Pilot traceability |
-| `sprayZoneName` | `AppFile.meta.areaOrZone` | AgNav only |
-| `sprayZoneArea_ha` | `AppFile.meta.sprCoverage[1]` | AgNav only |
-| `appRate` | `AppFile.meta.appRate` or `Job.appRate` | Target application rate |
-| `appRateUnit` | `AppFile.meta.appRateUnitStr` | String label |
-| `matType` | `AppFile.meta.matType` | `wet` or `dry` |
-| `flowController` | `AppFile.meta.fcName` | |
-| `sprayOnLag_s` | `AppFile.meta.sprOnLag` | seconds |
-| `sprayOffLag_s` | `AppFile.meta.sprOffLag` | seconds |
-| `pulsesPerLiter` | `AppFile.meta.pulsesPerLit` | Liquid AgNav only |
-| `overSprayedPct` | `(totalSprayed − mappedArea) / mappedArea × 100` | Computed from stored values |
-| `mappedArea_ha` | `Job.sprayAreas[].properties.area` (sum) | From job spray-area polygons |
-| `avgSpraySpeed_ms` | `App.avgSpraySpeed` (stored at import — see §9) | m/s — average ground speed during spray-on periods |
-
-#### Confirmed Application Summary (from Report Settings, with fallback)
-
-If the applicator has used the Report Settings dialog, the confirmed/overridden values are returned. If they have **not** (i.e. `rptOp` fields are null), the API falls back to values calculated from the uploaded data files so that this group is **always populated**. A `reportConfirmed` boolean signals which case applies.
-
-| API field | Source model → field | Fallback (when `rptOp` not set) | Notes |
-|---|---|---|---|
-| `reportConfirmed` | `Job.rptOp.coverage != null` | `false` | Boolean flag |
-| `areaSize_ha` | `Job.rptOp.areaSize` | Sum of `job.sprayAreas[].properties.area` | ha |
-| `coverage_ha` | `Job.rptOp.coverage` | Sum of `App.totalSprayed` across sessions | ha |
-| `appRate` | `Job.rptOp.appRate` | `AppFile.meta.appRate` (first session, or null if absent) | L/ha or Kg/ha |
-| `sprayVolume` | `rptOp.coverage × rptOp.appRate` | `coverage_ha(fallback) × appRate(fallback)` | Estimated total volume |
-| `useActualVolume` | `Job.rptOp.useActualVol` | `false` | `true` only when applicator explicitly chose actual vol |
-| `actualVolume` | `Job.rptOp.actualVol` | `null` | Manually entered; only present when `useActualVolume = true` |
-| `effectiveVolume` | `useActualVol ? actualVol : sprayVolume` | `sprayVolume` (fallback) | The authoritative volume for this job |
-| `useCustomWeather` | `Job.useCustWI` | `false` | |
-| `weather.windSpeed_kt` | `Job.weatherInfo.windSpd` | omitted | Only present when `useCustomWeather = true` |
-| `weather.windDir` | `Job.weatherInfo.windDir` | omitted | Only present when `useCustomWeather = true` |
-| `weather.temp_c` | `Job.weatherInfo.temp` | omitted | Only present when `useCustomWeather = true` |
-| `weather.humidity_pct` | `Job.weatherInfo.humid` | omitted | Only present when `useCustomWeather = true` |
-
-> **Design note (Q-A / Q-B / fallback):** `reportConfirmed = false` means the applicator has not yet reviewed the job in the Report Settings dialog. In this state the API returns auto-calculated values from `App` and `AppFile` data so that the consumer's data warehouse always has a usable record. When `reportConfirmed` later becomes `true` (applicator confirms), the consumer can re-fetch and update the stored row. The `isConfirmed` boundary is `rptOp.coverage != null`.
->
-> **Spray-area boundary polygons (Q-A — pending customer clarification):** Whether `job.sprayAreas` GeoJSON polygons should be included in the session summary or exposed as a separate `/jobs/:id/areas` endpoint is pending confirmation from the customer regarding their ArcGIS polygon import workflow. Both options are straightforward to implement.
-
----
-
-### 3.4 Feature 2B — Raw GPS Trace Records
-
-**Endpoint:** `GET /api/v1/jobs/:jobId/sessions/:fileId/records`
-
-Exposes the per-point `AppDetail` records that feed the playback UI. Cursor-paginated (same scheme as existing `filesdata_post`).
-
-**Query parameters:** `after` (cursor), `limit` (default 500, max 2000), `interval` (float seconds, e.g. `0.2`, `0.4`, `1`, `5`, `10` — when specified, only the first record within each interval window is returned, reducing payload size for overview queries and large-batch exports)
-
-**Response fields per record (all raw values, metric units):**
-
-#### GPS Data group
-
-| API field | Source field | Unit | Notes |
-|---|---|---|---|
-| `timeUtc` | derived from `gpsTime` | ISO 8601 UTC string | |
-| `lat` | `AppDetail.lat` | decimal degrees WGS84 | |
-| `lon` | `AppDetail.lon` | decimal degrees WGS84 | |
-| `utmX` | `AppDetail.utmX` | meters | |
-| `utmY` | `AppDetail.utmY` | meters | |
-| `alt` | `AppDetail.alt` | meters ASL | |
-| `grSpeed` | `AppDetail.grSpeed` | m/s | |
-| `heading` | `AppDetail.head` | degrees | |
-| `xTrack` | `AppDetail.xTrack` | meters | Cross-track error |
-| `lockedLine` | `AppDetail.llnum` | integer | AgNav only |
-| `hdop` | `AppDetail.stdHdop` | float | |
-| `satsInView` | `AppDetail.satsIn` decoded | integer | `satsIn > 99 ? satsIn−100 : satsIn` |
-| `correctionId` | `AppDetail.tslu` decoded | integer | `tslu > 100 ? tslu−100 : tslu` |
-| `waasId` | `AppDetail.calcodeFreq` decoded | integer | Only if `calcodeFreq` in 20001–29999 |
-| `sprayStat` | `AppDetail.sprayStat` | 0 or 1 | 0=off, 1=on |
-
-#### Applic Info group
-
-| API field | Source field | Unit | Notes |
-|---|---|---|---|
-| `flowRateApplied` | `AppDetail.lminApp` | L/min | |
-| `flowRateRequired` | `AppDetail.lminReq` | L/min | |
-| `appRateRequired` | `AppDetail.lhaReq` | L/ha or Kg/ha | SatLoc per-point value |
-| `appRateApplied` | derived: `lminApp / (grSpeed × swath) × 10000` | L/ha | **Only computed field in raw trace** — see Note 1 |
-| `swathWidth` | `AppDetail.swath` | meters | |
-| `boomPressure_psi` | `AppDetail.psi` | psi | AgNav liquid only |
-| `sprayOnLag_s` | `AppFile.meta.sprOnLag` | seconds | Session constant — repeated per record |
-| `sprayOffLag_s` | `AppFile.meta.sprOffLag` | seconds | Session constant — repeated per record |
-| `pulsesPerLiter` | `AppFile.meta.pulsesPerLit` | count | Liquid AgNav only; session constant — repeated per record |
-| `rpm` | `AppDetail.rpm[0..9]` | array | See Note 2 for dry vs. liquid semantics |
-
-#### MET group
-
-| API field | Source field | Unit | Notes |
-|---|---|---|---|
-| `windSpeed_ms` | `AppDetail.windSpd` | m/s | |
-| `windDir_deg` | `AppDetail.windDir` | degrees | |
-| `temp_c` | `AppDetail.temp` | °C | |
-| `humidity_pct` | `AppDetail.humid` | % | |
-
-> **Note 1 — appRateApplied:** This is the only derived value computed from raw fields. Formula: `appRateApplied = lminApp / (grSpeed_m_per_s × swath_m) × 10000`. If `grSpeed = 0` or `swath = 0`, return `null` to avoid division by zero. The UI equivalent is `PlayRecord.appRateAp`.
->
-> **Note 2 — rpm array semantics:**
-> - Liquid material: indices 0–9 = RPM pairs 1/2 through 9/10 (pump RPM channels)
-> - Dry material: index 0–1 = AppRPM 1/2; index 2–3 = TarRPM 1/2; index 4 = GFC VIn; index 6–7 = Revs/Kg (× 0.453592 for Revs/Lb); index 8–9 = Amp 1/2
-> - `matType` (from session summary) determines which interpretation applies
-
----
-
-### 3.5 Feature 2C — Export File (Async Download)
-
-**Endpoints:**
-- `POST /api/v1/jobs/:jobId/export` — trigger export generation, returns `{ exportId, status: "pending" }`
-- `GET /api/v1/exports/:exportId` — poll status; when `status: "ready"`, includes `downloadUrl`
-
-Reuses the existing temp-file infrastructure from `preAppReport_post` (`env.TEMP_DIR`, `env.REPORT_DIR`). Useful for ArcGIS bulk imports and the one-a-day batch pull at 17:00 Brasília.
-
-**Supported formats:** `csv`, `geojson` (query param `?format=csv`)
-
-**CSV columns:** All raw trace fields above, one row per `AppDetail` record, session/job header fields repeated for join convenience (`jobId`, `orderNumber`, `fileId`, `fileName`, `pilotName`).
-
----
-
-## 4. Data Architecture — Source Mapping Summary
-
-```
-Job → 3.2 Job List UI filters, 3.3 Session Summary (mappedArea)
- ├── App → 3.3 Session Summary (times, volumes, totalSprayed)
- │ └── AppFile → 3.3 Session Summary (meta: operator, areaOrZone, fcName, etc.)
- │ └── AppDetail → 3.4 Raw GPS Trace records (all per-point fields)
- └── sprayAreas[] → 3.3 mappedArea_ha (sum of properties.area)
-```
-
----
-
-## 5. Volume & Pagination Strategy
-
-**AppDetail** is indexed at billion+ document scale (see `model/application_detail.js` — `fileId` index, `_id` for cursor).
-
-| Endpoint | Pagination | Typical volume |
-|---|---|---|
-| Session summary | None (small) | 1–20 per job |
-| Raw trace records | Cursor on `_id`, default 500/page | 10K–500K+ per file |
-| Export file | None (async, full download) | Unlimited |
-
-> **Note:** Job listing/filtering is a UI screen enhancement (§3.2), not a standalone API endpoint. The export and session endpoints accept `jobId` directly.
-
-The daily batch at 17:00 Brasília is best served by the **Export File (3.5)** approach. The cursor-paginated records endpoint (3.4) is for Power BI incremental refresh or selective queries.
-
----
-
-## 6. Authentication & Key Management
-
-| Actor | Can create keys | Can revoke keys | Scope |
-|---|---|---|---|
-| AgMission platform admin | Yes (any applicator) | Yes (any) | Any applicator's data |
-| Master applicator account | Yes (own account) | Yes (own) | Own clients/jobs only |
-
-- API key is passed in `X-API-Key` request header
-- Keys are stored hashed (bcrypt); plain key shown only once at creation
-- Key resolves to `byPuid` (applicator), all existing ownership filters continue to apply
-- Rate limiting applies (reuse existing `express-rate-limit` config in `server.js`)
-
----
-
-## 7. Questions & Resolutions
-
-### Q-A — Coverage fields & confirmed aggregates ✅ Resolved
-
-**Answer:** The API must expose both the system-calculated aggregates AND the user-confirmed/adjusted values from the Report Settings dialog. See the confirmed application summary table in §3.3.
-
-The Report Settings dialog (screenshot) shows the following adjustable fields stored in `Job.rptOp` and `Job.weatherInfo`:
-- **Area Size** (`rptOp.areaSize`) — user-confirmed plan area with green checkmark
-- **Spray Coverage** (`rptOp.coverage`) — confirmed sprayed area
-- **AppRate** (`rptOp.appRate`) — confirmed application rate
-- **Spray Volume** — calculated (`coverage × appRate`), not stored separately
-- **Actual Spray Volume** (`rptOp.actualVol` + `rptOp.useActualVol` toggle) — optional manual override
-- **Weather Info** (`Job.useCustWI` + `Job.weatherInfo`) — manual weather if sensor data unavailable
-
-**Spray-area boundary polygons:** Whether to include `job.sprayAreas` GeoJSON for ArcGIS import is **pending customer confirmation**. Recommended: expose as a separate optional endpoint `GET /api/v1/jobs/:id/areas` to avoid bloating the session summary response.
-
----
-
-### Q-B — Calculated vs. raw values ✅ Resolved
-
-**Answer:** The API returns both. Specifically:
-- **`appRateApplied`** — computed per-point in the raw trace (`lminApp / (grSpeed × swath) × 10000`). This is the only in-flight calculation in the records endpoint, matching what the playback UI displays.
-- **`avgSpraySpeed_ms`** — stored at import time in the `App` model (see Q-D). Returned from the session summary endpoint with no on-the-fly cost.
-- **Confirmed aggregates** — from `Job.rptOp` as described in Q-A. The consumer receives system-calculated values AND the applicator's manually confirmed values and can decide which to use for their reports.
-
----
-
-### Q-C — Pilot Traceability scope ✅ Resolved with recommendations
-
-**Answer and recommendations:**
-
-File-level `pilotName` per session is the primary traceability field and matches what is recorded in the data file itself (`AppFile.meta.operator`). The following additional fields are recommended to make traceability robust:
-
-| Additional field | Source | Rationale |
-|---|---|---|
-| `pilotId` | `Job.operator` (ObjectId) | Stable identifier — name strings can change or have duplicates across missions |
-| `aircraftName` | `Job.vehicle.name` | Aircraft identifier alongside pilot for fleet operations |
-| `aircraftTailNumber` | `Vehicle.tailNumber` | ANAC / FAA registration number; standard traceability field in Brazil |
-| `assignedDate` | `JobAssign.createdAt` | When the applicator officially assigned this pilot to the job |
-| `sessionPilotName` | `AppFile.meta.operator` | Pilot name as recorded in the data file itself (may differ from assigned pilot if swapped in the field) |
-
-> **Multi-pilot and fleet note:** When multiple aircraft work the same job, each `AppFile` has its own `meta.operator`. The session summary (§3.3) returns one record per file, so traceability is inherently per-session. No per-GPS-point pilot attribution is needed — it would inflate the raw trace response with a constant repeated string.
-
-> **Recommendation:** Include `pilotId` + `aircraftTailNumber` in both the job listing (§3.2) and the session summary (§3.3). Do not repeat in per-point records.
-
----
-
-### Q-D — `AvgSprSpd` storage strategy ✅ Resolved
-
-**Answer:** Store `avgSpraySpeed` at import time, in the `App` model alongside the existing aggregate fields (`totalFlightTime`, `totalSprayTime`, `totalSprayed`, etc.).
-
-**Why not compute on-the-fly:** The `GET /api/v1/jobs/:jobId/sessions` endpoint (session summary) is designed to return only values already stored in `App` and `AppFile` — no `AppDetail` traversal. If `avgSpraySpeed` were computed on the fly at query time, it would require scanning potentially hundreds of thousands of `AppDetail` records per session, defeating the purpose of pre-aggregated session data.
-
-**Implementation:** During file import processing (in the existing import worker/service), add the same accumulation logic that the playback UI uses:
-```
-if (sprayStat === 1) { totalSpraySpeed += grSpeed; sprayPointCount++; }
-avgSpraySpeed = sprayPointCount > 0 ? totalSpraySpeed / sprayPointCount : 0;
-```
-Store the result in a new `App.avgSpraySpeed` field (m/s, metric). No existing import consumers are affected.
-
----
-
-## 8. Implementation Plan
-
-### 8.1 Step-by-step breakdown with estimates
-
-Estimates are in **working days** per developer, based on codebase familiarity with the existing patterns (existing cursor pagination, `job_worker.js` aggregate pattern, `preAppReport_post` temp-file infra, Angular service + component structure).
-
-| Step | Feature | Days (1 dev) | Notes |
-|---|---|---|---|
-| 1 | `App.avgSpraySpeed` — add field to model + compute in `job_worker.js` + back-fill migration script | 2 d | `job_worker.js` lines ~519–526 already show the exact insertion point alongside `totalSprayed`, `totalSprayTime`, etc. Migration script iterates `AppDetail` cursor per `fileId`. |
-| 2 | `ApiKey` model + `checkApiKey` middleware + CRUD routes (create/list/revoke) | 3 d | New Mongoose model, bcrypt hash, new Express middleware parallel to `checkUser`. Admin and customer scopes via role check. |
-| 3 | Job List screen filter enhancements (UI) | 1.5 d | Wire up `orderNumber`, date-range, and client dropdown filter controls in the `job-list` component. Ensure existing `searchJobs_post` pipeline accepts these params; minor backend query update if missing. |
-| 4 | `GET /api/v1/jobs/:id/sessions` — session summary | 2.5 d | Joins `App` + `AppFile` + `Job.rptOp` + `Job.weatherInfo`. Adds `avgSpraySpeed`, confirmed-aggregate fields, pilot traceability fields. |
-| 5 | `GET /api/v1/jobs/:id/sessions/:fileId/records` — raw trace | 2.5 d | Wraps existing `filesdata_post` cursor logic. Adds field mapping/decoding (`satsIn`, `tslu`, `calcodeFreq`, `sprayStat=3` filter, `appRateApplied` formula). |
-| 6 | `GET /api/v1/jobs/:id/areas` — spray-area GeoJSON | 1 d | Single aggregation on `job.sprayAreas`. Trivial once route infra is in place. |
-| 7 | `POST /api/v1/jobs/:id/export` + `GET /api/v1/exports/:id` — async CSV/GeoJSON | 4 d | Node.js stream-based CSV writer over `AppDetail` cursor. Status polling. Reuses `env.TEMP_DIR` / `env.REPORT_DIR` temp-file pattern from `preAppReport_post`. |
-| 8 | Key management UI (Angular) | 3.5 d | New settings page: list keys, generate (show once), revoke. Standard Angular service + PrimeNG table, same pattern as existing settings components. |
-| 9 | Sandbox data seeding script | 1 d | Script to insert representative sample jobs, applications, and AppDetail records for a test applicator account. |
-| — | **Testing, code review, bug fixes (~20% buffer)** | 4 d | Unit tests for middleware and field calculations; integration tests against sandbox. |
-| | **Total** | **25 d** | |
-
----
-
-### 8.2 Timeline by team size
-
-#### 1 Developer — ~5 weeks
-
-```
-Week 1 Steps 1–3 avgSpraySpeed import field, API key infra, job listing
-Week 2 Steps 4–5 Session summary + raw trace records endpoints
-Week 3 Steps 6–7 Areas GeoJSON endpoint + async export
-Week 4 Step 8 Key management UI
-Week 5 Step 9 + buffer Sandbox seeding + testing/review/fixes
-```
-
-**Delivery: end of Week 5**
-
----
-
-#### 2 Developers — ~3 weeks
-
-Split backend (Dev A) and frontend + export (Dev B) in parallel once API key middleware (Step 2) is done on Day 3:
-
-```
- Dev A (Backend) Dev B (Frontend + Export)
-Week 1 Step 1 (2d) → Step 2 (3d) Step 2 unblocks Day 3:
- Step 8 Key management UI (3.5d, starts Day 3)
-Week 2 Step 3 (1.5d) → Step 4 (2.5d) Finish Step 8 → Step 7 async export (4d)
-Week 3 Step 5 (2.5d) → Step 6 (1d) Finish Step 7 → Step 9 sandbox (1d)
- → buffer/testing (1.5d) → integration testing (1.5d)
-```
-
-**Delivery: end of Week 3**
-
----
-
-### 8.3 Risk & assumptions
-
-| Risk | Likelihood | Mitigation |
-|---|---|---|
-| Back-fill migration for `avgSpraySpeed` is slow on large `AppDetail` collections | Medium | Run as offline batch with cursor + bulk write; add progress logging |
-| Consumer's Power BI connector requires specific pagination or auth header format | Low | Validate against sandbox before sign-off; adjust header/response format if needed |
-| Async export generation times out for very large jobs (500K+ records) | Medium | Stream CSV via Node.js `Transform` instead of loading all records into memory; set job-level export size warning |
-| Spray-area polygon GeoJSON payload size (Step 6) | Low | Polygons are already stored simplified in `job.sprayAreas`; response stays small |
-
----
-
-## 9. Notes & Constraints
-
-- All API responses use **metric units** internally (ha, m/s, L/min, °C, meters). Unit conversion is the consumer's responsibility.
-- All dates/times returned as **ISO 8601 UTC strings**.
-- Coordinates in **WGS84 decimal degrees** (EPSG:4326). If SIRGAS 2000 (EPSG:4674) is needed for ArcGIS Brazil, note that it is numerically identical to WGS84 for practical purposes.
-- The `raserAlt` field in `AppDetail` schema has a typo (should be `laserAlt`). The API exposes it as `laserAlt_m` regardless.
-- `AppDetail.sprayStat` value `0` = spray off, `1` = spray on. Value `3` is an end-of-segment marker used internally; the API should filter it out or map it to `0`.
-- Existing `filesdata_post` cursor pagination uses the `_id` field index — the same scheme is reused for the public records endpoint.
-- `App.avgSpraySpeed` is a **new field** to be added to the `App` Mongoose model and populated during import processing. It must be back-filled for existing jobs (one-time migration script over existing `AppDetail` records).
-- The session summary endpoint (`GET /api/v1/jobs/:jobId/sessions`) is intentionally a **lightweight endpoint** — it reads only from `App` and `AppFile` models, never from `AppDetail`. This is why `avgSpraySpeed` must be pre-computed and stored rather than derived at query time.
-- **Remaining open item:** `job.sprayAreas` GeoJSON polygon inclusion — pending customer confirmation on ArcGIS integration requirements (see Q-A).
diff --git a/Documents/Requirements/archived/Data-Export-API-Customer-Summary.md b/Documents/Requirements/archived/Data-Export-API-Customer-Summary.md
deleted file mode 100644
index cd3c6ce..0000000
--- a/Documents/Requirements/archived/Data-Export-API-Customer-Summary.md
+++ /dev/null
@@ -1,251 +0,0 @@
-# Data Export API — Proposal Summary
-
-**Date:** April 7, 2026
-**Prepared by:** AgMission Team
-**For:** Customer Technical & Integration Team
-
----
-
-## 1. Overview
-
-Based on your requirements, we have analysed the current AgMission platform and designed an export API that will allow your team to pull mission data directly into your data infrastructure — data warehouse, Power BI, and ArcGIS — once per day or on demand.
-
-The data available through this API is the same data currently displayed in the AgMission web application's **Data Playback** and **Report** screens.
-
----
-
-## 2. What We Will Deliver
-
-### 2.1 API Key Authentication
-
-All API access will be secured using an **API Key** as requested. Keys will be managed from within the AgMission platform:
-
-- Your applicator account can create and revoke keys independently (self-service)
-- AgMission platform administrators can also manage keys on your behalf
-- Each key is shown only once at creation and is stored securely
-- All requests must include the key in the request header (`X-API-Key`)
-
----
-
-### 2.2 Job List Screen — Enhanced Filtering
-
-The existing **Job List** screen in the AgMission web application will have its filter controls improved so users can quickly narrow the mission list before opening or exporting a specific job.
-
-**Filter controls that will be available:**
-
-| Filter | Description |
-|---|---|
-| Client | Dropdown selection |
-| Id N° | Search by mission ID |
-| Order Number | Full or partial match |
-| Mission Name | Partial match |
-| Start Date | From date (inclusive) |
-| End Date | To date (inclusive) |
-| Status | Mission status (e.g. Sprayed, Completed, etc.) |
-
----
-
-### 2.3 Mission Session Summary
-
-For each mission, one record per **application session** (uploaded data file). This covers the **Coverage with application information** requirement.
-
-Each session record includes:
-
-**Session identification & timing**
-
-| Field | Description |
-|---|---|
-| Session ID | Unique session identifier |
-| File Name | Original data file name |
-| Start / End Date & Time | UTC |
-| Total Flight Time | seconds |
-| Total Spray Time | seconds |
-| Total Turn Time | seconds |
-
-**Application results**
-
-| Field | Description |
-|---|---|
-| Total Sprayed Area | Hectares actually covered |
-| Mapped Area | Planned area size from job definition (ha) |
-| Over-Spray % | `(Sprayed − Mapped) / Mapped × 100` |
-| Material Sprayed | Total volume or mass applied (litres or kg, metric) |
-| Material Type | Liquid (wet) or dry granular |
-| Target Application Rate | Planned rate from mission file |
-| Application Rate Unit | e.g. L/ha, Kg/ha |
-| Flow Controller | Equipment type used |
-| Average Spray Speed | Average ground speed during active spraying (m/s) |
-| Spray Zone Name | Named area or zone (Ag-NAV missions only) |
-| Spray Zone Area | Area size of the named zone (Ag-NAV only) |
-| Auto Spray On / Off Lag | Spray system timing offsets (seconds) |
-| Pulses per Litre | Flow calibration value (liquid Ag-NAV only) |
-
-**Confirmed / adjusted values**
-
-If the applicator has opened the **Report Settings** screen and confirmed the values, those confirmed values are returned. If they have **not** done so, the API automatically falls back to values calculated directly from the uploaded application data files — so this group is always populated.
-
-A `reportConfirmed` flag indicates which case applies.
-
-| Field | If report confirmed | If not confirmed (fallback) |
-|---|---|---|
-| Report Confirmed | `true` | `false` |
-| Area Size | Applicator-confirmed value (ha) | Calculated from planned spray area polygons |
-| Spray Coverage | Applicator-confirmed value (ha) | Sum of sprayed area across all sessions from data files |
-| Application Rate | Applicator-confirmed value | Target application rate from the data file |
-| Estimated Spray Volume | Coverage × Application Rate | Same formula using fallback values |
-| Actual Spray Volume | Manually entered by applicator (if provided) | Not available |
-| Effective Volume | Actual volume if entered, otherwise Estimated | Estimated Spray Volume (fallback) |
-
-**Weather information** *(returned when manually entered by applicator in the absence of sensor data)*
-
-| Field | Unit |
-|---|---|
-| Wind Speed | knots |
-| Wind Direction | Compass bearing (e.g. NE, SW) |
-| Temperature | °C |
-| Humidity | % |
-
-**Pilot traceability**
-
-| Field | Description |
-|---|---|
-| Pilot Name (assigned) | Pilot assigned to the mission |
-| Pilot Name (from file) | Pilot name as recorded in the data file itself *(may differ if pilot changed in the field)* |
-| Pilot ID | Stable unique identifier |
-| Aircraft Tail Number | Registration number |
-| Aircraft Name | |
-| Assignment Date | Date the applicator officially assigned this pilot |
-
----
-
-### 2.4 Raw GPS Trace Records — Applied Flow
-
-The per-point GPS and application data that feeds the playback screen, returned as paginated records (up to 2,000 per request). This covers the **Applied Flow** requirement.
-
-Each record corresponds to one GPS reading logged by the aircraft during the mission.
-
-**GPS Data**
-
-| Field | Unit | Description |
-|---|---|---|
-| Timestamp UTC | ISO 8601 | Local GPS time converted to UTC |
-| GPS Time | seconds | Raw GPS epoch seconds |
-| Latitude | decimal degrees | WGS84 |
-| Longitude | decimal degrees | WGS84 |
-| UTM X / Y | meters | UTM coordinates |
-| Altitude | meters | Above sea level |
-| Ground Speed | m/s | |
-| Heading | degrees | Direction of travel |
-| Cross-Track Error | meters | Deviation from planned line |
-| Locked Line | integer | Spray line number (Ag-NAV only) |
-| HDOP | — | GPS horizontal accuracy |
-| Satellites in View | count | |
-| Correction ID | — | GPS differential correction source |
-| WAAS ID | — | When applicable |
-| Spray Status | 0 / 1 | 0 = spray off, 1 = spray on |
-
-> **Time interval filtering:** When requesting raw trace records or exporting, a `interval` parameter can be specified (e.g. 0.2 s, 0.4 s, 1 s, 5 s, 10 s) to return only one record per interval rather than every logged GPS point. This significantly reduces payload size for overview queries and large-batch exports.
-
-**Application Info**
-
-| Field | Unit | Description |
-|---|---|---|
-| Flow Rate Applied | L/min | Actual flow rate measured |
-| Flow Rate Required | L/min | Target flow rate |
-| Application Rate Required | L/ha or Kg/ha | Target rate per point |
-| Application Rate Applied | L/ha | Calculated from measured flow, speed, and swath |
-| Swath Width | meters | |
-| Boom Pressure | psi | Liquid Ag-NAV only |
-| Auto Spray On Lag | seconds | Spray-on activation delay (session constant, repeated per record) |
-| Auto Spray Off Lag | seconds | Spray-off deactivation delay (session constant, repeated per record) |
-| Pulses per Litre | count | Flow meter calibration value — liquid Ag-NAV only (session constant) |
-| RPM values | array | Up to 10 channels; semantics depend on material type (liquid vs. dry) |
-
-**Meteorological (MET)**
-
-| Field | Unit | Description |
-|---|---|---|
-| Wind Speed | m/s | Measured in-flight |
-| Wind Direction | degrees | |
-| Temperature | °C | |
-| Humidity | % | |
-
-> **Note:** All values are in metric units. Date/times are UTC. Coordinates are in WGS84 (EPSG:4326), which is numerically equivalent to SIRGAS 2000 used in Brazil.
-
----
-
-### 2.5 Bulk Export (CSV / GeoJSON Download)
-
-For the daily 17:00 Brasília batch pull and ArcGIS imports, a **file-based export** endpoint is available:
-
-1. **Request export** — call the export endpoint for a mission; the system generates the file asynchronously
-2. **Poll for status** — check whether the file is ready (typically seconds to a few minutes for large missions)
-3. **Download** — retrieve the file via the returned download link
-
-**Available formats:** CSV (all raw trace data, one row per GPS record) and GeoJSON.
-
-CSV files include job and session header columns repeated on every row for easy import directly into Power BI or a data warehouse without needing a separate join step.
-
----
-
-### 2.6 Spray-Area Boundary Polygons *(Pending confirmation — see Section 4)*
-
-An optional endpoint to retrieve the **planned field boundary polygons** as GeoJSON, for direct import as ArcGIS layers.
-
----
-
-## 3. Data Units & Standards
-
-| Aspect | Standard used |
-|---|---|
-| All measurements | Metric (ha, m/s, L/min, L/ha, Kg/ha, °C, meters) |
-| Dates & times | ISO 8601 UTC strings |
-| Coordinates | WGS84 decimal degrees (EPSG:4326 / numerically equivalent to SIRGAS 2000) |
-| Volumes | Litres for liquid, Kg for dry granular |
-| Speeds | m/s in raw records; km/h can be derived by consumer (× 3.6) |
-
----
-
-## 4. Pending Confirmation — One Open Item
-
-**Spray-area boundary polygons for ArcGIS**
-
-> Do you need the **planned field boundary polygons** (the area outlines drawn in the mission) exported as GeoJSON geometry, in addition to the numeric coverage statistics (hectares)?
->
-> - If **yes** — we will provide a dedicated endpoint returning the boundaries as GeoJSON features, suitable for direct layer import into ArcGIS.
-> - If **no** — the numeric coverage and session summary fields are sufficient and no additional endpoint is needed.
->
-> *This is the only remaining item before implementation can begin.*
-
----
-
-## 5. Estimated Delivery Timeline
-
-| Phase | Estimated duration | Target delivery |
-|---|---|---|
-| 1 | ~4 weeks | ~mid-May 2026 |
-| 2 | ~2 weeks | ~end of May 2026 |
-
-Includes development, testing, sandbox environment setup, and a review cycle.
-
-A **sandbox environment with sample missions** will be made available for your team to validate the API responses before production go-live.
-
----
-
-## 6. Summary of Requirements Confirmed
-
-| Requirement | Status |
-|---|---|
-| API Key authentication | ✅ Confirmed — self-service key management for both the applicator account and AgMission admin |
-| Applied Flow data (per GPS point) | ✅ Confirmed — all fields from GPS Data and Applic Info tabs included |
-| Coverage with application info | ✅ Confirmed — session summaries include both raw aggregates and applicator-confirmed values |
-| Pilot Traceability | ✅ Confirmed — pilot name, ID, tail number, assignment date, and in-file pilot name all included |
-| Mission filtering (Client, Order No., Name, Dates) | ✅ Confirmed — UI enhancement to the existing Job List screen |
-| Pull delivery (not push) | ✅ Confirmed — pull endpoints + async file download |
-| Raw data (not calculated metrics) | ✅ Confirmed — all raw sensor values returned; calculated `appRateApplied` also included as it appears in the UI |
-| Power BI integration | ✅ Supported — cursor-paginated JSON for incremental refresh |
-| ArcGIS integration | ✅ Supported — GeoJSON export + boundary polygons (pending confirmation) |
-| Data warehouse / bulk load | ✅ Supported — CSV export endpoint |
-| Consumption once daily at 17:00 Brasília | ✅ Supported — async export endpoint designed for this pattern |
-| Sandbox environment | ✅ Will be provided before production go-live |
-| Spray-area boundary polygons | ⏳ **Pending your confirmation (Section 4)** |
diff --git a/Documents/Reviews/Job invoicing-BackEnd-Code-reviews-from-2024-07-11.txt b/Documents/Reviews/Job invoicing-BackEnd-Code-reviews-from-2024-07-11.txt
deleted file mode 100644
index aeb8763..0000000
--- a/Documents/Reviews/Job invoicing-BackEnd-Code-reviews-from-2024-07-11.txt
+++ /dev/null
@@ -1,226 +0,0 @@
-General:
-What's good:
- + User roles and permissions validation implemented in routes.
- + Endpoint params are granularly validated using 'joi' schemas.
- + Used helpers function for reusability and avoiding code duplication.
-
-What's Todo for production:
-- DRY (DO NOT REPEAT YOURSELF) principle to be applied.
-- To avoid potential crashing, it should do guard check (i.e.: !utils.isEmptyArray(array)) before accessing/calling functions of any array, object, string etc. especially on function's input arguments.
-- Do not modify/restore to the orginal format if there are no behaviour changes, and keep the same formattings as it's easier for us to review the codes.
-- All ending code statements to be ended with comma (;).
-- 2 spaces (tab) each level indents to applied to all code files. When modifying existing files. The previous indentations should be kept intacted.
-- When done editing, a good practice to always performe:
- + Pressing a combination of Ctrl+Shift+I to format the whole codes within the file to a common indentation. If using VSCode, these settings is already available under .vscode\settings.json file of each project folder.
- + Clean up dead codes or redandant codes such as empty/unsed functions, imports, constants, etc.
- + Add proper comments where there are complicated logic/algorithm.
-
-- Keep simple codes (such as properties of objects) inline when possible for better readability.
- For example:
- const job = job.findOne({
- _id: jobId,
- });
-
- Changed to: const job = job.findOne({ _id: jobId }); is much cleaner, shorter and easier to read.
-
-------------------
-Routes:
-- Most of Joi fluent objects or child objects/properties are similar in structure. Can they be resused to avoid duplicated codes?
-- const editRoles, viewRoles should not be duplicated in all Job Invoicing routes.
-
-invoice_settings.js:
-- Why paymentTerm in createInvoiceSettingSchema is required but optional in updateInvoiceSettingSchema?
-- .route('/byClient/:clientId') and .route('/:invoiceSettingId') are duplicated (repeated) with the exact similar handlings. How about using route regex be used or simply params 1 route? E.g.: .route('/:invoiceSettingId/byClient/:clientId')
-
-costing_items.js:
-- Why all field validation for updateCostingItemSchema are optional compared but required in createCostingItemSchema?
-
-invoices.js:
-- Most of properties and nested ones in .items({..}) in updateInvoiceSchema and in createInvoiceSchema are the same with only .code property different.
-- Why openDate, dueDate, jobs, clients are optional in updateInvoiceSchema but required in createInvoiceSchema?
-- Why companyName, address, logo, currency in createInvoiceSchema but not updateInvoiceSchema? Is currency optional in updateInvoiceSchema?
-- Why all costing item fields are optional in updateInvoiceSchema.jobs.items.costings? Are invalid costing items allowed?
-- What is clients.items.code for?
-- Is invoiceIds (array) expected in getExportMultipleInformationSchema intead of invoiceId?
-- route('/:id'). Validation schema is missing. To add validation for expected params.
-
-log_payment.js:
-- Check and revise comments to refer to logpayment instead of '*invoice'
-- Why updateLogPaymentSchema is and empty Joi schema object (Joi.object({})) with no validation?
-- /createMultipleLogPayments => to renamed to /createLogPayments. Notice that it already ending with plural (s).
-- Input fields Naming should be consistent to avoid confusion. All object Ids to be [name]Id. E.g.: invoice => invoiceId, client => clientId. Please remember to update controller's handlers after these changes.
-
-
-------------------
-Controllers:
-- Errors are contrialized handled in /middlewares/error_handle.js, thus all try catch at the top level in each controllers's API handlers are redundant. Thus, they should be removed. Specific try/catch based on known logic can also be used then they might need to be rethrown.
- Example:
- async apiHandler(req, res, next) {
- try {
- ....
- } catch (error) {
- AppParamError.throw(error);
- }
- }
-- Delete API handlers should not throw an error if the document(s) can not be found.
-- Naming. Controllers's API handler functions should be named ending with verb (_get, _post, _put) except delete.
-
-invoice_settings.js:
-- Did the FrontEnd handle Errors.INVOICE_SETTING_NOT_FOUND when calling RUD Endpoints ?
-
-costing_items.js:
-- In updateCostingItemSchema, why .name and .price are optional? When changing, please remember DRY.
-- Did the FrontEnd handle Errors.COSTING_ITEM_NOT_FOUND when calling RUD Endpoints ?
-
-invoices.js:
-- To revise:
- + Handler functions's naming convention (.._suffix exept delete). E.g.: getInvoices => getInvoices_get
- + Possible inline codes
-
-- getInvoices():
- + To guard the the case if puid is null (or not an valid objectid) to avoid unnecessary whole collection scanning.
- + To remove duplication codes for getting invoices and adding them to an array, line 34-76. Isn't there is only one field differed in the filter object?
-
-- getJobById():
- + if (!job) AppParamError.throw(Errors.TO_NOT_FOUND); => to correct the error code.
-
-- generateRandomInvoiceCode():
- + How about simplifing invoiceUnique by using utils.padZero(num, size) instead?
-
-- createInvoice():
- + What if invoiceBody.clients, invoiceBody.jobs, jobIds, listJobs are null or undefined (later)? => the whole server app would crash.
- + To guard for the case if puid is null (or not an valid ObjectId). If puid is not valid, it must return [] or throw an error to avoid potential return all invoices within the db.
-
-- getInvoiceById():
- + To guard for the case if puid, invoiceId are null (or not an valid objectid).
- + To avoid potential crashing, it should do guard check before accessing any array (e.g.: invoice.clients.filter()).
- + The same duplication pattern (only diff. in query filter object) repeated when querying invoices, then checkInvoiceStatus() like mentioned in getInvoices(). To be revised avoiding code duplication.
-
-- checkInvoiceStatus():
- + What if invoice param is null or app? => undefined the whole server will crash.
-
-- getPrintInformation():
- + Line 223, invoice.clients.find(..) and line 240 invoiceObject.jobs(). To avoid potential crashing, it should do guard check before accessing any array.
-
-- getUnitName(unit):
- + Line 260, unit.toString().toLowerCase(). It should do safe guard check before calling unit's string chain functions to avoid potential crash.
-
-- calInvoiceExport():
- + As mentioned in getInvoiceById(). To guard for the case if puid is null (or not an valid ObjectId). If puid is not valid, it must return [] or throw an error to avoid potential return all invoices within the db.
- + To revise and make sure always doing safe guard check before accessing an array (.clients, .jobs, .items,..)
- + It returns an complicated data but the structure was not documented. To add documentation mentioning about the params and return data structure.
-
-- geInvoiceCsv():
- + 1st line, calInvoiceExport(req, invoiceIds)).flat().flat().flat(). Doing shortcuts to access array functions like this is not a good practice because of potential crash.
- + Line 372, delimiter: ';'. Isn't it to be ',' for CSV output?
-
-- splitAddress(inputAddress):
- + inputAddress.trim(). What if inputAddress is null or undefined?
-
-- geInvoiceIIF():
- + 1st line, const invoices = await calInvoiceExport(req, invoiceIds); Is it expected to return an invoice list?. There is only 1 iteration for clients [line 419.. for (const clients of invoices)]
-
-- getExportInformation(), getExportMultipleInformation():
- + DRY. They looks almost 90+% duplicated. What is the real different in these two functions?
- + Refactoring to only one function exportInvoices(req ({ invoiceIds:[], invoiceType }), res) should be enough to handle export 1 or multiple invoices.
-
-- updateInvoiceById():
- + Firstly, must do safe guard checks on input params (invoiceId (potential null or undefined), data (potential null of clients and jobs and others), puid) before using.
- + When req.userInfo is null or undefined => It should not proceed but throw an AppError error.
- + Less param objects => inline 1 line for readability.
- + Invoice.update() is deprecated. To use other update[..] function instead.
- + If arrays like .clients is optional, .clients.map will potentially crash.
- + For effiency and data integrity, all updates logic (if status == Void) to be best made on a deep clone of the req.body then finally calling updateOne to the db at one time.
-
-- deleteInvoiceById(), deleteInvoiceByIdUsecase():
- + ..Invoice.findByIdAndDelete({ _id: invoiceId, byPuid: puid }). Model.findByIdAndDelete(id, ..) expect an single value id param not an condition object. To use Model.findOneAndDelete({_id, byPuid},..) instead.
- + Thowing Errors.INVOICE_NOT_FOUND is not necessary.
- + deleteInvoiceByIdUsecase() => rename to deleteInvoice(id, puid)
- + These two function do the same thing with duplicated content. deleteInvoiceById() should call deleteInvoice(id, puid).
-
-- deleteMultipleInvoices():
- + To rename to deleteInvoices(..); calling deleteInvoice(id, puid).
-
-- checkInvoiceStatus(invoice):
- + First must do safe guard check on invoice before accessing its properties or the app will potentially crash if invoice is null or indefined.
-
-
-log_payment.js:
-- Why are these API handler functions blank? getLogPaymentById(req, res), updateLogPayment(req, res), deleteLogPayment(req, res)
-- createMultipleLogPayments() => to rename to createLogPayments as mentioned in the route.
-
-- getLogPayments():
- + The same duplication pattern (only diff. in query filter object) repeated when querying for payment logs. To remove duplication codes by using query condition conditionally.
-
-- createLogPayment():
- + Firstly, must do safe guard check on invoice.clients before calling invoice.clients.find() to avoid potential crash.
- + Why saving the invoice without any changes? Line 74, invoice.save()
-
-- createMultipleLogPayments():
- + createMultipleLogPayments => renamed to createLogPayments as mentioned in route.
- + Why also saving invoice here? Line 122, await invoice.save()
- + Why throw Errors.TO_NOT_FOUND error (line 104)?
-
-- createLogPayment() and createMultipleLogPayments():
- + Both have the same duplicated logic to log an payment log. To refactor to one function called LogPayment(paymentLog). Then reuse them in these two handler functions.
-
-job.js:
-- handleCostingItems().... Why not use the costing items as being seen on the UI when the user creating/updating a job? Please remove this logic.
-
-- createJob_post():
- + In the UI, it does not clone the job's costing items. Why calling handleCostingItems() here?
- + As Why does it still query the invoices collection to get invoice's related info? This would create an future severve performance issue when there are lots of invoices. How's about adding an invoiceStatus field within the job model and updating the corresponding status when needed? Remember to update the status again when a job was removed from and invoice or when the invoice is deleted.
-
-- updateJob_put():
- + Why querying and updating invoices, jobs's costing items when updating a job? It is unnecessary and will create a problem of user data integrity and performance hit.
-
-- getJobs_get():
- + When added the invoiceStatus to the job, the join/lookup to invoices collection can be removed here. Then, It would help to improve the performance by avoid scanning invoices collection.
-
-
-------------------
-Models:
-- To simplify properties with inline codes for cleaner and readability.
-
-costing_item.js:
-- userParentId field to be renamed to byPuid for consistency.
-
-invoice_settings.js:
-- Please add comments for .userId and userParenetId. Is .userId for storing a client userid?
-
-invoice.js:
-- PaymentTerm should not be confused with real life invoice payment term (days to due(be paid) after open). Thus rename to something like createOp to represent how the invoice was created with one of the three options. Likewise, the PaymentTerm constant to be rename as InvCreateOption
-- overdueDateAllowance. Should be renamed to paymentTerm.
-
-location.js:
- + What is require('./invoice_settings'); for?
-
-job.js:
- + add invoiceStatus field to manage its invoicing status as mentioned in the job.js controller.
- + Please restore the original spacing and indents
-
-------------------
-Helpers:
-- We use global dotenv to avoid duplication in each project and for cleaner deployment. Thus, no dotenv in local dependencies. Reference: Preload in https://www.npmjs.com/package/dotenv
-
-validate.js:
-- To be relocated under /middlewares/ folder.
-- HTTP 400 status code and the validation error message. How/did the FrontEnd handle these Joi validation errors?. We will need to merge this into the application current architech by returning an AppInputError with 409 code and may be along with the additional message field.
-
-multer.js:
- + folderInvoiceSettingUpload => hidden path constant. It is to use an ENV param such as INV_CFG_UPLOAD_DIR. Please be noted that all ENV params need to be wrapped in the env.js helper module.
- + To be relocated under /middlewares/ folder.
-
-- invoiceSettingsUpload():
- + It should allow image file extentions in uppercase as well.
- + fileSize should use an ENV like INV_CFG_MAX_UPLOAD_SIZE_MB = 5 (default).
-
-env.js:
- + We are using global dotenv. Please remove dotenv related usages.
-
-utils.js
- + Why using Bignumber types?
- + Why using dayjs? momentjs has been used and should be used in the project.
-
-------------------
-
diff --git a/Documents/Reviews/Job invoicing-FronEnd-Code-reviews-from-2024-07-11.txt b/Documents/Reviews/Job invoicing-FronEnd-Code-reviews-from-2024-07-11.txt
deleted file mode 100644
index 355082a..0000000
--- a/Documents/Reviews/Job invoicing-FronEnd-Code-reviews-from-2024-07-11.txt
+++ /dev/null
@@ -1,246 +0,0 @@
-General:
-What's Todo for production:
-- DRY (DO NOT REPEAT YOURSELF) principle to be applied.
-- To avoid potential crashing, it should do guard check (i.e.: !utils.isEmptyArray(array)) before accessing/calling functions of any array, object, string etc. especially on function's input arguments.
-- Do not modify/restore to the orginal format if there are no behaviour changes, and keep the same formattings as it's easier for us to review the codes.
-- All ending code statements to be ended with comma (;).
-- 2 spaces (tab) each level indents to applied to all code files. When modifying existing files. The previous indentations should be kept intacted.
-- When done editing, a good practice to always performe:
- + Pressing a combination of Ctrl+Shift+I to format the whole codes within the file to a common indentation. If using VSCode, these settings is already available under .vscode\settings.json file of each project folder.
- + Clean up dead codes or redandant codes such as empty/unsed functions, imports, constants, etc.
- + Add proper comments where there are complicated logic/algorithm.
-
-- Error Handling: We suggest to centralize error handling using following pattern:
- 1. create a util function that accepts an error object and returns a message string
- function handleErr(error): string {
- // extract error code
- // if error code has message attached return error message,
- // else map error code with a message
- return 'error message'
- }
- 2. use the handleErr function inside the error block of any observable subscriptions
- 3. display the message to the user either inline or with the msgSvc like
- this.invoiceSvc.getSettingByClientId().subscribe({error : (err) => this.handleErr.msgSvc(addFailedMsg(err))})
-
-----------------------------
-customer-settings.component.ts
-invoice-settings-guard.service.ts
-settings.component.ts
-customer-settings-resolver.service.ts
-setting-resolver.service.ts
-
-- The spacing format have been modified in serveral contents from the original without any behaviour changes in the following files:
-job-edit.component.html
-job-edit.component.ts
-job-list.component.ts
-app.menu.components.ts
-- Example:
-
- have been changed to
-
-
- + Do not modify (if did please restore to) the orginal format if there are no behaviour changes, and keep the same formattings as it's easier for us to review the codes.
-
-- Checking for not null of a service is not necessary since the base component should already injected it.
- Example:
- this.authSvc != null && this.authSvc.hasRole([RoleIds.APP, RoleIds.APP_ADM]);
-
-- The the business logic to calculate the values for (subTotal, discounted, totalExcludingTax, taxed, total) are duplicated in
-invoice-detail.component.ts
-invoice-edit.component.ts
-invoices-list.component.ts
-- These calculations are critical to the application and should be placed in a single util function, and tested for various of edge cases.
- + The generic format of the function is something like:
- calcInvoice(subTotal, discount, taxRate) {
- // do calculation
- return { subTotal, discounted, totalExcludingTax, taxed, total };
- }
-
-- The calculation for the sum of an array is redundant in many components.
-
-job-list.component.ts
- Example:
- this.invoice.clients
- .map(i => this.billToListPriceObject(i).totalExcludingTax)
- .reduce(
- (accumulator, currentValue) => accumulator + currentValue, 0,
- );
-
- this.invoice.clients
- .map(i => +i.split)
- .reduce(
- (accumulator, currentValue) => accumulator + currentValue, 0,
- );
- Do:
- - Replace all these redundant sums with a common util function that return a default value in case the array is not defined. The structure of the util function can be something like:
- const sum = (arr: Array[], field: string, func?: Function) => arr?.map((item) => func ? +(func(item)[field]) : +item[field]).reduce((acc,val) => acc + val, 0) || 0;
- totalExcludingTax = sum(this.invoice.clients, 'totalExcludingTax', this.billToListPriceObject);
- totalSplit = sum(this.invoice.clients, 'split');
-
-- Make sure the min and max values of form fields are bounded according to context
- Example:
- - Taxes and Discount percentages should be bounded min = 0, max = 100
-
-- Various http calls to backend api that being done directly within these components:
-invoice-detail.component.ts
-invoice-edit.component.ts
-invoices-list.component.ts
- - Some of these functions have usability issues that may effect the user like downloadCSV, saveWithLogPayment, saveWithLogPayment. These calls don't have proper error handlings
- incase if the http calls return error conditions.
- Do:
- - Separate these calls into ngrx effects and dispatch them with proper error handlings or add error handling for each subscribe block.
-
-invoice-edit.component.html
-- Don't linebreak on translated text as it will add additional line when the text is translated to another language.
- Example:
-
- Amount is required
-
- should change to:
- Amount is required
-
-- Try to separate translated text from special characters like (:,%,!).
- Example:
-
- should change to:
-
-
-- Create css class for the rules
- "background-color: transparent; color: #212121;"
- "text-align: center; font-weight: 600; border-bottom: 1px solid #bdbdbd;"
- "gap: 4px; justify-content: center"
- "justify-content: end"
- "border-bottom: 1px solid #bdbdbd;"
- "display: flex; justify-content: space-between"
- Do:
- - Use css overriding to reuse rules
-
-- The logic isNew || isDraftEdit is repeated in many places in the template and the ts files
- Do:
- - Create a predicate function for the logic isNew || isDraftEdit
-
-invoice-edit.component.ts
-- getSettingByClientId http call on line 369-396, why is there such complex error handling mechanism, and it is being called again on line 773
-and the error handling here is different.
- Do:
- - If getSettingByClientId information is reused, use a resolver to fetch the data before the component is loaded. Otherwise, if this information is
- critical to the component, show a descriptive errror message to the user explaining why. As of right now, it seems that it just fails silently and resets the form to some values.
-
-- There are no error handlings of the observable subscriptions in the function saveNewTerm and removeTermOpt, loadJobs, fetchLogPayment, saveInvoice.
- Do:
- - Generally any function that make http request should have error handling condition with a message telling the source of the error.
-
-- In saveWithLogPayment, removeBillToClient, addClientToInvoice, saveNewTerm, addClientDlg make sure that
- this.selectedClients, and this.selectedJobs, this.paymentTermOpts this.orgSearchClientList are not null before calling map and [...] syntax
- Do:
- - Use optional chaining https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining or if statement to check for null arrays first
-
-- saveWithLogPayment, saveNewTerm, saveLog the asynchronous call sequence implementation are incorrect without proper error handlings.
- Do:
- - Use pipe to chain the http calls. For example,
- this.invoiceSvc.saveInvoice().pipe(
- switchMap(() => {
- return this.invoiceSvc.createListLogPayment(payload);
- })
- ).subscribe({error: (err) => handleErr(err)}); // Generic handleErr() mentioned above in General.
-
- if these same sequence of calls are repeated, create an ngrx action and effect for the sequence and dispatch it with a single command.
- see https://rxjs.dev/api/index/function/pipe on how to chain the async calls and catch the errors in one place.
-
-invoices-list.component.ts
-- On line 147-158 ngAfterViewInit, check dueDate.value and dueDate.value for null before calling map function. What happen when the user clear the calendar? is there a function that clears 'inv-ops' from sessionStorage?
-
-invoice-detail.component.html
-- See invoice-edit.component comments for translated text line breaks and special characters.
-
-- Create css class for rules
- "width: 200px; text-align: right; font-weight: 400;"
- "border-bottom: 1px solid #bdbdbd;"
- "font-weight: 600;"
- "min-width: 150px; padding-left: 0;"
- "padding-left: 0;"
-
-- Why is there an interpolation of a digit ({{1}}) value between lines 365 and 369 in invoice-detail.component.html
-
-- Separate translated text from nested elements with interpolation:
- Example:
-
Tax
- ({{printDetail.client.taxRate}}%)
-
- change to
-
- Tax
- ({{printDetail.client.taxRate}}%)
-
-
-- Create a function to compute the value as these are redundant.
- printDetail.client.amountDue ? printDetail.client.amountDue : printDetail.total
-
-invoice-detail.component.ts
-- In the saveLog function of the observable subscriptions between lines 316-327, do these async api calls need to be sequential?
-- what if fetchInvoiceDetail, fetchLogPayment failed? the message snackbar will still show "Create log payment succeeded" as it will be confusing to the user.
- Do:
- - See https://rxjs.dev/api/index/function/pipe on how chain the async calls and catch the errors in one place.
-
-settings.component.html
-- See invoice-edit.component.html comments for translated text line breaks and special characters.
-- See invoice-edit.component.html comments for separating translated text from nested elements.
-- Interpolate the allowedFormats array items for the allowed formats text on line (134-138) as these can change later on.
-- Max value for Tax and Discount % should be 100.
-
-settings.component.ts
-- Move allowedFormats, maxLogoSize constant into a shared variable in the module.
-- Create a requiredFieldTrimmed function in invoice service to share the logic in both setting.component and customer-setting.component.
- Example:
- requiredFieldTrimmed(setting: CustomerInvoiceSetting) {
- return setting?.companyName?.length == setting?.companyName?.trim().length && setting?.address?.length == setting?.address?.trim().length
- }
-- There are no error handling in the functions saveNewTerm,removeTermOpt see comments for invoice-edit.component.ts above for more details on how to handle http call errors.
-
-customer-settings.component.html
-- See invoice-edit.component comments for translated text line breaks and special characters.
-- See settings.component comments for allowedFormats, maxLogoSize, requiredFieldTrimmed.
-- Max value for Tax and Discount % should be 100.
-
-costing-item.component.html
-- Wrap translatable texts in p-header element with ng-container.
-
-job-list.component.ts
-- Create an enum constant for statuses ('all', 'new', 'ready', 'download', 'spray', 'invoiced').
-- Wrap lines 148-168 into a function and do a return after each this.statusFilter assignment avoiding future bugs where more than one of the conditions can be true.
- Example:
- if (!listFilter.filters.status?.value && listFilter.filters.invoices?.value == 1) {
- this.statusFilter = 'invoiced';
- }
- change to
- if (!listFilter.filters.status?.value && listFilter.filters.invoices?.value == 1) {
- return this.statusFilter = 'invoiced';
- }
-- Is there a default value for this.statusFilter if (listFilter && listFilter.filters) is false?
-- Should this.statusFilter = 'all' be in the the outter if/else as the default value instead?
-
-job-edit.component.ts
-- Why is there async await in createInvoice function?
-
-invoice-list-canactive.guard.ts
-- What is the purpose of this guard? its only function is to fetch the invoice list. The list then get fetched again in invoices-list.component.ts ngOnInit.
-
-invoice-edit-resolver.service.ts and invoice-detail-resolver.service.ts
-- These two resolvers get the exact same data from the backend. Try to combine them into one resolver.
-
-invoice-create-canactive.guard.ts and customer-setting-list-canactive.guard.ts
-- These two guards seem to have the same function. Try to combine them into one.
-
-enums folder contents
-- Combine these ts files into a single shared file in the module as these are just constant values.
-- Freeze the invoiceStatus object.
-
-invoice.service.ts
-- getPrintDetail need a return type from the backend.
-- Check for null array of jobs parameter in flattenJobCostingItem(jobs).
-
-client.service.ts
-- getClientWithInvoiceSetting needs a return type (important: API contract) from the backend.
-
diff --git a/Documents/Reviews/Subscription Mgt - For Justin.txt b/Documents/Reviews/Subscription Mgt - For Justin.txt
deleted file mode 100644
index 0e0921e..0000000
--- a/Documents/Reviews/Subscription Mgt - For Justin.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-** Back navigation: Need to have better control on the flow back and forth started from Services <-> Billing Address <-> Payment Method, ...**
-Expectation: restoring selected items or entered values in fields, the flow back and forth must be in the right order (base on the steps the user made).
-
-** New Route issues: on some screens, Job List, Job Detail, Job Map, Upload Job Data, etc.. Refresh (F5) will show the green screen with AgNav logo in the center, F5 again will lost the current screen and show Dashboard (root) screen. **
-Expectation: should reload the screen at the current path with selected item, if any.
-
-Billing Address:
-1. When loaded, it always filled with a fixed name, 'justin'? Why?
-Expectation:
-When loaded, it should filled with:
-- Name: the contact name of the Applicator as default or the previous input Name
-- Address fields: if there is none BA, extract the fields from address (normally would be the exact business address registered with us) of the appl. profile.
-
-2. When try to change the country, it emits error. However, it reset values of state/province and postal code.
-Expectation:
-- Can country be fixed and or not changable? The reason is the BA make senses only for the country the applicator's business location which is mostly within the same province/state.
-
-Q:
-- Sometimes it shows 'Error: Received unknown parameter: billing_details[address][valid] ?' at the bottom of the screen Why? Is the a better to handle this?
-- If the Stripe Address element is hard to use and how about simply implement a reuse-able Address Component ?
-
-Services:
-When selected a package then Confirm => Billing Address. If click on Back, it goes back to the Services with none selected. => Need to display previous selected services.
-Expectation:
-- At any time, when navigating back to, It should show the previous selected items.
-
-Payment Details:
-1. Total Amount: shows plain number only, the value is incorrect.
-Expectation:
-- Show value in US$: or with the suffix of USD next to the value.
-- The value number must be divided by 100, if getting from Stripe, and be formatted with US 2-decimal precision.
-
-2. 'Choose Existing Payment' and the dropdown with 'None'.
-Expectation:
-- When there is none PM, hide/disable 'Choose Existing Payment' and the dropdown with 'None'.
-
-3. 'Card Number':
-Expectation: auto focus on the 'Card Number' field
-
-4. When enter a well-form card number such as 4242 4242 4242 4245, it displays 'secret red error icon', What does it mean to the user? How does he know how to proceed and why?
-Expectation:
-- Should show clear and meaningly error, may be right below the CN field, so the user can correct.
-
-5. Errors: Received unknown parameter: billing_details[address][valid] when click on 'Continue to review'
-Expectation:
-- For the case of invalid customer address (unverified from Stripe), as mentioned before, It should show a clear warning message and a button to click on to go back to the Billing Address screen. Only proceed until the Address is valid and verified by Stripe.
-- Or if any error, check for the reason and and the request defails from the Debug and Stripe. For example: for this error, Response: "invalid_request_error", https://dashboard.stripe.com/test/logs/req_gWrKrTzQEHpMxI?t=1683291978
-
-
diff --git a/Documents/SVN-Guidelines.md b/Documents/SVN-Guidelines.md
deleted file mode 100644
index 6efc717..0000000
--- a/Documents/SVN-Guidelines.md
+++ /dev/null
@@ -1,407 +0,0 @@
-# SVN Guidelines — AgMission Project
-
-> Reference for daily SVN workflow, branching, merging, and common commands.
-> Repo root: `https://svn.agnav.com:8443/svn/AgMission`
-
----
-
-## Table of Contents
-1. [Repository Layout](#sec-1)
-2. [Daily Workflow](#sec-2)
-3. [Branching](#sec-3)
-4. [Keeping a Branch in Sync with Trunk](#sec-4)
-5. [Merging a Branch into Trunk (Reintegrate)](#sec-5)
-6. [After Reintegrate: Branch Lifecycle](#sec-6)
-7. [Resolving Conflicts](#sec-7)
-8. [Common Commands Reference](#sec-8)
-9. [Best Practices and What to Avoid](#sec-9)
-
----
-
-
-## 1. Repository Layout
-
-```mermaid
-graph TD
- ROOT["AgMission/ — repo root"]
- ROOT --> T["trunk/"]
- ROOT --> B["branches/"]
- ROOT --> G["tags/"]
- T --> DEV["Development/ stable · always deployable"]
- B --> SR["satloc-resume/ spent — reintegrated"]
- B --> DEA["data-export-api/ active feature branch"]
- B --> JI["job-invoicing/ active feature branch"]
- G --> R321["release-3.2.1/ immutable snapshot"]
-
- style DEV fill:#c8e6c9,stroke:#388e3c
- style SR fill:#ffcdd2,stroke:#c62828
- style DEA fill:#fff9c4,stroke:#f9a825
- style JI fill:#fff9c4,stroke:#f9a825
- style R321 fill:#e1bee7,stroke:#7b1fa2
-```
-
-**Rules:**
-- `trunk/Development` is always deployable. Never commit broken code directly here.
-- Each significant feature or release cycle gets its own branch.
-- Tags are read-only snapshots — never commit to a tag.
-
----
-
-
-## 2. Daily Workflow
-
-### Start of day — update your working copy
-
-```bash
-# Update the branch you're actively working in
-cd /path/to/AgMission/branches/data-export-api
-svn update
-
-# Also update trunk if you need to reference it
-cd /path/to/AgMission/trunk/Development
-svn update
-```
-
-### Check what you've changed
-
-```bash
-svn status # show all local changes (M=modified, A=added, D=deleted, ?=unversioned)
-svn status -q # quiet: versioned changes only (excludes ? files)
-svn diff # full diff of all changes
-svn diff path/to/file # diff of a specific file
-```
-
-### Stage and commit
-
-SVN has no staging area — `svn commit` sends everything marked for change.
-
-```bash
-# Commit all pending changes
-svn commit -m "Meaningful commit message describing WHAT and WHY"
-
-# Commit specific files only
-svn commit file1.js file2.js -m "Fix X in file1 and Y in file2"
-```
-
-**Commit message format (recommended):**
-```
-# Short summary (imperative mood)
-
-- Detail line 1
-- Detail line 2
-```
-
-### End of day — make sure nothing is left uncommitted
-
-```bash
-svn status -q # should return nothing if all work is committed
-```
-
----
-
-
-## 3. Branching
-
-### Create a new feature branch from trunk
-
-```bash
-# Server-side copy — instant, no data transferred
-svn copy ^/trunk/Development ^/branches/my-feature \
- -m "Create my-feature branch from trunk rXXX"
-
-# Then check it out locally (if your working copy doesn't cover /branches/)
-svn update branches/my-feature
-```
-
-### Create a release tag
-
-```bash
-svn copy ^/trunk/Development ^/tags/release-X.Y.Z \
- -m "Tag release X.Y.Z"
-```
-
-### Delete a branch (after reintegrate — see §6)
-
-```bash
-svn delete ^/branches/my-feature \
- -m "Remove spent my-feature branch post-reintegrate"
-```
-
----
-
-
-## 4. Keeping a Branch in Sync with Trunk
-
-**Do this regularly** (at least weekly, or whenever trunk has significant commits) to keep the
-branch up to date and avoid large conflict batches at reintegrate time.
-
-```bash
-# From inside your feature branch working copy
-cd branches/my-feature
-svn update # make sure WC is current
-svn merge ^/trunk/Development # merge trunk changes into branch
-# resolve any conflicts (see §7)
-svn commit -m "Sync trunk rXXX into my-feature"
-```
-
-> ⚠️ **Never use `--record-only` for a routine sync.** `--record-only` skips applying file
-> changes and only updates mergeinfo. Use it only to fix mergeinfo bookkeeping gaps caused by
-> very old revisions that predate the branch and have no content relevance. Using it incorrectly
-> will silently discard real changes from trunk.
-
----
-
-
-## 5. Merging a Branch into Trunk (Reintegrate)
-
-SVN 1.8+ detects reintegration automatically. You do **not** need `--reintegrate`
-(deprecated in 1.8, no-op in 1.14).
-
-### Prerequisites
-- The branch must have been **fully synced** with trunk (§4) and that sync committed.
-- Your trunk working copy must be clean (`svn status -q` returns nothing).
-- No local modifications in trunk WC.
-
-```bash
-# 1. Sync branch one last time (if there are recent trunk commits)
-cd branches/my-feature
-svn update
-svn merge ^/trunk/Development
-svn commit -m "Final trunk sync before reintegrate"
-
-# 2. Switch to trunk and update
-cd ../../trunk/Development
-svn update
-
-# 3. Run the reintegrate merge
-svn merge ^/branches/my-feature
-# SVN auto-detects this as reintegrate direction (branch → trunk)
-
-# 4. Review and resolve any conflicts (see §7)
-svn status
-svn diff
-
-# 5. Syntax-check JS files if applicable
-node --check server/path/to/changed.js
-
-# 6. Commit the merge
-svn commit -m "Merge my-feature into trunk (reintegrate r1000-rXXX)"
-```
-
-### What a reintegrate does
-
-```mermaid
-%%{init: {'gitGraph': {'mainBranchName': 'trunk', 'rotateCommitLabel': false}}}%%
-gitGraph
- commit id: "r999 — branch point"
- branch my-feature
- checkout my-feature
- commit id: "r1001 feature work"
- commit id: "r1002 feature work"
- checkout trunk
- commit id: "r1003 trunk work"
- checkout my-feature
- merge trunk id: "r1010 sync from trunk"
- commit id: "r1050 final feature commit"
- checkout trunk
- merge my-feature id: "r1051 reintegrate"
-```
-
-SVN applies only the revisions from the branch that haven't already been merged to trunk
-(tracked via `svn:mergeinfo`). After the reintegrate commit, the branch is **spent** — see §6.
-
----
-
-
-## 6. After Reintegrate: Branch Lifecycle
-
-A reintegrated branch is **spent** — its mergeinfo record says "all revisions merged." Do not
-continue developing on it; create a fresh branch from trunk instead.
-
-```mermaid
-stateDiagram-v2
- direction LR
- [*] --> Active : svn copy from trunk
- Active --> Active : commit feature work
- Active --> Syncing : svn merge trunk (weekly)
- Syncing --> Active : conflicts resolved + committed
- Active --> Reintegrating : feature complete
- Reintegrating --> Spent : merge committed to trunk
- Spent --> [*] : tag + svn delete
-
- note right of Syncing
- Never use --record-only
- for routine syncs
- end note
- note right of Spent
- Always tag before
- deleting the branch
- end note
-```
-
-```bash
-# Optional: tag the branch state before deleting
-svn copy ^/branches/my-feature ^/tags/my-feature-merged-rXXX \
- -m "Tag my-feature before deletion"
-
-# Delete the branch from the repository
-svn delete ^/branches/my-feature \
- -m "Remove spent my-feature branch post-reintegrate (rXXX)"
-
-# Remove it from your local working copy
-svn update # this will remove the local branches/my-feature directory
-```
-
----
-
-
-## 7. Resolving Conflicts
-
-### Listing conflicts
-
-```bash
-svn status | grep "^C\|^.C" # text conflicts (C) and tree conflicts
-```
-
-### Text conflict — manual resolution
-
-```bash
-# Edit the file and fix conflict markers (<<<<, ====, >>>>)
-# Then mark as resolved
-svn resolve --accept working path/to/file.js
-```
-
-### Text conflict — accept one side wholesale
-
-```bash
-svn resolve --accept mine-full path/to/file # keep your local version
-svn resolve --accept theirs-full path/to/file # take incoming version entirely
-```
-
-### Tree conflict (directory added/deleted/replaced)
-
-```bash
-svn info path/to/dir # read the conflict description
-# After deciding what to keep:
-svn resolve --accept working path/to/dir # keep local (working copy) state
-```
-
-### After resolving all conflicts
-
-```bash
-svn status | grep "^C" # should return nothing
-svn commit -m "Resolve merge conflicts"
-```
-
----
-
-
-## 8. Common Commands Reference
-
-### Information
-
-| Command | What it does |
-|---|---|
-| `svn info` | Show WC path, URL, revision, last-changed info |
-| `svn info ^/branches/my-feature` | Info on a repo path (no checkout needed) |
-| `svn log --limit 10` | Last 10 commits on current path |
-| `svn log ^/trunk/Development --limit 5` | Last 5 commits on trunk |
-| `svn log -v --limit 3` | Commits with list of changed files |
-| `svn blame file.js` | Show author/revision per line |
-
-### Status & diff
-
-| Command | What it does |
-|---|---|
-| `svn status` | All local changes (M, A, D, ?, C) |
-| `svn status -q` | Versioned changes only |
-| `svn diff` | Full diff of local changes |
-| `svn diff -r HEAD` | Diff against HEAD revision |
-| `svn diff ^/trunk/Development ^/branches/my-feature` | Diff two repo paths |
-
-### Update & revert
-
-| Command | What it does |
-|---|---|
-| `svn update` | Bring WC up to latest revision |
-| `svn update -r 993` | Update to a specific revision |
-| `svn revert file.js` | Discard local changes to one file |
-| `svn revert -R .` | Discard ALL local changes recursively |
-
-### Add / remove
-
-| Command | What it does |
-|---|---|
-| `svn add newfile.js` | Schedule new file for versioning |
-| `svn add --force new-dir/` | Add entire new directory |
-| `svn delete file.js` | Schedule file for deletion |
-| `svn delete ^/branches/old-branch -m "msg"` | Delete repo path directly |
-
-### Merge
-
-| Command | What it does |
-|---|---|
-| `svn merge ^/trunk/Development` | Sync trunk into current branch (run from branch WC) |
-| `svn merge ^/branches/my-feature` | Reintegrate branch into trunk (run from trunk WC) |
-| `svn merge ^/trunk/Development --dry-run` | Preview merge without applying |
-| `svn merge --record-only ^/trunk/Development` | Fix mergeinfo gaps only — no file changes |
-| `svn mergeinfo ^/branches/my-feature` | Show what has/hasn't been merged |
-
-### Copy / branch / tag
-
-| Command | What it does |
-|---|---|
-| `svn copy ^/trunk/Development ^/branches/new -m "msg"` | Create branch |
-| `svn copy ^/trunk/Development ^/tags/v1.0 -m "msg"` | Create tag |
-| `svn copy ^/branches/old ^/branches/new -m "msg"` | Copy branch |
-
----
-
-
-## 9. Best Practices and What to Avoid
-
-### ✅ Do
-
-- **Commit often** with small, focused commits and meaningful messages.
-- **Sync trunk into your branch regularly** (`svn merge ^/trunk/Development`) — at least weekly.
- Early, small syncs are far easier to resolve than one big sync at reintegrate time.
-- **Run `svn update` before every commit** to catch conflicts early.
-- **Syntax-check JS files** before committing: `node --check path/to/file.js`
-- **Use `--dry-run`** on any merge to preview the result before applying.
-- **Tag releases** before merging or deleting branches.
-- **Delete spent branches** to keep the branch list clean.
-- **Keep unversioned files out of the WC** — use `.svnignore` or the global `global-ignores` setting.
-
-### ❌ Avoid
-
-| Mistake | Why | Instead |
-|---|---|---|
-| Committing directly to trunk | Breaks other developers; trunk must stay stable | Work on a feature branch |
-| `--record-only` on a routine sync | Silently skips real trunk changes → content drift | Only use to fix bookkeeping gaps on very old revisions |
-| Letting a branch go unsynced for months | Massive conflict blast at reintegrate | Sync from trunk weekly |
-| Continuing to develop on a spent (reintegrated) branch | SVN mergeinfo considers all revisions merged; new commits will be missed on next merge | Create a new branch from trunk |
-| `svn revert -R .` without a backup | Permanent loss of uncommitted work | Back up modified files first, or stash with a patch: `svn diff > my.patch` |
-| Committing secrets/credentials | Irrecoverable from SVN history without admin intervention | Use `.env` files excluded by `svnignore` |
-
-### Saving work-in-progress without committing
-
-SVN has no `git stash`. Use a patch file instead:
-
-```bash
-svn diff > /tmp/my-wip.patch # save all local changes
-svn revert -R . # clean the WC
-# ... do the other work, commit it ...
-patch -p0 < /tmp/my-wip.patch # reapply your WIP
-```
-
-### Setting up global ignores (one-time, per machine)
-
-Add to `~/.subversion/config` under `[miscellany]`:
-
-```ini
-global-ignores = *.o *.so *.log node_modules .env *.swp .DS_Store .specstory
-```
-
----
-
-*Last updated: April 2026 — AgMission / AgNav*
diff --git a/Documents/ga-events.ods b/Documents/ga-events.ods
deleted file mode 100644
index 474d243..0000000
Binary files a/Documents/ga-events.ods and /dev/null differ
diff --git a/Others/configs/Install RabbitMq.txt b/Others/configs/Install RabbitMq.txt
deleted file mode 100644
index 0ae7320..0000000
--- a/Others/configs/Install RabbitMq.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-I.a. Upgrade/Install Erlang for Ubuntu version <= 18.04 LTS:
-# For Migrating from old Bintray repo's versions
-https://blog.rabbitmq.com/posts/2021/03/migrate-off-of-bintray/
-
-# RabbitMq Team's PPA LaunchPad Erlang. Ref: https://launchpad.net/~rabbitmq/+archive/ubuntu/rabbitmq-erlang
-sudo add-apt-repository ppa:rabbitmq/rabbitmq-erlang
-sudo apt update
-## Uninstall/Upgrade erlang. This will also remove depended installed RabbitMq old version 3.7/8.*
-sudo apt-get purge erlang*
-
-## Install erlang
-sudo apt install erlang
-# Or
-sudo apt-get install -y erlang-base \
- erlang-asn1 erlang-crypto erlang-eldap erlang-ftp erlang-inets \
- erlang-mnesia erlang-os-mon erlang-parsetools erlang-public-key \
- erlang-runtime-tools erlang-snmp erlang-ssl \
- erlang-syntax-tools erlang-tftp erlang-tools erlang-xmerl
-
-# Install Rabbitmq online repository and make it ready to install the rabbitmq-server package later
-https://packagecloud.io/rabbitmq/rabbitmq-server/install#bash-deb
-
-I.b For ubuntu version >= 18. Ref: https://www.rabbitmq.com/install-debian.html#installation-methods
-
-II. Upgrade/Install RabbitMq to current version
-## Add a new admin user
-sudo rabbitmqctl add_user agm Ag@Rabbit2019
-sudo rabbitmqctl set_user_tags agm administrator
-sudo rabbitmqctl set_permissions -p / agm ".*" ".*" ".*"
-
-sudo rabbitmq-server start -detached
-sudo rabbitmqctl stop
-# Web Management via port 15672
-## Enable web management plugins
-sudo rabbitmq-plugins enable rabbitmq_management
-
-# Run Web management app
-http://localhost:15672
-user: agm
-pwd: Ag@Rabbit2019
-
-### Important notes on mis-match depencies errors. If it happened, we need to pin the erlang packages version to a right version not always the latest.
-For example, for RabbitMq version 3.10.2, erlang must be < 25.* in Ubuntu 18 (bionic)
-# /etc/apt/preferences.d/erlang
-Package: erlang* esl-erlang
-Pin: version 1:24.3*
-Pin-Priority: 501
-
diff --git a/Others/configs/agmission b/Others/configs/agmission
deleted file mode 100755
index a1e9aad..0000000
--- a/Others/configs/agmission
+++ /dev/null
@@ -1,210 +0,0 @@
-# Upstream server for handling API call
-upstream agmission_server {
- server 127.0.0.1:7000;
-}
-
-upstream track_server {
- server 127.0.0.1:6100;
-}
-
-upstream track_server_secure {
- server 127.0.0.1:6101;
-}
-
-# Expires map: defines the mapping between the file type and how long that kind of file should be cached
-map $sent_http_content_type $expires {
- default off;
- text/html epoch; #means no cache, as it is not a static page
- text/css modified;
- application/javascript modified;
- application/woff2 max;
-}
-# ~image/ 30d; #it is only the logo, so maybe I could change it once a month now
-
-# Default server configuration
-#
-server {
- # SSL config
- listen *:443 ssl http2 default_server;
- listen [::]:443 ssl http2;
-
- ssl on;
- ssl_certificate /etc/nginx/ssl/agm-bundle.crt;
- ssl_certificate_key /etc/nginx/ssl/agm-server.key;
- ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
-
- http2_max_field_size 64k;
- http2_max_header_size 512k;
-
- server_name agmission.agnav.com;
-
- root /media/ssd1/agmission/dist;
-
- index index.html index.htm;
-
- expires $expires;
-
- location / {
- # First attempt to serve request as file, then
- # as directory, then fall back to index.html.
- try_files $uri $uri/ /index.html;
- }
-
- location /reports/ {
- # Simple requests
- if ($request_method ~* "(GET|POST)") {
- add_header "Access-Control-Allow-Origin" *;
- }
-
- # Preflighted requests
- if ($request_method = OPTIONS ) {
- add_header "Access-Control-Allow-Origin" *;
- add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS, HEAD";
- add_header "Access-Control-Allow-Headers" "Authorization, Origin, X-Requested-With, Content-Type, Accept";
- return 200;
- }
- alias /media/ssd1/agmission/reports/;
-
- try_files $uri $uri/ =404;
- }
-
-
- location /public/ {
- alias /media/ssd1/agmission/public/;
- try_files $uri $uri/ =404;
- }
-
- location /leaflet/ {
- alias /media/ssd1/agmission/node_modules/leaflet/dist/;
- try_files $uri $uri/ =404;
- }
-
- location /esri-leaflet/ {
- alias /media/ssd1/agmission/node_modules/esri-leaflet/dist/;
- try_files $uri $uri/ =404;
- }
-
- location /api/ {
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
-
- proxy_pass http://agmission_server/api/;
- }
-
- location = /track/stream {
- proxy_http_version 1.1;
- proxy_set_header Connection '';
-
- proxy_buffering off;
- proxy_cache off;
- chunked_transfer_encoding off;
- proxy_read_timeout 12h;
-
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header content-type "application/json";
-
- proxy_pass https://track_server_secure/track/stream;
-# access_log /home/trung/temp/logs.txt;
- }
-
-
- location /track/ {
- #proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
-
- proxy_pass http://track_server/track/;
- }
-
-
- location /forecast/ {
- proxy_http_version 1.1; // Must be explicit ver 1.1, Darksky upstream server
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
-
- proxy_pass https://api.darksky.net/forecast/;
-
- #proxy_redirect off;
- }
-
-
- location /gmapapi/ {
-
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
-
- proxy_pass https://maps.googleapis.com/maps/api/;
-
- #proxy_redirect off;
- }
-
- location /report/ {
- return 301 http://$host$request_uri;
- }
-
- # deny access to .htaccess files, if Apache's document root
- # concurs with nginx's one
- #
- #location ~ /\.ht {
- # deny all;
- #}
-}
-
-server {
- listen *:80 default_server;
-
- # Migrate legacy TrackerNav API to current with these route proxies
- location = /api/v1/tracks {
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
-
- proxy_pass http://track_server/track/tracks;
- }
-
- location = /api/v1/auth/login {
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
-
- proxy_pass http://agmission_server/api/users/login;
- }
-
-
- location / { # the default location redirects to https
- return 301 https://$host$request_uri;
- }
-
- location /report/ {
- # Simple requests
- if ($request_method ~* "(GET|POST)") {
- add_header "Access-Control-Allow-Origin" *;
- }
-
- # Preflighted requests
- if ($request_method = OPTIONS ) {
- add_header "Access-Control-Allow-Origin" *;
- add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS, HEAD";
- add_header "Access-Control-Allow-Headers" "Authorization, Origin, X-Requested-With, Content-Type, Accept";
- return 200;
- }
-
- root /media/ssd1/agmission/.tmp;
- try_files $uri $uri/ =404;
- }
-}
-
diff --git a/Others/configs/db/MongoDB Production.txt b/Others/configs/db/MongoDB Production.txt
deleted file mode 100644
index 6b3bdb7..0000000
--- a/Others/configs/db/MongoDB Production.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-# MongoDB server performance tuning notes
-## Ref:
-https://dzone.com/articles/tuning-linux-for-mongodb
-https://docs.mongodb.com/manual/tutorial/transparent-huge-pages/
-https://docs.mongodb.com/manual/administration/production-notes/#std-label-readahead
-https://www.mongodb.com/blog/post/performance-best-practices-transactions-and-read--write-concerns
-https://www.percona.com/blog/2018/09/27/automating-mongodb-log-rotation/
-
-# Add blockdev command to set readahead in /etc/rc.local
-# Set readahead for mongodb storage device
-blockdev --setra 32 /dev/
-
-Future Further Improvements:
-- It is best to use RAID 1+0 with 4 SSDs for MongoDB server storage
-- Further tuning with readahead from 16 to 32 sectors of 512 bytes (16 KBs)
-
diff --git a/Others/configs/db/logrotate_mongod.conf b/Others/configs/db/logrotate_mongod.conf
deleted file mode 100644
index d4736b7..0000000
--- a/Others/configs/db/logrotate_mongod.conf
+++ /dev/null
@@ -1,16 +0,0 @@
-# /etc/logrotate.d/mongod.conf
-
-/var/log/mongodb/mongod.log {
- daily
- size 2M
- rotate 14
- missingok
- compress
- delaycompress
- notifempty
- create 640 mongodb mongodb
- sharedscripts
- postrotate
- /bin/kill -SIGUSR1 `pidof mongod` >/dev/null 2>&1
- endscript
-}
diff --git a/Others/configs/db/mongod.conf b/Others/configs/db/mongod.conf
deleted file mode 100644
index 525e7b0..0000000
--- a/Others/configs/db/mongod.conf
+++ /dev/null
@@ -1,49 +0,0 @@
-# mongod.conf
-
-# for documentation of all options, see:
-# http://docs.mongodb.org/manual/reference/configuration-options/
-
-# Where and how to store data.
-storage:
- dbPath: /media/ssd1/mdbdata
- journal:
- enabled: true
-# engine:
-# mmapv1:
- wiredTiger:
- engineConfig:
- cacheSizeGB: 3.0
-
-# where to write logging data.
-systemLog:
- destination: file
- logAppend: true
- path: /var/log/mongodb/mongod.log
- logRotate: reopen
-
-# network interfaces
-net:
- port: 27017
- bindIp: 0.0.0.0
-# bindIp: 127.0.0.1,208.73.207.98
-
-
-# how the process runs
-processManagement:
- timeZoneInfo: /usr/share/zoneinfo
-
-security:
- authorization: enabled
-
-#operationProfiling:
-
-replication:
- replSetName: rs0
-
-#sharding:
-
-## Enterprise-Only Options:
-
-#auditLog:
-
-#snmp:
diff --git a/Others/configs/db/mongod_dev.conf b/Others/configs/db/mongod_dev.conf
deleted file mode 100644
index 709d567..0000000
--- a/Others/configs/db/mongod_dev.conf
+++ /dev/null
@@ -1,55 +0,0 @@
-# mongod.conf
-
-# for documentation of all options, see:
-# http://docs.mongodb.org/manual/reference/configuration-options/
-
-# Where and how to store data.
-storage:
-# dbPath:/var/lib/mongodb
- dbPath: /media/data/mongodb
- journal:
- enabled: true
-
-# engine:
-# mmapv1:
-# wiredTiger:
-
-# where to write logging data.
-systemLog:
- destination: file
- logAppend: true
- path: "/var/log/mongodb/mongod.log"
- logRotate: reopen
-
-# network interfaces
-net:
- port: 27017
- bindIp: 0.0.0.0
- tls:
- mode: preferTLS
- certificateKeyFile: /etc/mongodb/ssl/localhost.pem
- CAFile: /etc/mongodb/ssl/rootCA.crt
- clusterFile: /etc/mongodb/ssl/localhost.pem
-
-# how the process runs
-processManagement:
- timeZoneInfo: /usr/share/zoneinfo
-
-security:
-# authorization: enabled
-# keyFile or x.509 cert is mandantary from mongo version 4.4
-# keyFile: /media/data/mongodb.key
- clusterAuthMode: x509
-
-#operationProfiling:
-
-replication:
- replSetName: rs0
-
-#sharding:
-
-## Enterprise-Only Options:
-
-#auditLog:
-
-#snmp:
diff --git a/Others/configs/db/ulimits_mongod.conf b/Others/configs/db/ulimits_mongod.conf
deleted file mode 100644
index 4208027..0000000
--- a/Others/configs/db/ulimits_mongod.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-# /etc/security/limits.d/mongod.conf
-
-mongodb soft nproc 64000
-
-mongodb hard nproc 64000
-
-mongodb soft nofile 64000
-
-mongodb hard nofile 64000
diff --git a/Others/configs/db/vm_mongod.conf b/Others/configs/db/vm_mongod.conf
deleted file mode 100644
index 924c16a..0000000
--- a/Others/configs/db/vm_mongod.conf
+++ /dev/null
@@ -1,72 +0,0 @@
-#
-# /etc/sysctl.conf - Configuration file for setting system variables
-# See /etc/sysctl.d/ for additional system variables.
-# See sysctl.conf (5) for information.
-#
-
-#kernel.domainname = example.com
-
-# Uncomment the following to stop low-level messages on console
-#kernel.printk = 3 4 1 3
-
-##############################################################3
-# Functions previously found in netbase
-#
-
-# Uncomment the next two lines to enable Spoof protection (reverse-path filter)
-# Turn on Source Address Verification in all interfaces to
-# prevent some spoofing attacks
-#net.ipv4.conf.default.rp_filter=1
-#net.ipv4.conf.all.rp_filter=1
-
-# Uncomment the next line to enable TCP/IP SYN cookies
-# See http://lwn.net/Articles/277146/
-# Note: This may impact IPv6 TCP sessions too
-#net.ipv4.tcp_syncookies=1
-
-# Uncomment the next line to enable packet forwarding for IPv4
-#net.ipv4.ip_forward=1
-
-# Uncomment the next line to enable packet forwarding for IPv6
-# Enabling this option disables Stateless Address Autoconfiguration
-# based on Router Advertisements for this host
-#net.ipv6.conf.all.forwarding=1
-
-
-###################################################################
-# Additional settings - these settings can improve the network
-# security of the host and prevent against some network attacks
-# including spoofing attacks and man in the middle attacks through
-# redirection. Some network environments, however, require that these
-# settings are disabled so review and enable them as needed.
-#
-# Do not accept ICMP redirects (prevent MITM attacks)
-#net.ipv4.conf.all.accept_redirects = 0
-#net.ipv6.conf.all.accept_redirects = 0
-# _or_
-# Accept ICMP redirects only for gateways listed in our default
-# gateway list (enabled by default)
-# net.ipv4.conf.all.secure_redirects = 1
-#
-# Do not send ICMP redirects (we are not a router)
-#net.ipv4.conf.all.send_redirects = 0
-#
-# Do not accept IP source route packets (we are not a router)
-#net.ipv4.conf.all.accept_source_route = 0
-#net.ipv6.conf.all.accept_source_route = 0
-#
-# Log Martian Packets
-#net.ipv4.conf.all.log_martians = 1
-#
-fs.inotify.max_user_watches=524288
-
-vm.dirty_ratio=15
-vm.dirty_background_ratio=5
-vm.swappiness=1
-
-net.core.somaxconn=4096
-net.ipv4.tcp_fin_timeout=30
-net.ipv4.tcp_keepalive_intvl=30
-net.ipv4.tcp_keepalive_time=120
-net.ipv4.tcp_max_syn_backlog=4096
-
diff --git a/Others/configs/nginx.conf b/Others/configs/nginx.conf
deleted file mode 100644
index 0d773ee..0000000
--- a/Others/configs/nginx.conf
+++ /dev/null
@@ -1,74 +0,0 @@
-user www-data;
-worker_processes auto;
-pid /run/nginx.pid;
-include /etc/nginx/modules-enabled/*.conf;
-
-events {
- worker_connections 768;
- # multi_accept on;
-}
-
-http {
-
- ##
- # Basic Settings
- ##
-
- sendfile on;
- tcp_nopush on;
- tcp_nodelay on;
- keepalive_timeout 65;
- types_hash_max_size 2048;
- # server_tokens off;
-
- # server_names_hash_bucket_size 64;
- # server_name_in_redirect off;
-
- # set client body max size
- client_max_body_size 150M;
-
- # Custome timeout to avoid error “504: Gateway Timeout” in the upstream API
- proxy_read_timeout 180;
- proxy_connect_timeout 120;
- proxy_send_timeout 120;
-
- include /etc/nginx/mime.types;
- default_type application/octet-stream;
-
- ##
- # SSL Settings
- ##
-
- ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
- ssl_prefer_server_ciphers on;
-
- ##
- # Logging Settings
- ##
-
- access_log /var/log/nginx/access.log;
- error_log /var/log/nginx/error.log;
-
- ##
- # Gzip Settings
- ##
-
- gzip on;
- gzip_disable "msie6";
-
- # gzip_vary on;
- # gzip_proxied any;
- # gzip_comp_level 6;
- # gzip_buffers 16 8k;
- # gzip_http_version 1.1;
- # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
- gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
-
- ##
- # Virtual Host Configs
- ##
-
- include /etc/nginx/conf.d/*.conf;
- include /etc/nginx/sites-enabled/*;
-}
-
diff --git a/Others/configs/nginx.service b/Others/configs/nginx.service
deleted file mode 100644
index 8c4d521..0000000
--- a/Others/configs/nginx.service
+++ /dev/null
@@ -1,34 +0,0 @@
-# NgInx service config file. In Ubuntu 22 LTS path: /lib/systemd/system/nginx.service
-# Stop dance for nginx
-# =======================
-#
-# ExecStop sends SIGSTOP (graceful stop) to the nginx process.
-# If, after 5s (--retry QUIT/5) nginx is still running, systemd takes control
-# and sends SIGTERM (fast shutdown) to the main process.
-# After another 5s (TimeoutStopSec=5), and if nginx is alive, systemd sends
-# SIGKILL to all the remaining processes in the process group (KillMode=mixed).
-#
-# nginx signals reference doc:
-# http://nginx.org/en/docs/control.html
-#
-[Unit]
-Description=A high performance web server and a reverse proxy server
-Documentation=man:nginx(8)
-After=network.target nss-lookup.target
-#Ref: https://systemd.io/NETWORK_ONLINE/
-Wants=network-online.target
-
-[Service]
-Type=forking
-PIDFile=/run/nginx.pid
-ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
-ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
-ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
-ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
-TimeoutStopSec=5
-KillMode=mixed
-
-[Install]
-WantedBy=multi-user.target
-
-
diff --git a/Others/configs/ssl/Making SSL certs - Mongo.txt b/Others/configs/ssl/Making SSL certs - Mongo.txt
deleted file mode 100644
index e447485..0000000
--- a/Others/configs/ssl/Making SSL certs - Mongo.txt
+++ /dev/null
@@ -1,81 +0,0 @@
-I - Making Mongo SSL certs for Mongo DB Server replica set
-# Check openssl version
-openssl version
-# openssl options
-openssl genrsa: Generates a private key
-openssl req: Generates a CSR
-openssl x509: Generates the certificate
-
-1. Create Private Key for the root CA issuer
-openssl genrsa -passout file:./rootCA/pphrase -out ./rootCA/rootCA.key -aes256
-
-2. Create Root CA certificate
-openssl req -x509 -new -key ./rootCA/rootCA.key -days 7300 -config ./root-ssl-config.cnf -out ./rootCA/rootCA.crt
-# View the certificate
-openssl x509 -noout -text -in ./rootCA/rootCA.crt
-
-3. Create CSR for each of the member servers/hosts
-SUBJECT="/C=CA/ST=ON/L=Barrie/O=AG-NAV Inc./OU=SD/CN=localhost/emailAddress=software@agnav.com"
-openssl req -new -nodes -newkey rsa:2048 -subj "/C=CA/ST=ON/L=Barrie/O=AG-NAV Inc./OU=SD/CN=localhost/emailAddress=software@agnav.com" -keyout server1.key -out server1.csr
-
-or using the shell script:
-./makeCSR
-
-4. Sign the CSR then create certificate for the member
-openssl x509 -req -days 7300 -in server1.csr -CA ./rootCA/rootCA.crt -CAkey ./rootCA/rootCA.key -CAcreateserial -out ./server1.crt -sha256 -extfile v3-ext.cnf
-
-5. Create a privacy enhanced mail (PEM) for mongod
-cat server1.key server1.crt > server1.pem
-
-or using the shell script (for step 4 and 5):
-./makeCert
-
-6. Deploy (root)
-Move .crt/pem/csr file to /etc/ssl/certs/
-Move .key (private key) file to /etc/ssl/private
-Change permission for all to readonly 440
-
-# (Optional) Create appcerts usergroup, then add users: root,www-data,mongodb,rabbitmq to the group
-
-
-Reference:
- https://www.bustedware.com/blog/mongodb-ssl-tls-x509-authentication#create-certificate-authority
- https://www.mydbops.com/blog/securing-mongodb-cluster-with-tls-ssl
- https://www.ibm.com/docs/en/hpvs/1.2.x?topic=SSHPMH_1.2.x/topics/create_ca_signed_certificates.htm
- https://www.mydbops.com/blog/securing-mongodb-cluster-with-tls-ssl#
- https://www.filecloud.com/supportdocs/fcdoc/latest/server/filecloud-administrator-guide/filecloud-site-setup/filecloud-high-availability/configure-mongodb-cluster-to-use-tls-ssl-with-cluster-authentication-and-mongodb-authentication-on-linux
-
-
-II - Replace/Renew/Rotate SSL x509 certs for a replica set
-1. Make CSRs
-./makeCSR.sh agndb0.agnav.com
-./makeCSR.sh agndb1.agnav.com
-./makeCSR.sh agndb2.agnav.com
-
-2. Make Certs
-./makeCert.sh agndb0.agnav.com
-./makeCert.sh agndb1.agnav.com
-./makeCert.sh agndb2.agnav.com
-
-3. Copy them to each nodes to deploy
-scp ./rootCA/rootCA.crt agnav@kanboard.agnav.com:~/agnav_rootCA.crt
-scp agndb2* agnav@kanboard.agnav.com:~/
-scp -P 22222 agndb1* agm@agmission-1.agnav.com:~/
-scp -P 22889 agndb0* agmission@agmission.agnav.com:~/
-
-## Check after copying
-ssh agnav@kanboard.agnav.com 'ls ~/'
-ssh -p 22222 agm@agmission-1.agnav.com 'ls ~/'
-ssh -p 22889 agmission@agmission.agnav.com 'ls ~/'
-
-3.2 Copy them to the deploy storage location
-
-4. Restart each of the member from secondaries to the primary one
-
-5. Verify they all work.
-mongo -u admin -p 'Minad!2019' --authenticationDatabase 'admin'
-rs.status()
-tail -n 500 /var/log/mongodb/mongod.log
-
-Referece:
- https://www.mongodb.com/docs/manual/tutorial/rotate-x509-membership-certificates/
\ No newline at end of file
diff --git a/Others/configs/ssl/agm.crt b/Others/configs/ssl/agm.crt
deleted file mode 100644
index b1c3fb2..0000000
--- a/Others/configs/ssl/agm.crt
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEqjCCA5KgAwIBAgIUckN8Se/RK/askfiTUwBPzwNzeg8wDQYJKoZIhvcNAQEL
-BQAwgYUxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UEBwwGQmFycmll
-MRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxEjAQBgNVBAMMCWFn
-bmF2LmNvbTEhMB8GCSqGSIb3DQEJARYSc29mdHdhcmVAYWduYXYuY29tMB4XDTI1
-MDExNDIwMTgxM1oXDTQ1MDEwOTIwMTgxM1owgYoxCzAJBgNVBAYTAkNBMQswCQYD
-VQQIDAJPTjEPMA0GA1UEBwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjEW
-MBQGA1UECwwNTU9OR09fQ0xJRU5UUzEMMAoGA1UEAwwDYWdtMSEwHwYJKoZIhvcN
-AQkBFhJzb2Z0d2FyZUBhZ25hdi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
-ggEKAoIBAQDiFF/zeTPt6N1/VafvOXe/QvdagaCYGCKBq4YJIHHT552bBXAoYPLo
-bXWSJOZt4y3rYIzG3YcCuLZDAzUWZIPczl0PkXTwkPFBtiQQHRIQigTcNZXzPpuJ
-JXrSAHo9WGlkn/22TxYSspA0fV00AhLXvAk6mXtz+6klVIcJ5ujACgq8JDpmfklB
-5c89TvKB0Z9/qoAWTm+9lVvR7+VQ4i0BFooKCOKpEaLse3lb6hHhSxX4da0ICb7R
-6G7Xchne30SDitvRgWhiCuPc9WQDVXVsx3UNGi5ppNb79w/iEwMJ4/qxk5Ob/pjb
-xtibvVi2OpFHYiV2FJiOSzBeDD3y3zbjAgMBAAGjggEJMIIBBTCBrwYDVR0jBIGn
-MIGkoYGLpIGIMIGFMQswCQYDVQQGEwJDQTELMAkGA1UECAwCT04xDzANBgNVBAcM
-BkJhcnJpZTEUMBIGA1UECgwLQUctTkFWIEluYy4xCzAJBgNVBAsMAlNEMRIwEAYD
-VQQDDAlhZ25hdi5jb20xITAfBgkqhkiG9w0BCQEWEnNvZnR3YXJlQGFnbmF2LmNv
-bYIUUhbg9g1ttEbMvdhTD6/JouV4tcAwCQYDVR0TBAIwADALBgNVHQ8EBAMCBPAw
-GgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMB0GA1UdDgQWBBQKirMifYvhfsET
-Ex6v2AsXC0yjfTANBgkqhkiG9w0BAQsFAAOCAQEAFCg+UqerRIyt5FWP7ZhIp2xi
-5E1LOro127AlMDk7oT/FuALpQCzP4qzzePTp3qamJxuteSph3IGAW2ltg8h9ocM+
-NNK4fVKtxg5XpYHOuxmRwr0/pmWXDogs8C/+sSbx2QES6BlFEZRih4mY0ND+3b0j
-lmc+wP4SNlUNDJD7Fhgwz9IiUlo7ok4Mr0TYRn26Yql4zazKFV4ShGdPCBUpPQ39
-Phl3pLGzyWw8DUcoeG/D7cwyFXyscNK79dcQt11gqJMRdO4sTymAPzlfTlzfcW8x
-xjizOmFWkI4DYGJgmyq5x4L5bUVeq4/WMWsHpYDFcUd3b8safG7RgPRSg2OgXQ==
------END CERTIFICATE-----
diff --git a/Others/configs/ssl/agm.csr b/Others/configs/ssl/agm.csr
deleted file mode 100644
index 9410c18..0000000
--- a/Others/configs/ssl/agm.csr
+++ /dev/null
@@ -1,18 +0,0 @@
------BEGIN CERTIFICATE REQUEST-----
-MIIC0DCCAbgCAQAwgYoxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UE
-BwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjEWMBQGA1UECwwNTU9OR09f
-Q0xJRU5UUzEMMAoGA1UEAwwDYWdtMSEwHwYJKoZIhvcNAQkBFhJzb2Z0d2FyZUBh
-Z25hdi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDiFF/zeTPt
-6N1/VafvOXe/QvdagaCYGCKBq4YJIHHT552bBXAoYPLobXWSJOZt4y3rYIzG3YcC
-uLZDAzUWZIPczl0PkXTwkPFBtiQQHRIQigTcNZXzPpuJJXrSAHo9WGlkn/22TxYS
-spA0fV00AhLXvAk6mXtz+6klVIcJ5ujACgq8JDpmfklB5c89TvKB0Z9/qoAWTm+9
-lVvR7+VQ4i0BFooKCOKpEaLse3lb6hHhSxX4da0ICb7R6G7Xchne30SDitvRgWhi
-CuPc9WQDVXVsx3UNGi5ppNb79w/iEwMJ4/qxk5Ob/pjbxtibvVi2OpFHYiV2FJiO
-SzBeDD3y3zbjAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAdkaqqYUV+6RZ8oVq
-XrmE8JWlu1IRRnqXiMRQ8TW1/zjIjaGfHkflDYP0rnOYB7ifnv31sRlLyizTD4/6
-MIGlnmF28aK2CVf0TNzNeQosjs3FWirw0q8PLdsCFJetlM6LOwA+0RmN1mq5y/Vk
-r1ntpoK3LOFOacaWcDC92O0sNzD2gQI3abCpoELhiPDhIAhNJW3UdEy9H0UqGg6o
-Book8NHhxKZUAwstiOAVYdIg5RaDMJAPgD7czuVDuryP42efDSSqGtv9rBC/3N3u
-bhxKv7jkBGLqKwdSBF13NJZlIv5t+krQ6vVoquJlAebGDLxNCQTW8KW+UrlevlAF
-GvY58w==
------END CERTIFICATE REQUEST-----
diff --git a/Others/configs/ssl/agm.key b/Others/configs/ssl/agm.key
deleted file mode 100644
index 6878851..0000000
--- a/Others/configs/ssl/agm.key
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDiFF/zeTPt6N1/
-VafvOXe/QvdagaCYGCKBq4YJIHHT552bBXAoYPLobXWSJOZt4y3rYIzG3YcCuLZD
-AzUWZIPczl0PkXTwkPFBtiQQHRIQigTcNZXzPpuJJXrSAHo9WGlkn/22TxYSspA0
-fV00AhLXvAk6mXtz+6klVIcJ5ujACgq8JDpmfklB5c89TvKB0Z9/qoAWTm+9lVvR
-7+VQ4i0BFooKCOKpEaLse3lb6hHhSxX4da0ICb7R6G7Xchne30SDitvRgWhiCuPc
-9WQDVXVsx3UNGi5ppNb79w/iEwMJ4/qxk5Ob/pjbxtibvVi2OpFHYiV2FJiOSzBe
-DD3y3zbjAgMBAAECggEAFY6zeYq982UdiHtOnfE2ZGlKdHm2KlqUIRY4cTKuopyh
-etCMGdA+HqtKpzVX4vQ4guBm0WhP6+gDZSTEpqPOA7uKbyYZEalYf+y8x+vltuha
-P0noHaouIufigWzPOyiXis32CjDEs41i/RupIVZDSAlOPfq4UAkYCg8N01lj1I/S
-niaEXOH90xdWSJlgTdn9aNmVLSyp/4t1jDvH4Y7MxHey6fNSd7nDNd4/jK97aH37
-Gg/4Qf8itIEBNU5wWjiq4oDwg2DJeBxlh8xz+mD6NpZvy/AF3ByTfGL6vXovY7QS
-3Q4asdmW+CfZJ+M9/AG8zghBRXN9WM9Pbcw1yTKrpQKBgQD+dAma5LlE5EvfyFk3
-8hal06xzSuiT3YFaY9CNIS4ytzCKlddv0TQxdOw0i94hDNjPb1WX9J+6hVGjAa66
-nOLzza+7iMPu9td2E4wYQ9Y8Vrxnh9Mr3zlnlJbX3U28+FyynFs5MbTifG5uUTec
-3CSyJkALZBSUJ7O97Hzbxy5MDwKBgQDjdC8jr6LrIKUYxe2rWptKnM0kmTxCGlbw
-eenyuLRhfzyhipB1K2JYOhl4Gqn3zwejekk7kHOwphjI2+3lAp/3y251LAuRSsn/
-mibIsVhgi/2BO5MCWBGkRzEr74M47FHVIqa7MO50oGDxmOE+AmuluAwqgm7ITIm2
-vgQ069dj7QKBgDQRxMFomq0JVql1kyRKqu3GMhzpsExJ4KWBlXS73HtOV2WUoiBk
-nByew5NBJ/R1b4yLSOWujl0Z2QnVV08iuaKQbayfoRCufIrSFzID97wjN6yr87+f
-j1yt4GxOAhFwdW+rZVN/43cRZXu3rPyxY+T8xNBP65IhybtMwIQs70FrAoGBAJUK
-zNjVfiwUeBqDl/lwpdF+be5Neu7V06JAQMyLu6cneNNhuMcOZqLpb0cEMdvwDVFS
-ECq3vRdDv3neo0QtNCVraDXfZrUODM8wc7mOfBrHoJXOM8aVbvn2rIHdsF7ce8Lt
-sdN3fMlvThcB1paLf35X26D/VxhpDtRwLtF+uOUBAoGAUN7Cq2YUiPXp44vuIuH3
-dFbRkV6FPU1ixYniIpA8UGA6GfDKjsFio29kjnsHCcW0UhnpRRph7vMLUXDh/8q/
-bY13rLHJVuSp4pEIw4rgF/EINhi8wHNoIG0yFDIcLCgi0IeJRJXhz4IK9UT+ueg3
-CqXJXBDslc04zKpOiOqjdSQ=
------END PRIVATE KEY-----
diff --git a/Others/configs/ssl/agm.pem b/Others/configs/ssl/agm.pem
deleted file mode 100644
index 8e9a24f..0000000
--- a/Others/configs/ssl/agm.pem
+++ /dev/null
@@ -1,55 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDiFF/zeTPt6N1/
-VafvOXe/QvdagaCYGCKBq4YJIHHT552bBXAoYPLobXWSJOZt4y3rYIzG3YcCuLZD
-AzUWZIPczl0PkXTwkPFBtiQQHRIQigTcNZXzPpuJJXrSAHo9WGlkn/22TxYSspA0
-fV00AhLXvAk6mXtz+6klVIcJ5ujACgq8JDpmfklB5c89TvKB0Z9/qoAWTm+9lVvR
-7+VQ4i0BFooKCOKpEaLse3lb6hHhSxX4da0ICb7R6G7Xchne30SDitvRgWhiCuPc
-9WQDVXVsx3UNGi5ppNb79w/iEwMJ4/qxk5Ob/pjbxtibvVi2OpFHYiV2FJiOSzBe
-DD3y3zbjAgMBAAECggEAFY6zeYq982UdiHtOnfE2ZGlKdHm2KlqUIRY4cTKuopyh
-etCMGdA+HqtKpzVX4vQ4guBm0WhP6+gDZSTEpqPOA7uKbyYZEalYf+y8x+vltuha
-P0noHaouIufigWzPOyiXis32CjDEs41i/RupIVZDSAlOPfq4UAkYCg8N01lj1I/S
-niaEXOH90xdWSJlgTdn9aNmVLSyp/4t1jDvH4Y7MxHey6fNSd7nDNd4/jK97aH37
-Gg/4Qf8itIEBNU5wWjiq4oDwg2DJeBxlh8xz+mD6NpZvy/AF3ByTfGL6vXovY7QS
-3Q4asdmW+CfZJ+M9/AG8zghBRXN9WM9Pbcw1yTKrpQKBgQD+dAma5LlE5EvfyFk3
-8hal06xzSuiT3YFaY9CNIS4ytzCKlddv0TQxdOw0i94hDNjPb1WX9J+6hVGjAa66
-nOLzza+7iMPu9td2E4wYQ9Y8Vrxnh9Mr3zlnlJbX3U28+FyynFs5MbTifG5uUTec
-3CSyJkALZBSUJ7O97Hzbxy5MDwKBgQDjdC8jr6LrIKUYxe2rWptKnM0kmTxCGlbw
-eenyuLRhfzyhipB1K2JYOhl4Gqn3zwejekk7kHOwphjI2+3lAp/3y251LAuRSsn/
-mibIsVhgi/2BO5MCWBGkRzEr74M47FHVIqa7MO50oGDxmOE+AmuluAwqgm7ITIm2
-vgQ069dj7QKBgDQRxMFomq0JVql1kyRKqu3GMhzpsExJ4KWBlXS73HtOV2WUoiBk
-nByew5NBJ/R1b4yLSOWujl0Z2QnVV08iuaKQbayfoRCufIrSFzID97wjN6yr87+f
-j1yt4GxOAhFwdW+rZVN/43cRZXu3rPyxY+T8xNBP65IhybtMwIQs70FrAoGBAJUK
-zNjVfiwUeBqDl/lwpdF+be5Neu7V06JAQMyLu6cneNNhuMcOZqLpb0cEMdvwDVFS
-ECq3vRdDv3neo0QtNCVraDXfZrUODM8wc7mOfBrHoJXOM8aVbvn2rIHdsF7ce8Lt
-sdN3fMlvThcB1paLf35X26D/VxhpDtRwLtF+uOUBAoGAUN7Cq2YUiPXp44vuIuH3
-dFbRkV6FPU1ixYniIpA8UGA6GfDKjsFio29kjnsHCcW0UhnpRRph7vMLUXDh/8q/
-bY13rLHJVuSp4pEIw4rgF/EINhi8wHNoIG0yFDIcLCgi0IeJRJXhz4IK9UT+ueg3
-CqXJXBDslc04zKpOiOqjdSQ=
------END PRIVATE KEY-----
------BEGIN CERTIFICATE-----
-MIIEqjCCA5KgAwIBAgIUckN8Se/RK/askfiTUwBPzwNzeg8wDQYJKoZIhvcNAQEL
-BQAwgYUxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UEBwwGQmFycmll
-MRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxEjAQBgNVBAMMCWFn
-bmF2LmNvbTEhMB8GCSqGSIb3DQEJARYSc29mdHdhcmVAYWduYXYuY29tMB4XDTI1
-MDExNDIwMTgxM1oXDTQ1MDEwOTIwMTgxM1owgYoxCzAJBgNVBAYTAkNBMQswCQYD
-VQQIDAJPTjEPMA0GA1UEBwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjEW
-MBQGA1UECwwNTU9OR09fQ0xJRU5UUzEMMAoGA1UEAwwDYWdtMSEwHwYJKoZIhvcN
-AQkBFhJzb2Z0d2FyZUBhZ25hdi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
-ggEKAoIBAQDiFF/zeTPt6N1/VafvOXe/QvdagaCYGCKBq4YJIHHT552bBXAoYPLo
-bXWSJOZt4y3rYIzG3YcCuLZDAzUWZIPczl0PkXTwkPFBtiQQHRIQigTcNZXzPpuJ
-JXrSAHo9WGlkn/22TxYSspA0fV00AhLXvAk6mXtz+6klVIcJ5ujACgq8JDpmfklB
-5c89TvKB0Z9/qoAWTm+9lVvR7+VQ4i0BFooKCOKpEaLse3lb6hHhSxX4da0ICb7R
-6G7Xchne30SDitvRgWhiCuPc9WQDVXVsx3UNGi5ppNb79w/iEwMJ4/qxk5Ob/pjb
-xtibvVi2OpFHYiV2FJiOSzBeDD3y3zbjAgMBAAGjggEJMIIBBTCBrwYDVR0jBIGn
-MIGkoYGLpIGIMIGFMQswCQYDVQQGEwJDQTELMAkGA1UECAwCT04xDzANBgNVBAcM
-BkJhcnJpZTEUMBIGA1UECgwLQUctTkFWIEluYy4xCzAJBgNVBAsMAlNEMRIwEAYD
-VQQDDAlhZ25hdi5jb20xITAfBgkqhkiG9w0BCQEWEnNvZnR3YXJlQGFnbmF2LmNv
-bYIUUhbg9g1ttEbMvdhTD6/JouV4tcAwCQYDVR0TBAIwADALBgNVHQ8EBAMCBPAw
-GgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMB0GA1UdDgQWBBQKirMifYvhfsET
-Ex6v2AsXC0yjfTANBgkqhkiG9w0BAQsFAAOCAQEAFCg+UqerRIyt5FWP7ZhIp2xi
-5E1LOro127AlMDk7oT/FuALpQCzP4qzzePTp3qamJxuteSph3IGAW2ltg8h9ocM+
-NNK4fVKtxg5XpYHOuxmRwr0/pmWXDogs8C/+sSbx2QES6BlFEZRih4mY0ND+3b0j
-lmc+wP4SNlUNDJD7Fhgwz9IiUlo7ok4Mr0TYRn26Yql4zazKFV4ShGdPCBUpPQ39
-Phl3pLGzyWw8DUcoeG/D7cwyFXyscNK79dcQt11gqJMRdO4sTymAPzlfTlzfcW8x
-xjizOmFWkI4DYGJgmyq5x4L5bUVeq4/WMWsHpYDFcUd3b8safG7RgPRSg2OgXQ==
------END CERTIFICATE-----
diff --git a/Others/configs/ssl/agnav_rootCA.crt b/Others/configs/ssl/agnav_rootCA.crt
deleted file mode 100644
index 07f3c43..0000000
--- a/Others/configs/ssl/agnav_rootCA.crt
+++ /dev/null
@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDkzCCAnsCFFIW4PYNbbRGzL3YUw+vyaLleLXAMA0GCSqGSIb3DQEBCwUAMIGF
-MQswCQYDVQQGEwJDQTELMAkGA1UECAwCT04xDzANBgNVBAcMBkJhcnJpZTEUMBIG
-A1UECgwLQUctTkFWIEluYy4xCzAJBgNVBAsMAlNEMRIwEAYDVQQDDAlhZ25hdi5j
-b20xITAfBgkqhkiG9w0BCQEWEnNvZnR3YXJlQGFnbmF2LmNvbTAeFw0yNTAxMTAx
-NzE3NDBaFw00NTAxMDUxNzE3NDBaMIGFMQswCQYDVQQGEwJDQTELMAkGA1UECAwC
-T04xDzANBgNVBAcMBkJhcnJpZTEUMBIGA1UECgwLQUctTkFWIEluYy4xCzAJBgNV
-BAsMAlNEMRIwEAYDVQQDDAlhZ25hdi5jb20xITAfBgkqhkiG9w0BCQEWEnNvZnR3
-YXJlQGFnbmF2LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPO6
-zGIpSaBKYliTuGBvMWxXzrCvUUlqYAB2vJeBJf8prbfL2rRM5ac1vXx3HOFQOXF5
-dwJz+XaN45xkfYLNPta6kGTn0RDaFUpoE70SIsEP7Q1D3FhXMAocXhfSy6sZeZB/
-XKJ0dCjuVDOxjle6ksQdiKrJFzVF1xWwWijGD66jzDIcQJTCJqxyMblt/YQaI+q8
-uL2/wjiNRYALtSpd77HOQ075/Gd2sN8ppDCkpcg1F6Sx1Z9WcQx9pB/pI8wuuvdg
-xJsO1xpLsvltFMBcFTfIZYJckNPL/wEFL6XSuFBZ6S8LLTeNL8vk2JfZXai3rsXb
-LyTrFvVOavtcltcI5UECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA374T4MtxTCU/
-9VR7+aRjXb5QnTTBtigCGjo5la8eIQZiFnk8FJJZEIYI4obphTleAKAfypDlQ+Wj
-JY/4bOWvnjZIHi7XOnGLx6n9fSv4pC2PzMHThwHvY+vPGrHtoYT1xtfCRzaAMdvS
-kXbknniLS+QarurADQA5MT4v+gGuK4dq+LvgPWOhmr3/VTmdfFJQCb0J/JD8CcEu
-MSABLSIwmcu0tDd7647SkTQKBM5vdQVL15eBCMwf/sIYKokqy+It9xRzdFsEYviz
-sK2CtuheeEhJBU98ACAS2gK5CZKNTrSFNk5uTlVfUKMdF6FMmQ/vSPX+xfRpkS3R
-AWeVHLZ4OQ==
------END CERTIFICATE-----
diff --git a/Others/configs/ssl/agndb0.agnav.com.crt b/Others/configs/ssl/agndb0.agnav.com.crt
deleted file mode 100644
index ec0d192..0000000
--- a/Others/configs/ssl/agndb0.agnav.com.crt
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEqDCCA5CgAwIBAgIUckN8Se/RK/askfiTUwBPzwNzehAwDQYJKoZIhvcNAQEL
-BQAwgYUxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UEBwwGQmFycmll
-MRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxEjAQBgNVBAMMCWFn
-bmF2LmNvbTEhMB8GCSqGSIb3DQEJARYSc29mdHdhcmVAYWduYXYuY29tMB4XDTI1
-MDEyNzIwMjk0M1oXDTQ1MDEyMjIwMjk0M1owgYwxCzAJBgNVBAYTAkNBMQswCQYD
-VQQIDAJPTjEPMA0GA1UEBwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjEL
-MAkGA1UECwwCU0QxGTAXBgNVBAMMEGFnbmRiMC5hZ25hdi5jb20xITAfBgkqhkiG
-9w0BCQEWEnNvZnR3YXJlQGFnbmF2LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBAJlmik4GVXoS5c+695H7EWe9p2z5WQIOOMFla3Lytt+vapuHVPJy
-rZ5hFW3lq9vW5BFzTJHpThT0Ff89O2FvfEP04RATIKqUkK8I/RnwyOAoDKB4PUdX
-qGsf+1qMvav3c5Ma3fRGWPBTe+rme+NeclaNXzqTUApCGc8mYHDugC3s0kZtHl+Z
-MKZ+xONdhRDCa18Ko0Kfs6obMPK8fqYdrRYd53uPXCAnNXoQAXNT67QL/uNeMJxC
-kT/tV58r4eH0n1qU9pXMR4UmMHPs8HG47Sdk67LUhY6sJASTXQB3fKdTBt72oLsS
-gUZ5BpSUQaXk0mtiuL5rBYEn7HhRr1JhYAsCAwEAAaOCAQUwggEBMIGvBgNVHSME
-gacwgaShgYukgYgwgYUxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UE
-BwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxEjAQ
-BgNVBAMMCWFnbmF2LmNvbTEhMB8GCSqGSIb3DQEJARYSc29mdHdhcmVAYWduYXYu
-Y29tghRSFuD2DW20Rsy92FMPr8mi5Xi1wDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE
-8DAWBgNVHREEDzANggsqLmFnbmF2LmNvbTAdBgNVHQ4EFgQUTZxBYBMPCDnCf6zx
-eK9cPLAqYUgwDQYJKoZIhvcNAQELBQADggEBADeFFlZiKeAkKsqeCyBYa70iv8lB
-54bzIIjdvQUctDkfjkmZo5rwsGvtwD8peNFegKy7BiXzDZYJ0ZlAAEZ5bUazjnYS
-SHEqid8WVeCnsgp9TWi8uMX4BaVTJr2LvSsYF7SrlCTx+aRPLes2CP3FPXSFo81K
-zs9p9mPAHPbtByHb/CIkwyh2HX4Ap0SDYGAOVf9mC7sDWfDWTiIWSAl4YbAVily9
-E1JqwPMH8aHNBOTpXZPYto4TIrF4vVIeXP/EVrKVfbeZMNwNFfrRqWcAPMAXX3hE
-XTFzhFeepdXExckC6ulNmfLaitxLp9L0J8NqCzrLC2WIoau5CXqGR1Ux7FY=
------END CERTIFICATE-----
diff --git a/Others/configs/ssl/agndb0.agnav.com.csr b/Others/configs/ssl/agndb0.agnav.com.csr
deleted file mode 100644
index 054f275..0000000
--- a/Others/configs/ssl/agndb0.agnav.com.csr
+++ /dev/null
@@ -1,18 +0,0 @@
------BEGIN CERTIFICATE REQUEST-----
-MIIC0jCCAboCAQAwgYwxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UE
-BwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxGTAX
-BgNVBAMMEGFnbmRiMC5hZ25hdi5jb20xITAfBgkqhkiG9w0BCQEWEnNvZnR3YXJl
-QGFnbmF2LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJlmik4G
-VXoS5c+695H7EWe9p2z5WQIOOMFla3Lytt+vapuHVPJyrZ5hFW3lq9vW5BFzTJHp
-ThT0Ff89O2FvfEP04RATIKqUkK8I/RnwyOAoDKB4PUdXqGsf+1qMvav3c5Ma3fRG
-WPBTe+rme+NeclaNXzqTUApCGc8mYHDugC3s0kZtHl+ZMKZ+xONdhRDCa18Ko0Kf
-s6obMPK8fqYdrRYd53uPXCAnNXoQAXNT67QL/uNeMJxCkT/tV58r4eH0n1qU9pXM
-R4UmMHPs8HG47Sdk67LUhY6sJASTXQB3fKdTBt72oLsSgUZ5BpSUQaXk0mtiuL5r
-BYEn7HhRr1JhYAsCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQAMX9rqU5S4AxBR
-MiAEXzaJbI4rI8jf9GthZQT++EQRWN07CcRvw29xtOe22m5CSwkZw/1aBYGuxxIi
-+Cgc1YB6a3rFE2KuWciqj4+IAG56+oHDxW1/xkd+9f/HaN2NCuf2/AuSklBkadmB
-j6lYOUYcKJuj3y9CHneqAzMzB9SzUKt/Q8u5ywxwXXMxiAn2va3gUbylIzMnQ6tr
-aZtGmEKpyS/G1IFd0V49bxtlomZ9ygda/YbOnpHGdzqrwq4mnlqZuGmtAg5CGIzA
-scQb5qI0/e03u4NJeDdcpc20B2H+U+7B2jXXXj47Bk/iGm4RvYHS1exkYycUxSPu
-6kJMh+nH
------END CERTIFICATE REQUEST-----
diff --git a/Others/configs/ssl/agndb0.agnav.com.key b/Others/configs/ssl/agndb0.agnav.com.key
deleted file mode 100644
index 931f485..0000000
--- a/Others/configs/ssl/agndb0.agnav.com.key
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCZZopOBlV6EuXP
-uveR+xFnvads+VkCDjjBZWty8rbfr2qbh1Tycq2eYRVt5avb1uQRc0yR6U4U9BX/
-PTthb3xD9OEQEyCqlJCvCP0Z8MjgKAygeD1HV6hrH/tajL2r93OTGt30RljwU3vq
-5nvjXnJWjV86k1AKQhnPJmBw7oAt7NJGbR5fmTCmfsTjXYUQwmtfCqNCn7OqGzDy
-vH6mHa0WHed7j1wgJzV6EAFzU+u0C/7jXjCcQpE/7VefK+Hh9J9alPaVzEeFJjBz
-7PBxuO0nZOuy1IWOrCQEk10Ad3ynUwbe9qC7EoFGeQaUlEGl5NJrYri+awWBJ+x4
-Ua9SYWALAgMBAAECggEAArg5YH7zpsyzkiMmJRdQUvufGCsz8EZ9rBp9Tq+0UH/r
-W6MisILm+bIn72eNTFvH3FiKdXwvT02XUqVskMj2T4m4ycOrcxeO/x0HyINz1EGL
-HV2awutWoY7GGPAto3rC6+TW8lZvvwcD4Rsn/CGrB+AAXbX9F8l3ORLczkCN0wKW
-TEuRWZ8gPfZ4Jp84ST0B//qcy6Se9Hb+03k8VC9WJWtoE5uT/Wn4M0yEfdVENdWk
-7tjr7WRuwWz+PS5ydu9reAOJV0cHJj34NdoFdwXf4Jf5+sngI4meRhhF+8T/ezHA
-/vY3+KaY7QCfjPcncTc6BM+GV8V2sVzPbz7BuGjxdQKBgQDVlOpWaZIC2sqAtRmV
-krnHeJTKwmLPlPoZED4q88Bcb4a87F7fHnhQOVajBEaSqBWcbBRD+LhQWdr+3CmX
-teJqJFLg6oOPvOSpJY7/+QU2LltefoYPTlHjwIEm2lV/10IyqceLnuWDi4e34m2S
-fljKcemGBJgQWJ2qruFJQWAY1QKBgQC33dk6ZvOqcC1VgB+71Z2j4ZD5S6+SkGRi
-OwIJ+F7fXgvw+/nH/HhnscVVuDuKjewf3Mg+k3cCHTs0FSaLs6o2N9JBd6VWlG4z
-HSN13u0FrmZtEOjXGuJS1jRu8LUGFx0xB1/VfGDRT9/B7irprjJfLCOy5FiYTlkJ
-QqTSkEwFXwKBgHGcNg7jNzz3fxJ5wvMySkpV1OgKAJ+lAmhEoJ0ebet9k7F5Fnoe
-7ibWaURrqNKoQF6lix4g9oIfWgOJv0IpCRgm3EMx2+ugsg1bojZ9Ew2gGRApw0vv
-AFZi9xBgwWwwZ9ElSLT3P+T6WqYw9tIfDUIa1/pnBTBkwvGg9suNz/1FAoGANNNT
-Tvk3NpemHrOB6oh0ExqCeW1qUxSTErnbWxv1vf0aNzFd0TxTJ4+mn3sf+C3QUlMv
-YPMjNQNK+Cq/eVG0LIGbMd37LcXVZ3AOuRXESWaS3PEHxI1fyubqB5m2mLpZU7XH
-reFfO6PUKLaRs7OtmzRmSUZbwd54rDVuf2SfwscCgYBWb2b0LOBq87ULlI/RnId1
-DWu1tWop2DXkIAeotpL+yB6S0x9nNi7DbKR9wXOK52EkNx29ky/rJR91jI5IlxBL
-ydGuZpPL8ZP9WKQtRj16PgQv1HNN8u34MTnBHq6G1tTA0VOhgCS9eZel5EKAZluZ
-BzsnA+XhRTo2DUyO1IYpnQ==
------END PRIVATE KEY-----
diff --git a/Others/configs/ssl/agndb0.agnav.com.pem b/Others/configs/ssl/agndb0.agnav.com.pem
deleted file mode 100644
index 9729c03..0000000
--- a/Others/configs/ssl/agndb0.agnav.com.pem
+++ /dev/null
@@ -1,55 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCZZopOBlV6EuXP
-uveR+xFnvads+VkCDjjBZWty8rbfr2qbh1Tycq2eYRVt5avb1uQRc0yR6U4U9BX/
-PTthb3xD9OEQEyCqlJCvCP0Z8MjgKAygeD1HV6hrH/tajL2r93OTGt30RljwU3vq
-5nvjXnJWjV86k1AKQhnPJmBw7oAt7NJGbR5fmTCmfsTjXYUQwmtfCqNCn7OqGzDy
-vH6mHa0WHed7j1wgJzV6EAFzU+u0C/7jXjCcQpE/7VefK+Hh9J9alPaVzEeFJjBz
-7PBxuO0nZOuy1IWOrCQEk10Ad3ynUwbe9qC7EoFGeQaUlEGl5NJrYri+awWBJ+x4
-Ua9SYWALAgMBAAECggEAArg5YH7zpsyzkiMmJRdQUvufGCsz8EZ9rBp9Tq+0UH/r
-W6MisILm+bIn72eNTFvH3FiKdXwvT02XUqVskMj2T4m4ycOrcxeO/x0HyINz1EGL
-HV2awutWoY7GGPAto3rC6+TW8lZvvwcD4Rsn/CGrB+AAXbX9F8l3ORLczkCN0wKW
-TEuRWZ8gPfZ4Jp84ST0B//qcy6Se9Hb+03k8VC9WJWtoE5uT/Wn4M0yEfdVENdWk
-7tjr7WRuwWz+PS5ydu9reAOJV0cHJj34NdoFdwXf4Jf5+sngI4meRhhF+8T/ezHA
-/vY3+KaY7QCfjPcncTc6BM+GV8V2sVzPbz7BuGjxdQKBgQDVlOpWaZIC2sqAtRmV
-krnHeJTKwmLPlPoZED4q88Bcb4a87F7fHnhQOVajBEaSqBWcbBRD+LhQWdr+3CmX
-teJqJFLg6oOPvOSpJY7/+QU2LltefoYPTlHjwIEm2lV/10IyqceLnuWDi4e34m2S
-fljKcemGBJgQWJ2qruFJQWAY1QKBgQC33dk6ZvOqcC1VgB+71Z2j4ZD5S6+SkGRi
-OwIJ+F7fXgvw+/nH/HhnscVVuDuKjewf3Mg+k3cCHTs0FSaLs6o2N9JBd6VWlG4z
-HSN13u0FrmZtEOjXGuJS1jRu8LUGFx0xB1/VfGDRT9/B7irprjJfLCOy5FiYTlkJ
-QqTSkEwFXwKBgHGcNg7jNzz3fxJ5wvMySkpV1OgKAJ+lAmhEoJ0ebet9k7F5Fnoe
-7ibWaURrqNKoQF6lix4g9oIfWgOJv0IpCRgm3EMx2+ugsg1bojZ9Ew2gGRApw0vv
-AFZi9xBgwWwwZ9ElSLT3P+T6WqYw9tIfDUIa1/pnBTBkwvGg9suNz/1FAoGANNNT
-Tvk3NpemHrOB6oh0ExqCeW1qUxSTErnbWxv1vf0aNzFd0TxTJ4+mn3sf+C3QUlMv
-YPMjNQNK+Cq/eVG0LIGbMd37LcXVZ3AOuRXESWaS3PEHxI1fyubqB5m2mLpZU7XH
-reFfO6PUKLaRs7OtmzRmSUZbwd54rDVuf2SfwscCgYBWb2b0LOBq87ULlI/RnId1
-DWu1tWop2DXkIAeotpL+yB6S0x9nNi7DbKR9wXOK52EkNx29ky/rJR91jI5IlxBL
-ydGuZpPL8ZP9WKQtRj16PgQv1HNN8u34MTnBHq6G1tTA0VOhgCS9eZel5EKAZluZ
-BzsnA+XhRTo2DUyO1IYpnQ==
------END PRIVATE KEY-----
------BEGIN CERTIFICATE-----
-MIIEqDCCA5CgAwIBAgIUckN8Se/RK/askfiTUwBPzwNzehAwDQYJKoZIhvcNAQEL
-BQAwgYUxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UEBwwGQmFycmll
-MRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxEjAQBgNVBAMMCWFn
-bmF2LmNvbTEhMB8GCSqGSIb3DQEJARYSc29mdHdhcmVAYWduYXYuY29tMB4XDTI1
-MDEyNzIwMjk0M1oXDTQ1MDEyMjIwMjk0M1owgYwxCzAJBgNVBAYTAkNBMQswCQYD
-VQQIDAJPTjEPMA0GA1UEBwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjEL
-MAkGA1UECwwCU0QxGTAXBgNVBAMMEGFnbmRiMC5hZ25hdi5jb20xITAfBgkqhkiG
-9w0BCQEWEnNvZnR3YXJlQGFnbmF2LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBAJlmik4GVXoS5c+695H7EWe9p2z5WQIOOMFla3Lytt+vapuHVPJy
-rZ5hFW3lq9vW5BFzTJHpThT0Ff89O2FvfEP04RATIKqUkK8I/RnwyOAoDKB4PUdX
-qGsf+1qMvav3c5Ma3fRGWPBTe+rme+NeclaNXzqTUApCGc8mYHDugC3s0kZtHl+Z
-MKZ+xONdhRDCa18Ko0Kfs6obMPK8fqYdrRYd53uPXCAnNXoQAXNT67QL/uNeMJxC
-kT/tV58r4eH0n1qU9pXMR4UmMHPs8HG47Sdk67LUhY6sJASTXQB3fKdTBt72oLsS
-gUZ5BpSUQaXk0mtiuL5rBYEn7HhRr1JhYAsCAwEAAaOCAQUwggEBMIGvBgNVHSME
-gacwgaShgYukgYgwgYUxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UE
-BwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxEjAQ
-BgNVBAMMCWFnbmF2LmNvbTEhMB8GCSqGSIb3DQEJARYSc29mdHdhcmVAYWduYXYu
-Y29tghRSFuD2DW20Rsy92FMPr8mi5Xi1wDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE
-8DAWBgNVHREEDzANggsqLmFnbmF2LmNvbTAdBgNVHQ4EFgQUTZxBYBMPCDnCf6zx
-eK9cPLAqYUgwDQYJKoZIhvcNAQELBQADggEBADeFFlZiKeAkKsqeCyBYa70iv8lB
-54bzIIjdvQUctDkfjkmZo5rwsGvtwD8peNFegKy7BiXzDZYJ0ZlAAEZ5bUazjnYS
-SHEqid8WVeCnsgp9TWi8uMX4BaVTJr2LvSsYF7SrlCTx+aRPLes2CP3FPXSFo81K
-zs9p9mPAHPbtByHb/CIkwyh2HX4Ap0SDYGAOVf9mC7sDWfDWTiIWSAl4YbAVily9
-E1JqwPMH8aHNBOTpXZPYto4TIrF4vVIeXP/EVrKVfbeZMNwNFfrRqWcAPMAXX3hE
-XTFzhFeepdXExckC6ulNmfLaitxLp9L0J8NqCzrLC2WIoau5CXqGR1Ux7FY=
------END CERTIFICATE-----
diff --git a/Others/configs/ssl/agndb1.agnav.com.crt b/Others/configs/ssl/agndb1.agnav.com.crt
deleted file mode 100644
index e0d8b1d..0000000
--- a/Others/configs/ssl/agndb1.agnav.com.crt
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEqDCCA5CgAwIBAgIUckN8Se/RK/askfiTUwBPzwNzehEwDQYJKoZIhvcNAQEL
-BQAwgYUxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UEBwwGQmFycmll
-MRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxEjAQBgNVBAMMCWFn
-bmF2LmNvbTEhMB8GCSqGSIb3DQEJARYSc29mdHdhcmVAYWduYXYuY29tMB4XDTI1
-MDEyNzIwMzE1N1oXDTQ1MDEyMjIwMzE1N1owgYwxCzAJBgNVBAYTAkNBMQswCQYD
-VQQIDAJPTjEPMA0GA1UEBwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjEL
-MAkGA1UECwwCU0QxGTAXBgNVBAMMEGFnbmRiMS5hZ25hdi5jb20xITAfBgkqhkiG
-9w0BCQEWEnNvZnR3YXJlQGFnbmF2LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBAIzS5Y3k75MspjIKthkwfoGyOGYToCd3kTdXgk/rt4qFxOWdaTPp
-4cHlQeYg7MzXDBMU3PKi7VO2rqGyzHU9bBY/srT4G3zPoeggU+UgOLx5FPzJhJzb
-XiP/9V61JKjwzmKsCkH/0cozjJOKQQAEqXm4FRa6McFX1DR607Huc6lKJ6Rk3GP5
-MQCLEW844ZRet2kc2LXnoZ5RBkEQb6WGIiwyg/lys/qz6niQnT+ok5xTiIQ7b+lH
-yZu0vc+dE1tWg/8MWCPgIAfhIPLxmdhxMR0I3X+bGQIBefNKLTMBfRl0GkT15mgM
-rMNNOW8T5/DmijJwtoS9dV7RBxkMOWLnnPcCAwEAAaOCAQUwggEBMIGvBgNVHSME
-gacwgaShgYukgYgwgYUxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UE
-BwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxEjAQ
-BgNVBAMMCWFnbmF2LmNvbTEhMB8GCSqGSIb3DQEJARYSc29mdHdhcmVAYWduYXYu
-Y29tghRSFuD2DW20Rsy92FMPr8mi5Xi1wDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE
-8DAWBgNVHREEDzANggsqLmFnbmF2LmNvbTAdBgNVHQ4EFgQU43FtisxQn6v/AHgb
-fqXp8WcaqvQwDQYJKoZIhvcNAQELBQADggEBAID9NGobJSKUQOxbIEMXojOs5cnQ
-Rj70ZrnyxgUA+bqBfBSocN77KufvCyXgLAIfcD0di82VFyjkB8c69X2gZbzXeYJA
-pXXsEPCTDNzUDduyD9AF95fNKTnxQtQObs1E5aq2mmzE+I09rF+mnXwmehe3mkmY
-YBIlrokHiaIEQo1t760pFRLCwiRnXNd61xP/d+i3ARLeRD0bPy/PHerEN9uc9wO0
-i1HRnDO5LLWDgbgz+JUP4ImTyVphdYcgFngO1hvK5YfZ/j6uo1ynp9g0ff9UPAiR
-HSvIWuIxx+WQlS6e22+fHQcfdGVjVzW9oPoMZ9pgKPiGU0HdxwPXLuZbfc8=
------END CERTIFICATE-----
diff --git a/Others/configs/ssl/agndb1.agnav.com.csr b/Others/configs/ssl/agndb1.agnav.com.csr
deleted file mode 100644
index f68ad26..0000000
--- a/Others/configs/ssl/agndb1.agnav.com.csr
+++ /dev/null
@@ -1,18 +0,0 @@
------BEGIN CERTIFICATE REQUEST-----
-MIIC0jCCAboCAQAwgYwxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UE
-BwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxGTAX
-BgNVBAMMEGFnbmRiMS5hZ25hdi5jb20xITAfBgkqhkiG9w0BCQEWEnNvZnR3YXJl
-QGFnbmF2LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIzS5Y3k
-75MspjIKthkwfoGyOGYToCd3kTdXgk/rt4qFxOWdaTPp4cHlQeYg7MzXDBMU3PKi
-7VO2rqGyzHU9bBY/srT4G3zPoeggU+UgOLx5FPzJhJzbXiP/9V61JKjwzmKsCkH/
-0cozjJOKQQAEqXm4FRa6McFX1DR607Huc6lKJ6Rk3GP5MQCLEW844ZRet2kc2LXn
-oZ5RBkEQb6WGIiwyg/lys/qz6niQnT+ok5xTiIQ7b+lHyZu0vc+dE1tWg/8MWCPg
-IAfhIPLxmdhxMR0I3X+bGQIBefNKLTMBfRl0GkT15mgMrMNNOW8T5/DmijJwtoS9
-dV7RBxkMOWLnnPcCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQB4NQv1KAUTG067
-MxJkzRkr2+nXcx2iEvDpHlrUtXIDbqUYnd2VwqzMnWtvK4nssD158LEoKhQkyFX7
-6bOYc3DCl8AmObSlY8UBdr85iX8w/oFpoQKJ74mwsa2dTaIRY/h9zD8xnPsU3mCl
-+M1p2/9A2vNLFuiOjWfxauK2PPmYltUnMCTvVJNZhlmiYZb1D3Ot7omvFriVzP6l
-8ZCAuvZ0wWKaU96cx0xxOpAZur7vuYfW68LaBRBivgJD3/gO/E2myb76qOjulmKc
-rPo3dtc36hfMGXTbb9hn5XNG0u+2OjteKmQRcTWf07yopT8PhAZZ8YwR4PC8R30c
-1LRXvWsj
------END CERTIFICATE REQUEST-----
diff --git a/Others/configs/ssl/agndb1.agnav.com.key b/Others/configs/ssl/agndb1.agnav.com.key
deleted file mode 100644
index 225d6b2..0000000
--- a/Others/configs/ssl/agndb1.agnav.com.key
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQCM0uWN5O+TLKYy
-CrYZMH6BsjhmE6And5E3V4JP67eKhcTlnWkz6eHB5UHmIOzM1wwTFNzyou1Ttq6h
-ssx1PWwWP7K0+Bt8z6HoIFPlIDi8eRT8yYSc214j//VetSSo8M5irApB/9HKM4yT
-ikEABKl5uBUWujHBV9Q0etOx7nOpSiekZNxj+TEAixFvOOGUXrdpHNi156GeUQZB
-EG+lhiIsMoP5crP6s+p4kJ0/qJOcU4iEO2/pR8mbtL3PnRNbVoP/DFgj4CAH4SDy
-8ZnYcTEdCN1/mxkCAXnzSi0zAX0ZdBpE9eZoDKzDTTlvE+fw5ooycLaEvXVe0QcZ
-DDli55z3AgMBAAECgf9EwrVTk3VnZ2W/CXvg0Mz0hxECxpJssvU1EIWQgIgeGzMI
-6tNzhb+17TtAItN9MYOzzXwU/XjkZ07dpXAvzs34DyIzOvIw/zEPJrnQJuuNG5Ij
-3EHkVTiiW1hz5f8AfpRHmblogIDvOjxpzhfu6lfWFww1DLU+sqPq/A5h0wymm9U0
-tv7zcgKl/C9yYBfKPHzG0lS8ejIKEajFvcvXH3m6jvG+LEhzJWP9luy6nVmiZE6K
-WKnRn2+UmOWEpsYAmqfDk9MwsMaagpjoYyzh18P+rHa4Eshalgk2dLxJN05sCwqJ
-onDTRy0Bvxm+vO9iYJVDl5Elrz7XuwNPNExg5YkCgYEAxNzDPK3X95Dl9SI5Cwi/
-jj5sPAemajEd3Dqjj8kI6w+xwDg8rPHH7RvY76vpSUkQpUzhrRX2bzXXm/weUr5n
-pRAdDdrTHO78GTBa8q/TAyRpdlXd5oVCjJQyY30Yih8d5edwr8vtWlyZzXWFotNx
-5vPM0GocZBvYh946ikfEoEsCgYEAtyCfKZymbHGzyX8NEZlIk6Ljl3E1EYEjZb3s
-u9qbSdjZf43FVaGkovhn+r+y1yCW+eILhQwTZDi9b2nsv0Yk0eQ3z+GYWILOF0nJ
-rsxrs2x1gPuVQf8QDIDmvfNzAAHQIsq6gXGwukQLRA5dxeN4zPB1JBP4U8QLOYQa
-KtcnQoUCgYEAh7gjKaw4XkcZIp0Lcp3/YhOLDv+/LSrbiT8sEC7q5ROW2gxrWFgA
-G2m9b863MH0c6rlMRMYFdbpLAREZ3rXCQrwPK8QXE7V3O+5oZTPuaBYsVxbvusNY
-lA5/hrNxvZeiRyP+PlR7OHbq2gkRrqXTuwONyom9NQ81gsYk2byMxG0CgYAYM3p1
-UFt6F2iwJ1c9zSkXQb6cI/zkbFGWP4xKozBEiSDtR3odv/f3BacQL0deQNNTALmP
-ArKJWypF0BTWjlmNV4C8u06b2+WKlFjP/fn5w0qgGh92klO3o01bKxI2nQa5olsV
-gkXdx+JJQzDHVzF+vARvGSiHQXBOUJP4t2hb1QKBgEMXaOEGoffA77akNE7wdDuN
-yDTORLtB4pcQb1CjICrDyxmlK3xbejonkR+WvizoHQQ0DJK45zUtwErTMyRvtc+Q
-bR9w41DDhgixArH7wSbnw4CfFq7X/m4jb8sQBLHk60MH2kih0RRrHktXBNtRXGv2
-op+mE1ucT/ZC6F/dwadJ
------END PRIVATE KEY-----
diff --git a/Others/configs/ssl/agndb1.agnav.com.pem b/Others/configs/ssl/agndb1.agnav.com.pem
deleted file mode 100644
index 5a8c592..0000000
--- a/Others/configs/ssl/agndb1.agnav.com.pem
+++ /dev/null
@@ -1,55 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQCM0uWN5O+TLKYy
-CrYZMH6BsjhmE6And5E3V4JP67eKhcTlnWkz6eHB5UHmIOzM1wwTFNzyou1Ttq6h
-ssx1PWwWP7K0+Bt8z6HoIFPlIDi8eRT8yYSc214j//VetSSo8M5irApB/9HKM4yT
-ikEABKl5uBUWujHBV9Q0etOx7nOpSiekZNxj+TEAixFvOOGUXrdpHNi156GeUQZB
-EG+lhiIsMoP5crP6s+p4kJ0/qJOcU4iEO2/pR8mbtL3PnRNbVoP/DFgj4CAH4SDy
-8ZnYcTEdCN1/mxkCAXnzSi0zAX0ZdBpE9eZoDKzDTTlvE+fw5ooycLaEvXVe0QcZ
-DDli55z3AgMBAAECgf9EwrVTk3VnZ2W/CXvg0Mz0hxECxpJssvU1EIWQgIgeGzMI
-6tNzhb+17TtAItN9MYOzzXwU/XjkZ07dpXAvzs34DyIzOvIw/zEPJrnQJuuNG5Ij
-3EHkVTiiW1hz5f8AfpRHmblogIDvOjxpzhfu6lfWFww1DLU+sqPq/A5h0wymm9U0
-tv7zcgKl/C9yYBfKPHzG0lS8ejIKEajFvcvXH3m6jvG+LEhzJWP9luy6nVmiZE6K
-WKnRn2+UmOWEpsYAmqfDk9MwsMaagpjoYyzh18P+rHa4Eshalgk2dLxJN05sCwqJ
-onDTRy0Bvxm+vO9iYJVDl5Elrz7XuwNPNExg5YkCgYEAxNzDPK3X95Dl9SI5Cwi/
-jj5sPAemajEd3Dqjj8kI6w+xwDg8rPHH7RvY76vpSUkQpUzhrRX2bzXXm/weUr5n
-pRAdDdrTHO78GTBa8q/TAyRpdlXd5oVCjJQyY30Yih8d5edwr8vtWlyZzXWFotNx
-5vPM0GocZBvYh946ikfEoEsCgYEAtyCfKZymbHGzyX8NEZlIk6Ljl3E1EYEjZb3s
-u9qbSdjZf43FVaGkovhn+r+y1yCW+eILhQwTZDi9b2nsv0Yk0eQ3z+GYWILOF0nJ
-rsxrs2x1gPuVQf8QDIDmvfNzAAHQIsq6gXGwukQLRA5dxeN4zPB1JBP4U8QLOYQa
-KtcnQoUCgYEAh7gjKaw4XkcZIp0Lcp3/YhOLDv+/LSrbiT8sEC7q5ROW2gxrWFgA
-G2m9b863MH0c6rlMRMYFdbpLAREZ3rXCQrwPK8QXE7V3O+5oZTPuaBYsVxbvusNY
-lA5/hrNxvZeiRyP+PlR7OHbq2gkRrqXTuwONyom9NQ81gsYk2byMxG0CgYAYM3p1
-UFt6F2iwJ1c9zSkXQb6cI/zkbFGWP4xKozBEiSDtR3odv/f3BacQL0deQNNTALmP
-ArKJWypF0BTWjlmNV4C8u06b2+WKlFjP/fn5w0qgGh92klO3o01bKxI2nQa5olsV
-gkXdx+JJQzDHVzF+vARvGSiHQXBOUJP4t2hb1QKBgEMXaOEGoffA77akNE7wdDuN
-yDTORLtB4pcQb1CjICrDyxmlK3xbejonkR+WvizoHQQ0DJK45zUtwErTMyRvtc+Q
-bR9w41DDhgixArH7wSbnw4CfFq7X/m4jb8sQBLHk60MH2kih0RRrHktXBNtRXGv2
-op+mE1ucT/ZC6F/dwadJ
------END PRIVATE KEY-----
------BEGIN CERTIFICATE-----
-MIIEqDCCA5CgAwIBAgIUckN8Se/RK/askfiTUwBPzwNzehEwDQYJKoZIhvcNAQEL
-BQAwgYUxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UEBwwGQmFycmll
-MRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxEjAQBgNVBAMMCWFn
-bmF2LmNvbTEhMB8GCSqGSIb3DQEJARYSc29mdHdhcmVAYWduYXYuY29tMB4XDTI1
-MDEyNzIwMzE1N1oXDTQ1MDEyMjIwMzE1N1owgYwxCzAJBgNVBAYTAkNBMQswCQYD
-VQQIDAJPTjEPMA0GA1UEBwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjEL
-MAkGA1UECwwCU0QxGTAXBgNVBAMMEGFnbmRiMS5hZ25hdi5jb20xITAfBgkqhkiG
-9w0BCQEWEnNvZnR3YXJlQGFnbmF2LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBAIzS5Y3k75MspjIKthkwfoGyOGYToCd3kTdXgk/rt4qFxOWdaTPp
-4cHlQeYg7MzXDBMU3PKi7VO2rqGyzHU9bBY/srT4G3zPoeggU+UgOLx5FPzJhJzb
-XiP/9V61JKjwzmKsCkH/0cozjJOKQQAEqXm4FRa6McFX1DR607Huc6lKJ6Rk3GP5
-MQCLEW844ZRet2kc2LXnoZ5RBkEQb6WGIiwyg/lys/qz6niQnT+ok5xTiIQ7b+lH
-yZu0vc+dE1tWg/8MWCPgIAfhIPLxmdhxMR0I3X+bGQIBefNKLTMBfRl0GkT15mgM
-rMNNOW8T5/DmijJwtoS9dV7RBxkMOWLnnPcCAwEAAaOCAQUwggEBMIGvBgNVHSME
-gacwgaShgYukgYgwgYUxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UE
-BwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxEjAQ
-BgNVBAMMCWFnbmF2LmNvbTEhMB8GCSqGSIb3DQEJARYSc29mdHdhcmVAYWduYXYu
-Y29tghRSFuD2DW20Rsy92FMPr8mi5Xi1wDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE
-8DAWBgNVHREEDzANggsqLmFnbmF2LmNvbTAdBgNVHQ4EFgQU43FtisxQn6v/AHgb
-fqXp8WcaqvQwDQYJKoZIhvcNAQELBQADggEBAID9NGobJSKUQOxbIEMXojOs5cnQ
-Rj70ZrnyxgUA+bqBfBSocN77KufvCyXgLAIfcD0di82VFyjkB8c69X2gZbzXeYJA
-pXXsEPCTDNzUDduyD9AF95fNKTnxQtQObs1E5aq2mmzE+I09rF+mnXwmehe3mkmY
-YBIlrokHiaIEQo1t760pFRLCwiRnXNd61xP/d+i3ARLeRD0bPy/PHerEN9uc9wO0
-i1HRnDO5LLWDgbgz+JUP4ImTyVphdYcgFngO1hvK5YfZ/j6uo1ynp9g0ff9UPAiR
-HSvIWuIxx+WQlS6e22+fHQcfdGVjVzW9oPoMZ9pgKPiGU0HdxwPXLuZbfc8=
------END CERTIFICATE-----
diff --git a/Others/configs/ssl/agndb2.agnav.com.crt b/Others/configs/ssl/agndb2.agnav.com.crt
deleted file mode 100644
index 75e97d7..0000000
--- a/Others/configs/ssl/agndb2.agnav.com.crt
+++ /dev/null
@@ -1,27 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEqDCCA5CgAwIBAgIUckN8Se/RK/askfiTUwBPzwNzehIwDQYJKoZIhvcNAQEL
-BQAwgYUxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UEBwwGQmFycmll
-MRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxEjAQBgNVBAMMCWFn
-bmF2LmNvbTEhMB8GCSqGSIb3DQEJARYSc29mdHdhcmVAYWduYXYuY29tMB4XDTI1
-MDEyNzIwMzIzNloXDTQ1MDEyMjIwMzIzNlowgYwxCzAJBgNVBAYTAkNBMQswCQYD
-VQQIDAJPTjEPMA0GA1UEBwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjEL
-MAkGA1UECwwCU0QxGTAXBgNVBAMMEGFnbmRiMi5hZ25hdi5jb20xITAfBgkqhkiG
-9w0BCQEWEnNvZnR3YXJlQGFnbmF2LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBAKR9efW8IVVjHyON3PJZuIB0Y3NE8VBGtW60XIQvXPlW2CqLVAMB
-f9ZDsJ0jqkrSouo5X14WkMRj1n/Fi1vzBWBx3vqvJNHr5Idcoq+0V/R06ggHzKpn
-smXoAMls8Nm/uPN+PUuTUINY0JiYm0WNTjHkqiZyqnGOb/3cE3I9Lfb34uqFece6
-w06aqkUzc2BZ1ME3xaauZ5L60XaWU2gU2z1sYywh4kyNEyugjLwhrugyElQYAgvE
-6EofE3ds6cIjt1Z5gQauN2PSv1D1tIbXtY8b5xN3Zx5AT7CkXdGXB5vltjgq3UX/
-6w6ZIB3g6F/CnOd/9FlX8v1pWLKXGys9BncCAwEAAaOCAQUwggEBMIGvBgNVHSME
-gacwgaShgYukgYgwgYUxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UE
-BwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxEjAQ
-BgNVBAMMCWFnbmF2LmNvbTEhMB8GCSqGSIb3DQEJARYSc29mdHdhcmVAYWduYXYu
-Y29tghRSFuD2DW20Rsy92FMPr8mi5Xi1wDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE
-8DAWBgNVHREEDzANggsqLmFnbmF2LmNvbTAdBgNVHQ4EFgQUvAyRGjZQeVhXnSH7
-r0uvB1iav7AwDQYJKoZIhvcNAQELBQADggEBAFLyAC7HhYoae2KPz95H+ZPqRnpV
-1BjpuZ+jscADWlGWvwIvfx5a4vKZf/Fj+uhqcbCU4OCHRqj2qWdWeHRlO/uDDFCm
-LahRHaBYvSpnljQhlsuYLOCDii0Fz93tvnZjhM633p6p+yFkZ5jgVZ+7jJDhqXwj
-RF2j61diRHlIIIok9KFZlrPOjtWdfKaaFMsPMQlp+vhpVa6pEBGtTtMQfuJl1puM
-rA3NtKkyCVgF8iuNrLQzyGGy2WOShhxz+U830HcYAkha6ye0fU8FXXdKFTXZd0p/
-neu8iXLKuiGvnsMe1/E92r1uFNmmwQCpCjGNstV2rpoOmHVaB9MQ5YUEEVM=
------END CERTIFICATE-----
diff --git a/Others/configs/ssl/agndb2.agnav.com.csr b/Others/configs/ssl/agndb2.agnav.com.csr
deleted file mode 100644
index d976560..0000000
--- a/Others/configs/ssl/agndb2.agnav.com.csr
+++ /dev/null
@@ -1,18 +0,0 @@
------BEGIN CERTIFICATE REQUEST-----
-MIIC0jCCAboCAQAwgYwxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UE
-BwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxGTAX
-BgNVBAMMEGFnbmRiMi5hZ25hdi5jb20xITAfBgkqhkiG9w0BCQEWEnNvZnR3YXJl
-QGFnbmF2LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKR9efW8
-IVVjHyON3PJZuIB0Y3NE8VBGtW60XIQvXPlW2CqLVAMBf9ZDsJ0jqkrSouo5X14W
-kMRj1n/Fi1vzBWBx3vqvJNHr5Idcoq+0V/R06ggHzKpnsmXoAMls8Nm/uPN+PUuT
-UINY0JiYm0WNTjHkqiZyqnGOb/3cE3I9Lfb34uqFece6w06aqkUzc2BZ1ME3xaau
-Z5L60XaWU2gU2z1sYywh4kyNEyugjLwhrugyElQYAgvE6EofE3ds6cIjt1Z5gQau
-N2PSv1D1tIbXtY8b5xN3Zx5AT7CkXdGXB5vltjgq3UX/6w6ZIB3g6F/CnOd/9FlX
-8v1pWLKXGys9BncCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQB7nuxP9I2AQhEe
-5B3QTC5bR1NoynoWmggOBZpOmiHyNSZxtObTUqrPdVJf/fHLDDH5clXPZAFn2WhU
-WBV8Z8+F7ibARpv/wEA9tNbiJJlLd4E478qOxwsEu0UFnWRS+uLjSmhCv4TGYTem
-ZfXLe3SZju0zQIZytYIwfzM9DYIIlJi4YozorCGdoKsLVyF4HZS1UAFGmdSDn6Nq
-fFE24Z5Y15QtX1OUlD4tYe6cXDmeUNPQdZYnofz+WJjcF5hJUUWUbrpQVavjW7x6
-uW6sL9YNuS8gayILkATE+u5pQq+pExrTFPFE9JHsmGpXSflt2VUHj20yFFVBleS6
-mN1ZCa6S
------END CERTIFICATE REQUEST-----
diff --git a/Others/configs/ssl/agndb2.agnav.com.key b/Others/configs/ssl/agndb2.agnav.com.key
deleted file mode 100644
index 1217500..0000000
--- a/Others/configs/ssl/agndb2.agnav.com.key
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCkfXn1vCFVYx8j
-jdzyWbiAdGNzRPFQRrVutFyEL1z5Vtgqi1QDAX/WQ7CdI6pK0qLqOV9eFpDEY9Z/
-xYtb8wVgcd76ryTR6+SHXKKvtFf0dOoIB8yqZ7Jl6ADJbPDZv7jzfj1Lk1CDWNCY
-mJtFjU4x5Komcqpxjm/93BNyPS329+LqhXnHusNOmqpFM3NgWdTBN8WmrmeS+tF2
-llNoFNs9bGMsIeJMjRMroIy8Ia7oMhJUGAILxOhKHxN3bOnCI7dWeYEGrjdj0r9Q
-9bSG17WPG+cTd2ceQE+wpF3Rlweb5bY4Kt1F/+sOmSAd4Ohfwpznf/RZV/L9aViy
-lxsrPQZ3AgMBAAECggEACo4ovN2hL/cH4QKx+nVKcyfE1/lFGPconl2BkFPvvepW
-PciqD/VRLEE0vKi0vU9t9+TNm16MVKXpdlKJYlGidzFncyO5E6V1CUCnCepjOIMm
-H3TMq+SynHp6TnKos/hrGGjx6v8djAgvfpNQZ3KtYWNAje4OxPG0exBbyRlm8TGV
-i/kjTJdVVWvisNuAr6cbQOS7qp/0ZGZbTm0SnFBfxDMbYj04g/5Eoz/QfwRHYjXf
-hGd4kZay/T8HiU0d/E49tgY+pEip1ZeltVXcvXPGQiJfPtB0zHrB7pzClqPDK5zO
-qluEsqFpL6XiTDhbwbvJcrGoOg9z62uGz68nc52JDQKBgQDjDK9wjEu4wLxb9/Zz
-da2bSfKBdRHBkWjy60R9u1Gls4OE+X7RloJloP91ez/ItNmcFaTp9BMerq9yeFXA
-dWvFsX5JaH3cqH5lbU2LaplhRepfdlEql8W4belLo4RG8NRAk+phbdS38iqdU++f
-eU5kLQm+CxEOoiqlX/fBApWHtQKBgQC5dryqgBcN6sb0US7nrg5jKeb4kXLT8YPD
-xgIeozylLLim1+4qqQczP01gQANyNVa2kcj0547u5R8L9cSCXHC4zbz+yqNGWrH8
-RKc2ZFDaJoTAAESqdxrnh3mHhKStC17AOyTf6PaGiJFVV09N00bE44texdmwC0q9
-O26dh0gY+wKBgER3x9QFyrPdEzAct2ob+41PoFyfpAoeQmq2vcG4oid42dlYr4Ce
-hZYGFeMklph8yP6DnGacnsNq5Cd92EYgYq6MFswmQYYwHWWSr4ayT6yAu+urr1BL
-0mkKZAbWOYZ3C1qqAEz/JN2PnoAxFpqdpyEYX866YZtrFbcnGHxQvizdAoGACnCF
-lC9bnNvvUQdU1ZO1mZ1dM/az+PwqR5XYvrK/kiifSDz1Wg9jqV3R1C4mQ4J/HA2+
-uxJhuE7LXZf69L+RVMW38rujTy2BwUp4AxbIek8av9gEBXho2kmE7LzprBfswHNT
-0wrA/beoPp6Ihz/yRtjsGmyWoVMxZM94nYNk6osCgYAwsaBrs6mrP+O+mmnMVaxV
-BBtsHtmw8CM6ZvcmgkpB9soSyCoqVJ/2uhRSMSpX/5PpH7/hJvxJUFPoZrEkQqVm
-WnYp7ipuNwSH6XXNlr31etCX0VFIbedAB7kp+hUZYmW61ZKk1NkyP/E3mUA7J3x3
-RApxvwKRCebUik0wQEfBpA==
------END PRIVATE KEY-----
diff --git a/Others/configs/ssl/agndb2.agnav.com.pem b/Others/configs/ssl/agndb2.agnav.com.pem
deleted file mode 100644
index 09e310a..0000000
--- a/Others/configs/ssl/agndb2.agnav.com.pem
+++ /dev/null
@@ -1,55 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCkfXn1vCFVYx8j
-jdzyWbiAdGNzRPFQRrVutFyEL1z5Vtgqi1QDAX/WQ7CdI6pK0qLqOV9eFpDEY9Z/
-xYtb8wVgcd76ryTR6+SHXKKvtFf0dOoIB8yqZ7Jl6ADJbPDZv7jzfj1Lk1CDWNCY
-mJtFjU4x5Komcqpxjm/93BNyPS329+LqhXnHusNOmqpFM3NgWdTBN8WmrmeS+tF2
-llNoFNs9bGMsIeJMjRMroIy8Ia7oMhJUGAILxOhKHxN3bOnCI7dWeYEGrjdj0r9Q
-9bSG17WPG+cTd2ceQE+wpF3Rlweb5bY4Kt1F/+sOmSAd4Ohfwpznf/RZV/L9aViy
-lxsrPQZ3AgMBAAECggEACo4ovN2hL/cH4QKx+nVKcyfE1/lFGPconl2BkFPvvepW
-PciqD/VRLEE0vKi0vU9t9+TNm16MVKXpdlKJYlGidzFncyO5E6V1CUCnCepjOIMm
-H3TMq+SynHp6TnKos/hrGGjx6v8djAgvfpNQZ3KtYWNAje4OxPG0exBbyRlm8TGV
-i/kjTJdVVWvisNuAr6cbQOS7qp/0ZGZbTm0SnFBfxDMbYj04g/5Eoz/QfwRHYjXf
-hGd4kZay/T8HiU0d/E49tgY+pEip1ZeltVXcvXPGQiJfPtB0zHrB7pzClqPDK5zO
-qluEsqFpL6XiTDhbwbvJcrGoOg9z62uGz68nc52JDQKBgQDjDK9wjEu4wLxb9/Zz
-da2bSfKBdRHBkWjy60R9u1Gls4OE+X7RloJloP91ez/ItNmcFaTp9BMerq9yeFXA
-dWvFsX5JaH3cqH5lbU2LaplhRepfdlEql8W4belLo4RG8NRAk+phbdS38iqdU++f
-eU5kLQm+CxEOoiqlX/fBApWHtQKBgQC5dryqgBcN6sb0US7nrg5jKeb4kXLT8YPD
-xgIeozylLLim1+4qqQczP01gQANyNVa2kcj0547u5R8L9cSCXHC4zbz+yqNGWrH8
-RKc2ZFDaJoTAAESqdxrnh3mHhKStC17AOyTf6PaGiJFVV09N00bE44texdmwC0q9
-O26dh0gY+wKBgER3x9QFyrPdEzAct2ob+41PoFyfpAoeQmq2vcG4oid42dlYr4Ce
-hZYGFeMklph8yP6DnGacnsNq5Cd92EYgYq6MFswmQYYwHWWSr4ayT6yAu+urr1BL
-0mkKZAbWOYZ3C1qqAEz/JN2PnoAxFpqdpyEYX866YZtrFbcnGHxQvizdAoGACnCF
-lC9bnNvvUQdU1ZO1mZ1dM/az+PwqR5XYvrK/kiifSDz1Wg9jqV3R1C4mQ4J/HA2+
-uxJhuE7LXZf69L+RVMW38rujTy2BwUp4AxbIek8av9gEBXho2kmE7LzprBfswHNT
-0wrA/beoPp6Ihz/yRtjsGmyWoVMxZM94nYNk6osCgYAwsaBrs6mrP+O+mmnMVaxV
-BBtsHtmw8CM6ZvcmgkpB9soSyCoqVJ/2uhRSMSpX/5PpH7/hJvxJUFPoZrEkQqVm
-WnYp7ipuNwSH6XXNlr31etCX0VFIbedAB7kp+hUZYmW61ZKk1NkyP/E3mUA7J3x3
-RApxvwKRCebUik0wQEfBpA==
------END PRIVATE KEY-----
------BEGIN CERTIFICATE-----
-MIIEqDCCA5CgAwIBAgIUckN8Se/RK/askfiTUwBPzwNzehIwDQYJKoZIhvcNAQEL
-BQAwgYUxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UEBwwGQmFycmll
-MRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxEjAQBgNVBAMMCWFn
-bmF2LmNvbTEhMB8GCSqGSIb3DQEJARYSc29mdHdhcmVAYWduYXYuY29tMB4XDTI1
-MDEyNzIwMzIzNloXDTQ1MDEyMjIwMzIzNlowgYwxCzAJBgNVBAYTAkNBMQswCQYD
-VQQIDAJPTjEPMA0GA1UEBwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjEL
-MAkGA1UECwwCU0QxGTAXBgNVBAMMEGFnbmRiMi5hZ25hdi5jb20xITAfBgkqhkiG
-9w0BCQEWEnNvZnR3YXJlQGFnbmF2LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBAKR9efW8IVVjHyON3PJZuIB0Y3NE8VBGtW60XIQvXPlW2CqLVAMB
-f9ZDsJ0jqkrSouo5X14WkMRj1n/Fi1vzBWBx3vqvJNHr5Idcoq+0V/R06ggHzKpn
-smXoAMls8Nm/uPN+PUuTUINY0JiYm0WNTjHkqiZyqnGOb/3cE3I9Lfb34uqFece6
-w06aqkUzc2BZ1ME3xaauZ5L60XaWU2gU2z1sYywh4kyNEyugjLwhrugyElQYAgvE
-6EofE3ds6cIjt1Z5gQauN2PSv1D1tIbXtY8b5xN3Zx5AT7CkXdGXB5vltjgq3UX/
-6w6ZIB3g6F/CnOd/9FlX8v1pWLKXGys9BncCAwEAAaOCAQUwggEBMIGvBgNVHSME
-gacwgaShgYukgYgwgYUxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJPTjEPMA0GA1UE
-BwwGQmFycmllMRQwEgYDVQQKDAtBRy1OQVYgSW5jLjELMAkGA1UECwwCU0QxEjAQ
-BgNVBAMMCWFnbmF2LmNvbTEhMB8GCSqGSIb3DQEJARYSc29mdHdhcmVAYWduYXYu
-Y29tghRSFuD2DW20Rsy92FMPr8mi5Xi1wDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE
-8DAWBgNVHREEDzANggsqLmFnbmF2LmNvbTAdBgNVHQ4EFgQUvAyRGjZQeVhXnSH7
-r0uvB1iav7AwDQYJKoZIhvcNAQELBQADggEBAFLyAC7HhYoae2KPz95H+ZPqRnpV
-1BjpuZ+jscADWlGWvwIvfx5a4vKZf/Fj+uhqcbCU4OCHRqj2qWdWeHRlO/uDDFCm
-LahRHaBYvSpnljQhlsuYLOCDii0Fz93tvnZjhM633p6p+yFkZ5jgVZ+7jJDhqXwj
-RF2j61diRHlIIIok9KFZlrPOjtWdfKaaFMsPMQlp+vhpVa6pEBGtTtMQfuJl1puM
-rA3NtKkyCVgF8iuNrLQzyGGy2WOShhxz+U830HcYAkha6ye0fU8FXXdKFTXZd0p/
-neu8iXLKuiGvnsMe1/E92r1uFNmmwQCpCjGNstV2rpoOmHVaB9MQ5YUEEVM=
------END CERTIFICATE-----
diff --git a/Others/configs/ssl/makeCSR.sh b/Others/configs/ssl/makeCSR.sh
deleted file mode 100755
index ed083d7..0000000
--- a/Others/configs/ssl/makeCSR.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-if [ "$1" = "" ]; then
-echo 'Exit. Your must enter hostname (CN) !'
-exit 1
-fi
-#cd /home/agm/ssl/
-HOST_NAME="$1"
-SUBJECT="/C=CA/ST=ON/L=Barrie/O=AG-NAV Inc./OU=SD/CN=$HOST_NAME/emailAddress=software@agnav.com"
-openssl req -new -nodes -newkey rsa:2048 -subj "$SUBJECT" -keyout $HOST_NAME.key -out $HOST_NAME.csr
diff --git a/Others/configs/ssl/makeCert.sh b/Others/configs/ssl/makeCert.sh
deleted file mode 100755
index 705979c..0000000
--- a/Others/configs/ssl/makeCert.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-if [ "$1" = "" ]; then
-echo 'Exit. Your must enter hostname (CN) !'
-exit 1
-fi
-#cd /home/agm/ssl/
-HOST_NAME="$1"
-
-# Sign the certificate with the root CA
-openssl x509 -req -days 7300 -in $HOST_NAME.csr -CA ./rootCA/rootCA.crt -CAkey ./rootCA/rootCA.key -CAcreateserial -out ./$HOST_NAME.crt -sha256 -extfile v3-ext.cnf
-
-# Make pem file combining the key and certificate file
-cat $HOST_NAME.key $HOST_NAME.crt > $HOST_NAME.pem
diff --git a/Others/configs/ssl/makeClientCSR.sh b/Others/configs/ssl/makeClientCSR.sh
deleted file mode 100755
index 4f3db1c..0000000
--- a/Others/configs/ssl/makeClientCSR.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-if [ "$1" = "" ]; then
-echo 'Exit. Your must enter client hostname (CN) !'
-exit 1
-fi
-#cd /home/agm/ssl/
-HOST_NAME="$1"
-SUBJECT="/C=CA/ST=ON/L=Barrie/O=AG-NAV Inc./OU=MONGO_CLIENTS/CN=$HOST_NAME/emailAddress=software@agnav.com"
-openssl req -new -nodes -newkey rsa:2048 -subj "$SUBJECT" -keyout $HOST_NAME.key -out $HOST_NAME.csr
diff --git a/Others/configs/ssl/makeClientCert.sh b/Others/configs/ssl/makeClientCert.sh
deleted file mode 100755
index c0d22b2..0000000
--- a/Others/configs/ssl/makeClientCert.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-if [ "$1" = "" ]; then
-echo 'Exit. Your must enter the client hostname (CN) !'
-exit 1
-fi
-#cd /home/agm/ssl/
-HOST_NAME="$1"
-
-# Sign the certificate with the root CA
-openssl x509 -req -days 7300 -in $HOST_NAME.csr -CA ./rootCA/rootCA.crt -CAkey ./rootCA/rootCA.key -CAcreateserial -out ./$HOST_NAME.crt -sha256 -extfile v3-ext_client.cnf
-
-# Make pem file combining the key and certificate file
-cat $HOST_NAME.key $HOST_NAME.crt > $HOST_NAME.pem
diff --git a/Others/configs/ssl/root-ssl-config.cnf b/Others/configs/ssl/root-ssl-config.cnf
deleted file mode 100644
index 768e879..0000000
--- a/Others/configs/ssl/root-ssl-config.cnf
+++ /dev/null
@@ -1,30 +0,0 @@
-[req]
-default_bits = 4096
-req_extensions = extension_requirements
-distinguished_name = dn_requirements
-
-[extension_requirements]
-basicConstraints = critical,CA:true
-keyUsage = critical, digitalSignature, keyEncipherment, keyCertSign, cRLSign
-subjectAltName = @sans_list
-
-[dn_requirements]
-countryName = CA
-countryName_default = CA
-stateOrProvinceName = ON
-stateOrProvinceName_default = ON
-localityName = Barrie
-localityName_default = Barrie
-0.organizationName = AG-NAV Inc.
-0.organizationName_default = AG-NAV Inc.
-organizationalUnitName = SD
-organizationalUnitName_default = SD
-commonName = agnav.com
-commonName_default = agnav.com
-emailAddress = software@agnav.com
-emailAddress_default = software@agnav.com
-
-[sans_list]
-DNS.1 = localhost
-DNS.2 = *.agnav.com
-IP.1 = 127.0.0.1
\ No newline at end of file
diff --git a/Others/configs/ssl/rootCA/rootCA.crt b/Others/configs/ssl/rootCA/rootCA.crt
deleted file mode 100644
index 07f3c43..0000000
--- a/Others/configs/ssl/rootCA/rootCA.crt
+++ /dev/null
@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDkzCCAnsCFFIW4PYNbbRGzL3YUw+vyaLleLXAMA0GCSqGSIb3DQEBCwUAMIGF
-MQswCQYDVQQGEwJDQTELMAkGA1UECAwCT04xDzANBgNVBAcMBkJhcnJpZTEUMBIG
-A1UECgwLQUctTkFWIEluYy4xCzAJBgNVBAsMAlNEMRIwEAYDVQQDDAlhZ25hdi5j
-b20xITAfBgkqhkiG9w0BCQEWEnNvZnR3YXJlQGFnbmF2LmNvbTAeFw0yNTAxMTAx
-NzE3NDBaFw00NTAxMDUxNzE3NDBaMIGFMQswCQYDVQQGEwJDQTELMAkGA1UECAwC
-T04xDzANBgNVBAcMBkJhcnJpZTEUMBIGA1UECgwLQUctTkFWIEluYy4xCzAJBgNV
-BAsMAlNEMRIwEAYDVQQDDAlhZ25hdi5jb20xITAfBgkqhkiG9w0BCQEWEnNvZnR3
-YXJlQGFnbmF2LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPO6
-zGIpSaBKYliTuGBvMWxXzrCvUUlqYAB2vJeBJf8prbfL2rRM5ac1vXx3HOFQOXF5
-dwJz+XaN45xkfYLNPta6kGTn0RDaFUpoE70SIsEP7Q1D3FhXMAocXhfSy6sZeZB/
-XKJ0dCjuVDOxjle6ksQdiKrJFzVF1xWwWijGD66jzDIcQJTCJqxyMblt/YQaI+q8
-uL2/wjiNRYALtSpd77HOQ075/Gd2sN8ppDCkpcg1F6Sx1Z9WcQx9pB/pI8wuuvdg
-xJsO1xpLsvltFMBcFTfIZYJckNPL/wEFL6XSuFBZ6S8LLTeNL8vk2JfZXai3rsXb
-LyTrFvVOavtcltcI5UECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA374T4MtxTCU/
-9VR7+aRjXb5QnTTBtigCGjo5la8eIQZiFnk8FJJZEIYI4obphTleAKAfypDlQ+Wj
-JY/4bOWvnjZIHi7XOnGLx6n9fSv4pC2PzMHThwHvY+vPGrHtoYT1xtfCRzaAMdvS
-kXbknniLS+QarurADQA5MT4v+gGuK4dq+LvgPWOhmr3/VTmdfFJQCb0J/JD8CcEu
-MSABLSIwmcu0tDd7647SkTQKBM5vdQVL15eBCMwf/sIYKokqy+It9xRzdFsEYviz
-sK2CtuheeEhJBU98ACAS2gK5CZKNTrSFNk5uTlVfUKMdF6FMmQ/vSPX+xfRpkS3R
-AWeVHLZ4OQ==
------END CERTIFICATE-----
diff --git a/Others/configs/ssl/rootCA/rootCA.key b/Others/configs/ssl/rootCA/rootCA.key
deleted file mode 100644
index 50935c2..0000000
--- a/Others/configs/ssl/rootCA/rootCA.key
+++ /dev/null
@@ -1,30 +0,0 @@
------BEGIN ENCRYPTED PRIVATE KEY-----
-MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIx3mXoPi8+oMCAggA
-MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBC846VrlknwllZXsk134KhHBIIE
-0LY2sSuhf7XLFYKHi/xZXwxviUVTe0y2aZErFSa2G+swiQvU2quz2rOiBG+t9bPW
-EEIBOBXDyUExnVcK+P7Ka64CB/ugiaAu7Tb9V+spR13wLoclhxrTGFbgAG3GHbth
-XMPbtsrlQbQtZMxUvQR+ot6RtzuQ7RUCVStDdFCBk5ZG+UwpuKtlHT0JrhquSqWs
-3kdCY3y78C2eaOnENY7dKb9T+2UL0jNRvPCOWX/uiMSibdSAmeRVZwSzsmtTLDQn
-gke0JY/qV3gBYpSbFC1xEsbYvGyKIyg2E4yNJty0kWQJtkDAR4k4VBBghh9Fg2rk
-QvNcHSTkkhyvkL8pUMUPE4h8kjOwu89nrSYQrvgpe8Y1E6om+YXdj9ilnwy0/WWi
-hsz29TXY9mnEHqFTtLPRbwCdVFhApbJPXA9CV1S1q2BVyCWSUt0kxb3/bcEqdg2n
-8SFk65CvbD3n3j7xpl2VM7WIb6rMzEG7QoNGSkt19j9km2n0zoFFfc0hkjmA39QQ
-BjPWo0hOZu8d0nqg3wajnM6Qu6cB/HmhZubILNZ5cpbbm7OrhpV2wG/4TmfbBRSn
-f98m0sdPAMhJ5cpvgC0b5GvxD+8QGBAG9nkvHWulMtH9do2x3yfLECPj7S65O0Sd
-liVH6sJNeiC2lCBT+QEBBpbxkL1INZ2mPaCJ4wC4/oufTC1c9/Qd7nJCKiTtbdYb
-ngcV6xKH0o04mFhZSZQECvWn6tdZ8Syq6rdYjB+Ab11qGYmXuUptq5wawUZ4o6yd
-q/VucdpYgkYLZ7UQ2yQiUr3tUKEPJyk2/EgMm7YZdelUTdj5GcUVmOkDMdDXCZGq
-o188f3NssNY8n+g37/TZUfxRC7jGyS8EUfXWFD+MJrIVJq2uGqDAvDtZ+qqiJaq7
-2NLeJzhN1SW9Eay9idKHp21GIh5rG7V+AXzcMW0N0ATbYohFSYSxcYayPoGKbJN1
-9TWEFnJE1htWWhKM1XWfan1H9ucV22eMwGMdV+MV0+QukOFnIqjJqGIHT9+mKK6w
-5daAyg8HOjP9/xLmRjkafY2sAKi7QmvjbrfFJYNAIL59jhu5AsI7RFJtjhfJ/ye+
-n5tvJjHs1YqOjG1+Q+z2EtGHsWBD6dk9FuEsKCly2/C0g0DXQOVm01kez4OFbSUN
-nfOBSJwvUIEJm5X9YAWzNlFbIgFNq7plC95eUq78Sn7kLNN2yUaVBKxVbnEQKYqd
-Lg60j2ZuyK5x9gdmAViX4Gp84rXHKN1w8oJFPcaoT9Tn91n6UaxDMTQ9IrACxXnI
-MBrbwfE3yZGyP7IpG4Ugnn0l38GO0alqfTKf98G/xbg+voe7udSFdCw3WElt8OvK
-KQLe4SpfItjBErs+q4l/4P7NKc/vHoGmompc3cDtu8W8Xx8hE+bLJczMm01KvYeN
-4Ev7x4SuPnweDZn7pVF3czFfoIKJZSBFiXCaQdymKqcgJJWOUzvzY5ZFkzv1CB3S
-/AmmJyLAWp0tWbFOPyj4B+maKJwFcP3aNl0HSx1JoAQOmbt5WQ3+LNIkt519SmZc
-U4qGnQy1NXtgL8DNpIIiHIyBkYHT6U2lQCB+Xri6TWC0HPLOMtxzwpqcpMAxlaL/
-9tj4emEgSjEe4M3OMtA9MMjUme5BUMXUgYfidjWsKAd2
------END ENCRYPTED PRIVATE KEY-----
diff --git a/Others/configs/ssl/rootCA/rootCA.srl b/Others/configs/ssl/rootCA/rootCA.srl
deleted file mode 100644
index 73a2340..0000000
--- a/Others/configs/ssl/rootCA/rootCA.srl
+++ /dev/null
@@ -1 +0,0 @@
-72437C49EFD12BF6AC91F89353004FCF03737A13
diff --git a/Others/configs/ssl/v3-ext.cnf b/Others/configs/ssl/v3-ext.cnf
deleted file mode 100644
index 8dc34a3..0000000
--- a/Others/configs/ssl/v3-ext.cnf
+++ /dev/null
@@ -1,11 +0,0 @@
-authorityKeyIdentifier=keyid,issuer
-basicConstraints=CA:FALSE
-keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
-#extendedKeyUsage = clientAuth, serverAuth
-subjectAltName = @alt_names
-
-[alt_names]
-#DNS.1 = localhost
-#IP.1 = 127.0.0.1
-# For client/replica members of agnav.com
-DNS.1 = *.agnav.com
diff --git a/Others/configs/ssl/v3-ext_client.cnf b/Others/configs/ssl/v3-ext_client.cnf
deleted file mode 100644
index 25e921c..0000000
--- a/Others/configs/ssl/v3-ext_client.cnf
+++ /dev/null
@@ -1,13 +0,0 @@
-authorityKeyIdentifier=keyid,issuer
-basicConstraints=CA:FALSE
-keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
-#extendedKeyUsage = clientAuth, serverAuth
-subjectAltName = @alt_names
-
-[alt_names]
-# For localhost
-DNS.1 = localhost
-IP.1 = 127.0.0.1
-
-# For client of agnav
-#DNS.1 = *.agnav.com
\ No newline at end of file
diff --git a/Others/scripts/NOTES.txt b/Others/scripts/NOTES.txt
deleted file mode 100644
index 0c5c3fa..0000000
--- a/Others/scripts/NOTES.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-# Backup schedule in AgMission server to AGNAS within AgNav
-# m h dom mon dow command
-# Run backup of agmission db every 3 days at 21:00 midnight
- 0 21 */3 * * /home/agmission/backups/scripts/backup_agm.sh
-# Run system backup every 1st day of the month at 1:30 am
- 30 1 1 * * /home/agmission/backups/scripts/backup_sys.sh
diff --git a/Others/scripts/agmission-pm2.json b/Others/scripts/agmission-pm2.json
deleted file mode 100755
index c778d3f..0000000
--- a/Others/scripts/agmission-pm2.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "apps": [
- {
- "name": "agmission-prod",
- "version": "3.0.0",
- "script": "server.js",
- "node_args": [
- "--expose-gc",
- "--max-old-space-size=2048",
- "--nouse-idle-notification",
- "--icu-data-dir=/home/agmission/.nvm/versions/node/v16.20.2/lib/node_modules/full-icu"
- ],
- "watch": false,
- "ignore_watch": [
- "[\/\\]\\./",
- "node_modules",
- ".tmp",
- "job-unzip",
- "job-uploads",
- "backup"
- ],
- "merge_logs": true,
- "cwd": "/media/ssd1/agmission",
- "env": {
- "DISPLAY": ":99",
- "NODE_ENV": "production",
- "AGM_PORT": "7000",
- "PRODUCTION": 1,
- "DEBUG": "agm:*"
- },
- "log_date_format": "YYYY-MM-DD HH:mm:ss"
- }
- ]
-}
diff --git a/Others/scripts/backup_agm.sh b/Others/scripts/backup_agm.sh
deleted file mode 100644
index f8b8053..0000000
--- a/Others/scripts/backup_agm.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/sh
-
-# Variables
-agnroot='/media/ssd1/agmission'
-bkroot='/home/agmission/backups'
-fname=agmdb_$(date +\%Y\%m\%d)
-logfn=$bkroot/logs/agm_$(date +\%Y\%m\%d).log
-uplogfn=$bkroot/logs/agm_files_$(date +\%Y\%m\%d).log
-
-# Dump db to file then gzip
-mongodump --archive=$bkroot/$fname.gz --gzip --db agmission --username "agm" --password "Agm2017" --authenticationDatabase "agmission" --forceTableScan
-
-find $bkroot/agmdb_*.gz -mtime +11 -exec rm {} \;
-find $bkroot/logs/agm_*.log $bkroot/logs/agm_files_*.log -mtime +11 -exec rm {} \;
-
-# Sync agm db to NAS
-rsync -arzh --delete --exclude '/uploads' --exclude '/sys' --exclude '/scripts' --exclude '/logs' $bkroot/ rsync://rsync@data.agnav.com:/agm/ --password-file $bkroot/scripts/pass --log-file $logfn --stats --ignore-existing
-
-# Sync job uploaded files to NAS
-rsync -arzh --delete-delay $agnroot/job-uploads/ rsync://rsync@data.agnav.com:/agm/uploads/ --password-file $bkroot/scripts/pass --log-file $uplogfn --stats --ignore-existing
-
-# Sync the rsync log files to the destination
-rsync -arzh --delete-delay $bkroot/logs/ rsync://rsync@data.agnav.com:/agm/logs/ --password-file $bkroot/scripts/pass
-
-
diff --git a/Others/scripts/backup_sys.sh b/Others/scripts/backup_sys.sh
deleted file mode 100644
index 48b7102..0000000
--- a/Others/scripts/backup_sys.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-bkroot='/home/agmission/backups'
-fname=$bkroot/sys/sys_$(date +\%Y\%m\%d).tgz
-logfn=$bkroot/logs/agm_sys_$(date +\%Y\%m\%d).log
-
-find $bkroot/sys/sys_*.tgz -mtime +32 -exec rm {} \;
-
-sudo tar -cpzf $fname --exclude=$bkroot --exclude='/home/agmission' --exclude='/media/ssd1' / --one-file-system
-
-# Sync job uploaded files to NAS
-rsync -arzhP --delete-delay $bkroot/sys/ rsync://rsync@data.agnav.com:/agm/sys/ --password-file $bkroot/scripts/pass --log-file $logfn --stats --ignore-existing
-
-# Sync the rsync log files to the destination
-rsync -arzh --delete-delay $bkroot/logs/ rsync://rsync@data.agnav.com:/agm/logs/ --password-file $bkroot/scripts/pass
diff --git a/Others/scripts/backup_test.sh b/Others/scripts/backup_test.sh
deleted file mode 100755
index a61c67c..0000000
--- a/Others/scripts/backup_test.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-sudo tar -cpzf /home/trung/bk_$(date +\%Y\%m\%d).tgz /home/trung/temp
diff --git a/Others/scripts/c_users_map.js b/Others/scripts/c_users_map.js
deleted file mode 100644
index 4d4b5ac..0000000
--- a/Others/scripts/c_users_map.js
+++ /dev/null
@@ -1,51 +0,0 @@
-// Aggregate from user to user's settings
-var custUsers = db.customers.aggregate([
- { $match: { active: true } },
-// { $project: { lang: { $ifNull: [ "$lang", "en" ] } } },
- {
- $lookup:
- {
- from: "users",
- localField: "user",
- foreignField: "_id",
- as: "c_users"
- }
- },
- { $unwind: { path: "$c_users", "preserveNullAndEmptyArrays": true } },
- {
- $project: {
- '_id': 0,
- 'id': '$c_users._id',
- 'name': 1,
-// 'contact': 1,
- 'username':'$c_users.username'
- }
- }
-]).toArray();
-// print (custUsers)
-var phead = true, hline = '', line = '';
-custUsers.forEach(cu => {
- for (var p in cu) {
-
- if (phead) {
- if (p == 'id')
- hline = 'userId,' + hline;
- else
- hline += p + ','
- }
- if (p == 'id')
- line = cu[p] + ',' + line;
- else {
-// if (cu[p].indexOf(',') != -1)
-// print (cu[p])
- cu[p] = (cu[p] || '').replace(/,/g, '');
- line = line + cu[p] + ',';
- }
- }
- if (phead) {
- print (hline.slice(0, -1))
- phead = false;
- }
- print (line.slice(0, -1))
- line = '';
-});
diff --git a/Others/scripts/cleanup_worker-pm2.json b/Others/scripts/cleanup_worker-pm2.json
deleted file mode 100755
index ed116ac..0000000
--- a/Others/scripts/cleanup_worker-pm2.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "apps": [
- {
- "interpreter" : "node@14.17.2",
- "name": "cleanup-worker",
- "script": "cleanup_worker.js",
- "node_args" : ["--expose-gc", "--max-old-space-size=1048", "--nouse-idle-notification"],
- "watch": false,
- "exec_mode": "fork",
- "instances": 1,
- "cwd": "/media/ssd1/agmission/workers",
- "error_file": "~/.pm2/logs/err.log",
- "out_file": "~/.pm2/logs/out.log",
- "merge_logs": false,
- "env": {
- "NODE_ENV": "production",
- "PRODUCTION": 1,
- "DEBUG": "agm:*"
- },
- "max_restarts" : 10,
- "min_uptime" : "24h",
- "log_date_format": ""
- }
- ]
-}
diff --git a/Others/scripts/deploy/README.txt b/Others/scripts/deploy/README.txt
deleted file mode 100644
index c2a04e6..0000000
--- a/Others/scripts/deploy/README.txt
+++ /dev/null
@@ -1,67 +0,0 @@
-Full deployment documentation: trunk/Documents/DEPLOYMENT.md
-
-Configuration Variables:
- AGM_BASE_DIR - Local AgMission directory
- AGN_LIBS_DIR - Local AGN libraries directory
- AGM_DEST_HOST - Remote server (user@hostname)
- AGM_DEST_PORT - SSH port
- AGM_DEST_PATH - Remote destination directory
-
-Command Backend Frontend Use Case
-.[agm-deploy.sh] 0 branch Dry-run Dry-run Test everything safely
-.[agm-deploy.sh] 1 branch Deploy Skipped Backend-only deployment
-.[agm-deploy.sh] 2 branch dry-run Deploy Dry-run Deploy backend, test frontend
-.[agm-deploy.sh] 2 branch Deploy Deploy Full production deployment
-
-Usage Examples:
- One-time override:
- # Custom base directory
- AGM_BASE_DIR="/projects/AgMission" ./agm-deploy.sh 1 trunk
-
- # Different AGN libs location
- AGN_LIBS_DIR="/opt/@agn" ./agm-deploy.sh 2 subscription-signup
-
- # Deploy to staging server
- AGM_DEST_HOST="agm@staging.agnav.com" ./agm-deploy.sh 1 main
-
- Using configuration file:
- # Create config file
- cp agm-deploy.conf.template ~/.agm-deploy.conf
- nano ~/.agm-deploy.conf
-
- # Use the config
- source ~/.agm-deploy.conf && ./agm-deploy.sh 1 trunk
-
- Environment-specific configs:
- # Development
- source ~/.agm-deploy-dev.conf && ./agm-deploy.sh 2 subscription-signup
-
- # Staging
- source ~/.agm-deploy-staging.conf && ./agm-deploy.sh 1 main
-
- # Production
- source ~/.agm-deploy-prod.conf && ./agm-deploy.sh 1 trunk
-
-Benefits:
-✅ Flexible development environments - Different developers can use different paths
-✅ Multi-environment support - Easy switching between dev/staging/prod
-✅ CI/CD friendly - Environment variables work well in automation
-✅ Backwards compatible - Still works with default paths if no overrides
-✅ Clear feedback - Shows exactly which paths are being used
-✅ Template provided - Easy to get started with custom configurations
-
-Advanced Usage:
-# Deploy from custom location to staging
-AGM_BASE_DIR="/backup/AgMission" \
-AGM_DEST_HOST="agm@staging.com" \
-AGM_DEST_PORT="2222" \
-./agm-deploy.sh 2 subscription-signup
-
-# Use alternative AGN libs
-AGN_LIBS_DIR="/shared/@agn" ./agm-deploy.sh 1 trunk
-
-Summary:
-The script is now much more flexible and can adapt to different development environments and deployment targets without code changes!
-This makes the deployment script completely environment-agnostic and suitable for any deployment scenario!
-
-
diff --git a/Others/scripts/deploy/agm-deploy.conf.template b/Others/scripts/deploy/agm-deploy.conf.template
deleted file mode 100644
index da60b0e..0000000
--- a/Others/scripts/deploy/agm-deploy.conf.template
+++ /dev/null
@@ -1,37 +0,0 @@
-# AGM Deployment Configuration Template
-# Copy this file to ~/.agm-deploy.conf and customize as needed
-
-# Base directories
-export AGM_BASE_DIR="$HOME/work/AgMission"
-export AGN_LIBS_DIR="$HOME/work/@agn"
-
-# Deployment targets
-export AGM_DEST_HOST="agm@agmission-1.agnav.com"
-export AGM_DEST_PORT="22222"
-export AGM_DEST_PATH="/home/agm/apps"
-
-# Alternative configurations (uncomment and modify as needed)
-
-# For development environment:
-# export AGM_DEST_HOST="agm@dev.agnav.com"
-# export AGM_DEST_PORT="2222"
-# export AGM_DEST_PATH="/home/dev/applications"
-
-# For staging environment:
-# export AGM_DEST_HOST="agm@staging.agnav.com"
-# export AGM_DEST_PORT="22222"
-# export AGM_DEST_PATH="/opt/staging/apps"
-
-# For custom local paths:
-# export AGM_BASE_DIR="/custom/path/to/AgMission"
-# export AGN_LIBS_DIR="/custom/path/to/@agn"
-# export AGM_DEST_PATH="/custom/remote/path"
-
-# Usage:
-# 1. Copy this file: cp agm-deploy.conf.template ~/.agm-deploy.conf
-# 2. Edit the configuration: nano ~/.agm-deploy.conf
-# 3. Source it before deployment: source ~/.agm-deploy.conf && ./agm-deploy.sh 1 trunk
-# 4. Or create environment-specific configs:
-# - ~/.agm-deploy-dev.conf
-# - ~/.agm-deploy-staging.conf
-# - ~/.agm-deploy-prod.conf
\ No newline at end of file
diff --git a/Others/scripts/deploy/agm-deploy.sh b/Others/scripts/deploy/agm-deploy.sh
deleted file mode 100755
index 316a0dd..0000000
--- a/Others/scripts/deploy/agm-deploy.sh
+++ /dev/null
@@ -1,300 +0,0 @@
-#!/bin/bash
-
-# AGM Deployment Script
-# Usage: ./agm-deploy.sh [run_mode] [branch_name] [fe_mode]
-# run_mode: 0=dry-run, 1=deploy-backend, 2=deploy-all
-# branch_name: branch to deploy from (default: subscription-invoicing)
-# fe_mode: frontend mode for mode 2 (run|dry-run), optional
-#
-# Environment Variables (optional overrides):
-# AGM_BASE_DIR - Base AgMission directory (default: ~/work/AgMission)
-# AGN_LIBS_DIR - AGN libraries directory (default: ~/work/@agn)
-# AGM_DEST_HOST - Deployment target host (default: agm@agmission-1.agnav.com)
-# AGM_DEST_PORT - SSH port (default: 22222)
-# AGM_DEST_PATH - Remote destination path (default: /home/agm/apps)
-#
-# Examples:
-# ./agm-deploy.sh # Dry run with default branch
-# ./agm-deploy.sh 1 # Deploy backend from default branch
-# ./agm-deploy.sh 1 subscription-signup # Deploy backend from subscription-signup branch
-# ./agm-deploy.sh 2 subscription-signup # Deploy everything from subscription-signup branch
-# ./agm-deploy.sh 2 subscription-signup dry-run # Deploy backend, dry-run frontend
-# AGM_BASE_DIR=/custom/path ./agm-deploy.sh 1 # Use custom base directory
-# AGM_DEST_PATH=/opt/apps ./agm-deploy.sh 2 trunk # Deploy to custom remote path
-
-# Show help if requested
-if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
- echo "AGM Deployment Script"
- echo ""
- echo "Usage: $0 [run_mode] [branch_name] [fe_mode]"
- echo ""
- echo "Arguments:"
- echo " run_mode - Deployment mode:"
- echo " 0 or empty = dry-run (show what would be deployed)"
- echo " 1 = deploy backend only"
- echo " 2 = deploy backend and frontend"
- echo " branch_name - Branch to deploy from (default: subscription-invoicing)"
- echo " Use 'trunk' or 'main' to deploy from SVN trunk"
- echo " fe_mode - Frontend mode for mode 2 (run|dry-run), optional"
- echo ""
- echo "Examples:"
- echo " $0 # Dry run with default branch"
- echo " $0 1 # Deploy backend from default branch"
- echo " $0 1 subscription-signup # Deploy backend from subscription-signup"
- echo " $0 2 subscription-signup # Deploy everything from subscription-signup"
- echo " $0 2 subscription-signup dry-run # Deploy backend, dry-run frontend"
- echo " $0 1 trunk # Deploy backend from SVN trunk"
- echo " $0 2 main # Deploy everything from SVN trunk (alias)"
- echo ""
- echo "Available branches:"
- # Use configurable base directory if set, otherwise default
- BRANCH_LIST_DIR="${AGM_BASE_DIR:-~/work/AgMission}/branches"
- BRANCH_LIST_DIR=$(eval echo "$BRANCH_LIST_DIR")
- ls "$BRANCH_LIST_DIR/" 2>/dev/null || echo "No branches directory found"
- echo ""
- echo "SVN Trunk location: ${AGM_BASE_DIR:-~/work/AgMission}/trunk/Development"
- exit 0
-fi
-
-# Validate run mode parameter
-if [ -n "$1" ]; then
- case "$1" in
- 0|1|2)
- # Valid parameters
- ;;
- *)
- echo "Error: Invalid run mode '$1'"
- echo ""
- echo "Supported run modes:"
- echo " 0 or empty = dry-run (show what would be deployed)"
- echo " 1 = deploy backend only"
- echo " 2 = deploy backend and frontend"
- echo ""
- echo "Use '$0 --help' for more information"
- exit 1
- ;;
- esac
-fi
-
-# Configurable paths - can be overridden by environment variables
-AGM_BASE_DIR="${AGM_BASE_DIR:-~/work/AgMission}"
-AGN_LIBS_DIR="${AGN_LIBS_DIR:-~/work/@agn}"
-AGM_DEST_HOST="${AGM_DEST_HOST:-agm@agmission-1.agnav.com}"
-AGM_DEST_PORT="${AGM_DEST_PORT:-22222}"
-AGM_DEST_PATH="${AGM_DEST_PATH:-/home/agm/apps}"
-
-# Expand tilde in paths
-AGM_BASE_DIR=$(eval echo "$AGM_BASE_DIR")
-AGN_LIBS_DIR=$(eval echo "$AGN_LIBS_DIR")
-
-# Display configuration
-echo "=== Path Configuration ==="
-echo "AGM Base Directory: $AGM_BASE_DIR"
-echo "AGN Libraries Directory: $AGN_LIBS_DIR"
-echo "Deployment Target: $AGM_DEST_HOST:$AGM_DEST_PORT"
-echo "Remote Destination Path: $AGM_DEST_PATH"
-echo "=========================="
-
-# Configuration - Easy to modify deployment sources
-DEFAULT_BRANCH="subscription-invoicing"
-BRANCH="${2:-$DEFAULT_BRANCH}" # Use second argument or default branch
-
-# Determine source path based on branch name
-if [ "$BRANCH" = "trunk" ] || [ "$BRANCH" = "main" ]; then
- SOURCE_ROOT="$AGM_BASE_DIR/trunk/Development"
- BRANCH_TYPE="trunk"
- DISPLAY_NAME="trunk (main)"
-else
- SOURCE_ROOT="$AGM_BASE_DIR/branches/$BRANCH"
- BRANCH_TYPE="branch"
- DISPLAY_NAME="branch: $BRANCH"
-fi
-
-# Validate that the source directory exists
-if [ ! -d "$SOURCE_ROOT" ]; then
- echo "Error: Source directory $SOURCE_ROOT does not exist"
- echo ""
- if [ "$BRANCH_TYPE" = "trunk" ]; then
- echo "Trunk directory not found. Expected: $AGM_BASE_DIR/trunk/Development"
- else
- echo "Available branches:"
- ls "$AGM_BASE_DIR/branches/" 2>/dev/null || echo "No branches directory found"
- echo ""
- echo "To deploy from trunk/main, use: $0 [mode] trunk"
- fi
- exit 1
-fi
-
-# Display current configuration
-echo "=== Deployment Configuration ==="
-echo "Source Type: $DISPLAY_NAME"
-echo "Source Path: $SOURCE_ROOT"
-echo "================================"
-
-# Use configurable AGN libraries path
-agnLibs="$AGN_LIBS_DIR/"
-
-# Set paths based on source type
-if [ "$BRANCH_TYPE" = "trunk" ]; then
- # Trunk structure (Development directory)
- agmFE="$SOURCE_ROOT/client/dist/"
- agmBE="$SOURCE_ROOT/server/"
-else
- # Branch structure
- agmFE="$SOURCE_ROOT/client/dist/"
- agmBE="$SOURCE_ROOT/server/"
-fi
-
-# Validate that required directories exist
-if [ ! -d "$agmFE" ]; then
- echo "Warning: Frontend dist directory not found: $agmFE"
- echo "You may need to build the frontend first: npm run build"
-fi
-
-if [ ! -d "$agmBE" ]; then
- echo "Error: Backend directory not found: $agmBE"
- exit 1
-fi
-
-# Use configurable base directory for track and GPS servers
-trackSrv="$AGM_BASE_DIR/trunk/Development/track-server/"
-gpsSrv="$AGM_BASE_DIR/trunk/Development/gps-server/"
-sharedMods="$AGM_BASE_DIR/trunk/Development/shared/"
-
-# Use configurable destination settings
-destUserHost="$AGM_DEST_HOST"
-destPort="$AGM_DEST_PORT"
-destRoot="$destUserHost:$AGM_DEST_PATH"
-
-curDate=$(date +\%Y\%m\%d)
-DIR="$(cd "$(dirname "$0")" && pwd)" # Get the current script directory
-echo "PWD: ${DIR}"
-
-# Create logs directory if not exists
-logdir=$DIR/logs
-ls $logdir &>/dev/null || mkdir $logdir
-
-# Default options for rsync, --size-only
-rsync_args=(-ahiz --stats --exclude-from='excludes.txt')
-sshCmd="ssh -p $destPort"
-
-# Determine run mode and frontend deployment flag
-RUN_MODE="dry-run" # Default to safe mode
-DEPLOY_FRONTEND=false
-FE_DRY_RUN=false
-
-# Normalize fe_mode: strip leading dashes so both "dry-run" and "--dry-run" work
-FE_MODE_ARG="${3#--}"
-
-case "${1:-0}" in
- ""|"0")
- RUN_MODE="dry-run"
- rsync_args+=(--dry-run)
- DEPLOY_FRONTEND=true # Show what would be deployed for frontend too
- ;;
- "1")
- RUN_MODE="run"
- DEPLOY_FRONTEND=false
- ;;
- "2")
- RUN_MODE="run"
- DEPLOY_FRONTEND=true
- # Check for frontend mode override (3rd parameter)
- if [ "$FE_MODE_ARG" = "dry-run" ]; then
- FE_DRY_RUN=true
- # Also apply dry-run to backend when fe_mode is dry-run
- RUN_MODE="dry-run"
- rsync_args+=(--dry-run)
- fi
- ;;
-esac
-
-# Make symlinks for assets of other languages, es and pt. etc.
-if [ ! -L $agmFE/es/assets ]; then
- echo "create assets link to ../assets/ in $agmFE/es/"
- cd $agmFE/es/
- rm -R assets
- ln -s ../assets/ assets
-fi
-if [ ! -L $agmFE/pt/assets ]; then
- echo "create assets link to ../assets/ in $agmFE/pt/"
- cd $agmFE/pt/
- rm -R assets
- ln -s ../assets/ assets
-fi
-
-# CD back to the current script directory
-cd $DIR
-#echo "$( cd "$( dirname "$0" )" && pwd )"
-
-echo "Run mode: $RUN_MODE"
-
-# Sync the base libs under /@agn
-echo "====== For @agn libs, run mode: $RUN_MODE"
-if [ "$RUN_MODE" != "dry-run" ]; then
- logfile=$logdir/@agn_$curDate.log
- rsync_ops="${rsync_args[@]} --log-file=$logfile"
-else
- rsync_ops="${rsync_args[@]}"
-fi
-rsync $rsync_ops $agnLibs $destRoot/@agn/ -e "${sshCmd}"
-
-# Sync Agmission BE
-echo "====== For Agmission BE, run mode: $RUN_MODE"
-if [ "$RUN_MODE" != "dry-run" ]; then
- logfile=$logdir/agmBE_$curDate.log
- rsync_ops="${rsync_args[@]} --log-file=$logfile"
-else
- rsync_ops="${rsync_args[@]}"
-fi
-rsync $rsync_ops $agmBE $destRoot/agmission/ -e "${sshCmd}"
-
-# Sync Agmission FE
-if [ "$DEPLOY_FRONTEND" = true ]; then
- echo "====== For Agmission FE, run mode: $RUN_MODE"
- if [ "$FE_DRY_RUN" = true ]; then
- fe_rsync_ops="${rsync_args[@]} --dry-run"
- echo "FE dry-run (frontend-specific dry-run mode)"
- elif [ "$RUN_MODE" = "run" ]; then
- logfile=$logdir/agmFE_$curDate.log
- fe_rsync_ops="${rsync_args[@]} --log-file=$logfile"
- echo "FE deployment enabled"
- else
- fe_rsync_ops="${rsync_args[@]} --dry-run"
- echo "FE dry-run (global dry-run mode)"
- fi
- rsync $fe_rsync_ops -l $agmFE $destRoot/agmission/dist-$curDate/ -e "${sshCmd}"
-else
- echo "====== For Agmission FE: SKIPPED (frontend deployment disabled)"
-fi
-## -l to copy symlinks as symlinks
-
-# Sync Track Server
-echo "====== For Track Server, run mode: $RUN_MODE"
-if [ "$RUN_MODE" != "dry-run" ]; then
- logfile=$logdir/trackSrv_$curDate.log
- rsync_ops="${rsync_args[@]} --log-file=$logfile"
-else
- rsync_ops="${rsync_args[@]}"
-fi
-rsync $rsync_ops $trackSrv $destRoot/track-server/ -e "${sshCmd}"
-
-# Sync GPS Server
-echo "====== For GPS Server, run mode: $RUN_MODE"
-if [ "$RUN_MODE" != "dry-run" ]; then
- logfile=$logdir/gpsSrv_$curDate.log
- rsync_ops="${rsync_args[@]} --log-file=$logfile"
-else
- rsync_ops="${rsync_args[@]}"
-fi
-rsync $rsync_ops $gpsSrv $destRoot/gps-server/ -e "${sshCmd}"
-
-# Sync Others such as maintainer
-
-# Run a script on the remote server to update the symlinks, node_modules and restart the pm2 apps
-# ssh -p $destPort $destUserHost "cd /home/agm/apps/agmission && ./deploy.sh $curDate"
-
-# Clean up logs older than 15 days
-if [ "$RUN_MODE" != "dry-run" ]; then
- find "$logdir" -name "*.log" -mtime +15 -exec rm {} \; # | xargs -r rm {} \;
-fi
diff --git a/Others/scripts/deploy/excludes.txt b/Others/scripts/deploy/excludes.txt
deleted file mode 100644
index 0e4a62e..0000000
--- a/Others/scripts/deploy/excludes.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-.vscodeignore
-**/node_modules/
-**/.svn
-**/apidoc/
-apidoc.json
-reports/
-**/docs*
-**/*md
-test*
-**/.tmp
-**/demo/
-.specstory/
-# NOTES: Patterns in excludes.txt must be relative to the source directory. E.g: Use package*.json if syncing starts from the /server directory.
-package*.json
-# **/track-server/package*.json
-# **/gps-server/package*.json
-# **/error-handler/package-lock.json
-# **/mailer/package-lock.json
-.eslintrc.json
-.vscode/
-.github/
-environment*.env
-**/emails/ref-template/
-downloadMap.*.html
-**/report-01*
-**/sample-job*
-README.md
-**/*.*log
-**/workers/custList*.json
-**/scripts/**.json
-migrateToSM.js
-track_*.json
-**/satloc/*
-*.csv
-*.txt
-*.pdf
-*.log
-cleanup_*.js
-debug*.js
-*_test.js
-csv_extract.js
-db-scripts.js
-satloc_usage_*.js
- setup_*
-start_*.js
-setup_partners.js
-_examples.js
-*-pm2.json
-
diff --git a/Others/scripts/gps_server-agnav-pm2.json b/Others/scripts/gps_server-agnav-pm2.json
deleted file mode 100644
index a9d0622..0000000
--- a/Others/scripts/gps_server-agnav-pm2.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "apps": [
- {
- "name": "gps-server-agnav-6080",
- "script": "gps-server.js",
- "args": [
- "--max-old-space-size 1536"
- ],
- "watch": false,
- "node_args": [],
- "merge_logs": false,
- "cwd": "/media/ssd1/agmission/gps-server/",
- "env": {
- "NODE_ENV": "development",
- "DEBUG": "gps-*",
- "PROTOCOL": "AGNAV",
- "LOG_DEBUG": "0",
- "LOG_RAW": "0",
- "LOG_IDS": "0000000001,0000000003, 0000000005"
- },
- "log_date_format": ""
- }
- ]
-}
diff --git a/Others/scripts/gps_server-rap-pm2.json b/Others/scripts/gps_server-rap-pm2.json
deleted file mode 100644
index 19dddb8..0000000
--- a/Others/scripts/gps_server-rap-pm2.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "apps": [
- {
- "name": "gps-server-RAP-6082",
- "script": "gps-server.js",
- "args": [
- "--max-old-space-size 1536"
- ],
- "watch": false,
- "node_args": [],
- "merge_logs": false,
- "cwd": "/media/ssd1/agmission/gps-server/",
- "env": {
- "NODE_ENV": "production",
- "DEBUG": "gps-*",
- "PROTOCOL": "RAP",
- "LOG_DEBUG": "0",
- "LOG_RAW": "0",
- "LOG_IDS": "0000000001,0000000003"
- },
- "log_date_format": ""
- }
- ]
-}
diff --git a/Others/scripts/harden server security.txt b/Others/scripts/harden server security.txt
deleted file mode 100644
index 70a449b..0000000
--- a/Others/scripts/harden server security.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-Resolved w/ following done:
-
-Stopped db brute-force and DoS attacks
-Hardened server security with configs ref: https://www.cyberciti.biz/tips/linux-security.html
-Prevented brute-force ssh password-guessing attack (ufw rules, change default ssh port, use iptables chain from rutgers university scripts, etc.)
-Tighten UFW firewall rules (allow only traffics for trusted agnav WAN IP, updated via dynamic DNS using a customer script)
-Notes: A good thing is with the auto update UFW script to periodical detect when AgNav WAN IP changed and updating related UFW firewall rule, we still do not have to add fixed static public IP to our service yet.
-
-Other references:
-
-https://unix.stackexchange.com/questions/91701/ufw-allow-traffic-only-from-a-domain-with-dynamic-ip-address
-https://report.cs.rutgers.edu/mrtg/drop/dropstat.cgi?start=-1week
-
-Added crontab tasks (root):
-# Check and update firewall rule to allow any traffic from AgNav
-*/5 * * * * /usr/local/sbin/update-agn-UFW.sh > /usr/local/sbin/update-agn-UFW.log
-
-# Check and update the iptables block chain (adds LCSRDrop) to prevent Bruteforce attackers, database from IPs registered at blocklist.de
-1-56/15 * * * * /usr/local/sbin/lcsrdrop.sh > /dev/null 2>&1
-
diff --git a/Others/scripts/job-importer-pm2.json b/Others/scripts/job-importer-pm2.json
deleted file mode 100755
index fc51b90..0000000
--- a/Others/scripts/job-importer-pm2.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "apps": [
- {
- "interpreter" : "node@14.17.2",
- "name": "job-importer",
- "script": "job_worker.js",
- "node_args" : ["--expose-gc", "--max-old-space-size=1048", "--nouse-idle-notification"],
- "watch": false,
- "exec_mode": "fork",
- "instances": 4,
- "cwd": "/media/ssd1/agmission/workers",
- "error_file": "~/.pm2/logs/err.log",
- "out_file": "~/.pm2/logs/out.log",
- "merge_logs": false,
- "env": {
- "NODE_ENV": "production",
- "PRODUCTION": 1,
- "DEBUG": "agm:*"
- },
- "max_restarts" : 5,
- "min_uptime" : "24h",
- "log_date_format": ""
- }
- ]
-}
diff --git a/Others/scripts/jsreport-pm2.json b/Others/scripts/jsreport-pm2.json
deleted file mode 100755
index 8dc60d7..0000000
--- a/Others/scripts/jsreport-pm2.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "apps": [
- {
- "name": "jsreport",
- "script": "server.js",
- "node_args" : ["--max-old-space-size=1048", "--nouse-idle-notification"],
- "watch": false,
- "exec_mode": "fork",
- "instances": 1,
- "cwd": "/media/ssd1/jsreport",
- "merge_logs": true,
- "env": {
- "NODE_ENV": "production"
- },
- "log_date_format": ""
- }
- ]
-}
diff --git a/Others/scripts/lcsrdrop.sh b/Others/scripts/lcsrdrop.sh
deleted file mode 100755
index 9ef1ac5..0000000
--- a/Others/scripts/lcsrdrop.sh
+++ /dev/null
@@ -1,456 +0,0 @@
-#!/bin/bash
-
-#originally spamhaus drop script.
-#modified by Hanz Makmur 2015-03-13
-#using LCSR DROP file add to iptables
-# locking added by daw; 9/14/16
-# logger added by daw; 10/2/17
-# efficiency improvement: instead of flushing and re-adding entire list,
-# only delete expired entries and add new ones -- daw, 10/23/17
-# switch to using curl, iptables-{save,restore}; 4/25/18
-# syslog "version";, refuse to run if not on public internet; 10/11/18
-# check status after iptables commands; syslog potential reason on file retrieval failure;
-# defend against iptables-restore errors; 10/12/18
-# begin ipset mod (indicate presence or absence of ipset in SUMN); 10/19/18
-# continue ipset mod (maintain LCSRDrop ipset); 10/23/18
-# finish ipset mod (if ipset present, remove LCSRDrop chain, don't maintain LCSRDrop chain,
-# and add LOG_AND_DROP chain using LCSRDrop ipset); 11/7/18
-# ipset might be in /sbin rather than /usr/sbin; 11/12/18
-# insert LOG_AND_DROP chain at beginning rather then end; 11/12/18
-# use wget if curl is not available; 12/7/18
-# save temp copy of iptables -L for debugging if removing chain in favor of ipset; 12/9/18
-# save a copy of ipset restore file if ipset restore fails; 12/11/18
-# use timeout for curl/wget if available; 1/17/19
-# avoid buggy timeout on Fedora Core 3 (dhcp2.srv.lcsr) ; 1/22/19
-# workaround for buggy timeout on Fedora Core 3 (dhcp2.srv.lcsr) ; 1/23/19
-# don't assume LOG_AND_DROP chain exists if LCSRDrop ipset does ; 4/21/19
-# install ipset if needed and available ; 4/25/19
-# add "-exist" to ipset del commands ; 4/25/19
-# make sure 0 return status from yum info means ipset is available ; 4/26/19
-# "yum info" needs "timeout -s 9" on Fedora Core 3 (dhcp2.srv.lcsr) ; 4/26/19
-# avoid flurry of errors due to simultaneous "iptables restart" ; 4/27/19
-# drop previous edit -- reschedule or eliminate "iptables restart" ; 5/7/19
-# fix apt/apt-get code; 8/12/19
-# add a clue when refusing to run; 8/13/19
-# remove s from https because old SSL library ; 6/23/21
-
-# path to iptables
-export PATH=$PATH:/sbin # sbin needed fot apt on klinzhai.rutgers.edu
-IPTABLES="/sbin/iptables";
-IPSAVE="/sbin/iptables-save";
-IPRESTORE="/sbin/iptables-restore";
-if [ -e /usr/sbin/ipset ]; then IPSET=/usr/sbin/ipset ; fi
-if [ -e /sbin/ipset ]; then IPSET=/sbin/ipset ; fi
-CURL="`which curl`";
-WGET="`which wget`";
-if [ -e /usr/bin/timeout ]; then TIMEOUT=/usr/bin/timeout ; fi
-if [ -e /bin/timeout ]; then TIMEOUT=/bin/timeout ; fi
-if [ $TIMEOUT"x" != "x" ]; then
- TIMEOUT9="$TIMEOUT -s 9 15"
- TIMEOUT="$TIMEOUT 15"
-fi
-
-# if timeout is broken here, don't use it
-# (switch within command confuses timeout on FC3)
-# I found a workaround. Put command into a file
-#$TIMEOUT echo x -x > /dev/null 2>&1
-#if [ $? -ne 0 ]; then TIMEOUT="" ; fi
-LOGGER="`which logger`";
-BASENAME="`which basename`";
-BASE="`$BASENAME $0`";
-TMP="/tmp/$BASE.$$";
-NOIPSETF=/tmp/$BASE.noipset
-DQ='"'
-
-SUM=`which sum`
-# use SUMSEP to indicate whether using ipset
-SUMSEP='-'
-if [ $IPSET"x" != "x" ]; then SUMSEP="+"; fi
-SUMN=`$SUM $0 | sed "s; *;$SUMSEP;"`
-BASEDATE=`grep '; *[0-9]*/[0-9]*/[0-9]* *$' $0 | tail -1 | awk '{print $NF}'`
-VFILE=/tmp/$BASE.version
-
-# list of known IPs
-URL="http://report.rutgers.edu/DROP/attackers";
-
-# save local copy here
-FILE="/tmp/attackers.drop"
-
-# iptables custom chain
-CHAIN="LCSRDrop";
-
-# ipset custom set
-SETNAME="LCSRDrop";
-
-# lockfile
-LOCKF=/tmp/lcsrdrop.lock
-
-# create temp lockfile
-( echo $$ > $LOCKF.$$ ) > /dev/null 2>&1
-
-# put that in real lockfile place of that doesn't exist already
-if [ ! -f $LOCKF ]; then
- /bin/mv $LOCKF.$$ $LOCKF > /dev/null 2>&1
-fi
-
-# see who now has the lock
-if [ -f $LOCKF ]; then
- if [ -r $LOCKF ]; then
- LPID=`cat $LOCKF`
- else
- LPID=1 # if we cannot read file, use init's pid
- fi
-else
- LPID=1 # if we cannot create file, use init's pid
-fi
-
-# if it's not us, try removing lockfile if that process no longer exists
-# and clean up after self
-if [ $$ != "$LPID" ]; then
- /bin/ps -p $LPID > /dev/null
- if [ $? -ne 0 ]; then
- /bin/rm -f $LOCKF > /dev/null 2>&1
- fi
- /bin/rm -f $LOCKF.$$
- exit 1
-fi
-
-# Check if version changed. If so, log it
-if [ ! -e $VFILE ]; then touch $VFILE ; fi
-echo "$SUMN ($BASEDATE)" > $VFILE.new
-diff $VFILE $VFILE.new > /dev/null
-if [ $? -ne 0 ]; then
- LOGVERSION=1
-else
- LOGVERSION=0
-fi
-
-# If the date has changed, log the version as well
-DN=`/bin/ls -l $VFILE $VFILE.new | awk '{print $6,$7}' | sort -u | wc -l`
-if [ $DN -ne 1 ]; then LOGVERSION=1 ; fi
-/bin/mv -f $VFILE.new $VFILE
-
-if [ $LOGVERSION -eq 1 ]; then
- $LOGGER -t$BASE -pdaemon.err "SUMN=$SUMN ($BASEDATE)"
- echo `date` "SUMN=$SUMN ($BASEDATE)"
-fi
-
-# commented R private net assert to test
-: <<'END'
-/sbin/ip addr show | egrep "128\.6\.|165\.230\." > /dev/null
-if [ $? -ne 0 ] && [ ! -e $0.run-in-private-IP-space ]; then
-# $LOGGER -t$BASE -pdaemon.err "refusing to run in private IP space"
- IP=`ifconfig -a | grep 'inet 172\.' | sed -e 's;.* inet ;;' -e 's; .*;;'`
- $LOGGER -t$BASE -pdaemon.err "refusing to run in private IP space ($IP)"
- echo $BASE need not be run on Rutgers private IP space
- exit 0
-fi
-END
-
-# make sure ipset is installed if possible
-if [ $IPSET"x" == "x" -a ! -e $NOIPSETF ]; then
- if [ -e /usr/bin/apt ]; then APT=/usr/bin/apt ; fi
- if [ -e /bin/apt ]; then APT=/bin/apt ; fi
-# if [ -e /usr/bin/apt-get ]; then APTGET=/usr/bin/apt-get ; fi
-# if [ -e /bin/apt-get ]; then APTGET=/bin/apt ; fi
- if [ -e /usr/bin/yum ]; then YUM=/usr/bin/yum ; fi
- if [ -e /bin/yum ]; then YUM=/bin/yum ; fi
- if [ $APT"x" == "x" -a $YUM"x" == "x" ]; then
- $LOGGER -t$BASE -pdaemon.err "Neither apt nor yum found"
- fi
-# if [ $APT"x" != "x" -a $APTGET"x" != "x" ]; then
- if [ $APT"x" != "x" ]; then
- $APT show ipset > /dev/null 2>&1
- if [ $? -eq 0 ]; then
-# echo `date +%T` "Installing ipset using apt-get"
-# $LOGGER -t$BASE -pdaemon.err "Installing ipset using apt-get"
-# $APTGET install -y ipset
- echo `date +%T` "Installing ipset using apt"
- $LOGGER -t$BASE -pdaemon.err "Installing ipset using apt"
-# Avoid sometimes exiting install with status 100
- $APT update
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$APT update returned status $STATUS"
- fi
- $APT install -y ipset
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
-# $LOGGER -t$BASE -pdaemon.err "$APTGET install -y ipset returned status $STATUS"
- $LOGGER -t$BASE -pdaemon.err "$APT install -y ipset returned status $STATUS"
- else
- if [ -e /usr/sbin/ipset ]; then IPSET=/usr/sbin/ipset ; fi
- if [ -e /sbin/ipset ]; then IPSET=/sbin/ipset ; fi
- fi
- else
- touch $NOIPSETF
- fi
- fi
- if [ $YUM"x" != "x" ]; then
- $TIMEOUT9 $YUM info ipset > $TMP 2>&1
- if [ $? -eq 0 ]; then
- grep ipset $TMP /dev/null 2>&1
- if [ $? -eq 0 ]; then
- echo `date +%T` "Installing ipset using yum"
- $LOGGER -t$BASE -pdaemon.err "Installing ipset using yum"
- $YUM install -y ipset
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$YUM install -y ipset returned status $STATUS"
- else
- if [ -e /usr/sbin/ipset ]; then IPSET=/usr/sbin/ipset ; fi
- if [ -e /sbin/ipset ]; then IPSET=/sbin/ipset ; fi
- fi
- else
- touch $NOIPSETF
- fi
- else
- touch $NOIPSETF
- fi
- fi
-fi
-
-# check to see if the chain already exists
-$IPTABLES -L $CHAIN -n > /dev/null 2>&1
-ILCSTATUS=$?
-if [ $ILCSTATUS -ne 0 ]; then
-# only create chain if ipset not present
- if [ $IPSET"x" == "x" ]; then
- echo `date +%T` "Chain $CHAIN not detected. Creating new chain...."
-
- # create a new chain set
- $IPTABLES -N $CHAIN
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPTABLES -N $CHAIN returned status $STATUS"
- fi
-
- # tie chain to input rules so it runs
- $IPTABLES -A INPUT -j $CHAIN
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPTABLES -A INPUT -j $CHAIN returned status $STATUS"
- fi
-
- # don't allow this traffic through
- $IPTABLES -A FORWARD -j $CHAIN
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPTABLES -A FORWARD -j $CHAIN returned status $STATUS"
- fi
- fi
-else
-# chain exists -- remove it if ipset present
- if [ $IPSET"x" != "x" ]; then
-# Save an old copy of this for debugging, just in case...
- echo `date +%T` "Saving old copy of $IPSET -L -n"
- $LOGGER -t$BASE -pdaemon.err "Saving old copy of $IPSET -L -n"
- $IPTABLES -L -n > /tmp/$BASE.iptables-L-n 2>&1
- echo `date +%T` "$IPSET exists. Removing chain $CHAIN...."
- $IPTABLES -D FORWARD -j $CHAIN
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPTABLES -D FORWARD -j $CHAIN returned status $STATUS"
- fi
-
- $IPTABLES -D INPUT -j $CHAIN
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPTABLES -D INPUT -j $CHAIN returned status $STATUS"
- fi
-
- $IPTABLES -F $CHAIN
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPTABLES -F $CHAIN returned status $STATUS"
- fi
-
- $IPTABLES -X $CHAIN
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPTABLES -X $CHAIN returned status $STATUS"
- fi
- fi
-fi;
-
-# Create LCSRDrop ipset if it does not exist
-
-if [ $IPSET"x" != "x" ]; then
- $IPSET list $SETNAME > /dev/null 2>&1
- if [ $? -ne 0 ]; then
- echo `date +%T` "set $SETNAME not detected. Creating new set...."
-
- # create new ipset set
- $IPSET create $SETNAME iphash
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPSET create $SETNAME iphash returned status $STATUS"
- fi
- fi
-
-# Make sure LOG_AND_DROP chain exists
-
-# $IPTABLES -L LOG_AND_DROP -n > /dev/null 2>&1
-# On report.cs at 0700, this sometimes does not detect LOG_AND_DROP because of simultaneous "iptables restart"
-# No simple way to avoid this other than rescheduling (or removing) "iptables restart"
- $IPTABLES -L LOG_AND_DROP -n > $TMP 2>&1
- if [ $? -ne 0 ]; then
- echo " error from $IPTABLES -L LOG_AND_DROP -n:"
- sed 's;.; &;' $TMP
-
- echo `date +%T` "Chain LOG_AND_DROP not detected. Creating new chain...."
-
- # create LOG_AND_DROP chain
- $IPTABLES -N LOG_AND_DROP
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPTABLES -N LOG_AND_DROP returned status $STATUS"
- fi
-
- $IPTABLES -A LOG_AND_DROP -m limit --limit 10/min -j LOG --log-prefix "LCSRDropped: "
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPTABLES -A LOG_AND_DROP -m limit --limit 10/min -j LOG --log-prefix ${DQ}LCSRDropped: $DQ returned status $STATUS"
- fi
-
- $IPTABLES -A LOG_AND_DROP -j REJECT
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPTABLES -A LOG_AND_DROP -j REJECT returned status $STATUS"
- fi
-
-# $IPTABLES -A INPUT -m set --match-set $SETNAME src -j LOG_AND_DROP
-# Put this at the beginning rather than the end
- $IPTABLES -I INPUT -m set --match-set $SETNAME src -j LOG_AND_DROP
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPTABLES -I INPUT -m set --match-set $SETNAME src -j LOG_AND_DROP returned status $STATUS"
- fi
- fi
-fi
-
-# make sure $FILE does not exist
-if [ -e $FILE ]; then
- /bin/mv -f $FILE $FILE.old
-fi;
-
-# get a copy of the drop list
-echo `date +%T` "Retrieving copy of attackers data...."
-if [ $CURL"x" != "x" ]; then
-# $TIMEOUT $CURL -k -s $URL -o $FILE
-# Put command into file so buggy timeout won't see it under FC3
- echo $CURL -k -s $URL -o $FILE > $TMP
- $TIMEOUT /bin/sh $TMP
- CSTATUS=$?
-else
-# if curl doesn't exist, use wget
- $TIMEOUT $WGET -q --no-check-certificate -O $FILE $URL
- CSTATUS=$?
-fi
-
-# make sure it has nothing but IPs
-grep -v '^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$' $FILE > /dev/null
-CJSTATUS=$?
-
-# check to see if curl succeeded and did not get junk
-if [ $CSTATUS -eq 0 -a $CJSTATUS -eq 1 ]; then
-
- /bin/rm -f $FILE.old
-
-else
- # use old data
- /bin/mv -f $FILE $FILE.fail
- /bin/mv -f $FILE.old $FILE
-
- STRING=` grep '' $FILE.fail | sed -e 's;.*;;' -e 's:<.*:; :'`
-
- echo `date +%T` "Retrieval of attackers data failed. Reusing old data...."
- $LOGGER -t$BASE -pdaemon.err "Retrieval of attackers data failed ($STRING$CSTATUS,$CJSTATUS). Reusing old data...."
-fi;
-
-# Maintain LCSRDrop ipset if ipset program exists
-
-if [ $IPSET"x" != "x" ]; then
- $IPSET list $SETNAME > $TMP.ipset 2>&1
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- echo `date +%T` "$IPSET list $SETNAME returned status $STATUS"
- $LOGGER -t$BASE -pdaemon.err "$IPSET list $SETNAME returned status $STATUS"
- fi
- grep '^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$' $TMP.ipset | sort -u > $TMP.old
- sort -u $FILE > $TMP.new
- comm -23 $TMP.{old,new} > $TMP.remove
- comm -13 $TMP.{old,new} > $TMP.add
- sed "s;.;del -exist $SETNAME &;" $TMP.remove > $TMP.restore
- sed "s;.;add -exist $SETNAME &;" $TMP.add >> $TMP.restore
- if [ -s $TMP.restore ]; then
- echo `date +%T` "restoring new $SETNAME ipset...."
- $IPSET restore < $TMP.restore
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPSET restore < $TMP.restore returned status $STATUS"
- echo `date +%T` "Saving copy of failed restore file"
- $LOGGER -t$BASE -pdaemon.err "Saving copy of failed restore file"
- /bin/mv $TMP.restore /tmp/$BASE.ipset.restore.failed
- fi
- else
- echo `date +%T` "No changes $SETNAME ipset...."
- fi
-fi
-
-# Old code maintains LCSRDrop chain if ipset not present
-
-if [ $IPSET"x" == "x" ]; then
- # save current iptables rules with/without LCSRDRop
- $IPSAVE > $TMP.old
- SSTATUS=$?
- if [ $SSTATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPSAVE > $TMP.old returned status $SSTATUS"
- fi
- $IPSAVE | grep -v '^-A LCSRDrop' > $TMP
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- $LOGGER -t$BASE -pdaemon.err "$IPSAVE | grep -v '^-A LCSRDrop' > $TMP returned status $STATUS"
- fi
-
-# build new save file to load
-
- CLINE=`grep -n "COMMIT" $TMP | tail -1 | sed 's;:.*;;'`
- (( CLINEm1=$CLINE-1 ))
-
- head -$CLINEm1 $TMP > $TMP.new
- sed 's;.*;-A LCSRDrop -s &/32 -j DROP;' $FILE >> $TMP.new
- tail --lines=+$CLINE $TMP >> $TMP.new
-
- diff $TMP.{old,new} > /dev/null
-
- if [ $? -ne 0 ]; then
-
- echo `date +%T` "restoring new iptables rules...."
- # "restore" new iptables rules
- $IPRESTORE < $TMP.new
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- echo `date +%T` "restore failed"
- $LOGGER -t$BASE -pdaemon.err "$IPRESTORE < $TMP.new returned status $STATUS"
- if [ $SSTATUS -eq 0 ]; then
- echo " restoring to saved state"
- $LOGGER -t$BASE -pdaemon.err "Restoring to saved state"
- $IPRESTORE < $TMP.old
- STATUS=$?
- if [ $STATUS -ne 0 ]; then
- echo `date +%T` "that restore failed too"
- $LOGGER -t$BASE -pdaemon.err "$IPRESTORE < $TMP.old returned status $STATUS"
- fi
- fi
- fi
-
- else
-
- echo `date +%T` "No changes in iptables rules...."
-
- fi;
-fi
-
-# remove lockfile (and possible leftover temp lockfile) and temp files
-/bin/rm -f $LOCKF $LOCKF.$$ $TMP*
diff --git a/Others/scripts/maintainer-pm2.json b/Others/scripts/maintainer-pm2.json
deleted file mode 100644
index f4b68f6..0000000
--- a/Others/scripts/maintainer-pm2.json
+++ /dev/null
@@ -1,37 +0,0 @@
-{
- "apps": [
- {
- "name": "maintainer",
- "cwd": "/media/ssd1/maintainer/",
- "script": "index.js",
- "node_args": [
- "--expose-gc",
- "--max-old-space-size=1048",
- "--nouse-idle-notification"
- ],
- "exec_mode": "fork",
- "instances": 1,
- "env": {
- "NODE_ENV": "production",
- "DISPLAY": ":99",
- "DEBUG": "agm:*,maintainer:*"
- },
- "watch": false,
- "ignore_watch": [
- "[\/\\]\\./",
- "node_modules",
- ".tmp"
- ],
- "merge_logs": false,
- "max_restarts": 5,
- "min_uptime": "24h",
- },
- {
- "name": "Xvfb",
- "interpreter": "none",
- "script": "Xvfb",
- "args": ":99"
- }
- ]
-}
-
diff --git a/Others/scripts/mongo-scripts/aggregation.js b/Others/scripts/mongo-scripts/aggregation.js
deleted file mode 100644
index be9cf48..0000000
--- a/Others/scripts/mongo-scripts/aggregation.js
+++ /dev/null
@@ -1,125 +0,0 @@
-db.getCollection('job_logs').find({ job: 54}).sort({ job: 1, date: -1})
-
-var jobs = [177];
-db.getCollection('job_logs').aggregate(
- [
- { $match: { job: { $in: jobs } , type: 2 }},
- { $sort: { user: 1, date: -1 }},
- {
- $lookup: {
- from: 'users',
- localField: 'user',
- foreignField: '_id',
- as: 'userD'
- }
- },
-// {
-// $match: {
-// 'userD.type': 9
-// }
-// },
- {
- $project: {
- "date": 1,
- "username": "$userD.username"
- }
- },
- { $unwind: '$username'},
- {
- $group:
- {
- _id: "$username",
- date: { $first: "$date" }
- }
- },
- {
- $project: {
- "user": "$_id",
- "date": 1,
- "_id": 0,
- }
- }
- ]
-);
-
-// Get all Customer info
-db.customers.aggregate([
- {
- $lookup: {
- from: "users",
- localField: "user",
- foreignField: "_id",
- as: "user_detail"
- }
- },
-]);
-
-db.customers.aggregate([
- {
- $lookup:
- {
- from: "users",
- localField: "user",
- foreignField: "_id",
- as: "user_detail"
- }
- },
- { $unwind: { path: "$user_detail" } },
- { $project:
- {
- name: 1,
- contact: 1,
- phone: 1,
- email: 1,
- username: "$user_detail.username",
- password: "$user_detail.password",
- }
- }
-]);
-
-///////////////////////////////////////////////////////
-var sprayIds = [
- "5c646e5d1e53e006dd0b76c5",
- "5c646e5d1e53e006dd0b76a7",
- "5c646e5d1e53e006dd0b76a6",
- "5c646e5d1e53e006dd0b76a5",
- "5c646e5d1e53e006dd0b76a4",
- "5c646e5d1e53e006dd0b76a3",
- "5c646e5d1e53e006dd0b76a2",
- "5c646e5d1e53e006dd0b76a1"
-];
-sprayIds = sprayIds.map(i => ObjectId(i));
-var pipeline = [
- {
- "$match": {
- _id: 64
- }
- },
- {
- "$unwind": "$sprayAreas"
- },
- {
- "$match": {
- "sprayAreas._id": { $in: sprayIds}
- }
- },
- {
- "$group": {
- "_id": "$_id",
- "sprayAreas": { $push: "$sprayAreas" }
- }
- }
-];
-db.jobs.aggregate(pipeline);
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Others/scripts/mongo-scripts/common_scripts.js b/Others/scripts/mongo-scripts/common_scripts.js
deleted file mode 100644
index d8f4b09..0000000
--- a/Others/scripts/mongo-scripts/common_scripts.js
+++ /dev/null
@@ -1,141 +0,0 @@
-var jobId = 62;
-// db.applications.find({ "jobId": jobId })
-var appIds = db.applications.find({ "jobId": jobId }, { "_id": 1 }).map(item => item._id);
-// print (appIds)
-// db.application_details.remove({'appId':{'$in': apps}},function(){ });
-// db.applications.remove({ "jobId": jobId });
-db.appfiles.find({ appId: {$in: appIds}});
-var fileIds = db.appfiles.find({ appId: {$in: appIds}}, { _id: 1}).map(it => it._id);
-// print (fileIds);
-// db.appfiles.remove({ appId: {$in: appIds}});
-
-// Filter spray data by fileIds
-// var fileIds = [ObjectId("5b27bc30c3f755062ee1ad10"), ObjectId("5b27bc30c3f755062ee1ad0a")];
-// db.application_details.find({ fileId: {$in: fileIds}}, {_id: 0, gpsTime:1});
-db.application_details.count({ fileId: {$in: fileIds}}, {_id: 0, gpsTime:1, sprayStat:1, satsIn:1, lat:1, lon: 1});
-
-// db.application_details.aggregate(
-// [
-// { $match: { fileId: {$in: fileIds} } },
-// { $sort: { gpsTime : 1} },
-// {
-// $group:
-// {
-// _id: "Time",
-// first: { $first: "$gpsTime" },
-// last: { $last: "$gpsTime" }
-// }
-// }
-// ]
-// )
-//
-
-
-// var apps = [];
-// var appC = db.applications.find({ jobId: 111}, { _id: 1 });
-// appC.forEach(item => {
-// apps.push(item._id);
-// });
-// print(apps);
-// var result = db.application_details.remove({'appId':{'$in': apps}}, function() {
-// });
-// print(result);
-// db.applications.remove({ jobId: 111});
-
-var kmlcoors = [];
-var appC = db.jobs.find({ _id: 111}, { excludedAreas: 1 });
-appC.forEach(item => {
- item.excludedAreas.forEach(geo => {
- geo.geometry.coordinates[0].forEach(coor => {
- kmlcoors.push((coor[0] + ',' + coor[1] + ',0'));
- });
- });
-});
-
-print(kmlcoors.join(' '));
-
-// Change job Timestamp document field name to default
-//db.jobs.update({}, { $rename : { "createDate" : "createdAt" }}, false, true );
-
-// Find max document size in a collection
-var max = 0;
-db.jobs.find().forEach(function(obj) {
- var curr = Object.bsonsize(obj);
- if(max < curr) {
- max = curr;
- }
-})
-print(max * 1e-6 + " MB")
-
-// Aug.16/2019
-// Find orphan entities - products
-var pIds = db.products.aggregate([
- {
- $lookup:
- {
- from: "users",
- localField: "byPuid",
- foreignField: "_id",
- as: "user_detail"
- }
- },
- {
- $match: {
- "user_detail": { $size: 0 }
- }
- },
- {
- $project: {
- "_id": 1
- }
- }
-]).toArray();
-pIds = pIds.map(i => i._id);
-// print (pIds);
-// db.pilots.find({ _id : { $in: pIds } });
-db.products.deleteMany({ _id : { $in: pIds } });
-
-// 1. Update product => products with new data fields
-db.products.update({ type: null }, { $set: { type: 1, restricted: false, epaReg: '' }, { multi: true } });
-
-// 2. Create default one Water product if not any (by customer)
-var userIds = db.products.aggregate([
-{
- $lookup:
- {
- from: "users",
- localField: "byPuid",
- foreignField: "_id",
- as: "user_detail"
- }
-},
-{
- $unwind: { path: "$user_detail" }
-},
-{
- $match: { $and:[ { name: 'Water' }, { 'user_detail.type' : 1 } ] }
-},
-{
- $group: {
- _id: '$byPuid'
- }
-}
-]).toArray().map(i => i._id);
-// print (userIds)
-// db.customers.find({ user: { $nin: userIds } });
-var users = db.customers.find({ user: { $nin: userIds } }, { user: 1, _id: 0 }).toArray();
-var newProds = [];
-for (var i = 0; i < users.length; i++) {
- newProds.push({ name: 'Water', type: 9, restricted: false, epaReg: '', byPuid: users[i].user });
-}
-db.products.insertMany(newProds);
-
-// 3. Update product to products for each job
-var jobwProds = db.jobs.find({ $and: [ { product: { $ne: null }}, { $or: [{ products: null }, { products: { $size : 0 } } ] } ] }, { product: 1, appRate: 1, measureUnit: 1, appRateUnit: 1 }).toArray();
-// db.jobs.find({ $and: [ { product: { $ne: null }}, { products: null } ] }, { appRateUnit: 1 }).toArray();
-// print (jobwProds)
-jobwProds.forEach(jp => {
-// print ({ job: jp._id, product: jp.product, rate: jp.appRate, unit: jp.appRateUnit })
- db.jobs.update({ _id: jp._id }, { $set: { products: [ { product: jp.product, rate: jp.appRate, unit: jp.appRateUnit } ] } });
-});
-
diff --git a/Others/scripts/start_pm2_apps.sh b/Others/scripts/start_pm2_apps.sh
deleted file mode 100644
index a26de42..0000000
--- a/Others/scripts/start_pm2_apps.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-## Agmission servers ##
-$scriptsPath='/home/agm/apps/pm2-apps/'
-
-pm2 startOrRestart $scriptsPath/agmission-pm2.json --env production
-pm2 startOrRestart $scriptsPath/job-importer-pm2.json --env production
-#pm2 startOrRestart $scriptsPath/maintainer-pm2.json --env production
-pm2 startOrRestart $scriptsPath/cleanup_worker-pm2.json --env production
-pm2 startOrRestart $scriptsPath/invoice_worker-pm2.json --env production
-
-## Tracking, GPS servers ##
-pm2 startOrRestart $scriptsPath/track_server-pm2.json --env production
-pm2 startOrRestart $scriptsPath/gps_server-agnav-pm2.json --env production
-pm2 startOrRestart $scriptsPath/gps_server-rap-pm2.json --env production
-
diff --git a/Others/scripts/track_server-pm2.json b/Others/scripts/track_server-pm2.json
deleted file mode 100644
index 4b3e6f8..0000000
--- a/Others/scripts/track_server-pm2.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "apps": [
- {
- "name": "tracker-server-6100",
- "script": "track-server.js",
- "args": [
- "--max-old-space-size 1536"
- ],
- "watch": false,
- "node_args": [],
- "merge_logs": false,
- "cwd": "/media/ssd1/agmission/track-server/",
- "env": {
- "NODE_ENV": "production",
- "DEBUG": "track-*",
- "UV_THREADPOOL_SIZE": "8"
- },
- "log_date_format": ""
- }
- ]
-}
diff --git a/Others/scripts/update-agn-UFW.sh b/Others/scripts/update-agn-UFW.sh
deleted file mode 100755
index 02d534f..0000000
--- a/Others/scripts/update-agn-UFW.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-HOSTNAME='agnhome.agnav.com'
-
-if [[ $EUID -ne 0 ]]; then
- echo "This script must be run as root"
- exit 1
-fi
-
-new_ip=$(host $HOSTNAME | tail -n1 | cut -f4 -d ' ')
-old_ip=$(/usr/sbin/ufw status | grep $HOSTNAME | head -n1 | tr -s ' ' | cut -f3 -d ' ')
-
-if [ "$new_ip" = "$old_ip" ] ; then
- echo IP address has not changed
-else
- echo newIP: $new_ip
- if [ -n "$old_ip" ] ; then
- /usr/sbin/ufw delete allow from $old_ip to any
- fi
- /usr/sbin/ufw insert 1 allow from $new_ip to any comment $HOSTNAME
- echo UFW rules have been updated
-fi
diff --git a/Others/scripts/updateAppStats.js b/Others/scripts/updateAppStats.js
deleted file mode 100644
index cbe71bd..0000000
--- a/Others/scripts/updateAppStats.js
+++ /dev/null
@@ -1,87 +0,0 @@
-Number.prototype.fixedDown = function(digits) {
- var re = new RegExp("(\\d+\\.\\d{" + digits + "})(\\d)"),
- m = this.toString().match(re);
- return m ? parseFloat(m[1]) : this.valueOf();
-};
-
-//{ $and: [{ totalSprayTime: null, totalSprayed : { $gt: 1 } }]}
-var apps = db.applications.find({ $and: [{ totalFlightTime: null, totalSprayed : { $gt: 1 } }]}).toArray();
-var app;
-for(var k = 0; k < apps.length; k++) {
- app = apps[k];
- var totalSprTime = 0, totalTurn = 0, totalTime = 0;
- var files = db.appfiles.find({ appId: app._id }).toArray();
-
- for(var i = 0; i < files.length; i++) {
- var turnTime = { line: null, at: null, nextOff: false, total: 0 };
- var timeDif = 0, prevTime = prevSprTime = -999, record;
- var details = db.application_details.find({ fileId: files[i]._id }, { _id: 0, gpsTime: 1, llnum: 1, sprayStat: 1 }).sort({ gpsTime: 1 }).toArray();
-
- for(var j = 0; j < details.length; j++) {
- record = details[j];
-
- // Calculate total flight time
- if (prevTime != -999 && prevTime !== record.gpsTime) {
- timeDif = record.gpsTime - prevTime;
- if (timeDif < 0) {
- if (Math.abs(timeDif) >= 0.1)
- timeDif = (86400 - prevTime) + record.gpsTime;
- }
- if (timeDif > 0 && timeDif <= 120)
- totalTime += timeDif;
- }
- prevTime = record.gpsTime;
-
- // Calculate spray time (secs)
- if (record.sprayStat > 0) {
- if (prevSprTime != -999 && record.sprayStat !== 3) {
- timeDif = record.gpsTime - prevSprTime;
- if (timeDif > 0 && timeDif <= 120)
- totalSprTime += timeDif;
- }
- prevSprTime = record.gpsTime;
- }
-
- if (!(/.asc/i.test(files[i].name))) {
- // Calculate turn time (secs)
- if (null === turnTime.line && !record.sprayStat) {
- turnTime.line = record.llnum;
- turnTime.at = record.gpsTime;
- continue;
- }
- if (turnTime.line != record.llnum) {
- if (record.sprayStat) {
- timeDif = record.gpsTime - turnTime.at;
- if (timeDif < 0) {
- if (Math.abs(timeDif) >= 0.1)
- timeDif = (86400 - turnTime.at) + record.gpsTime;
- }
- if (timeDif >= 5 && timeDif <= 120)
- turnTime.total += timeDif;
-
- turnTime.line = record.llnum;
- turnTime.nextOff = true;
- }
- }
- else {
- if (!record.sprayStat && turnTime.nextOff) {
- turnTime.at = record.gpsTime;
- turnTime.nextOff = false;
- } else if (record.sprayStat) {
- turnTime.nextOff = true;
- }
- }
- }
- }
-
- if (!isNaN(turnTime.total))
- totalTurn += turnTime.total;
- }
- // print ('app: ' + app._id + ', totalSpray: ' + totalSprTime + ', totalTurn: ' + totalTurn);
- // Update the app with the calculated totals of: sprayTime and turnTurnTime
- db.applications.updateOne({ _id: app._id },
- { $set : { totalSprayTime: totalSprTime.fixedDown(2), totalTurnTime: totalTurn.fixedDown(2), totalFlightTime: totalTime.fixedDown(2) } });
-}
-
-print ("DONE updating appStats :) for " + apps.length + " apps")
-
diff --git a/Others/scripts/updateGpsTime.js b/Others/scripts/updateGpsTime.js
deleted file mode 100644
index ad974bd..0000000
--- a/Others/scripts/updateGpsTime.js
+++ /dev/null
@@ -1,25 +0,0 @@
-var bulk = db.application_details.initializeUnorderedBulkOp();
-var ops = 0, myDoc;
-var docs = db.application_details.find({ gpsTime: { $type: 'string' } }).limit(10000).toArray();
-while (docs && docs.length) {
- for(var i = 0; i< docs.length; i++) {
- myDoc = docs[i];
- bulk.find({ _id: myDoc._id }).updateOne(
- {
- $set : { gpsTime: parseFloat(myDoc.gpsTime) }
- }
- );
- if ((++ops % 1000) === 0){
- print (ops);
- bulk.execute();
- bulk = db.application_details.initializeUnorderedBulkOp();
- }
- }
- bulk.execute();
-
- docs = db.application_details.find({ gpsTime: { $type: 'string' } }).limit(10000).toArray();
- bulk = db.application_details.initializeUnorderedBulkOp();
-}
-bulk.execute();
-
-
diff --git a/README.md b/README.md
deleted file mode 100644
index d446521..0000000
--- a/README.md
+++ /dev/null
@@ -1,64 +0,0 @@
-# AgMission Trunk
-
-This is the main development line of the AgMission SaaS platform.
-
-## Documentation
-
-| Document | Description |
-|---|---|
-| [Documents/ARCHITECTURE.md](Documents/ARCHITECTURE.md) | Software architecture overview — components, data flow, diagrams |
-| [Documents/DEPLOYMENT.md](Documents/DEPLOYMENT.md) | Deployment guide — scripts, PM2, backups, env vars |
-| [Documents/Requirements/Data-Export-API.md](Documents/Requirements/Data-Export-API.md) | Data Export Public API requirements |
-| [Documents/SVN-Guidelines.md](Documents/SVN-Guidelines.md) | SVN branching and commit conventions |
-| [Development/server/docs/DOCUMENTATION_INDEX.md](Development/server/docs/DOCUMENTATION_INDEX.md) | Server-side documentation index |
-| [Development/server/docs/API_SPECIFICATION.md](Development/server/docs/API_SPECIFICATION.md) | REST API reference |
-| [Development/server/docs/PARTNER_INTEGRATION_ARCHITECTURE.md](Development/server/docs/PARTNER_INTEGRATION_ARCHITECTURE.md) | Partner integration (SatLoc) details |
-| [Development/server/docs/DATABASE_DESIGN.md](Development/server/docs/DATABASE_DESIGN.md) | MongoDB schema design |
-| [Development/server/docs/DLQ_INDEX.md](Development/server/docs/DLQ_INDEX.md) | Dead Letter Queue system guide |
-| [Development/server/docs/PAYMENT_FAILURE_HANDLING.md](Development/server/docs/PAYMENT_FAILURE_HANDLING.md) | Billing and payment failure handling |
-| [Development/server/README_PARTNER_INTEGRATION.md](Development/server/README_PARTNER_INTEGRATION.md) | Partner integration quick-start |
-
-## Quick Start Development
-
-### API Server
-
-```bash
-cd Development/server
-cp environment.env.example environment.env # fill in dev values
-npm install
-npm start
-```
-
-### Web Client
-
-```bash
-cd Development/client
-npm install
-npm start # serves at https://localhost:4200 with SSL proxy
-```
-
-### GPS Server AgNav
-
-```bash
-cd Development/gps-server
-PROTOCOL=AGNAV node gps-server.js
-```
-
-### Track Server
-
-```bash
-cd Development/track-server
-node track-server.js
-```
-
-## Deployment
-
-See [Documents/DEPLOYMENT.md](Documents/DEPLOYMENT.md) for full instructions.
-
-Quick deploy from trunk:
-
-```bash
-cd trunk/Others/scripts/deploy
-./agm-deploy.sh 1 trunk # backend only
-./agm-deploy.sh 2 trunk # backend + frontend
-```
diff --git a/Development/agmission-pm2.json b/agmission-pm2.json
similarity index 100%
rename from Development/agmission-pm2.json
rename to agmission-pm2.json
diff --git a/Development/client/.editorconfig b/client/.editorconfig
similarity index 100%
rename from Development/client/.editorconfig
rename to client/.editorconfig
diff --git a/Development/client/.jshintrc b/client/.jshintrc
similarity index 100%
rename from Development/client/.jshintrc
rename to client/.jshintrc
diff --git a/Development/client/.vscode/launch.json b/client/.vscode/launch.json
similarity index 100%
rename from Development/client/.vscode/launch.json
rename to client/.vscode/launch.json
diff --git a/Development/client/.vscode/settings.json b/client/.vscode/settings.json
similarity index 100%
rename from Development/client/.vscode/settings.json
rename to client/.vscode/settings.json
diff --git a/Development/client/AgMission-BigQuery-Analytics-Mapping.md b/client/AgMission-BigQuery-Analytics-Mapping.md
similarity index 100%
rename from Development/client/AgMission-BigQuery-Analytics-Mapping.md
rename to client/AgMission-BigQuery-Analytics-Mapping.md
diff --git a/Development/client/AgMission-GA4-Complete-Reference.csv b/client/AgMission-GA4-Complete-Reference.csv
similarity index 100%
rename from Development/client/AgMission-GA4-Complete-Reference.csv
rename to client/AgMission-GA4-Complete-Reference.csv
diff --git a/Development/client/README.md b/client/README.md
similarity index 100%
rename from Development/client/README.md
rename to client/README.md
diff --git a/Development/client/angular.json b/client/angular.json
similarity index 94%
rename from Development/client/angular.json
rename to client/angular.json
index c0831f7..1c8a705 100644
--- a/Development/client/angular.json
+++ b/client/angular.json
@@ -52,6 +52,21 @@
"glob": "**/*",
"input": "node_modules/leaflet/dist/images",
"output": "/assets/images"
+ },
+ {
+ "glob": "CHANGELOG.md",
+ "input": "docs",
+ "output": "/assets/docs"
+ },
+ {
+ "glob": "**/*",
+ "input": "docs/releases",
+ "output": "/assets/docs/releases"
+ },
+ {
+ "glob": "**/*",
+ "input": "../server/public/images",
+ "output": "/images"
}
],
"styles": [
diff --git a/Development/client/browserslist b/client/browserslist
similarity index 100%
rename from Development/client/browserslist
rename to client/browserslist
diff --git a/client/docs/ADVANCED_REPORTS_API.md b/client/docs/ADVANCED_REPORTS_API.md
new file mode 100644
index 0000000..afb462a
--- /dev/null
+++ b/client/docs/ADVANCED_REPORTS_API.md
@@ -0,0 +1,460 @@
+# Advanced Reports — API Design Reference
+
+**Version:** 1.0
+
+**Date:** July 9, 2026
+
+**Status:** Draft — contract for Phase 1 implementation (endpoint not yet implemented)
+
+**Scope:** Backend API contract for the Advanced Application Report. This document is the single source of truth for **both backend and frontend/client** development.
+
+**Related Documents:** `ADVANCED_REPORTS_FEASIBILITY.md`, `ADVANCED_REPORTS_FUNCTIONAL.md`, `ADVANCED_REPORTS_NON_FUNCTIONAL.md`, `ADVANCED_REPORTS_IMPLEMENTATION_PLAN.md`, `ADVANCED_REPORTS_PROPOSAL.md`
+
+---
+
+## Table of Contents
+
+- [1 Overview](#1-overview)
+- [2 Authentication](#2-authentication)
+- [3 Report Generation Flow](#3-report-generation-flow)
+- [4 Endpoints](#4-endpoints)
+ - [4.1 Generate Advanced Report](#41-generate-advanced-report)
+ - [4.2 Report Options (existing, reused)](#42-report-options-existing-reused)
+ - [4.3 Save Report Template (existing, reused)](#43-save-report-template-existing-reused)
+- [5 Generated Artifacts](#5-generated-artifacts)
+- [6 Datasource Contract (`rptDS.json`)](#6-datasource-contract-rptdsjson)
+- [7 Error Responses](#7-error-responses)
+- [8 Data Model Notes](#8-data-model-notes)
+- [9 Frontend Integration Guide](#9-frontend-integration-guide)
+- [10 Backend Architecture Notes](#10-backend-architecture-notes)
+ - [10.1 Generation Data Flow Diagram](#101-generation-data-flow-diagram)
+ - [10.2 Component Interaction Diagram](#102-component-interaction-diagram)
+ - [10.3 Map Capture Decision Diagram](#103-map-capture-decision-diagram)
+ - [10.4 Report Generation Sequence](#104-report-generation-sequence)
+- [11 Open Decisions](#11-open-decisions)
+- [12 Changelog](#12-changelog)
+
+---
+
+## 1 Overview
+
+The Advanced Application Report is a mission-level, multi-page report (Mission Overview,
+Mission Coverage, Zone Detail per zone) generated for a single completed job ("mission").
+The server computes all analytics, renders map images, and writes a JSON datasource;
+the **client Stimulsoft viewer** renders and exports the report — identical to the legacy
+report contract.
+
+**Base path**: `/api/jobs`
+
+**To be implemented in**:
+
+- `controllers/advanced_report.js` (new)
+- `helpers/report_util.js` (new — analytics engine)
+- `routes/job.js` (new route)
+- `public/sprayMap.html` (map page variants)
+- `reports/app_advanced.mrt` (authored manually in the embedded Stimulsoft designer)
+
+The legacy endpoints (`/preAppReport`, `/preLoadReport`) are unchanged.
+
+---
+
+## 2 Authentication
+
+Same as the legacy report endpoints. All routes require a valid JWT bearer token; the
+`checkUser` middleware is applied globally in `server.js`.
+
+```
+Authorization: Bearer
+```
+
+The `/api/jobs` route group applies the subscription middleware (`checkRqPkgSubscription`),
+so the caller must hold an active package. The job must belong to the caller's customer
+scope; otherwise `401 not_authorized`.
+
+---
+
+## 3 Report Generation Flow
+
+```
+Client (Report Settings dialog)
+ │ POST /api/jobs/preAdvancedReport { jobId, rptOp, reportContents, ... }
+ ▼
+Server
+ 1. Load job + populated refs (client, operator, vehicle, products, crop)
+ 2. Persist report settings onto the job (rptOp incl. reportContents)
+ 3. Stream ApplicationDetail (by the job's fileIds, projected fields)
+ 4. Analytics engine: per-line → per-zone → mission aggregates (one pass over the data)
+ 5. Render map images (one Chromium instance: mission map, zone maps, thumbnails)
+ 6. Write REPORT_DIR/dat//rptDS.json + map images
+ 7. Select template: app_advanced_.mrt else app_advanced.mrt
+ │ 200 { rid, path, c }
+ ▼
+Client (Stimulsoft viewer)
+ GET /reports/.mrt
+ GET /reports/dat//rptDS.json (+ map images referenced within)
+ → render, print, export PDF (client-side)
+```
+
+Generation is synchronous within the HTTP request. Budget: ~35 s per 10 zones,
+~15 s for a typical 3-zone job (NFR-1.1). Repeat exports from an open viewer are
+client-side and cost nothing.
+
+---
+
+## 4 Endpoints
+
+### 4.1 Generate Advanced Report
+
+```
+POST /api/jobs/preAdvancedReport
+```
+
+#### Request Body (JSON)
+
+| Field | Type | Required | Description |
+|---|---|---|---|
+| `jobId` | number | yes | The job/mission to report on (`Job._id` — numeric auto-increment id) |
+| `lang` | string | no | Report language: `en` (default), `pt`, `es` |
+| `rptOp` | object | no | Report settings (persisted onto the job, legacy shape) |
+| `rptOp.printArea` | boolean | no | Print the planned area size |
+| `rptOp.areaSize` | number | no | Planned area (job units; acres converted to ha server-side when `measureUnit` is US) |
+| `rptOp.coverage` | number | no | Sprayed area (job units) |
+| `rptOp.appRate` | number | no | Application rate override |
+| `rptOp.actualVol` | number | no | Actual spray volume |
+| `rptOp.useActualVol` | boolean | no | Use `actualVol` instead of computed volume |
+| `reportContents` | object | no | **New** — Report Contents selections (persisted with `rptOp`) |
+| `reportContents.includeZoneDetail` | boolean | no | Include Zone Detail pages. Default `true` |
+| `reportContents.sprayedZonesOnly` | boolean | no | Zone Detail pages only for zones with spray data. Default `false`; ignored when `includeZoneDetail` is `false` |
+| `reportContents.includeFlightLineStats` | boolean | no | Include the flight-line table on Zone Detail pages. Default `true` |
+| `reportContents.hideMapBackground` | boolean | no | Render all report maps on a plain dark-green background (mockup styling) instead of satellite imagery — smaller files, faster capture. Default `false` |
+| `useCustWI` | boolean | no | Use manually entered weather instead of logged averages |
+| `weatherInfo` | object | no | Manual weather: `{ windSpd, windDir, temp, humid }` |
+
+#### Example Request Body
+
+```json
+{
+ "jobId": 10234,
+ "lang": "en",
+ "rptOp": { "printArea": true, "areaSize": 6681.1, "coverage": 6201.3, "appRate": 10.0, "useActualVol": false },
+ "reportContents": { "includeZoneDetail": true, "sprayedZonesOnly": false, "includeFlightLineStats": true, "hideMapBackground": false },
+ "useCustWI": false
+}
+```
+
+#### Response `200 OK`
+
+```json
+{
+ "rid": "app_advanced",
+ "path": "appadv_10234_1720537200000",
+ "c": 0
+}
+```
+
+| Field | Type | Description |
+|---|---|---|
+| `rid` | string | Template id — `app_advanced` (default) or `app_advanced_` (customer-customized) |
+| `path` | string | Generated-artifact folder under `REPORT_DIR/dat/` |
+| `c` | number | `1` when a customer-customized template was selected, else `0` |
+
+This is the exact `{ rid, path, c }` contract of the legacy `/preAppReport`, so the
+existing viewer flow needs no changes beyond calling the new endpoint.
+
+### 4.2 Report Options (existing, reused)
+
+```
+POST /api/jobs/reportOps { jobId }
+```
+
+Unchanged. Returns coverage / actual volume / area size defaults for pre-filling the
+Report Settings dialog (values in ha; client converts per `measureUnit`).
+
+### 4.3 Save Report Template (existing, reused)
+
+```
+POST /api/jobs/saveReport
+```
+
+Unchanged. The in-product report designer saves an edited template as `.mrt`;
+saving under `app_advanced_` creates the per-customer override (FR-7.1).
+
+---
+
+## 5 Generated Artifacts
+
+Written to `REPORT_DIR/dat//` and served from the same static `/reports` path as
+legacy report artifacts (hosting sits outside this Express app; non-guessable folder
+names are the effective access control, as with legacy reports — see NFR-4.3):
+
+| Artifact | Description |
+|---|---|
+| `rptDS.json` | Full report datasource (section 6) |
+| `map.jpg` | Mission overview map (single-viewport or locator mode, FR-2.3) |
+| `zone_.jpg` | Zone Detail map, one per included zone |
+| `thumb_.jpg` | Coverage-grid thumbnail; **absent** when zone count > 12 (compact layout, FR-3.5) or cropped from `map.jpg` in single-viewport missions |
+
+Folder names are server-generated and non-guessable; artifacts are retained and later
+removed by the separate maintainer app's periodic cleanup (legacy pattern). A repeat
+request regenerates into a fresh folder.
+
+---
+
+## 6 Datasource Contract (`rptDS.json`)
+
+All display values are **pre-localized, pre-formatted strings** (units, locale numbers,
+local times) — the template renders them exactly as written, with no further processing. Missing/unavailable values are the
+em-dash string `"–"`. Optional sections are suppressed via **empty datasets**, never
+empty objects.
+
+```json
+{
+ "reports": { "type": 2 },
+ "mission": [{
+ "jobId": 10234,
+ "name": "Spring Fertilizer 2026",
+ "jobType": "Fertilizer Application",
+ "crop": "Corn",
+ "planDates": "May 22, 2026 - May 22, 2026",
+ "actualDates": "May 22, 2026, 10:15 AM - 3:57 PM",
+ "duration": "5h 42m",
+ "customer": "Greenfield Farms",
+ "customerAddress": "12 Harvie Road, Barrie, ON",
+ "pilot": "John Smith",
+ "licence": "AG-48213-ON",
+ "aircraft": "Air Tractor AT-802",
+ "flightNumber": "C-GNAV",
+ "applicator": "AgMission Aerial Services",
+ "applicatorAddress": "45 Airport Road, Barrie, ON",
+ "mapfile": "https:///reports/dat//map.jpg",
+ "coveragePct": "95.7%",
+ "avgSpeed": "143.6 mph",
+ "avgHeight": "12.3 ft",
+ "avgXtError": "2.07 ft",
+ "totalVolume": "12,845 gal",
+ "zonesSprayed": "5 / 9",
+ "plannedArea": "6,681.1 ac",
+ "sprayedArea": "6,201.3 ac",
+ "totalFlightTime": "5h 42m",
+ "totalSprayTime": "4h 31m",
+ "ferryTime": "1h 11m",
+ "totalDistance": "1,245.2 mi",
+ "sprayDistance": "903.4 mi",
+ "ferryDistance": "341.8 mi",
+ "avgAppRate": "0.50 gal/ac",
+ "avgFlowRate": "46.8 GPM",
+ "swathWidth": "60.0 ft",
+ "remark": "Light crosswind after 14:00; zones 6, 8 and 9 deferred.",
+ "createdDate": "Jul 9, 2026"
+ }],
+ "coverageCards": [{
+ "zoneNum": 1, "name": "North 40",
+ "sprayedPlanned": "299.3 / 312.4 ac", "coveragePct": "95.8%",
+ "thumbFile": "https:///reports/dat//thumb_1.jpg"
+ }],
+ "zones": [{
+ "zoneNum": 1, "name": "North 40", "crop": "Corn",
+ "plannedArea": "312.4 ac", "sprayedArea": "299.3 ac", "coveragePct": "95.8%",
+ "volumeApplied": "625 gal", "avgAppRate": "0.50 gal/ac",
+ "flightTime": "26m", "sprayTime": "23m", "avgTurnTime": "17.4 s",
+ "avgSpeed": "145.1 mph", "avgHeight": "12.2 ft",
+ "avgFlowRate": "46.5 GPM", "avgXtError": "1.90 ft",
+ "mapfile": "https:///reports/dat//zone_1.jpg",
+ "zoneIndexLabel": "Zone 1 of 9"
+ }],
+ "lines": [{
+ "zoneNum": 1, "lineNum": 1, "startTime": "09:15:00",
+ "sprayTime": "97.2 s", "sprayLength": "4,085 ft", "avgSpeed": "146.1 mph",
+ "areaCovered": "22.85 ac", "appRate": "0.50 gal/ac",
+ "avgXtError": "1.80 ft", "turnTime": "17.1 s"
+ }],
+ "products": [{
+ "name": "28-0-0 UAN Blend", "restricted": "No", "epaReg": "–",
+ "rateStr": "0.50 gal/ac", "totalRateStr": "12,845 gal", "count": 1
+ }],
+ "weather": [{
+ "windSpd": "8.6 mph", "windDir": "215° SW", "temp": "21.8°C", "humid": "56%"
+ }]
+}
+```
+
+#### Field Notes
+
+- `reports.type` — `0` planning, `1` legacy application report, **`2` advanced report**.
+- `zones[]` is already filtered per `reportContents` (excluded zones don't appear);
+ `coverageCards[]` always contains **all** zones regardless of filtering.
+- Unsprayed zones in `zones[]` carry `"–"` values, a boundary/ferry-only `mapfile`, and
+ exactly one `lines[]` placeholder row of `"–"` cells (FR-4.6).
+- `lines[]` is empty when `includeFlightLineStats` is `false` (template band collapses).
+- When zone count > 12, `coverageCards[].thumbFile` is `""` and the template renders the
+ compact text layout (FR-3.5).
+- `mission.remark` — `job.remark` verbatim; `"–"` when the job has none (Remark line, FR-2.10).
+- Temperature is always °C; every other quantity follows the job's `measureUnit` (FR-6.1).
+- Mission totals are computed in the same data pass as the zone values — they always
+ reconcile (NFR-3.3).
+
+---
+
+## 7 Error Responses
+
+Standard AgMission error format:
+
+```json
+{ "error": { ".tag": "error_constant_value", "message": "Detail (development mode only)" } }
+```
+
+| HTTP Status | `.tag` value | When it occurs |
+|---|---|---|
+| `401` | `not_authorized` | Missing/invalid JWT, or job not in caller's scope |
+| `409` | `job_not_found` | Job does not exist |
+| `409` | `invalid_param` | Malformed `jobId`, unknown `lang`, invalid option values |
+| `409` | `report_limits_exceeded` *(new)* | Mission exceeds the supported limits: > 50 zones or > 2,000 flight lines (NFR-2.1) |
+| `429` | `report_busy` *(new)* | Max concurrent generations (2 per process) reached — client should retry (NFR-2.2) |
+| `500` | `report_generation_failed` *(new)* | Mission map capture failed or datasource write failed (zone-map failures degrade to placeholders instead, NFR-3.1) |
+
+---
+
+## 8 Data Model Notes
+
+- **Mission = Job.** `Job._id` is a Number (auto-increment; there is no separate `jobId` field on Job); zones are the `job.sprayAreas` polygon array.
+- **Report settings persistence**: `rptOp` (extended with `reportContents`) is saved onto
+ the job on every request, so the dialog restores the last-used selections per job.
+ Values arrive in job units and are stored metric (acre→ha conversion server-side when
+ `measureUnit` is US) — same as legacy.
+- **Analytics granularity**: per-line and per-zone values are computed on the fly from
+ `ApplicationDetail` (read once via a streaming cursor, projected fields, queried by the job's
+ `fileId`s — the collection's only index). Nothing new is persisted by report generation.
+- **Known data gaps** (render as `"–"`): `lminApp` flat 0 without a flow controller;
+ SatLoc-imported applications lack xTrack/turn statistics; devices without xTrack
+ recording have no XT error anywhere.
+
+---
+
+## 9 Frontend Integration Guide
+
+1. Open Report Settings; pre-fill from `POST /reportOps` (existing behaviour).
+2. Render the **Report Contents** panel (right side): Include All Zone Detail (default on),
+ nested Sprayed Zones Only (default off, disabled when parent off), Include Flight Line
+ Statistics (default on), each with an info tooltip (FR-7.4).
+3. On **Preview**: `POST /preAdvancedReport` with the dialog state; show a progress
+ indicator sized to the NFR-1.1 budget (~15–35+ s; consider zone count).
+4. Hand `{ rid, path }` to the existing Stimulsoft viewer component unchanged; the viewer
+ loads the template and datasource and handles print/PDF export client-side.
+5. On `report_busy`, offer retry; on `report_limits_exceeded`, surface the zone/line limits.
+6. The viewer's `localizeReport()` cultures (en-US / pt-PT / es-ES) are guaranteed present
+ in `app_advanced.mrt` — no client change needed.
+
+---
+
+## 10 Backend Architecture Notes
+
+### 10.1 Generation Data Flow Diagram
+
+The `ApplicationDetail` records are read **once per report**, and the per-line, per-zone
+and mission values are all computed during that single pass over the data. No dataset is
+produced by a separate query or code path, so the values always agree with each other
+(NFR-1.2, NFR-3.3).
+
+```mermaid
+flowchart LR
+ A["Job by jobId"] --> B["Applications and AppFiles of the job"]
+ B --> C["fileId list"]
+ C --> D["Read ApplicationDetail once streaming cursor, projected fields"]
+ D --> E["Line segmentation by llnum / sprayStat"]
+ E --> F["Zone assignment: point-in-polygon vs job.sprayAreas"]
+ F --> G["Per-line stats"]
+ G --> H["Zone roll-ups"]
+ H --> I["Mission totals"]
+ G --> J["rptDS lines dataset"]
+ H --> K["rptDS zones and coverageCards datasets"]
+ I --> L["rptDS mission dataset"]
+```
+
+### 10.2 Component Interaction Diagram
+
+```mermaid
+flowchart TD
+ FE["Frontend: Report Settings dialog"] --> EP["POST /api/jobs/ preAdvancedReport"]
+ EP --> CTL["controllers/ advanced_report.js"]
+ CTL --> RU["helpers/report_util.js analytics engine"]
+ CTL --> WU["helpers/web_util.js single shared Chromium"]
+ WU --> SM["public/sprayMap.html variants"]
+ CTL --> FS[("REPORT_DIR/dat/genFolder: rptDS.json + map images")]
+ CTL --> TPL{"customer template app_advanced_applicatorId.mrt exists?"}
+ TPL -->|yes| C1["rid = customized c = 1"]
+ TPL -->|no| C0["rid = app_advanced c = 0"]
+ RU --> J[("jobs")]
+ RU --> AP[("applications")]
+ RU --> AF[("application_files")]
+ RU --> AD[("application_details")]
+ FE2["Stimulsoft viewer"] --> MRT["GET /reports/ rid.mrt"]
+ FE2 --> DS["GET /reports/dat/path/ rptDS.json + images"]
+```
+
+### 10.3 Map Capture Decision Diagram
+
+Capture count follows the effective page selection, not the zone count (NFR-1.3).
+
+```mermaid
+flowchart TD
+ A["Start captures: one shared browser"] --> B{"Zones fit legibly in one viewport?"}
+ B -->|yes| C["Mission map: full polygons"]
+ B -->|no| D["Mission map: locator badges (FR-2.3.2)"]
+ C --> E{"More than 12 zones?"}
+ D --> E
+ E -->|yes| F["Skip thumbnails: compact layout (FR-3.5)"]
+ E -->|no| G{"Single viewport?"}
+ G -->|yes| H["Crop thumbnails from the mission capture"]
+ G -->|no| I["Per-zone thumbnail captures"]
+ F --> K{"includeZoneDetail?"}
+ H --> K
+ I --> K
+ K -->|yes| L["Zone map capture per included zone (sprayedZonesOnly filter)"]
+ K -->|no| M["No zone captures"]
+```
+
+### 10.4 Report Generation Sequence
+
+```mermaid
+sequenceDiagram
+ participant FE as Frontend
+ participant API as Jobs API
+ participant DB as MongoDB
+ participant CH as Shared Chromium
+ participant FS as REPORT_DIR
+
+ FE->>API: POST preAdvancedReport jobId, rptOp, reportContents
+ API->>DB: Load job and its related records, persist report settings
+ API->>DB: Read ApplicationDetail once by fileIds, streaming cursor
+ DB-->>API: Points aggregated to line, zone, mission values
+ API->>CH: Render mission map, thumbnails, zone maps (10.3)
+ CH-->>API: JPEG captures (zone-map failure = placeholder)
+ API->>FS: Write rptDS.json + images to dat/genFolder
+ API-->>FE: 200 rid, path, c
+ FE->>FS: GET template .mrt, rptDS.json + images
+ Note over FE: Viewer renders. Print and PDF export client-side
+```
+
+Concurrency: a simple in-process counter caps generation at 2 concurrent requests
+(`429 report_busy` beyond that, NFR-2.2). The generation function is isolated from the
+HTTP layer so it can later move behind the existing worker framework unchanged (NFR-2.3).
+
+---
+
+## 11 Open Decisions
+
+| # | Decision | Status |
+|---|---|---|
+| 1 | Page orientation (portrait-only vs landscape variant) — affects template only, not this API | Awaiting PO (F-OQ-1) |
+| 2 | Regeneration reuse/caching for unchanged repeat requests (same `{rid, path}` returned) | Deferred — out of Phase 1 scope; API shape already compatible |
+| 3 | Exact `.tag` strings for the new error constants (`helpers/constants.js` naming review) | To be finalized during D2 implementation |
+| 4 | Compact coverage layout threshold — exact rule (more than 12 vs 12 and above) and threshold value; affects when `coverageCards[].thumbFile` is empty | Awaiting PO (F-OQ-2) |
+
+---
+
+## 12 Changelog
+
+| Version | Date | Notes |
+|---|---|---|
+| 1.0 | 2026-07-09 | Initial draft — contract derived from the approved Phase 1 planning set (feasibility, FR, NFR, implementation plan) |
+| 1.1 | 2026-07-13 | `mission.remark` added to `mission[]` (Remark line on page 1, FR-2.10). Overview-map zone/field names (FR-2.3 rev.) — map rendering only, no contract impact |
diff --git a/client/docs/ADVANCED_REPORTS_IMPLEMENTATION_PLAN.md b/client/docs/ADVANCED_REPORTS_IMPLEMENTATION_PLAN.md
new file mode 100644
index 0000000..28b25d1
--- /dev/null
+++ b/client/docs/ADVANCED_REPORTS_IMPLEMENTATION_PLAN.md
@@ -0,0 +1,104 @@
+# Advanced Reports — Implementation Plan (Phase 1)
+
+**Version:** 1.0
+
+**Date:** July 9, 2026
+
+**Status:** Draft — derived from the approved planning set
+
+**Related Documents:** `ADVANCED_REPORTS_FEASIBILITY.md`, `ADVANCED_REPORTS_FUNCTIONAL.md`, `ADVANCED_REPORTS_NON_FUNCTIONAL.md`, `ADVANCED_REPORTS_API.md`, `ADVANCED_REPORTS_PROPOSAL.md`
+
+---
+
+## 1. Approach
+
+Build the Advanced Application Report on the existing report pipeline: the server computes analytics, renders map images, and writes a datasource; the client Stimulsoft viewer renders and exports. One analytics engine feeds every page (FR-5.4); content options are applied by shaping the datasource, not by swapping templates.
+
+Guiding principles:
+
+- **Pure-function analytics core** — testable without HTTP, Mongo, or Puppeteer (NFR-6.3).
+- **Legacy untouched** — no shared-code change may alter `preAppReport`/loadsheet behaviour (NFR-5.1).
+- **`ApplicationDetail` is read once per report** (single pass, streaming cursor, projected fields only — NFR-1.2/1.4).
+- **One Chromium instance** per report for all captures (NFR-1.3).
+
+## 2. Deliverables map
+
+| # | Deliverable | New/Changed files |
+|---|---|---|
+| D1 | Analytics engine | `helpers/report_util.js` (new) |
+| D2 | Endpoint + datasource builder | `controllers/advanced_report.js` (new), `routes/job.js` (route), `model/job.js` (report-contents options persistence) |
+| D3 | Map page variants + multi-capture | `public/sprayMap.html` (variants), `helpers/web_util.js` (browser reuse) |
+| D4 | Template + validation | `reports/app_advanced.mrt` (authored in the Stimulsoft designer), `scripts/validate_advanced_report_template.js` (new) |
+| D5 | Frontend wiring | client repo: Report Settings "Report Contents" panel, advanced-report option, viewer call |
+| D6 | Tests + offline harness | `tests/` fixtures + unit/integration tests, offline Stimulsoft harness |
+| D7 | Rollout | backfill verification, template deploy to `REPORT_DIR`, release notes |
+
+Sequencing: **D1 → D2 → (D3 ∥ D4) → D5 → D6 → D7.** D3 and D4 are independent once D2 fixes the datasource shape. D6 grows alongside every deliverable (D1 unit tests land with D1).
+
+## 3. D1 — Analytics engine (`helpers/report_util.js`)
+
+Pure functions over point arrays; no I/O.
+
+1. **Line segmentation** — group `ApplicationDetail` points by `llnum`; `sprayStat == 3` marks line start, spray-on = `sprayStat ∈ {1, 3}` (pattern: `getSprayOnSegments`, `controllers/job.js:604`).
+2. **Zone assignment** — point-in-polygon (turf) of each line's points against `job.sprayAreas`; majority wins for straddling lines (FR-5.2).
+3. **Per-line stats** — start time, spray time, length (`geoUtil.distance()` — **km**), avg speed, area = length × swath, app rate, avg |xTrack|, turn time (gap to next line; pattern: turn-time loop `workers/job_worker.js:1486`).
+4. **Zone roll-ups** — sprayed area, coverage %, volume, flight/spray time, avg turn time, avg height, avg XT, avg flow rate (mean `lminApp` — completely new calculation, degrade when flat 0).
+5. **Mission totals** — sums/weighted averages of zone values (must equal page-1 figures exactly, NFR-3.3); ferry time/distance = flight − spray.
+6. **Planned areas** — turf area from polygon geometry (`sprayAreas[].properties.area` absent in live data); coverage capped at 100.0% display.
+7. **Weather** — reuse `jobUtil.getDataWeatherInfo(fileIds)` (`helpers/job_util.js:325`); manual `job.weatherInfo` override.
+
+Unit-test fixtures (with D1): typical multi-zone job, no-flow-controller job (`lminApp` = 0), SatLoc-style job (no xTrack/turn data), unsprayed zone, single-zone job, boundary-straddling line.
+
+## 4. D2 — Endpoint + datasource (`controllers/advanced_report.js`)
+
+1. `POST /preAdvancedReport` in `routes/job.js`, same auth middleware as `preAppReport` (NFR-4.1). Returns `{ rid, path, c }` (FR-1.1).
+2. Controller flow (mirrors `preAppReport_post`): load job with populated refs → persist Report Settings incl. Report Contents selections (`job.rptOp` pattern, FR-7.5) → stream `ApplicationDetail` by the job's `fileId`s with field projection (NFR-1.2) → run D1 engine → render maps via D3 → write `rptDS.json` → select template.
+3. Datasource shape (all display values pre-localized strings, FR-1.3):
+ - `mission` — header/info block, KPI tiles, statistics, generation date.
+ - `zones[]` — per-zone info + stats + map image refs; empty-state zones carry `–` placeholder values (FR-4.6); filtered per Report Contents options.
+ - `lines[]` nested per zone — flight-line table rows; single `–` placeholder row for unsprayed zones; omitted when Flight Line Statistics is off.
+ - `products[]`, `weather` (suppressed when unavailable), `coverageCards[]` (all zones, always).
+4. Template selection: `app_advanced_.mrt` else `app_advanced.mrt`; applicator id sanitized to hex ObjectId (NFR-4.2).
+5. Structure the generation function so a worker can call it without the HTTP layer (NFR-2.3); a simple in-process counter limits it to max 2 concurrent generations (NFR-2.2).
+6. Per-phase pino logging: query, data aggregation, each capture, datasource write (NFR-7.1).
+
+## 5. D3 — Maps (`public/sprayMap.html` variants + `helpers/web_util.js`)
+
+1. Extend `web_util` to open one Chromium instance and capture multiple pages/states per report (NFR-1.3).
+2. Mission map variant: fitBounds over all zones; numbered markers + zone/field names + acreage labels; spray/ferry layers; legend/scale/north arrow. **Mode switch** (FR-2.3.3): compute zone pixel footprint at fitted zoom — below threshold (~25 px) render locator badges (`divIcon`, zone numbers) instead of polygons (FR-2.3.2).
+3. Zone detail variant: per-zone fitBounds, boundary + spray lines + dashed ferry lines, neighbouring zones faded into the background; unsprayed zones render boundary + ferry only.
+4. Background toggle: when *Hide Map Background* is selected (FR-7.4), all variants skip the satellite tile layer and render on the plain dark-green background used in the mockups (a fixed CSS background on the map container) — no tile downloads during capture.
+4. Thumbnails: crop from the single all-zones capture when single-viewport; per-zone captures when dispersed; **skipped entirely above 12 zones** (FR-3.5) or when zone pages are excluded (Report Contents).
+5. Zone capture failure → placeholder image + log, report continues; mission map failure → request fails (NFR-3.1).
+
+## 6. D4 — Template (`reports/app_advanced.mrt`, authored in the Stimulsoft designer)
+
+1. Three page designs authored manually in the embedded Stimulsoft designer: Mission Overview, Mission Coverage (grid ≤12 zones / compact table >12), Zone Detail (master band per zone, flight-line table as StiPanel-wrapped child band).
+2. Validation script `scripts/validate_advanced_report_template.js` (NFR-5.2/6.1), run after every designer save and before deploy. Checks: no empty `{}` collections in the `.mrt` JSON; `GlobalizationStrings` for en-US / pt-PT / es-ES with non-empty Items targeting existing components; unique component names; every band's `DataSourceName` / `MasterComponent` / `DataRelationName` resolves against the Dictionary; DataBands nested inside DataBands are StiPanel-wrapped; every `{table.column}` expression references a declared Dictionary column.
+3. Section suppression via empty datasets; dash placeholder rows come from the datasource, not template logic.
+4. Committed `.mrt` is the source of truth (NFR-6.1). Footer `Created ` + `page/totalPages`; header band per page type.
+
+## 7. D5 — Frontend (client repo)
+
+1. Report Settings dialog: add right-side **Report Contents** panel — Include All Zone Detail (default on), nested Sprayed Zones Only (default off), Include Flight Line Statistics (default on), Hide Map Background (default off), info tooltips (FR-7.4); restore last selections per job.
+2. Advanced Report as a report option alongside the legacy report; on Preview call `preAdvancedReport` and hand `{rid, path}` to the existing viewer unchanged.
+
+## 8. D6 — Testing & verification
+
+1. D1 unit tests over fixtures (all FR-8 degradation rows covered, NFR-3.4).
+2. Cross-page consistency test: mission totals ≡ zone roll-ups (NFR-3.3).
+3. Offline Stimulsoft harness (file:// + `stimulsoft.reports.pack.js`) loading the real `.mrt` + generated `rptDS.json` — reproduces viewer load/localize/render without the app (NFR-6.2); Trial watermark acceptable in tests.
+4. Integration run against a live-like multi-zone job; visual check of all three page types, both map modes, >12-zone compact layout, both unit systems, all three cultures.
+5. Performance measurement against NFR-1.1 (~35 s per 10 zones; ~15 s typical 3-zone job) with per-phase timings from NFR-7.1 logs.
+
+## 9. D7 — Rollout
+
+1. Verify production aggregate coverage (`avgXtError`, `avgSpraySpeed`, `totalFlightLength`) on recent Applications; re-run `scripts/migrate_applications.js` only if gaps found (NFR-8.1).
+2. Deploy `app_advanced*.mrt` to the environment's `REPORT_DIR` (may be outside this repo — NFR-6.4).
+3. Release notes: flow-rate fields require a flow controller; SatLoc-sourced applications omit XT/turn statistics (NFR-8.2).
+
+## 10. Open items
+
+- **F-OQ-1 Page orientation** (portrait-only vs landscape variant) — blocks D4 template freeze; portrait assumed until the PO decides.
+- **F-OQ-2 Compact coverage layout threshold** (more than 12 vs 12-and-above; threshold value) — affects D3 thumbnail logic and the D4 coverage page; "more than 12" assumed until the PO decides.
+- **Regeneration reuse/caching** for repeat downloads of unchanged reports — not in Phase 1 scope; candidate optimization if NFR-1.1 budgets prove tight in practice (D2's worker-ready structure keeps the door open).
diff --git a/client/docs/BROWSER_CACHE_SERVICE.md b/client/docs/BROWSER_CACHE_SERVICE.md
new file mode 100644
index 0000000..4fd1bef
--- /dev/null
+++ b/client/docs/BROWSER_CACHE_SERVICE.md
@@ -0,0 +1,149 @@
+# BrowserCacheService
+
+**File**: `src/app/domain/services/browser-cache.service.ts`
+
+A generic, injectable Angular service that provides a typed read/write/invalidate API over the browser's [Cache Storage API](https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage). Intended as a shared foundation for any feature that wants to cache HTTP responses across navigation events without a Service Worker.
+
+---
+
+## Why Cache Storage?
+
+| Mechanism | Survives navigation | Survives page reload | Configurable TTL | Storage limit |
+|---|---|---|---|---|
+| Component state | ✗ | ✗ | — | Memory |
+| NgRx store | ✓ (same tab) | ✗ | — | Memory |
+| `sessionStorage` | ✓ | ✗ | Manual | ~5 MB |
+| `localStorage` | ✓ | ✓ | Manual | ~5 MB |
+| **Cache Storage** | ✓ | ✓ | ✓ (per entry) | Quota-managed |
+
+Cache Storage was chosen because it:
+- Is available in all modern browsers (Chrome 40+, Firefox 44+, Safari 11.1+)
+- Stores structured data alongside an expiry timestamp without size pressure
+- Is already used by Service Workers and the browser's native HTTP cache, so quota management is handled by the browser
+- Falls back gracefully (service becomes a no-op) when unavailable
+
+---
+
+## API
+
+```typescript
+@Injectable({ providedIn: 'root' })
+class BrowserCacheService {
+
+ get(cacheName: string, key: string, maxAgeMs?: number): Observable
+
+ put(cacheName: string, key: string, data: T): void
+
+ invalidate(cacheName: string): void
+}
+```
+
+### `get(cacheName, key, maxAgeMs?)`
+
+Returns an `Observable` that emits the cached value (`T`) or `null` when:
+
+- The Cache Storage API is unavailable (e.g. older browser, unit test environment)
+- No entry exists for the given `cacheName` + `key` combination
+- The entry is older than `maxAgeMs` (default: `60 000` ms / 1 minute)
+
+Errors from the Cache API are caught and converted to `null` — they never propagate to the caller.
+
+### `put(cacheName, key, data)`
+
+Stores `data` in the named cache bucket under `key`. A `cachedAt` timestamp is embedded alongside the data so staleness can be checked on the next `get`.
+
+Fire-and-forget: errors are silently swallowed.
+
+### `invalidate(cacheName)`
+
+Deletes the **entire** Cache Storage bucket for `cacheName`. This removes all entries for that feature in one call.
+
+Fire-and-forget: errors are silently swallowed.
+
+---
+
+## Cache key format
+
+Internally, entries are stored under a pseudo-URL:
+
+```
+/browser-cache/?
+```
+
+This keeps entries within a single Cache Storage bucket readable via browser DevTools (Application → Cache Storage).
+
+---
+
+## Adding a new feature cache
+
+Create a typed facade service that delegates to `BrowserCacheService`. This keeps the cache name and TTL in one place and gives callers a clean domain API.
+
+```typescript
+// src/app/domain/services/customer-cache.service.ts
+
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+import { BrowserCacheService } from './browser-cache.service';
+import { ICustomer } from '../../customers/models/customer.model';
+
+const CACHE_NAME = 'agm-customer-list-v1';
+const MAX_AGE_MS = 60_000; // 1 minute
+
+@Injectable({ providedIn: 'root' })
+export class CustomerCacheService {
+
+ constructor(private readonly browserCache: BrowserCacheService) {}
+
+ get(queryParams: string): Observable {
+ return this.browserCache.get(CACHE_NAME, queryParams, MAX_AGE_MS);
+ }
+
+ put(queryParams: string, data: ICustomer[]): void {
+ this.browserCache.put(CACHE_NAME, queryParams, data);
+ }
+
+ invalidate(): void {
+ this.browserCache.invalidate(CACHE_NAME);
+ }
+}
+```
+
+Then, in the corresponding service:
+
+```typescript
+// In CustomerService.loadCustomers():
+const cacheKey = params.toString();
+
+return this.customerCache.get(cacheKey).pipe(
+ switchMap(cached => {
+ if (cached !== null) return of(cached);
+ return this.http.get(this.url, { params }).pipe(
+ tap(data => this.customerCache.put(cacheKey, data))
+ );
+ })
+);
+```
+
+And in the effects, call `this.customerCache.invalidate()` after any create / update / delete action succeeds.
+
+---
+
+## Existing implementations
+
+| Feature | Facade | Cache name | TTL |
+|---|---|---|---|
+| Job list | `JobCacheService` | `agm-jobs-list-v1` | 60 s |
+
+---
+
+## Versioning the cache name
+
+Append a version suffix (e.g. `-v1`, `-v2`) to `cacheName` whenever the shape of the stored data changes. The old bucket will be orphaned in the browser until the browser's quota manager evicts it, or you can explicitly delete the old name during app initialisation.
+
+---
+
+## Browser DevTools
+
+Cached entries are visible under:
+
+**Chrome DevTools** → Application tab → Cache Storage → `agm-jobs-list-v1`
diff --git a/Development/client/docs/MANAGE_SERVICES_PROMO_DISPLAY.md b/client/docs/MANAGE_SERVICES_PROMO_DISPLAY.md
similarity index 100%
rename from Development/client/docs/MANAGE_SERVICES_PROMO_DISPLAY.md
rename to client/docs/MANAGE_SERVICES_PROMO_DISPLAY.md
diff --git a/Development/client/docs/NOTIFICATION-DEEP-LINKS.md b/client/docs/NOTIFICATION-DEEP-LINKS.md
similarity index 100%
rename from Development/client/docs/NOTIFICATION-DEEP-LINKS.md
rename to client/docs/NOTIFICATION-DEEP-LINKS.md
diff --git a/client/docs/PILOT_DASHBOARD_QUICK_REFERENCE.md b/client/docs/PILOT_DASHBOARD_QUICK_REFERENCE.md
new file mode 100644
index 0000000..1c76ae9
--- /dev/null
+++ b/client/docs/PILOT_DASHBOARD_QUICK_REFERENCE.md
@@ -0,0 +1,399 @@
+# Pilot Analytics Dashboard - Quick Reference Summary
+
+**Project Name:** Pilot Analytics Dashboard
+**Duration:** 4-5 weeks (22-27 working days)
+**Target Go-Live:** Late May 2026
+**Team:** Frontend (8 days setup + core delivery) + Backend parallel (8-13 days if needed)
+
+## Source Files
+
+This quick reference is derived from the following source files:
+
+- Primary: `Pilot_Dashboard_PO_Brief_v1.md` (v1.5)
+- Primary: `Pilot_Dashboard_Requirements_v1.md` (v1.5)
+- Supporting: `plan-pilotAnalyticsDashboard.prompt.md`
+- Supporting: `Pilot-Dashboard-UI-UX-Design-Specification.md`
+- Supporting visual reference: `Sample Pilot Dashboard.png` and the PyQt mockup
+
+When source documents conflict, the v1.5 PO brief and v1.5 technical requirements take precedence.
+
+---
+
+## At a Glance
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ TOTAL PROJECT TIMELINE │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ Week 1: Phase 1 (Foundation) ████░░░░░░░░░░░ [Days 1-5] │
+│ Week 2: Phase 2 (Core UI) ███████░░░░░░░░░░ [Days 6-12] │
+│ Week 3: Phase 3 (Analytics) ██████░░░░░░░░░░░ [Days 13-20] │
+│ Week 4: Phase 4 (QA+Release) ████░░░░░░░░░░░░ [Days 21-27] │
+│ Week 5: UAT & Fix ██░░░░░░░░░░░░░░░ [Days 28-30] │
+│ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## Four Phases Overview
+
+| Phase | Focus | Duration | Key Deliverable | Risk Level |
+|-------|-------|----------|-----------------|------------|
+| **Phase 1** | Foundation, API contracts, role branching | 5-6 days | Service layer + mock data + spec | Low |
+| **Phase 2** | Core UI (layout, KPI, active jobs) | 7-8 days | Responsive dashboard shell | Medium |
+| **Phase 3** | Charts, indicators, i18n, error handling | 6-8 days | Full-featured dashboard | Medium |
+| **Phase 4** | Testing, integration, release prep | 4-5 days | Production-ready build | Low |
+
+---
+
+## Key Milestones
+
+| Milestone | Date | Criteria |
+|-----------|------|----------|
+| **M1: Service Layer Ready** | EOW 1 (May 2) | All 5 endpoints spec'd, mocks created, service layer tested |
+| **M2: UI Complete** | EOW 2 (May 9) | KPI, summary, active jobs, responsive tested |
+| **M3: Analytics Complete** | EOW 3 (May 16) | Charts, indicators, i18n, error states all working |
+| **M4: QA Complete** | EOW 4 (May 23) | E2E tests pass, cross-browser OK, performance audit complete |
+| **M5: Release Ready** | Mid-May 28 | Staging deployment, UAT approved, release notes ready |
+
+---
+
+## Phase Breakdown (Quick View)
+
+### Phase 1: Foundation (Days 1-6)
+**Tasks:** 7
+**Components:** 0 (service layer only)
+**Deliverables:** Dashboard service, mock data, backend spec
+```
+Day 1: Extend JobStatus enum
+Days 2-3: Dashboard service + contracts
+Day 4: Role-based home branching
+Days 5-6: Backend API spec
+```
+
+### Phase 2: Core UI (Days 7-14)
+**Tasks:** 11
+**Components:** 3 (KPI card, summary, active jobs)
+**Deliverables:** Responsive layout, all primary sections
+```
+Day 7: Layout shell
+Days 8-9: KPI + summary strips
+Days 10-12: Active jobs panel with interactions
+Days 13-14: Responsive + accessibility testing
+```
+
+### Phase 3: Analytics & Polish (Days 15-22)
+**Tasks:** 12
+**Components:** 2 new (charts, indicators) + refinements
+**Deliverables:** Charting, performance metrics, full i18n, error handling
+```
+Days 15-16: Charts + date range control
+Days 17-18: Performance indicators (XT, altitude)
+Days 19-20: Loading/empty/error states
+Days 21-22: i18n localization (EN/PT/ES)
+```
+
+### Phase 4: QA & Release (Days 23-27)
+**Tasks:** 9
+**Components:** 0 (QA & testing focus)
+**Deliverables:** Tested, documented, production build
+```
+Days 23-24: Integration & E2E testing
+Days 25-26: Cross-browser + perf testing
+Day 27: Documentation & release prep
+```
+
+---
+
+## File Structure (Expected)
+
+```
+src/app/dashboard/
+├── dashboard.component.ts (updated: role branching)
+├── dashboard.component.html (updated: template branching)
+├── dashboard.component.css (minimal changes)
+│
+├── pilot-dashboard/ (NEW FOLDER)
+│ ├── pilot-dashboard.component.ts (main container)
+│ ├── pilot-dashboard.component.html
+│ └── pilot-dashboard.component.scss
+│
+└── components/ (NEW FOLDER)
+ ├── kpi-card/
+ │ ├── kpi-card.component.ts
+ │ ├── kpi-card.component.html
+ │ └── kpi-card.component.scss
+ │
+ ├── daily-summary/
+ │ ├── daily-summary.component.ts
+ │ ├── daily-summary.component.html
+ │ └── daily-summary.component.scss
+ │
+ ├── operations-today/
+ │ ├── operations-today.component.ts
+ │ ├── operations-today.component.html
+ │ └── operations-today.component.scss
+ │
+ ├── active-jobs/
+ │ ├── active-jobs.component.ts
+ │ ├── job-row.component.ts
+ │ ├── active-jobs.component.html
+ │ └── active-jobs.component.scss
+ │
+ ├── hours-chart/
+ │ ├── hours-chart.component.ts
+ │ ├── hours-chart.component.html
+ │ └── hours-chart.component.scss
+ │
+ ├── hectares-chart/
+ │ ├── hectares-chart.component.ts
+ │ ├── hectares-chart.component.html
+ │ └── hectares-chart.component.scss
+ │
+ ├── date-range-selector/
+ │ ├── date-range-selector.component.ts
+ │ ├── date-range-selector.component.html
+ │ └── date-range-selector.component.scss
+ │
+ ├── xt-error-indicator/
+ │ ├── xt-error-indicator.component.ts
+ │ ├── xt-error-indicator.component.html
+ │ └── xt-error-indicator.component.scss
+ │
+ └── altitude-indicator/
+ ├── altitude-indicator.component.ts
+ ├── altitude-indicator.component.html
+ └── altitude-indicator.component.scss
+
+src/app/domain/
+├── models/
+│ └── pilot-dashboard.model.ts (NEW: TS interfaces)
+│
+└── services/
+ └── pilot-dashboard.service.ts (NEW: API service)
+ └── pilot-dashboard.service.spec.ts (NEW: tests)
+
+src/app/shared/
+├── mock/
+│ └── pilot-dashboard-mock.ts (NEW: mock data)
+│
+└── global.ts (UPDATED: JobStatus enum)
+
+docs/
+├── PILOT_DASHBOARD_ROADMAP.md (THIS FILE)
+├── PILOT_DASHBOARD_TASK_CHECKLIST.md
+└── PILOT_DASHBOARD_API_SPEC.md (NEW: backend contract)
+```
+
+---
+
+## Total Code Changes Summary
+
+| Category | Lines Changed | Impact |
+|----------|----------------|--------|
+| **New TypeScript** | ~1500-2000 | 8-10 new component classes + service |
+| **New Templates** | ~800-1200 | HTML for 8 components |
+| **New Styles** | ~600-800 | SCSS for responsive layout + indicators |
+| **Updated Files** | ~150-200 | global.ts, dashboard.component.* |
+| **Tests** | ~1000-1500 | Jasmine specs for service + components |
+| **Documentation** | ~500 | API spec + user guide + comments |
+| **Total** | **~4500-6700** | 12-18 feature files + 2-3 updated files |
+
+---
+
+## PR Strategy (4 PRs Total)
+
+| PR | Phase | Files Changed | Commits | Review Focus |
+|----|-------|----------------|---------|--------------|
+| **PR1** | Foundation | 5-10 files | 4 commits | Service layer, constants, role gating |
+| **PR2** | Core UI | 15-20 files | 3 commits | Layout, KPI, active jobs, responsive |
+| **PR3** | Analytics | 10-15 files | 3 commits | Charts, indicators, i18n, error handling |
+| **PR4** | Release | 5-10 files | 3 commits | Tests, docs, final polish, build |
+
+---
+
+## Success Criteria Checklist
+
+**Functionality:**
+- [ ] Pilot users see new dashboard (role isolation works)
+- [ ] Non-pilots see original disclaimer (backward compatible)
+- [ ] All 5 API endpoints wired and displaying data
+- [ ] Active jobs clickable → navigate to job detail
+- [ ] Charts render with date range control
+- [ ] Performance indicators show correct color bands
+- [ ] i18n works (EN, PT, ES selectable)
+
+**Quality:**
+- [ ] No console errors or warnings
+- [ ] Performance audit completed
+- [ ] Mobile responsive (320px - 1920px)
+- [ ] Cross-browser (Chrome, Safari, Firefox, Edge)
+- [ ] Accessibility: WCAG AA compliant
+- [ ] Appropriate dashboard test coverage added
+
+**Release Readiness:**
+- [ ] Production build succeeds for all locales
+- [ ] No breaking changes to existing features
+- [ ] Documentation complete (user guide + tech docs)
+- [ ] Release notes published
+- [ ] Staging deployment successful
+
+---
+
+## Top Risks & Mitigations
+
+| Risk | Probability | Impact | Mitigation |
+|------|-------------|--------|-----------|
+| Backend endpoints not ready | Medium | Phase 4 blocked | Use mock data through Phase 3; swap at integration |
+| i18n extraction tool issues | Low | Phase 3 delayed | Pre-test localization workflow in Phase 1 |
+| Chart library compatibility | Low | Phase 3 blocked | Verify chart.js 2.9.3 + PrimeNG 9 integration early |
+| Mobile responsive issues late | Medium | Phase 4 delayed | Test mobile throughout Phase 2, not at end |
+| Job detail route not working | Low | Phase 2 blocker | E2E test navigation at end of Phase 2 |
+| Status enum break existing jobs | High | Regression | Coordinate with backend; test all statuses |
+
+---
+
+## Communication Plan
+
+**Daily Standup:**
+- 10 mins, same time each day
+- Report: completed, in-progress, blockers
+- Update task checklist
+
+**Weekly Demo (Friday EOD):**
+- Show completed phase sections to PO/team
+- Gather feedback for next phase
+- Update roadmap if needed
+
+**Backend Sync (if parallel):**
+- Mon & Thu: align on API spec and integration readiness
+- Verify mock data contracts match backend plan
+
+---
+
+## Development Setup Checklist
+
+- [ ] Clone repo / pull latest
+- [ ] `npm install` dependencies
+- [ ] `ng serve` runs without errors
+- [ ] Verify existing tests pass: `ng test`
+- [ ] Read PILOT_DASHBOARD_API_SPEC.md (when created in Phase 1)
+- [ ] Review requirements docs in `/attachments/`
+- [ ] Have Pilot Dashboard UI mockup open (reference)
+- [ ] Setup code editor: prettier, tslint extensions
+- [ ] Create feature branch: `git checkout -b feature/pilot-dashboard`
+
+---
+
+## Key Decisions Made (Frozen for Phase 1)
+
+1. **Active Jobs Display:** UI states are NEW, IN PROGRESS, COMPLETED, where IN PROGRESS groups READY, DOWNLOADED, and SPRAYED backend states.
+2. **Altitude Priority:** sprayHeight → radarAlt → no-data state
+3. **Operations Today:** Distance and spray volume only in Phase 1; Flights Today is excluded due to unreliable source data.
+4. **i18n:** EN/PT/ES from day 1
+5. **Charts:** PrimeNG p-chart (wraps chart.js 2.9.3)
+6. **Status Colors:** Blue (NEW), Yellow (IN PROGRESS), Green (COMPLETED)
+
+---
+
+## Quick Start Commands
+
+```bash
+# Clone and setup
+git clone
+cd client
+npm install
+
+# Development
+npm start # http://localhost:4200
+
+# Testing
+ng test # Unit tests
+ng test --code-coverage
+ng lint
+
+# Build
+ng build --prod --localize # Production + all locales
+
+# i18n workflow
+npm run sync-i18n # Extract + merge
+
+# Useful for reference
+cat docs/PILOT_DASHBOARD_ROADMAP.md # Detailed roadmap
+cat docs/PILOT_DASHBOARD_TASK_CHECKLIST.md # Detailed tasks
+cat docs/PILOT_DASHBOARD_API_SPEC.md # Backend contract (Phase 1.6)
+```
+
+---
+
+## Related Documents
+
+- **docs/PILOT_DASHBOARD_ROADMAP.md** ← Full detailed roadmap
+- **docs/PILOT_DASHBOARD_TASK_CHECKLIST.md** ← Day-by-day task checklist
+- **Pilot_Dashboard_PO_Brief_v1.md** ← Business requirements (in attachments)
+- **Pilot_Dashboard_Requirements_v1.md** ← Technical spec (in attachments)
+- **Pilot-Dashboard-UI-UX-Design-Specification.md** ← Design rules (in attachments)
+- **Sample Pilot Dashboard.png** ← Reference mockup (in attachments)
+
+---
+
+## Verification Checklist (End of Each Phase)
+
+### End of Phase 1
+- [ ] Service layer complete and tested
+- [ ] Mock data provider ready
+- [ ] Backend API spec documented and approved
+- [ ] Role branching working (pilot sees placeholder, others see disclaimer)
+- [ ] All Phase 1 PRs merged
+
+### End of Phase 2
+- [ ] All UI sections render correctly
+- [ ] Layout responsive on all breakpoints
+- [ ] KPI cards, summary, operations, active jobs all visible
+- [ ] Job row clicks navigate to job detail
+- [ ] Accessibility audit passed
+- [ ] All Phase 2 PRs merged
+
+### End of Phase 3
+- [ ] Charts render and respond to date range changes
+- [ ] Performance indicators show data with correct thresholds
+- [ ] i18n working (all 3 languages)
+- [ ] Error and loading states display correctly
+- [ ] No console errors
+- [ ] All Phase 3 PRs merged
+
+### End of Phase 4
+- [ ] Integration testing passed
+- [ ] Cross-browser testing passed
+- [ ] E2E tests passed
+- [ ] Performance audit completed
+- [ ] Production build succeeds for all locales
+- [ ] Documentation complete
+- [ ] All Phase 4 PRs merged
+- [ ] Ready for staging deployment
+
+---
+
+## Effort Distribution
+
+```
+Foundation & Contracts: 22% (5-6 days)
+├─ Service layer, models, role gating
+
+Core UI Delivery: 30% (7-8 days)
+├─ Layout, KPI, summary, active jobs
+
+Analytics & Polish: 28% (6-8 days)
+├─ Charts, indicators, i18n, error handling
+
+QA & Release: 20% (4-5 days)
+├─ Testing, documentation, build prep
+```
+
+---
+
+**Status:** Ready to Start
+**Created:** April 28, 2026
+**Last Updated:** April 28, 2026
diff --git a/client/docs/PILOT_DASHBOARD_ROADMAP.md b/client/docs/PILOT_DASHBOARD_ROADMAP.md
new file mode 100644
index 0000000..35c7883
--- /dev/null
+++ b/client/docs/PILOT_DASHBOARD_ROADMAP.md
@@ -0,0 +1,304 @@
+# Pilot Analytics Dashboard - Development Roadmap
+
+**Project Duration:** ~4-5 weeks (25-30 working days)
+**Start Date:** April 28, 2026
+**Target Delivery:** Late May 2026
+
+## Source Files
+
+This roadmap is derived from the following source files:
+
+- Primary: `Pilot_Dashboard_PO_Brief_v1.md` (v1.5)
+- Primary: `Pilot_Dashboard_Requirements_v1.md` (v1.5)
+- Supporting: `plan-pilotAnalyticsDashboard.prompt.md`
+- Supporting: `Pilot-Dashboard-UI-UX-Design-Specification.md`
+- Supporting visual reference: `Sample Pilot Dashboard.png` and the PyQt mockup
+
+When source documents conflict, the v1.5 PO brief and v1.5 technical requirements take precedence.
+
+---
+
+## Roadmap Overview
+
+This roadmap is divided into 4 phases with specific deliverables and time estimates. Each phase culminates in a PR-ready checkpoint.
+
+### Phase Breakdown
+- **Phase 1 (Foundation):** 5-6 days
+- **Phase 2 (Core UI):** 7-8 days
+- **Phase 3 (Analytics & Polish):** 6-8 days
+- **Phase 4 (QA & Release):** 4-5 days
+
+**Total:** 22-27 working days (frontend only)
+
+---
+
+## Phase 1: Foundation & Contracts (5-6 Days)
+
+### Deliverables
+- Extended job statuses (COMPLETED, INVOICED)
+- Dashboard data models and API service layer
+- Role-based home branching
+- Backend endpoint specification document
+- Dev environment with mock data setup
+
+### Tasks
+
+| Task | Owner | Days | Dependencies | Notes |
+|------|-------|------|--------------|-------|
+| **1.1** Extend JobStatus enum + global constants | Frontend | 1.0 | None | Update global.ts, JobStatuses map, status pipes |
+| **1.2** Design dashboard data contracts (TypeScript interfaces) | Frontend | 1.0 | None | Define KPI, Summary, Operations, ActiveJobs, Trend, Performance models |
+| **1.3** Implement dashboard service with 5 endpoints | Frontend | 1.5 | 1.2 | GET /api/dashboard/pilot/[kpi, summary, active-jobs, trend, performance] |
+| **1.4** Add service unit tests with mock data | Frontend | 0.75 | 1.3 | Jasmine tests for service methods; mock HttpClient |
+| **1.5** Update dashboard component role branching | Frontend | 0.75 | 1.1 | Inject AuthService, template *ngIf branching |
+| **1.6** Backend spec: finalize 5 endpoints (NO implementation) | Backend/Frontend | 1.0 | 1.2 | Document request/response contracts; ready for backend sprint |
+| **1.7** Setup mock data provider for dev | Frontend | 0.5 | 1.2 | Mock service for UI development without backend |
+
+**Phase 1 Checkpoint:** PR ready, all constants aligned, service contracts finalized, no backend dependency blocking UI work.
+
+**Commit Messages:**
+```
+refactor(global): add completed and invoiced job statuses
+feat(dashboard): add pilot dashboard api service and response models
+feat(home): add pilot-only dashboard branch with fallback disclaimer
+docs(dashboard): specify pilot dashboard backend api contracts
+```
+
+---
+
+## Phase 2: Core UI Delivery (7-8 Days)
+
+### Deliverables
+- Responsive 2-column layout
+- KPI cards, daily summary, operations panels
+- Active jobs list with status coloring and progress bars
+- All core sections visually complete and clickable
+
+### Tasks
+
+| Task | Owner | Days | Dependencies | Notes |
+|------|-------|------|--------------|-------|
+| **2.1** Build responsive grid layout (65/35 split) | Frontend | 1.5 | 1.5 | PrimeNG p-grid, mobile stack order |
+| **2.2** Create KPI card component + template | Frontend | 1.0 | 1.1 | Icons, historical sub-lines, responsive cards |
+| **2.3** Implement daily summary strip (today vs yesterday) | Frontend | 0.75 | 1.1 | Trend arrows, percent deltas, green/red coloring |
+| **2.4** Add operations today panel (distance + spray volume) | Frontend | 0.5 | 1.1 | 2-metric strip below summary |
+| **2.5** Build active jobs list panel | Frontend | 2.0 | 1.1 | Status badges, left color bar, progress bars, row click → job detail |
+| **2.6** Implement job row status colors (NEW=Blue, IN PROGRESS=Yellow, COMPLETED=Green) | Frontend | 0.75 | 2.5 | IN PROGRESS groups READY, DOWNLOADED, SPRAYED |
+| **2.7** Add scroll behavior to active jobs panel | Frontend | 0.5 | 2.5 | Independently scrollable; handles 10+ jobs |
+| **2.8** Unit + integration tests for layout (responsive, mobile) | Frontend | 0.5 | 2.1-2.7 | Karma tests, viewport mocking |
+| **2.9** Accessibility audit (ARIA, keyboard nav, contrast) | Frontend | 0.5 | 2.1-2.7 | Lighthouse, manual review |
+
+**Phase 2 Checkpoint:** Left column fully functional, responsive on mobile/tablet, all clicks wired to mock data.
+
+**Commit Messages:**
+```
+feat(dashboard-ui): add responsive pilot dashboard layout shell
+feat(dashboard-kpi): implement kpi cards daily summary and operations strip
+feat(active-jobs): implement status-driven list with progress and navigation
+test(dashboard-layout): add responsive and accessibility tests
+```
+
+---
+
+## Phase 3: Analytics & Polish (6-8 Days)
+
+### Deliverables
+- Trend charts (hours flown, hectares per day)
+- Performance indicators (XT Error, Altitude)
+- Date range controls
+- i18n for EN, PT, ES
+- Error and loading states
+
+### Tasks
+
+| Task | Owner | Days | Dependencies | Notes |
+|------|-------|------|--------------|-------|
+| **3.1** Integrate p-chart for hours flown (line chart) | Frontend | 1.0 | 1.1 | Chart.js 2.9.3, PrimeNG wrapper |
+| **3.2** Integrate p-chart for hectares per day (bar chart) | Frontend | 1.0 | 1.1 | Similar setup; add target line overlay |
+| **3.3** Build date range control (calendar picker) | Frontend | 1.0 | 3.1-3.2 | Default current week Mon-Sun; user can change |
+| **3.4** Implement XT Error indicator with thresholds | Frontend | 0.75 | 1.1 | Green <1m, Yellow 1-3m, Red >3m; color bar |
+| **3.5** Implement altitude indicator with thresholds | Frontend | 0.75 | 1.1 | Target ~3.7m, Good ±0.15m, Monitor ±0.46m, Poor beyond ±0.46m |
+| **3.6** Add no-data state for altitude (device dependency) | Frontend | 0.25 | 3.5 | Show placeholder if sprayHeight/radarAlt not available |
+| **3.7** Add loading skeletons for all sections | Frontend | 0.75 | 2.1-3.6 | PrimeNG skeleton or custom shimmer; per widget |
+| **3.8** Add empty state messages | Frontend | 0.5 | 3.7 | "No jobs assigned", "No data available", etc. |
+| **3.9** Implement API error handling & fallbacks | Frontend | 0.75 | 1.3 | Toast notifications, graceful degradation |
+| **3.10** Extract i18n strings (EN baseline) | Frontend | 0.5 | All sections | Update locale/en-Application.json, all $localize() calls |
+| **3.11** Generate PT and ES translations | Frontend | 1.0 | 3.10 | Run xliffmerge workflow; validate no hardcoded English |
+| **3.12** Test all chart responsiveness on mobile | Frontend | 0.5 | 3.1-3.3 | Charts stack/hide on small screens |
+| **3.13** Unit tests for indicators, error handling, i18n | Frontend | 0.75 | 3.1-3.11 | Mock API responses, locale switching |
+
+**Phase 3 Checkpoint:** Right column fully functional, all i18n ready, error scenarios handled.
+
+**Commit Messages:**
+```
+feat(trends): add weekly trend charts with date range controls
+feat(performance): add xt error and altitude indicators with threshold bands
+feat(dashboard-state): add loading empty and error states for dashboard widgets
+feat(i18n): localize pilot dashboard labels for en pt es
+```
+
+---
+
+## Phase 4: QA, Integration & Release Hardening (4-5 Days)
+
+### Deliverables
+- Full end-to-end testing (mock and real backend if available)
+- Role and permission isolation verification
+- Cross-browser and device testing
+- Performance and bundle size checks
+- Release-ready build
+
+### Tasks
+
+| Task | Owner | Days | Dependencies | Notes |
+|------|-------|------|--------------|-------|
+| **4.1** Integration test: dashboard calls all 5 backend endpoints correctly | Frontend | 0.75 | 1.3 | Mock or real backend; verify request/response shapes |
+| **4.2** Role isolation test: non-pilot roles see disclaimer only | Frontend | 0.5 | 1.5 | Test ADMIN, APP, OFFICER, CLIENT, INSPECTOR roles |
+| **4.3** Responsive + mobile layout testing (Chrome, Safari, Firefox, Edge) | Frontend | 1.0 | All phases | Desktop, tablet, mobile; landscape/portrait |
+| **4.4** Performance audit (performance and bundle impact) | Frontend | 0.5 | All phases | Record findings and optimize only if needed |
+| **4.5** Regression testing: existing home for non-pilots unchanged | Frontend | 0.5 | 1.5 | Verify disclaimer still works; no side effects |
+| **4.6** E2E tests (user flow: login → dashboard → click job → detail) | Frontend | 0.75 | 4.1-4.5 | Protractor or Cypress if available |
+| **4.7** Lint, build, and final code review | Frontend | 0.5 | All phases | ng lint, ng build, tslint clean |
+| **4.8** Documentation: user guide for pilot dashboard features | Frontend | 0.5 | All phases | README section, feature overview, known limitations |
+| **4.9** Deployment dry-run and release notes | DevOps/Frontend | 0.5 | 4.7 | Build prod, verify localization (en/pt/es), changelog |
+
+**Phase 4 Checkpoint:** Ready for staging and user acceptance testing.
+
+**Commit Messages:**
+```
+test(dashboard): add e2e and integration tests
+test(dashboard): add role isolation and regression tests
+chore(dashboard): final ui polish and accessibility adjustments
+docs(dashboard): add pilot dashboard user guide and feature summary
+```
+
+---
+
+## 🔗 Backend Parallel Track (NOT included in frontend days)
+
+**If backend endpoints do NOT exist yet, backend team should start immediately:**
+
+| Task | Owner | Days | Dependencies |
+|------|-------|------|--------------|
+| **B.1** Design and implement 5 dashboard endpoints | Backend | 3-4 | Spec from 1.6 |
+| **B.2** Add aggregation queries (KPI, trend, performance) | Backend | 2-3 | Database schema review |
+| **B.3** Implement pilot data isolation (filter by Job.operator = pilotId) | Backend | 1-2 | Auth middleware |
+| **B.4** Unit tests + load tests (concurrent dashboard calls) | Backend | 1-2 | B.1-B.3 |
+| **B.5** Integration with frontend (live API testing) | Backend/Frontend | 1-2 | Phase 4.1 |
+
+**Backend Total:** 8-13 days (parallel to Phase 2-3)
+
+---
+
+## 📅 Suggested Sprint Schedule
+
+### Week 1 (Mon Apr 28 - Fri May 2)
+- **Days 1-3:** Phase 1 (foundation, contracts, mock service)
+- **Days 4-5:** Phase 2 start (layout, KPI cards)
+- **PR 1 merged by EOW**
+
+### Week 2 (Mon May 5 - Fri May 9)
+- **Days 1-3:** Phase 2 complete (active jobs panel, responsive polish)
+- **Days 4-5:** Phase 3 start (charts, date range)
+- **PR 2 merged by EOW**
+
+### Week 3 (Mon May 12 - Fri May 16)
+- **Days 1-3:** Phase 3 continue (performance indicators, i18n)
+- **Days 4-5:** Phase 3 complete (error states, final tests)
+- **PR 3 merged by EOW**
+
+### Week 4 (Mon May 19 - Fri May 23)
+- **Days 1-3:** Phase 4 (integration testing, E2E, role isolation)
+- **Days 4-5:** Phase 4 complete (perf audit, release prep)
+- **PR 4 merged by EOW**
+
+### Week 5 (Mon May 26 - Wed May 28)
+- **Day 1:** Staging deployment, user acceptance testing
+- **Days 2-3:** Bug fixes, final release sign-off
+
+---
+
+## Daily Time Breakdown (Typical)
+
+Assuming 8-hour work day:
+
+- **Development:** 5-6 hours
+- **Testing (manual + automated):** 1-1.5 hours
+- **Code review + feedback incorporation:** 0.5-1 hour
+- **Documentation + commit messages:** 0.5 hour
+- **Sync meetings (standup, demos):** 0.5 hour
+
+---
+
+## Critical Path & Blockers
+
+### Critical Path
+1. **Phase 1.6 (Backend spec)** must be done before backend team starts
+2. **Phase 1.3 (Service layer)** must exist before Phase 2 UI work (can use mocks)
+3. **Phase 2 (UI)** can proceed in parallel with backend if using mock data
+4. **Phase 4.1 (Integration)** requires backend endpoints live
+
+### Known Risks & Mitigation
+
+| Risk | Impact | Mitigation |
+|------|--------|-----------|
+| Backend endpoints not ready | Phase 4 blocked | Use mock service in Phase 2-3; swap endpoints at Phase 4 |
+| Status enum changes break existing jobs | High | Coordinate with backend; test all job statuses after enum change |
+| i18n extraction tool issues | Phase 3 delayed | Pre-test localization workflow in Phase 1 |
+| Chart.js version compatibility | Phase 3 blocked | Verify chart.js 2.9.3 works with PrimeNG 9.2.8 early in Phase 3.1 |
+| Mobile responsive issues late | Phase 4 delay | Test mobile early (end of Phase 2), not Phase 4 |
+| Job detail navigation broken | Phase 4 blocker | End-to-end test in Phase 2 with mock routing |
+
+---
+
+## Effort Distribution
+
+```
+Phase 1: ████░░░░░░░░░░░░░░ (22% - Foundation)
+Phase 2: ███████░░░░░░░░░░░░ (30% - UI Core)
+Phase 3: ██████░░░░░░░░░░░░░ (28% - Analytics + Polish)
+Phase 4: ████░░░░░░░░░░░░░░░ (20% - QA + Release)
+```
+
+---
+
+## Success Criteria (Definition of Done)
+
+- [ ] All 12 steps implemented
+- [ ] All 5 dashboard API endpoints wired and tested
+- [ ] Pilot-only home working; non-pilots see original disclaimer
+- [ ] Active jobs panel scrollable, clickable, color-coded
+- [ ] Charts render with date range control
+- [ ] Performance indicators show correct thresholds
+- [ ] i18n works for EN, PT, ES
+- [ ] No console errors or warnings
+- [ ] Performance audit completed and documented
+- [ ] Mobile responsive (320px to 1920px)
+- [ ] Cross-browser tested (Chrome, Safari, Firefox, Edge)
+- [ ] All 4 PRs merged and passed review
+- [ ] Release notes and user guide complete
+
+---
+
+## Contingency Buffer
+
+- **Unplanned blockers:** +2 days
+- **Backend integration issues:** +1-2 days
+- **UX revisions:** +1-2 days
+
+**Total realistic completion:** 26-32 working days (5-6.5 weeks)
+
+---
+
+## Notes
+
+1. **Mock Data:** Use throughout Phase 2-3 to avoid backend dependency blocking UI work.
+2. **Commit Frequency:** Daily commits, PR every 1-2 days.
+3. **Testing:** Continuous throughout all phases, not batched at end.
+4. **Documentation:** Update README and include known limitations.
+5. **Accessibility:** Test with keyboard nav, screen reader, high contrast mode.
+6. **Performance:** Keep watch on bundle size; lazy-load charts if needed.
+
+---
+
+Generated: April 28, 2026
+Last Updated: April 28, 2026
diff --git a/client/docs/PILOT_DASHBOARD_TASK_CHECKLIST.md b/client/docs/PILOT_DASHBOARD_TASK_CHECKLIST.md
new file mode 100644
index 0000000..93d02e9
--- /dev/null
+++ b/client/docs/PILOT_DASHBOARD_TASK_CHECKLIST.md
@@ -0,0 +1,506 @@
+# Pilot Analytics Dashboard - Task Checklist & Tracking
+
+Use this checklist to track your daily progress. Update status as you move through each task.
+
+## Source Files
+
+This checklist is derived from the following source files:
+
+- Primary: `Pilot_Dashboard_PO_Brief_v1.md` (v1.5)
+- Primary: `Pilot_Dashboard_Requirements_v1.md` (v1.5)
+- Supporting: `plan-pilotAnalyticsDashboard.prompt.md`
+- Supporting: `Pilot-Dashboard-UI-UX-Design-Specification.md`
+- Supporting visual reference: `Sample Pilot Dashboard.png` and the PyQt mockup
+
+When source documents conflict, the v1.5 PO brief and v1.5 technical requirements take precedence.
+
+---
+
+## Phase 1: Foundation & Contracts (Days 1-6)
+
+### Day 1: Extend JobStatus Enum
+
+**Task 1.1** - Extend JobStatus enum + global constants
+- [ ] Add `COMPLETED = 4` to JobStatus enum in global.ts
+- [ ] Add `INVOICED = 5` to JobStatus enum in global.ts
+- [ ] Update JobStatuses map with new status labels
+- [ ] Update GC.selJobStatuses with new options
+- [ ] Add new status to jobListStatus constants
+- [ ] Update JobStatusPipe to handle new values
+- [ ] Lint and verify no breaking changes
+- [ ] **Commit:** `refactor(global): add completed and invoiced job statuses`
+
+---
+
+### Days 2-3: Dashboard Service Layer
+
+**Task 1.2** - Design dashboard data contracts
+- [ ] Create new file: `src/app/domain/models/pilot-dashboard.model.ts`
+- [ ] Define `PilotKpiResponse` interface
+- [ ] Define `PilotSummaryResponse` interface (today vs yesterday)
+- [ ] Define `PilotOperationsResponse` interface
+- [ ] Define `PilotActiveJobsResponse` interface
+- [ ] Define `PilotTrendResponse` interface
+- [ ] Define `PilotPerformanceResponse` interface
+- [ ] Add detailed JSDoc comments to each
+
+**Task 1.3** - Implement dashboard service
+- [ ] Create `src/app/domain/services/pilot-dashboard.service.ts`
+- [ ] Inject HttpClient
+- [ ] Implement `getKpi()` → GET /api/dashboard/pilot/kpi
+- [ ] Implement `getSummary()` → GET /api/dashboard/pilot/summary
+- [ ] Implement `getActiveJobs()` → GET /api/dashboard/pilot/active-jobs
+- [ ] Implement `getTrend(startDate, endDate)` → GET /api/dashboard/pilot/trend
+- [ ] Implement `getPerformance()` → GET /api/dashboard/pilot/performance
+- [ ] Add proper HttpParams for query strings
+- [ ] Add operators like `catchError` for error handling
+- [ ] **Commit:** `feat(dashboard): add pilot dashboard api service and response models`
+
+**Task 1.4** - Add service unit tests
+- [ ] Create `src/app/domain/services/pilot-dashboard.service.spec.ts`
+- [ ] Mock HttpClient
+- [ ] Test each endpoint method calls correct URL
+- [ ] Test response type mapping
+- [ ] Test error handling
+- [ ] Verify tests pass: `ng test --include='*dashboard.service.spec.ts'`
+
+**Task 1.5** - Setup mock data provider
+- [ ] Create `src/app/shared/mock/pilot-dashboard-mock.ts`
+- [ ] Export mock KPI data
+- [ ] Export mock summary data
+- [ ] Export mock active jobs (3-5 sample jobs)
+- [ ] Export mock trend data (7 days)
+- [ ] Export mock performance data
+- [ ] Create `PilotDashboardMockService extends PilotDashboardService` (optional)
+- [ ] Document how to use in development
+
+---
+
+### Day 4: Role-Based Home Branching
+
+**Task 1.5** - Update dashboard component
+- [ ] Open `src/app/dashboard/dashboard.component.ts`
+- [ ] Inject `AuthService` and `PilotDashboardService`
+- [ ] Add `isPilot$: Observable` property
+- [ ] Add `isPilot = this.authSvc.isPilotUser` getter
+- [ ] Add component initialization logic
+
+**Task 1.5b** - Update dashboard template
+- [ ] Open `src/app/dashboard/dashboard.component.html`
+- [ ] Replace entire template with:
+ ```html
+