nodejs-poolcontroller 7.3.1 → 7.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/.eslintrc.json +44 -44
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +52 -52
  3. package/CONTRIBUTING.md +74 -74
  4. package/Changelog +215 -195
  5. package/Dockerfile +17 -17
  6. package/Gruntfile.js +40 -40
  7. package/LICENSE +661 -661
  8. package/README.md +191 -186
  9. package/app.ts +2 -0
  10. package/config/Config.ts +27 -2
  11. package/config/VersionCheck.ts +33 -14
  12. package/config copy.json +299 -299
  13. package/controller/Constants.ts +88 -0
  14. package/controller/Equipment.ts +2459 -2225
  15. package/controller/Errors.ts +180 -157
  16. package/controller/Lockouts.ts +437 -0
  17. package/controller/State.ts +364 -79
  18. package/controller/boards/BoardFactory.ts +45 -45
  19. package/controller/boards/EasyTouchBoard.ts +2653 -2489
  20. package/controller/boards/IntelliCenterBoard.ts +4230 -3973
  21. package/controller/boards/IntelliComBoard.ts +63 -63
  22. package/controller/boards/IntelliTouchBoard.ts +241 -167
  23. package/controller/boards/NixieBoard.ts +1675 -1105
  24. package/controller/boards/SystemBoard.ts +4697 -3201
  25. package/controller/comms/Comms.ts +222 -10
  26. package/controller/comms/messages/Messages.ts +13 -9
  27. package/controller/comms/messages/config/ChlorinatorMessage.ts +13 -4
  28. package/controller/comms/messages/config/CircuitGroupMessage.ts +6 -0
  29. package/controller/comms/messages/config/CircuitMessage.ts +0 -0
  30. package/controller/comms/messages/config/ConfigMessage.ts +0 -0
  31. package/controller/comms/messages/config/CoverMessage.ts +1 -0
  32. package/controller/comms/messages/config/CustomNameMessage.ts +30 -30
  33. package/controller/comms/messages/config/EquipmentMessage.ts +4 -0
  34. package/controller/comms/messages/config/ExternalMessage.ts +53 -33
  35. package/controller/comms/messages/config/FeatureMessage.ts +8 -1
  36. package/controller/comms/messages/config/GeneralMessage.ts +8 -0
  37. package/controller/comms/messages/config/HeaterMessage.ts +14 -28
  38. package/controller/comms/messages/config/IntellichemMessage.ts +4 -1
  39. package/controller/comms/messages/config/OptionsMessage.ts +38 -2
  40. package/controller/comms/messages/config/PumpMessage.ts +4 -20
  41. package/controller/comms/messages/config/RemoteMessage.ts +4 -0
  42. package/controller/comms/messages/config/ScheduleMessage.ts +347 -331
  43. package/controller/comms/messages/config/SecurityMessage.ts +1 -0
  44. package/controller/comms/messages/config/ValveMessage.ts +13 -3
  45. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +2 -3
  46. package/controller/comms/messages/status/EquipmentStateMessage.ts +79 -25
  47. package/controller/comms/messages/status/HeaterStateMessage.ts +86 -53
  48. package/controller/comms/messages/status/IntelliChemStateMessage.ts +445 -386
  49. package/controller/comms/messages/status/IntelliValveStateMessage.ts +35 -35
  50. package/controller/comms/messages/status/PumpStateMessage.ts +0 -0
  51. package/controller/comms/messages/status/VersionMessage.ts +0 -0
  52. package/controller/nixie/Nixie.ts +162 -160
  53. package/controller/nixie/NixieEquipment.ts +103 -103
  54. package/controller/nixie/bodies/Body.ts +120 -117
  55. package/controller/nixie/bodies/Filter.ts +135 -135
  56. package/controller/nixie/chemistry/ChemController.ts +2498 -2395
  57. package/controller/nixie/chemistry/Chlorinator.ts +314 -313
  58. package/controller/nixie/circuits/Circuit.ts +248 -210
  59. package/controller/nixie/heaters/Heater.ts +649 -441
  60. package/controller/nixie/pumps/Pump.ts +661 -599
  61. package/controller/nixie/schedules/Schedule.ts +257 -256
  62. package/controller/nixie/valves/Valve.ts +170 -170
  63. package/defaultConfig.json +286 -271
  64. package/issue_template.md +51 -51
  65. package/logger/DataLogger.ts +448 -433
  66. package/logger/Logger.ts +0 -0
  67. package/package.json +56 -54
  68. package/tsconfig.json +25 -25
  69. package/web/Server.ts +522 -31
  70. package/web/bindings/influxDB.json +1022 -894
  71. package/web/bindings/mqtt.json +654 -543
  72. package/web/bindings/mqttAlt.json +684 -574
  73. package/web/bindings/rulesManager.json +54 -54
  74. package/web/bindings/smartThings-Hubitat.json +31 -31
  75. package/web/bindings/valveRelays.json +20 -20
  76. package/web/bindings/vera.json +25 -25
  77. package/web/interfaces/baseInterface.ts +136 -136
  78. package/web/interfaces/httpInterface.ts +124 -122
  79. package/web/interfaces/influxInterface.ts +245 -240
  80. package/web/interfaces/mqttInterface.ts +475 -464
  81. package/web/services/config/Config.ts +181 -152
  82. package/web/services/config/ConfigSocket.ts +0 -0
  83. package/web/services/state/State.ts +118 -7
  84. package/web/services/state/StateSocket.ts +18 -1
  85. package/web/services/utilities/Utilities.ts +42 -42
@@ -14,16 +14,21 @@ GNU Affero General Public License for more details.
14
14
  You should have received a copy of the GNU Affero General Public License
15
15
  along with this program. If not, see <http://www.gnu.org/licenses/>.
16
16
  */
17
+ import * as fs from "fs";
17
18
  import * as express from "express";
18
19
  import * as extend from 'extend';
20
+ import * as multer from 'multer';
21
+ import * as path from "path";
19
22
  import { sys, LightGroup, ControllerType, Pump, Valve, Body, General, Circuit, ICircuit, Feature, CircuitGroup, CustomNameCollection, Schedule, Chlorinator, Heater } from "../../../controller/Equipment";
20
23
  import { config } from "../../../config/Config";
21
24
  import { logger } from "../../../logger/Logger";
22
25
  import { utils } from "../../../controller/Constants";
26
+ import { ServiceProcessError } from "../../../controller/Errors";
23
27
  import { state } from "../../../controller/State";
24
28
  import { stopPacketCaptureAsync, startPacketCapture } from '../../../app';
25
29
  import { conn } from "../../../controller/comms/Comms";
26
- import { webApp } from "../../Server";
30
+ import { webApp, BackupFile, RestoreFile } from "../../Server";
31
+ import { release } from "os";
27
32
 
28
33
  export class ConfigRoute {
29
34
  public static initRoutes(app: express.Application) {
@@ -52,7 +57,8 @@ export class ConfigRoute {
52
57
  clockSources: sys.board.valueMaps.clockSources.toArray(),
53
58
  clockModes: sys.board.valueMaps.clockModes.toArray(),
54
59
  pool: sys.general.get(true),
55
- sensors: sys.board.system.getSensors()
60
+ sensors: sys.board.system.getSensors(),
61
+ systemUnits: sys.board.valueMaps.systemUnits.toArray()
56
62
  };
57
63
  return res.status(200).send(opts);
58
64
  });
@@ -106,7 +112,7 @@ export class ConfigRoute {
106
112
  invalidIds: sys.board.equipmentIds.invalidIds.get(),
107
113
  equipmentIds: sys.equipment.equipmentIds.features,
108
114
  equipmentNames: sys.board.circuits.getCircuitNames(),
109
- functions: sys.board.valueMaps.featureFunctions.toArray(),
115
+ functions: sys.board.features.getFeatureFunctions(),
110
116
  features: sys.features.get()
111
117
  };
112
118
  return res.status(200).send(opts);
@@ -115,7 +121,8 @@ export class ConfigRoute {
115
121
  let opts = {
116
122
  maxBodies: sys.equipment.maxBodies,
117
123
  bodyTypes: sys.board.valueMaps.bodies.toArray(),
118
- bodies: sys.bodies.get()
124
+ bodies: sys.bodies.get(),
125
+ capacityUnits: sys.board.valueMaps.volumeUnits.toArray()
119
126
  };
120
127
  return res.status(200).send(opts);
121
128
  });
@@ -282,7 +289,8 @@ export class ConfigRoute {
282
289
  bodies: sys.board.bodies.getBodyAssociations(),
283
290
  chlorinators: sys.chlorinators.get(),
284
291
  maxChlorinators: sys.equipment.maxChlorinators,
285
- models: sys.board.valueMaps.chlorinatorModel.toArray()
292
+ models: sys.board.valueMaps.chlorinatorModel.toArray(),
293
+ equipmentMasters: sys.board.valueMaps.equipmentMaster.toArray()
286
294
  };
287
295
  return res.status(200).send(opts);
288
296
  });
@@ -307,13 +315,16 @@ export class ConfigRoute {
307
315
  let opts = {
308
316
  interfaces: config.getSection('web.interfaces'),
309
317
  types: [
310
- {name: 'rem', desc: 'Relay Equipment Manager'},
311
- {name: 'mqtt', desc: 'MQTT'}
318
+ { name: 'rest', desc: 'Rest' },
319
+ { name: 'http', desc: 'Http' },
320
+ { name: 'rem', desc: 'Relay Equipment Manager' },
321
+ { name: 'mqtt', desc: 'MQTT' },
322
+ { name: 'influx', desc: 'InfluxDB' }
312
323
  ],
313
324
  protocols: [
314
325
  { val: 0, name: 'http://', desc: 'http://' },
315
326
  { val: 1, name: 'https://', desc: 'https://' },
316
- { val: 2, name: 'mqtt://', desc: 'mqtt://' },
327
+ { val: 2, name: 'mqtt://', desc: 'mqtt://' }
317
328
  ]
318
329
  }
319
330
  return res.status(200).send(opts);
@@ -332,6 +343,8 @@ export class ConfigRoute {
332
343
  bodies: sys.board.bodies.getBodyAssociations(),
333
344
  filters: sys.filters.get(),
334
345
  areaUnits: sys.board.valueMaps.areaUnits.toArray(),
346
+ pressureUnits: sys.board.valueMaps.pressureUnits.toArray(),
347
+ circuits: sys.board.circuits.getCircuitReferences(true, true, true, false),
335
348
  servers: []
336
349
  };
337
350
  if (sys.controllerType === ControllerType.Nixie) opts.servers = await sys.ncp.getREMServers();
@@ -340,14 +353,14 @@ export class ConfigRoute {
340
353
  });
341
354
  /******* END OF CONFIGURATION PICK LISTS/REFERENCES AND VALIDATION ***********/
342
355
  /******* ENDPOINTS FOR MODIFYING THE OUTDOOR CONTROL PANEL SETTINGS **********/
343
- app.put('/config/rem', async (req, res, next)=>{
356
+ app.put('/config/rem', async (req, res, next) => {
344
357
  try {
345
358
  // RSG: this is problematic because we now enable multiple rem type interfaces that may not be called REM.
346
359
  // This is now also a dupe of PUT /app/interface and should be consolidated
347
360
  // config.setSection('web.interfaces.rem', req.body);
348
361
  config.setInterface(req.body);
349
362
  }
350
- catch (err) {next(err);}
363
+ catch (err) { next(err); }
351
364
  })
352
365
  app.put('/config/tempSensors', async (req, res, next) => {
353
366
  try {
@@ -362,7 +375,7 @@ export class ConfigRoute {
362
375
  });
363
376
  app.put('/config/filter', async (req, res, next) => {
364
377
  try {
365
- let sfilter = sys.board.filters.setFilter(req.body);
378
+ let sfilter = await sys.board.filters.setFilterAsync(req.body);
366
379
  return res.status(200).send(sfilter.get(true));
367
380
  }
368
381
  catch (err) { next(err); }
@@ -375,7 +388,7 @@ export class ConfigRoute {
375
388
  });
376
389
  app.delete('/config/filter', async (req, res, next) => {
377
390
  try {
378
- let sfilter = sys.board.filters.deleteFilter(req.body);
391
+ let sfilter = await sys.board.filters.deleteFilterAsync(req.body);
379
392
  return res.status(200).send(sfilter.get(true));
380
393
  }
381
394
  catch (err) { next(err); }
@@ -579,8 +592,8 @@ export class ConfigRoute {
579
592
  return res.status(200).send(circuitFunctions);
580
593
  });
581
594
  app.get('/config/features/functions', (req, res) => {
582
- let circuitFunctions = sys.board.circuits.getCircuitFunctions();
583
- return res.status(200).send(circuitFunctions);
595
+ let featureFunctions = sys.board.features.getFeatureFunctions();
596
+ return res.status(200).send(featureFunctions);
584
597
  });
585
598
  app.get('/config/circuit/:id', (req, res) => {
586
599
  // todo: need getInterfaceById.get() in case features are requested here
@@ -589,18 +602,18 @@ export class ConfigRoute {
589
602
  });
590
603
  app.get('/config/circuit/:id/lightThemes', (req, res) => {
591
604
  let circuit = sys.circuits.getInterfaceById(parseInt(req.params.id, 10));
592
- let themes = typeof circuit !== 'undefined' && typeof circuit.getLightThemes === 'function' ? circuit.getLightThemes() : [];
605
+ let themes = typeof circuit !== 'undefined' && typeof circuit.getLightThemes === 'function' ? circuit.getLightThemes(circuit.type) : [];
593
606
  return res.status(200).send(themes);
594
607
  });
608
+ app.get('/config/circuit/:id/lightCommands', (req, res) => {
609
+ let circuit = sys.circuits.getInterfaceById(parseInt(req.params.id, 10));
610
+ let commands = typeof circuit !== 'undefined' && typeof circuit.getLightThemes === 'function' ? circuit.getLightCommands(circuit.type) : [];
611
+ return res.status(200).send(commands);
612
+ });
613
+
595
614
  app.get('/config/chlorinator/:id', (req, res) => {
596
615
  return res.status(200).send(sys.chlorinators.getItemById(parseInt(req.params.id, 10)).get());
597
616
  });
598
- //app.put('/config/chlorinator', (req, res) => {
599
- // let chlor = sys.chlorinators.getItemById(parseInt(req.body.id, 10), true);
600
- // sys.board.chlorinator.setChlorProps(chlor, req.body);
601
- // // if (chlor.isVirtual) { sys.board.virtualChlorinatorController.start(); }
602
- // return res.status(200).send(sys.chlorinators.getItemById(parseInt(req.params.id, 10)).get());
603
- //});
604
617
  app.get('/config/chlorinators/search', async (req, res, next) => {
605
618
  // Change the options for the pool.
606
619
  try {
@@ -611,103 +624,6 @@ export class ConfigRoute {
611
624
  next(err);
612
625
  }
613
626
  });
614
- /* app.get('/config/pump/:id/circuits', (req, res) => {
615
- return res.status(200).send(sys.pumps.getItemById(parseInt(req.params.id, 10)).get().circuits);
616
- });
617
- app.get('/config/pump/availableCircuits', (req, res) => {
618
- return res.status(200).send(sys.board.pumps.availableCircuits());
619
- });
620
- app.get('/config/pump/:id/circuit/:circuitid', (req, res) => {
621
- return res.status(200).send(sys.pumps.getItemById(parseInt(req.params.id, 10)).get().circuits[parseInt(req.params.circuitid, 10)]);
622
- });
623
- app.get('/config/pump/:id/nextAvailablePumpCircuit', (req, res) => {
624
- // if no pumpCircuitId is available, 0 will be returned
625
- let _id = sys.pumps.getItemById(parseInt(req.params.id, 10)).nextAvailablePumpCircuit();
626
- return res.status(200).send(_id.toString());
627
- }); */
628
- /*
629
- app.put('/config/pump/:id/pumpCircuit', (req, res) => {
630
- // if no pumpCircuitId is specified, set it as 0 and take the next available one
631
- req.url = `${ req.url }/0`;
632
- req.next();
633
- });
634
- app.put('/config/pump/:id/pumpCircuit/:pumpCircuitId', (req, res) => {
635
- // RSG - do we want a /config/pump/:id/pumpCircuit/ that will just assume the next circuit?
636
- let pump = sys.pumps.getItemById(parseInt(req.params.id, 10));
637
- let _pumpCircuitId = parseInt(req.params.pumpCircuitId, 10);
638
- let _circuit = parseInt(req.body.circuit, 10);
639
- let _rate = parseInt(req.body.rate, 10);
640
- let _units = parseInt(req.body.units, 10) || pump.defaultUnits;
641
- let pumpCircuit = {
642
- pump: parseInt(req.params.id, 10),
643
- pumpCircuitId: isNaN(_pumpCircuitId) ? undefined : _pumpCircuitId,
644
- circuit: isNaN(_circuit) ? undefined : _circuit,
645
- rate: isNaN(_rate) ? undefined : _rate,
646
- units: isNaN(_units) ? undefined : _units
647
- };
648
- let { result, reason } = pump.setPumpCircuit(pumpCircuit);
649
- if (result === 'OK')
650
- return res.status(200).send({ result: result, reason: reason });
651
- else
652
- return res.status(500).send({ result: result, reason: reason });
653
- }); */
654
- /* app.delete('/config/pump/:id/pumpCircuit/:pumpCircuitId', (req, res) => {
655
- let pump = sys.pumps.getItemById(parseInt(req.params.id, 10));
656
- // pump.circuits.removeItemById(parseInt(req.params.pumpCircuitId, 10));
657
- pump.deletePumpCircuit(parseInt(req.params.pumpCircuitId, 10));
658
- return res.status(200).send('OK');
659
- }); */
660
- /* app.get('/config/pump/types', (req, res) => {
661
- let pumpTypes = sys.board.pumps.getPumpTypes();
662
- return res.status(200).send(pumpTypes);
663
- });
664
- app.get('/config/pump/units', (req, res) => {
665
- // get all units for all system board
666
- let pumpTypes = sys.board.pumps.getCircuitUnits();
667
- return res.status(200).send(pumpTypes);
668
- });
669
- app.get('/config/pump/:id/units', (req, res) => {
670
- // get units for all specific pump types
671
- // need to coorerce into array if only a single unit is returned; by default getExtended will return an array
672
- // if there is 1+ object so this creates parity
673
- let pump = sys.pumps.getItemById(parseInt(req.params.id, 10));
674
- let pumpTypes = sys.board.pumps.getCircuitUnits(pump);
675
- if (!Array.isArray(pumpTypes)) pumpTypes = [pumpTypes];
676
- return res.status(200).send(pumpTypes);
677
- });
678
- app.put('/config/pump/:pumpId/type', (req, res) => {
679
- const _type = parseInt(req.body.pumpType, 10);
680
- const _pumpId = parseInt(req.params.pumpId, 10);
681
- // RG - this was left as it's own end point because trying to combine changing the pump type (which requires resetting the pump values) while simultaneously setting new pump values was tricky.
682
- let pump = sys.pumps.getItemById(_pumpId);
683
- if (sys.controllerType === ControllerType.Virtual) {
684
- pump.isVirtual = true;
685
- }
686
- if (_type !== pump.type) {
687
- pump.setType(_type);
688
- }
689
- return res.status(200).send('OK');
690
- }); */
691
- /* app.get('/config/pump/:pumpId', (req, res) => {
692
- let pump = sys.pumps.getItemById(parseInt(req.params.pumpId, 10)).get(true);
693
- return res.status(200).send(pump);
694
- });
695
- app.put('/config/pump/:pumpId', (req, res) => {
696
- // this will change the pump type
697
- let _type = parseInt(req.body.pumpType, 10);
698
- let pump = sys.pumps.getItemById(parseInt(req.params.pumpId, 10));
699
- if (sys.controllerType === ControllerType.Virtual) {
700
- // if virtualController, add the virtual pump
701
- pump.isVirtual = true;
702
- }
703
-
704
- if (_type !== pump.type && typeof _type !== 'undefined') {
705
- pump.setType(_type);
706
- }
707
- // get a new instance of the pump here because setType will remove/add a new instance
708
- if (Object.keys(req.body).length) sys.pumps.getItemById(parseInt(req.params.pumpId, 10)).setPump(req.body);
709
- return res.status(200).send('OK');
710
- }); */
711
627
  app.delete('/config/pump/:pumpId', (req, res) => {
712
628
  let pump = sys.pumps.getItemById(parseInt(req.params.pumpId, 10));
713
629
  if (pump.type === 0) {
@@ -725,13 +641,12 @@ export class ConfigRoute {
725
641
  });
726
642
  app.get('/config/lightGroups/themes', (req, res) => {
727
643
  // RSG: is this and /config/circuit/:id/lightThemes both needed?
728
-
729
- // if (sys.controllerType === ControllerType.IntelliCenter) {
730
- let grp = sys.lightGroups.getItemById(parseInt(req.params.id, 10));
644
+ let grp = sys.lightGroups.getItemById(parseInt(req.body.id, 10));
731
645
  return res.status(200).send(grp.getLightThemes());
732
- // }
733
- // else
734
- // return res.status(200).send(sys.intellibrite.getLightThemes());
646
+ });
647
+ app.get('/config/lightGroups/commands', (req, res) => {
648
+ let grp = sys.lightGroups.getItemById(parseInt(req.body.id, 10));
649
+ return res.status(200).send(grp.getLightCommands());
735
650
  });
736
651
  app.get('/config/lightGroup/:id', (req, res) => {
737
652
  // if (sys.controllerType === ControllerType.IntelliCenter) {
@@ -760,16 +675,16 @@ export class ConfigRoute {
760
675
  let grp = sys.circuitGroups.getItemById(parseInt(req.params.id, 10));
761
676
  return res.status(200).send(grp.getExtended());
762
677
  });
763
- /* app.get('/config/chemController/search', async (req, res, next) => {
764
- // Change the options for the pool.
765
- try {
766
- let result = await sys.board.virtualChemControllers.search();
767
- return res.status(200).send(result);
768
- }
769
- catch (err) {
770
- next(err);
771
- }
772
- }); */
678
+ /* app.get('/config/chemController/search', async (req, res, next) => {
679
+ // Change the options for the pool.
680
+ try {
681
+ let result = await sys.board.virtualChemControllers.search();
682
+ return res.status(200).send(result);
683
+ }
684
+ catch (err) {
685
+ next(err);
686
+ }
687
+ }); */
773
688
  app.put('/config/chemController', async (req, res, next) => {
774
689
  try {
775
690
  let chem = await sys.board.chemControllers.setChemControllerAsync(req.body);
@@ -792,17 +707,17 @@ export class ConfigRoute {
792
707
  catch (err) { next(err); }
793
708
 
794
709
  });
795
- /* app.get('/config/intellibrite', (req, res) => {
796
- return res.status(200).send(sys.intellibrite.getExtended());
797
- });
798
- app.get('/config/intellibrite/colors', (req, res) => {
799
- return res.status(200).send(sys.board.valueMaps.lightColors.toArray());
800
- });
801
- app.put('/config/intellibrite/setColors', (req, res) => {
802
- let grp = extend(true, { id: 0 }, req.body);
803
- sys.board.circuits.setIntelliBriteColors(new LightGroup(grp));
804
- return res.status(200).send('OK');
805
- }); */
710
+ /* app.get('/config/intellibrite', (req, res) => {
711
+ return res.status(200).send(sys.intellibrite.getExtended());
712
+ });
713
+ app.get('/config/intellibrite/colors', (req, res) => {
714
+ return res.status(200).send(sys.board.valueMaps.lightColors.toArray());
715
+ });
716
+ app.put('/config/intellibrite/setColors', (req, res) => {
717
+ let grp = extend(true, { id: 0 }, req.body);
718
+ sys.board.circuits.setIntelliBriteColors(new LightGroup(grp));
719
+ return res.status(200).send('OK');
720
+ }); */
806
721
  app.get('/config', (req, res) => {
807
722
  return res.status(200).send(sys.getSection('all'));
808
723
  });
@@ -828,11 +743,11 @@ export class ConfigRoute {
828
743
  return res.status(200).send('OK');
829
744
  });
830
745
  app.put('/app/interface', async (req, res, next) => {
831
- try{
832
- await webApp.updateServerInterface(req.body);
833
- return res.status(200).send('OK');
834
- }
835
- catch (err) {next(err);}
746
+ try {
747
+ let iface = await webApp.updateServerInterface(req.body);
748
+ return res.status(200).send(iface);
749
+ }
750
+ catch (err) { next(err); }
836
751
  });
837
752
  app.put('/app/rs485Port', async (req, res, next) => {
838
753
  try {
@@ -849,15 +764,129 @@ export class ConfigRoute {
849
764
  startPacketCapture(false);
850
765
  return res.status(200).send('OK');
851
766
  });
852
- app.get('/app/config/stopPacketCapture', async (req, res,next) => {
767
+ app.get('/app/config/stopPacketCapture', async (req, res, next) => {
853
768
  try {
854
769
  let file = await stopPacketCaptureAsync();
855
770
  res.download(file);
856
771
  }
857
- catch (err) {next(err);}
772
+ catch (err) { next(err); }
858
773
  });
859
774
  app.get('/app/config/:section', (req, res) => {
860
775
  return res.status(200).send(config.getSection(req.params.section));
861
776
  });
777
+ app.get('/app/config/options/backup', async (req, res, next) => {
778
+ try {
779
+ let opts = config.getSection('controller.backups', { automatic: false, interval: { days: 30, hours: 0 }, keepCount: 5, servers: [] });
780
+ let servers = await sys.ncp.getREMServers();
781
+ if (typeof servers !== 'undefined') {
782
+ // Just in case somebody deletes the backup section and doesn't put it back properly.
783
+ for (let i = 0; i < servers.length; i++) {
784
+ let srv = servers[i];
785
+ if (typeof opts.servers.find(elem => elem.uuid === srv.uuid) === 'undefined') opts.servers.push({ name: srv.name, uuid: srv.uuid, backup: false, host: srv.interface.options.host });
786
+ }
787
+ for (let i = opts.servers.length - 1; i >= 0; i--) {
788
+ let srv = opts.servers[i];
789
+ if (typeof servers.find(elem => elem.uuid === srv.uuid) === 'undefined') opts.servers.splice(i, 1);
790
+ }
791
+ }
792
+ if (typeof opts.servers === 'undefined') opts.servers = [];
793
+ return res.status(200).send(opts);
794
+ } catch (err) { next(err); }
795
+ });
796
+ app.get('/app/config/options/restore', async (req, res, next) => {
797
+ try {
798
+ let opts = config.getSection('controller.backups', { automatic: false, interval: { days: 30, hours: 0 }, keepCount: 5, servers: [], backupFiles: [] });
799
+ let servers = await sys.ncp.getREMServers();
800
+ if (typeof servers !== 'undefined') {
801
+ for (let i = 0; i < servers.length; i++) {
802
+ let srv = servers[i];
803
+ if (typeof opts.servers.find(elem => elem.uuid === srv.uuid) === 'undefined') opts.servers.push({ name: srv.name, uuid: srv.uuid, backup: false });
804
+ }
805
+ for (let i = opts.servers.length - 1; i >= 0; i--) {
806
+ let srv = opts.servers[i];
807
+ if (typeof servers.find(elem => elem.uuid === srv.uuid) === 'undefined') opts.servers.splice(i, 1);
808
+ }
809
+ }
810
+ if (typeof opts.servers === 'undefined') opts.servers = [];
811
+ opts.backupFiles = await webApp.readBackupFiles();
812
+ return res.status(200).send(opts);
813
+ } catch (err) { next(err); }
814
+
815
+ });
816
+ app.put('/app/config/options/backup', async (req, res, next) => {
817
+ try {
818
+ config.setSection('controller.backups', req.body);
819
+ let opts = config.getSection('controller.backups', { automatic: false, interval: { days: 30, hours: 0 }, keepCount: 5, servers: [] });
820
+ webApp.autoBackup = utils.makeBool(opts.automatic);
821
+ await webApp.checkAutoBackup();
822
+ return res.status(200).send(opts);
823
+ } catch (err) { next(err); }
824
+
825
+ });
826
+ app.put('/app/config/createBackup', async (req, res, next) => {
827
+ try {
828
+ let ret = await webApp.backupServer(req.body);
829
+ res.download(ret.filePath);
830
+ }
831
+ catch (err) { next(err); }
832
+ });
833
+ app.delete('/app/backup/file', async (req, res, next) => {
834
+ try {
835
+ let opts = req.body;
836
+ fs.unlinkSync(opts.filePath);
837
+ return res.status(200).send(opts);
838
+ }
839
+ catch (err) { next(err); }
840
+ });
841
+ app.post('/app/backup/file', async (req, res, next) => {
842
+ try {
843
+ let file = multer({
844
+ limits: { fileSize: 1000000 },
845
+ storage: multer.memoryStorage()
846
+ }).single('backupFile');
847
+ file(req, res, async (err) => {
848
+ try {
849
+ if (err) { next(err); }
850
+ else {
851
+ // Validate the incoming data and save it off only if it is valid.
852
+ let bf = await BackupFile.fromBuffer(req.file.originalname, req.file.buffer);
853
+ if (typeof bf === 'undefined') {
854
+ err = new ServiceProcessError(`Invalid backup file: ${req.file.originalname}`, 'POST: app/backup/file', 'extractBackupOptions');
855
+ next(err);
856
+ }
857
+ else {
858
+ if (fs.existsSync(bf.filePath))
859
+ return next(new ServiceProcessError(`File already exists ${req.file.originalname}`, 'POST: app/backup/file', 'writeFile'));
860
+ else {
861
+ try {
862
+ fs.writeFileSync(bf.filePath, req.file.buffer);
863
+ } catch (e) { logger.error(`Error writing backup file ${e.message}`); }
864
+ }
865
+ return res.status(200).send(bf);
866
+ }
867
+ }
868
+ } catch (e) {
869
+ err = new ServiceProcessError(`Error uploading file: ${e.message}`, 'POST: app/backup/file', 'uploadFile');
870
+ next(err);
871
+ logger.error(e);
872
+ }
873
+ });
874
+ } catch (err) { next(err); }
875
+ });
876
+ app.put('/app/restore/validate', async (req, res, next) => {
877
+ try {
878
+ // Validate all the restore options.
879
+ let opts = req.body;
880
+ let ctx = await webApp.validateRestore(opts);
881
+ return res.status(200).send(ctx);
882
+ } catch (err) { next(err); }
883
+ });
884
+ app.put('/app/restore/file', async (req, res, next) => {
885
+ try {
886
+ let opts = req.body;
887
+ let results = await webApp.restoreServers(opts);
888
+ return res.status(200).send(results);
889
+ } catch (err) { next(err); }
890
+ });
862
891
  }
863
892
  }
File without changes