@camera.ui/server 0.0.14-alpha.4 → 0.0.14-test.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 (169) hide show
  1. package/dist/interface/@tanstack.BYxL60eQ.0.0.14-test.1.js +1 -0
  2. package/dist/interface/{@vee-validate.DH43ss6R.0.0.14-alpha.4.js → @vee-validate.Z5A35PDk.0.0.14-test.1.js} +1 -1
  3. package/dist/interface/@vueform.CoF_hlnG.0.0.14-test.1.js +1 -0
  4. package/dist/interface/@vueuse.Dtsyi5jU.0.0.14-test.1.js +1 -0
  5. package/dist/interface/Account.CFvGELyY.0.0.14-test.1.js +1 -0
  6. package/dist/interface/Adminpanel.CpUaqKuO.0.0.14-test.1.js +2 -0
  7. package/dist/interface/Appearance.BeTnHL4h.0.0.14-test.1.js +1 -0
  8. package/dist/interface/Backup.DgnbWwwU.0.0.14-test.1.js +1 -0
  9. package/dist/interface/Camera.Fi9q-JJz.0.0.14-test.1.js +1 -0
  10. package/dist/interface/Cameras.uhunebYe.0.0.14-test.1.js +1 -0
  11. package/dist/interface/Camview.CBQetoTX.0.0.14-test.1.js +1 -0
  12. package/dist/interface/Config.C-emlu62.0.0.14-test.1.js +1 -0
  13. package/dist/interface/Console.CTAg7QJ8.0.0.14-test.1.js +4 -0
  14. package/dist/interface/FirstSteps.4oJT6F8n.0.0.14-test.1.js +1 -0
  15. package/dist/interface/{NewCamera.CVpitp4W.0.0.14-alpha.4.js → NewCamera.BmFio_jk.0.0.14-test.1.js} +1 -1
  16. package/dist/interface/Plugin.C8u8kzUS.0.0.14-test.1.js +1 -0
  17. package/dist/interface/Plugins.Ib7pIgRX.0.0.14-test.1.js +1 -0
  18. package/dist/interface/Recordings.BNweXQaW.0.0.14-test.1.js +1 -0
  19. package/dist/interface/Recordings.wMMJyUuE.0.0.14-test.1.js +1 -0
  20. package/dist/interface/Settings.Dm0BkVOl.0.0.14-test.1.js +1 -0
  21. package/dist/interface/{System.DLJuO_7K.0.0.14-alpha.4.js → System.KD-3Ch_y.0.0.14-test.1.js} +1 -1
  22. package/dist/interface/User.DMJur9L6.0.0.14-test.1.js +1 -0
  23. package/dist/interface/VConfigEditor.vue_vue_type_script_setup_true_lang.BPGSYHs9.0.0.14-test.1.js +2 -0
  24. package/dist/interface/VConsole.TmsHBk2t.0.0.14-test.1.js +1 -0
  25. package/dist/interface/VPlayer.B08m_Ph4.0.0.14-test.1.js +1 -0
  26. package/dist/interface/VPluginCard.ClWtyeYd.0.0.14-test.1.js +4 -0
  27. package/dist/interface/{ace-builds.DFZjdKko.0.0.14-alpha.4.js → ace-builds.BYEaFNIm.0.0.14-test.1.js} +1 -1
  28. package/dist/interface/assets/VPluginCard.0.0.14-test.1.C2GrhESv.css +1 -0
  29. package/dist/interface/dnd-core.BrzyTz8F.0.0.14-test.1.js +1 -0
  30. package/dist/interface/{draggabilly.B1sGLlkC.0.0.14-alpha.4.js → draggabilly.5IGjU2se.0.0.14-test.1.js} +1 -1
  31. package/dist/interface/engine.io-client.cEOCchJv.0.0.14-test.1.js +1 -0
  32. package/dist/interface/ev-emitter.DAoLkz_R.0.0.14-test.1.js +1 -0
  33. package/dist/interface/fast-deep-equal.D9PHWU3f.0.0.14-test.1.js +1 -0
  34. package/dist/interface/{highlight.js.0YB-0mi8.0.0.14-alpha.4.js → highlight.js.BtO7eVnE.0.0.14-test.1.js} +1 -1
  35. package/dist/interface/index.html +33 -33
  36. package/dist/interface/linkify-it.DhPv0h2f.0.0.14-test.1.js +1 -0
  37. package/dist/interface/main.CltGwAA4.0.0.14-test.1.js +9 -0
  38. package/dist/interface/{markdown-it.BKNgIiXo.0.0.14-alpha.4.js → markdown-it.CZ5YJsu-.0.0.14-test.1.js} +1 -1
  39. package/dist/interface/{nprogress.CyIg_Gxi.0.0.14-alpha.4.js → nprogress.Cq5vR4Jv.0.0.14-test.1.js} +1 -1
  40. package/dist/interface/{pinia.CecoA7CB.0.0.14-alpha.4.js → pinia.DNrSQ_WG.0.0.14-test.1.js} +1 -1
  41. package/dist/interface/plugins.BTgTjWNZ.0.0.14-test.1.js +1 -0
  42. package/dist/interface/{qrcode.DendPh0T.0.0.14-alpha.4.js → qrcode.DItk5pm2.0.0.14-test.1.js} +1 -1
  43. package/dist/interface/react-dnd-touch-backend.DOH37glt.0.0.14-test.1.js +1 -0
  44. package/dist/interface/{rxjs.CRxlDfq_.0.0.14-alpha.4.js → rxjs.CqHoc4dZ.0.0.14-test.1.js} +1 -1
  45. package/dist/interface/socket.io-client.BM4RxW2s.0.0.14-test.1.js +1 -0
  46. package/dist/interface/socket.io-parser.CCkPvANf.0.0.14-test.1.js +1 -0
  47. package/dist/interface/sw.js +1 -1
  48. package/dist/interface/system.cy3GsLx7.0.0.14-test.1.js +1 -0
  49. package/dist/interface/ui.D9eeRe8i.0.0.14-test.1.js +1 -0
  50. package/dist/interface/{unidragger.D3rkYrUG.0.0.14-alpha.4.js → unidragger.CMnDy0od.0.0.14-test.1.js} +1 -1
  51. package/dist/interface/v-calendar.CIdjtHc_.0.0.14-test.1.js +1 -0
  52. package/dist/interface/{vee-validate.pvxJMmMq.0.0.14-alpha.4.js → vee-validate.BKu3RTZB.0.0.14-test.1.js} +1 -1
  53. package/dist/interface/{vue-i18n.CozUMf0M.0.0.14-alpha.4.js → vue-i18n.B49hBhBm.0.0.14-test.1.js} +1 -1
  54. package/dist/interface/vue-inline-svg.BywEbXHj.0.0.14-test.1.js +1 -0
  55. package/dist/interface/{vue-router.BvzGucu2.0.0.14-alpha.4.js → vue-router.CSFF0HsC.0.0.14-test.1.js} +1 -1
  56. package/dist/interface/{vue3-apexcharts.CVBbHYHw.0.0.14-alpha.4.js → vue3-apexcharts.C-YNZOcD.0.0.14-test.1.js} +1 -1
  57. package/dist/interface/vue3-dnd.Lf_Rv2HM.0.0.14-test.1.js +1 -0
  58. package/dist/interface/vue3-toastify.D6RWCdqq.0.0.14-test.1.js +1 -0
  59. package/dist/interface/{vuetify.h38bTLGK.0.0.14-alpha.4.js → vuetify.C3H8hJxO.0.0.14-test.1.js} +1 -1
  60. package/dist/server/bin/installer/freebsd.js +3 -2
  61. package/dist/server/bin/installer/freebsd.js.map +1 -1
  62. package/dist/server/bin/installer/linux.js +3 -2
  63. package/dist/server/bin/installer/linux.js.map +1 -1
  64. package/dist/server/package.json +3 -2
  65. package/dist/server/src/api/controllers/plugins.controller.d.ts +11 -9
  66. package/dist/server/src/api/controllers/plugins.controller.js +513 -246
  67. package/dist/server/src/api/controllers/plugins.controller.js.map +1 -1
  68. package/dist/server/src/api/routes/system.routes.js +1 -1
  69. package/dist/server/src/plugins/index.js +1 -1
  70. package/dist/server/src/plugins/index.js.map +1 -1
  71. package/dist/server/src/services/config/index.d.ts +0 -2
  72. package/dist/server/src/services/config/index.js +3 -20
  73. package/dist/server/src/services/config/index.js.map +1 -1
  74. package/dist/server/src/utils/npm.js +4 -0
  75. package/dist/server/src/utils/npm.js.map +1 -1
  76. package/package.json +3 -2
  77. package/upgrade-server.sh +95 -0
  78. package/dist/interface/@tanstack.DxR9TqNT.0.0.14-alpha.4.js +0 -1
  79. package/dist/interface/@vueform.BEK-4lp8.0.0.14-alpha.4.js +0 -1
  80. package/dist/interface/@vueuse.0r_4ERfz.0.0.14-alpha.4.js +0 -1
  81. package/dist/interface/Account.BKb5WCug.0.0.14-alpha.4.js +0 -1
  82. package/dist/interface/Adminpanel.CV_YvWCC.0.0.14-alpha.4.js +0 -2
  83. package/dist/interface/Appearance.DHt3Aut6.0.0.14-alpha.4.js +0 -1
  84. package/dist/interface/Backup.rY0lEVR6.0.0.14-alpha.4.js +0 -1
  85. package/dist/interface/Camera.CtLcCWEv.0.0.14-alpha.4.js +0 -1
  86. package/dist/interface/Cameras.CFeTbskA.0.0.14-alpha.4.js +0 -1
  87. package/dist/interface/Camview.BtrGE5XA.0.0.14-alpha.4.js +0 -1
  88. package/dist/interface/Config.qRLVQl_r.0.0.14-alpha.4.js +0 -1
  89. package/dist/interface/Console.5Xw8RJIu.0.0.14-alpha.4.js +0 -4
  90. package/dist/interface/FirstSteps.Bc-g53Gw.0.0.14-alpha.4.js +0 -1
  91. package/dist/interface/Plugin.DQMR2bcj.0.0.14-alpha.4.js +0 -1
  92. package/dist/interface/Plugins.BlnUOFH6.0.0.14-alpha.4.js +0 -1
  93. package/dist/interface/Recordings.YR4raWR-.0.0.14-alpha.4.js +0 -1
  94. package/dist/interface/Recordings.a9uscpmX.0.0.14-alpha.4.js +0 -1
  95. package/dist/interface/Settings.Bqtc0cQ3.0.0.14-alpha.4.js +0 -1
  96. package/dist/interface/User.LrBOLpvr.0.0.14-alpha.4.js +0 -1
  97. package/dist/interface/VConfigEditor.vue_vue_type_script_setup_true_lang.D7BKvf1P.0.0.14-alpha.4.js +0 -2
  98. package/dist/interface/VConsole.B6n_q1I4.0.0.14-alpha.4.js +0 -1
  99. package/dist/interface/VPlayer.Cse-EfS2.0.0.14-alpha.4.js +0 -1
  100. package/dist/interface/VPluginCard.BXBkPqIn.0.0.14-alpha.4.js +0 -4
  101. package/dist/interface/assets/VPluginCard.0.0.14-alpha.4.B4G51MYn.css +0 -1
  102. package/dist/interface/dnd-core.BM-WmXXD.0.0.14-alpha.4.js +0 -1
  103. package/dist/interface/engine.io-client.C1WzcoRG.0.0.14-alpha.4.js +0 -1
  104. package/dist/interface/ev-emitter.BTd_mWjj.0.0.14-alpha.4.js +0 -1
  105. package/dist/interface/fast-deep-equal.HwPw-pxY.0.0.14-alpha.4.js +0 -1
  106. package/dist/interface/linkify-it.BFvjiZc3.0.0.14-alpha.4.js +0 -1
  107. package/dist/interface/main.V0WK2QMW.0.0.14-alpha.4.js +0 -9
  108. package/dist/interface/plugins.CXiJ27uA.0.0.14-alpha.4.js +0 -1
  109. package/dist/interface/react-dnd-touch-backend.C5ABPVRb.0.0.14-alpha.4.js +0 -1
  110. package/dist/interface/socket.io-client.DRrdPiqk.0.0.14-alpha.4.js +0 -1
  111. package/dist/interface/socket.io-parser.CgdzaAil.0.0.14-alpha.4.js +0 -1
  112. package/dist/interface/system.CTiWjzrS.0.0.14-alpha.4.js +0 -1
  113. package/dist/interface/ui.DS0tO89R.0.0.14-alpha.4.js +0 -1
  114. package/dist/interface/v-calendar.BsChHPaY.0.0.14-alpha.4.js +0 -1
  115. package/dist/interface/vue-inline-svg.Dt8SW6Et.0.0.14-alpha.4.js +0 -1
  116. package/dist/interface/vue3-dnd.10LbMhwv.0.0.14-alpha.4.js +0 -1
  117. package/dist/interface/vue3-toastify.RTQ6RfPx.0.0.14-alpha.4.js +0 -1
  118. /package/dist/interface/{@babel.l0sNRNKZ.0.0.14-alpha.4.js → @babel.l0sNRNKZ.0.0.14-test.1.js} +0 -0
  119. /package/dist/interface/{@intlify.YW_pt133.0.0.14-alpha.4.js → @intlify.YW_pt133.0.0.14-test.1.js} +0 -0
  120. /package/dist/interface/{@mdi.jq8isxax.0.0.14-alpha.4.js → @mdi.jq8isxax.0.0.14-test.1.js} +0 -0
  121. /package/dist/interface/{@popperjs.BQBsAJpH.0.0.14-alpha.4.js → @popperjs.BQBsAJpH.0.0.14-test.1.js} +0 -0
  122. /package/dist/interface/{@react-dnd.CcwRs9xb.0.0.14-alpha.4.js → @react-dnd.CcwRs9xb.0.0.14-test.1.js} +0 -0
  123. /package/dist/interface/{@socket.io.Dkula2eQ.0.0.14-alpha.4.js → @socket.io.Dkula2eQ.0.0.14-test.1.js} +0 -0
  124. /package/dist/interface/{@vue.CIFymae3.0.0.14-alpha.4.js → @vue.CIFymae3.0.0.14-test.1.js} +0 -0
  125. /package/dist/interface/{@xterm.B_cEcIAu.0.0.14-alpha.4.js → @xterm.Bi08To3N.0.0.14-test.1.js} +0 -0
  126. /package/dist/interface/assets/{@vueform.0.0.14-alpha.4.h7tNX9p6.css → @vueform.0.0.14-test.1.h7tNX9p6.css} +0 -0
  127. /package/dist/interface/assets/{@xterm.0.0.14-alpha.4.CGrrDQr5.css → @xterm.0.0.14-test.1.CGrrDQr5.css} +0 -0
  128. /package/dist/interface/assets/{Account.0.0.14-alpha.4.DlOUWknb.css → Account.0.0.14-test.1.DlOUWknb.css} +0 -0
  129. /package/dist/interface/assets/{Adminpanel.0.0.14-alpha.4.BHh3IMeu.css → Adminpanel.0.0.14-test.1.BHh3IMeu.css} +0 -0
  130. /package/dist/interface/assets/{Backup.0.0.14-alpha.4.BIwaSlv3.css → Backup.0.0.14-test.1.BIwaSlv3.css} +0 -0
  131. /package/dist/interface/assets/{Camera.0.0.14-alpha.4.B5hAZmeQ.css → Camera.0.0.14-test.1.B5hAZmeQ.css} +0 -0
  132. /package/dist/interface/assets/{Cameras.0.0.14-alpha.4.D7bnvH42.css → Cameras.0.0.14-test.1.D7bnvH42.css} +0 -0
  133. /package/dist/interface/assets/{Camview.0.0.14-alpha.4.cgjDTjXy.css → Camview.0.0.14-test.1.cgjDTjXy.css} +0 -0
  134. /package/dist/interface/assets/{Config.0.0.14-alpha.4.CncRTcjC.css → Config.0.0.14-test.1.CncRTcjC.css} +0 -0
  135. /package/dist/interface/assets/{Console.0.0.14-alpha.4.DWrfRWs9.css → Console.0.0.14-test.1.DWrfRWs9.css} +0 -0
  136. /package/dist/interface/assets/{FirstSteps.0.0.14-alpha.4.LZWhAsRR.css → FirstSteps.0.0.14-test.1.LZWhAsRR.css} +0 -0
  137. /package/dist/interface/assets/{NewCamera.0.0.14-alpha.4.DJ3tUBT_.css → NewCamera.0.0.14-test.1.DJ3tUBT_.css} +0 -0
  138. /package/dist/interface/assets/{Plugin.0.0.14-alpha.4.BGZE7hpr.css → Plugin.0.0.14-test.1.BGZE7hpr.css} +0 -0
  139. /package/dist/interface/assets/{Plugins.0.0.14-alpha.4.Cwu0aGUO.css → Plugins.0.0.14-test.1.Cwu0aGUO.css} +0 -0
  140. /package/dist/interface/assets/{Settings.0.0.14-alpha.4.BfSP_zcY.css → Settings.0.0.14-test.1.BfSP_zcY.css} +0 -0
  141. /package/dist/interface/assets/{User.0.0.14-alpha.4.BHWFyck9.css → User.0.0.14-test.1.BHWFyck9.css} +0 -0
  142. /package/dist/interface/assets/{VConsole.0.0.14-alpha.4.BZT1WyS-.css → VConsole.0.0.14-test.1.BZT1WyS-.css} +0 -0
  143. /package/dist/interface/assets/{VPlayer.0.0.14-alpha.4.H4sGdlHU.css → VPlayer.0.0.14-test.1.H4sGdlHU.css} +0 -0
  144. /package/dist/interface/assets/{highlight.0.0.14-alpha.4.DgoK_Xib.css → highlight.0.0.14-test.1.DgoK_Xib.css} +0 -0
  145. /package/dist/interface/assets/{logo-256.0.0.14-alpha.4.BCQk9H1-.png → logo-256.0.0.14-test.1.BCQk9H1-.png} +0 -0
  146. /package/dist/interface/assets/{logo-512.0.0.14-alpha.4.DLKVOU2S.png → logo-512.0.0.14-test.1.DLKVOU2S.png} +0 -0
  147. /package/dist/interface/assets/{logo.0.0.14-alpha.4.DmEiRk0z.png → logo.0.0.14-test.1.DmEiRk0z.png} +0 -0
  148. /package/dist/interface/assets/{main.0.0.14-alpha.4.BPl82sqN.css → main.0.0.14-test.1.BPl82sqN.css} +0 -0
  149. /package/dist/interface/assets/{mode-json.0.0.14-alpha.4.Br7xisR1.js → mode-json.0.0.14-test.1.Br7xisR1.js} +0 -0
  150. /package/dist/interface/assets/{mode-yaml.0.0.14-alpha.4.CIYL8U3d.js → mode-yaml.0.0.14-test.1.CIYL8U3d.js} +0 -0
  151. /package/dist/interface/assets/{v-calendar.0.0.14-alpha.4.CtSuDYZU.css → v-calendar.0.0.14-test.1.CtSuDYZU.css} +0 -0
  152. /package/dist/interface/assets/{vue3-toastify.0.0.14-alpha.4.DTY1DMRs.css → vue3-toastify.0.0.14-test.1.DTY1DMRs.css} +0 -0
  153. /package/dist/interface/assets/{vuetify.0.0.14-alpha.4.DPyVfAPv.css → vuetify.0.0.14-test.1.DPyVfAPv.css} +0 -0
  154. /package/dist/interface/{axios.BI2i94Vb.0.0.14-alpha.4.js → axios.BI2i94Vb.0.0.14-test.1.js} +0 -0
  155. /package/dist/interface/{compare-versions.DqEOhma6.0.0.14-alpha.4.js → compare-versions.DqEOhma6.0.0.14-test.1.js} +0 -0
  156. /package/dist/interface/{dijkstrajs.D_NXgYpA.0.0.14-alpha.4.js → dijkstrajs.D_NXgYpA.0.0.14-test.1.js} +0 -0
  157. /package/dist/interface/{engine.io-parser.BiEtp6m2.0.0.14-alpha.4.js → engine.io-parser.BiEtp6m2.0.0.14-test.1.js} +0 -0
  158. /package/dist/interface/{entities.C20TfXL6.0.0.14-alpha.4.js → entities.C20TfXL6.0.0.14-test.1.js} +0 -0
  159. /package/dist/interface/{get-size.D1dS-IaP.0.0.14-alpha.4.js → get-size.D1dS-IaP.0.0.14-test.1.js} +0 -0
  160. /package/dist/interface/{mdurl.k9Sl0PQj.0.0.14-alpha.4.js → mdurl.k9Sl0PQj.0.0.14-test.1.js} +0 -0
  161. /package/dist/interface/{punycode.js.Dj65hjkv.0.0.14-alpha.4.js → punycode.js.Dj65hjkv.0.0.14-test.1.js} +0 -0
  162. /package/dist/interface/{react-dnd-html5-backend.1HQjlkwI.0.0.14-alpha.4.js → react-dnd-html5-backend.1HQjlkwI.0.0.14-test.1.js} +0 -0
  163. /package/dist/interface/{redux.CuxFRhX_.0.0.14-alpha.4.js → redux.CuxFRhX_.0.0.14-test.1.js} +0 -0
  164. /package/dist/interface/{tslib.9k7AwCpt.0.0.14-alpha.4.js → tslib.9k7AwCpt.0.0.14-test.1.js} +0 -0
  165. /package/dist/interface/{uc.micro.kMc2yuuw.0.0.14-alpha.4.js → uc.micro.kMc2yuuw.0.0.14-test.1.js} +0 -0
  166. /package/dist/interface/{utils.BvINh1mj.0.0.14-alpha.4.js → utils.BvINh1mj.0.0.14-test.1.js} +0 -0
  167. /package/dist/interface/{uuid.DvF23Exx.0.0.14-alpha.4.js → uuid.DvF23Exx.0.0.14-test.1.js} +0 -0
  168. /package/dist/interface/{vue.l0sNRNKZ.0.0.14-alpha.4.js → vue.l0sNRNKZ.0.0.14-test.1.js} +0 -0
  169. /package/dist/interface/{zod.BaF2dDCd.0.0.14-alpha.4.js → zod.BaF2dDCd.0.0.14-test.1.js} +0 -0
@@ -3,21 +3,20 @@ import { cyan, green, red, yellow } from 'ansicolor';
3
3
  import fs from 'fs-extra';
4
4
  import { LRUCache } from 'lru-cache';
5
5
  import { spawn } from 'node:child_process';
6
+ import { createHash } from 'node:crypto';
6
7
  import { platform, tmpdir, userInfo } from 'node:os';
7
- import { basename, dirname, join, parse, resolve, sep } from 'node:path';
8
- import { fileURLToPath } from 'node:url';
8
+ import { basename, dirname, join, resolve } from 'node:path';
9
9
  import { gt, parse as parseVersion, satisfies } from 'semver';
10
10
  import sharp from 'sharp';
11
+ import { x as extract } from 'tar';
11
12
  import { container } from 'tsyringe';
12
13
  import { PluginManager } from '../../plugins/index.js';
13
14
  import { ConfigService } from '../../services/config/index.js';
14
15
  import { getNpmPath } from '../../utils/npm.js';
15
- import { orderBy } from '../../utils/utils.js';
16
+ import { orderBy, sleep } from '../../utils/utils.js';
16
17
  import { CamerasService } from '../services/cameras.service.js';
17
18
  import { PluginsService } from '../services/plugins.service.js';
18
19
  import fetchInstance from '../utils/fetch.js';
19
- const __filename = fileURLToPath(import.meta.url);
20
- const __dirname = dirname(__filename);
21
20
  const npmPluginCache = new LRUCache({ ttl: 300, max: 100, allowStale: false });
22
21
  const searchResultBlacklist = new Set(['camera.ui', 'homebridge-camera-ui']);
23
22
  const pluginLogoCache = new LRUCache({ max: 100, ttl: 1000 * 60 * 60 });
@@ -545,96 +544,6 @@ export class PluginsController {
545
544
  });
546
545
  }
547
546
  }
548
- async installOrUpdate(req, reply) {
549
- try {
550
- let plugin = this.pluginManager.plugins.get(req.body.pluginname);
551
- const installPath = await this.managePlugin('install', { pluginName: req.body.pluginname, version: req.body.pluginversion });
552
- if (!plugin) {
553
- plugin = await this.pluginManager.loadPlugin(`${installPath}/node_modules/${req.body.pluginname}`);
554
- this.logger.log(`Plugin installed: ${plugin.pluginName}.${plugin.displayName} (${plugin.info.installedVersion})`);
555
- this.logger.log(`Plugin Path: ${plugin.installPath}`);
556
- this.pluginManager.initializeInstalledPlugin(plugin);
557
- }
558
- else {
559
- this.logger.log(`Plugin updated: ${plugin.pluginName}.${plugin.displayName} (${plugin.info.installedVersion}), restarting ...`);
560
- const pluginProcess = this.service.getPluginProcessByName(req.body.pluginname);
561
- pluginProcess?.restart();
562
- }
563
- npmPluginCache.delete(plugin.pluginName);
564
- plugin.reparsePackageJson();
565
- const serverNsp = this.socketService.namespaces.get('/server');
566
- serverNsp.checkUpdate();
567
- return reply.code(201).send(plugin.info);
568
- }
569
- catch (error) {
570
- return reply.code(500).send({
571
- statusCode: 500,
572
- message: error.message,
573
- });
574
- }
575
- }
576
- async updateSystem(req, reply) {
577
- try {
578
- const installPath = await this.managePlugin('install', { pluginName: req.body.pluginname, version: req.body.pluginversion });
579
- this.logger.log(`System updated: ${ConfigService.APP_NAME}@${ConfigService.VERSION} (${installPath})`);
580
- const serverNsp = this.socketService.namespaces.get('/server');
581
- serverNsp.checkUpdate();
582
- return reply.code(204).send();
583
- }
584
- catch (error) {
585
- return reply.code(500).send({
586
- statusCode: 500,
587
- message: error.message,
588
- });
589
- }
590
- }
591
- async managePlugin(action, pluginInfo) {
592
- pluginInfo.version = pluginInfo.version || 'latest';
593
- if (pluginInfo.pluginName === ConfigService.APP_NAME) {
594
- if (action === 'uninstall' || action === 'unlink') {
595
- throw new Error(`Cannot uninstall ${pluginInfo.pluginName} from ${ConfigService.APP_NAME}.`);
596
- }
597
- if (!ConfigService.INSTALL_PATH) {
598
- throw new Error(`Can not determine the install path for ${pluginInfo.pluginName}`);
599
- }
600
- }
601
- if (action === 'install' && pluginInfo.version === 'latest') {
602
- pluginInfo.version = await this.getNpmModuleLatestVersion(pluginInfo.pluginName);
603
- }
604
- let installPath = this.configService.PLUGINS_INSTALL_PATH;
605
- if (pluginInfo.pluginName === ConfigService.APP_NAME) {
606
- installPath = ConfigService.INSTALL_PATH;
607
- }
608
- else {
609
- const existingPlugin = this.service.getPluginByName(pluginInfo.pluginName);
610
- if (existingPlugin) {
611
- installPath = existingPlugin.installPath;
612
- }
613
- }
614
- installPath = this.getLastNodeModulesPath(installPath) || installPath;
615
- installPath = resolve(installPath, '../');
616
- const installOptions = [];
617
- if (platform() === 'win32' || pluginInfo.pluginName === ConfigService.APP_NAME) {
618
- installOptions.push('-g');
619
- }
620
- const npmPluginLabel = action === 'uninstall' || action === 'unlink' ? pluginInfo.pluginName : `${pluginInfo.pluginName}@${pluginInfo.version}`;
621
- try {
622
- await this.runNpmCommand([...getNpmPath(), action, ...installOptions, npmPluginLabel], installPath, pluginInfo);
623
- await this.ensurePluginDirExists();
624
- if (pluginInfo.id && (action === 'uninstall' || action === 'unlink') && pluginInfo.pluginName !== ConfigService.APP_NAME) {
625
- await this.camerasService.removeByPluginId(pluginInfo.id);
626
- }
627
- return installPath;
628
- }
629
- catch (error) {
630
- if (pluginInfo.pluginName === ConfigService.APP_NAME) {
631
- this.io.of('/logs').emit('stdout', yellow('\r\nCleaning up npm cache, please wait...\r\n'));
632
- await this.cleanNpmCache();
633
- this.io.of('/logs').emit('stdout', yellow('npm cache cleared, please try updating camera.ui again.\r\n'));
634
- }
635
- throw error;
636
- }
637
- }
638
547
  list(req, reply) {
639
548
  try {
640
549
  const installedPlugins = this.service.installedPlugins();
@@ -834,6 +743,90 @@ export class PluginsController {
834
743
  });
835
744
  }
836
745
  }
746
+ ////////////////////////////
747
+ async installOrUpdate(req, reply) {
748
+ try {
749
+ let plugin = this.pluginManager.plugins.get(req.body.pluginname);
750
+ const command = plugin ? 'update' : 'install';
751
+ this.logger.log(`${plugin ? 'Updating' : 'Installing'} plugin: ${req.body.pluginname}@${req.body.pluginversion}`);
752
+ const installPath = await this.manage(req.body.pluginname, command, req.body.pluginversion, plugin?.id);
753
+ if (!plugin) {
754
+ plugin = await this.pluginManager.loadPlugin(installPath);
755
+ this.logger.log(`Plugin installed: ${plugin.pluginName}.${plugin.displayName} (${plugin.info.installedVersion})`);
756
+ this.pluginManager.initializeInstalledPlugin(plugin);
757
+ }
758
+ else {
759
+ this.logger.log(`Plugin updated: ${plugin.pluginName}.${plugin.displayName} (${req.body.pluginversion}), restarting ...`);
760
+ const pluginProcess = this.service.getPluginProcessByName(req.body.pluginname);
761
+ pluginProcess?.restart();
762
+ }
763
+ npmPluginCache.delete(plugin.pluginName);
764
+ plugin.reparsePackageJson();
765
+ const serverNsp = this.socketService.namespaces.get('/server');
766
+ serverNsp.checkUpdate();
767
+ return reply.code(201).send(plugin.info);
768
+ }
769
+ catch (error) {
770
+ return reply.code(500).send({
771
+ statusCode: 500,
772
+ message: error.message,
773
+ });
774
+ }
775
+ }
776
+ async updateServer(req, reply) {
777
+ try {
778
+ this.logger.log(`Updating server: ${ConfigService.APP_NAME}@${req.body.pluginversion}`);
779
+ const installPath = await this.manage(ConfigService.APP_NAME, 'installServer', req.body.pluginversion);
780
+ this.logger.log(`Server updated: ${ConfigService.APP_NAME}@${ConfigService.VERSION} (${installPath})`);
781
+ const serverNsp = this.socketService.namespaces.get('/server');
782
+ serverNsp.checkUpdate();
783
+ return reply.code(204).send();
784
+ }
785
+ catch (error) {
786
+ return reply.code(500).send({
787
+ statusCode: 500,
788
+ message: error.message,
789
+ });
790
+ }
791
+ }
792
+ // public async managePlugin(action: 'install' | 'uninstall' | 'unlink', pluginInfo: PluginData): Promise<string> {
793
+ // pluginInfo.version = pluginInfo.version || 'latest';
794
+ // if (pluginInfo.pluginName === ConfigService.APP_NAME) {
795
+ // if (action === 'uninstall' || action === 'unlink') {
796
+ // throw new Error(`Cannot uninstall ${pluginInfo.pluginName} from ${ConfigService.APP_NAME}.`);
797
+ // }
798
+ // if (!ConfigService.INSTALL_PATH) {
799
+ // throw new Error(`Can not determine the install path for ${pluginInfo.pluginName}`);
800
+ // }
801
+ // }
802
+ // if (action === 'install' && pluginInfo.version === 'latest') {
803
+ // pluginInfo.version = await this.getNpmModuleLatestVersion(pluginInfo.pluginName);
804
+ // }
805
+ // let installPath = this.configService.PLUGINS_INSTALL_PATH;
806
+ // if (pluginInfo.pluginName === ConfigService.APP_NAME) {
807
+ // installPath = ConfigService.INSTALL_PATH!;
808
+ // }
809
+ // const installOptions: string[] = [];
810
+ // if (platform() === 'win32' || pluginInfo.pluginName === ConfigService.APP_NAME) {
811
+ // installOptions.push('-g');
812
+ // }
813
+ // const npmPluginLabel = action === 'uninstall' || action === 'unlink' ? pluginInfo.pluginName : `${pluginInfo.pluginName}@${pluginInfo.version}`;
814
+ // try {
815
+ // await this.runNpmCommand([...getNpmPath(), action, ...installOptions, npmPluginLabel], installPath, pluginInfo);
816
+ // await this.ensurePluginDirExists();
817
+ // if (pluginInfo.id && (action === 'uninstall' || action === 'unlink') && pluginInfo.pluginName !== ConfigService.APP_NAME) {
818
+ // await this.camerasService.removeByPluginId(pluginInfo.id);
819
+ // }
820
+ // return installPath;
821
+ // } catch (error: any) {
822
+ // if (pluginInfo.pluginName === ConfigService.APP_NAME) {
823
+ // this.io.of('/logs').emit('stdout', yellow('\r\nCleaning up npm cache, please wait...\r\n'));
824
+ // await this.cleanNpmCache();
825
+ // this.io.of('/logs').emit('stdout', yellow('npm cache cleared, please try updating camera.ui again.\r\n'));
826
+ // }
827
+ // throw error;
828
+ // }
829
+ // }
837
830
  async uninstallByName(req, reply) {
838
831
  try {
839
832
  const pluginName = this.resolvePluginName(req.params);
@@ -844,10 +837,12 @@ export class PluginsController {
844
837
  message: 'Plugin not exists',
845
838
  });
846
839
  }
847
- await this.managePlugin(plugin.isSymLink ? 'unlink' : 'uninstall', { pluginName: plugin.pluginName, id: plugin.id });
840
+ this.logger.log(`Uninstalling plugin: ${plugin.pluginName}.${plugin.displayName} (${plugin.info.installedVersion})`);
841
+ await this.manage(plugin.pluginName, 'uninstall', undefined, plugin.id);
848
842
  await this.pluginManager.removePlugin(plugin, req.query.removeStorage);
849
843
  npmPluginCache.delete(plugin.pluginName);
850
844
  npmPluginCache.delete(`lookup-${plugin.pluginName}`);
845
+ this.logger.log(`Plugin uninstalled: ${plugin.pluginName}.${plugin.displayName} (${plugin.info.installedVersion})`);
851
846
  return reply.code(204).send();
852
847
  }
853
848
  catch (error) {
@@ -859,14 +854,16 @@ export class PluginsController {
859
854
  }
860
855
  async uninstallAll(req, reply) {
861
856
  try {
857
+ this.logger.log('Uninstalling all plugins');
862
858
  const plugins = this.service.installedPlugins();
863
859
  const pluginNames = plugins.map((plugin) => plugin.pluginName);
864
- await Promise.all(plugins.map((plugin) => this.managePlugin(plugin.isSymLink ? 'unlink' : 'uninstall', { pluginName: plugin.pluginName, id: plugin.id })));
860
+ await Promise.all(plugins.map((plugin) => this.manage(plugin.pluginName, 'uninstall', undefined, plugin.id)));
865
861
  for (const plugin of plugins) {
866
862
  await this.pluginManager.removePlugin(plugin, req.query.removeStorage);
867
863
  }
868
864
  const pluginsToDelete = [...pluginNames, ...pluginNames.map((pluginName) => `lookup-${pluginName}`)];
869
865
  pluginsToDelete.forEach((p) => npmPluginCache.delete(p));
866
+ this.logger.log('All plugins uninstalled');
870
867
  return reply.code(204).send();
871
868
  }
872
869
  catch (error) {
@@ -876,6 +873,381 @@ export class PluginsController {
876
873
  });
877
874
  }
878
875
  }
876
+ // private async runNpmCommand(command: Array<string>, cwd: string, pluginInfo: PluginData): Promise<void> {
877
+ // // remove synology @eaDir folders from the node_modules
878
+ // await this.removeSynologyMetadata();
879
+ // let timeoutTimer: NodeJS.Timeout;
880
+ // command = command.filter((x) => x.length);
881
+ // let sudoEnabled = this.configService.config.plugins.sudo;
882
+ // if (pluginInfo.pluginName === ConfigService.APP_NAME) {
883
+ // sudoEnabled = true;
884
+ // if (!command.includes('-g')) {
885
+ // command.push('-g');
886
+ // }
887
+ // } else {
888
+ // const pluginDir = resolve(this.configService.PLUGINS_INSTALL_PATH, pluginInfo.pluginName);
889
+ // await fs.mkdir(resolve(pluginDir, 'node_modules'), { recursive: true });
890
+ // cwd = pluginDir;
891
+ // // Entfernen Sie -g, falls vorhanden
892
+ // command = command.filter((arg) => arg !== '-g');
893
+ // // Fügen Sie --prefix hinzu, um den spezifischen Pluginordner zu verwenden
894
+ // // command.push('--prefix', pluginDir);
895
+ // if (command[1] === 'install') {
896
+ // // Extrahieren Sie den Paketnamen und die Version
897
+ // const packageSpec = command[command.length - 1];
898
+ // // Ändern Sie den Installationsbefehl
899
+ // command = [command[0], 'install', '--no-save', packageSpec];
900
+ // }
901
+ // }
902
+ // if (sudoEnabled) {
903
+ // command.unshift('sudo', '-E', '-n');
904
+ // } else if (pluginInfo.pluginName !== ConfigService.APP_NAME) {
905
+ // // do a pre-check to test for write access when not using sudo mode
906
+ // try {
907
+ // await fs.access(resolve(cwd, 'node_modules'), fs.constants.W_OK);
908
+ // } catch {
909
+ // this.io
910
+ // .of('/logs')
911
+ // .emit(`stdout/${pluginInfo.pluginName}`, yellow(`The user "${userInfo().username}" does not have write access to the target directory:\n\r\n\r`));
912
+ // this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, `${resolve(cwd, 'node_modules')}\n\r\n\r`);
913
+ // this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, yellow('This may cause the operation to fail.\n\r'));
914
+ // this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, yellow('See the docs for details on how to enable sudo mode:\n\r'));
915
+ // }
916
+ // }
917
+ // this.logger.log(`Running Command: ${command.join(' ')}`);
918
+ // if (!satisfies(process.version, `>=${ConfigService.MIN_NODE_VERSION}`)) {
919
+ // this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, yellow(`Node.js v${ConfigService.MIN_NODE_VERSION} higher is required for camera.ui.\n\r`));
920
+ // this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, yellow(`You may experience issues while running on Node.js ${process.version}.\n\r\n\r`));
921
+ // }
922
+ // // set up the environment for the call
923
+ // const env = {};
924
+ // Object.assign(env, process.env);
925
+ // Object.assign(env, {
926
+ // npm_config_global_style: 'false',
927
+ // npm_config_unsafe_perm: 'true',
928
+ // npm_config_update_notifier: 'false',
929
+ // npm_config_prefer_online: 'true',
930
+ // npm_config_foreground_scripts: 'true',
931
+ // npm_config_loglevel: pluginInfo.pluginName === ConfigService.APP_NAME ? 'verbose' : 'error',
932
+ // npm_config_prefix: cwd,
933
+ // });
934
+ // // set global prefix for unix based systems
935
+ // if (pluginInfo.pluginName === ConfigService.APP_NAME && platform() !== 'win32') {
936
+ // if (basename(cwd) === 'lib') {
937
+ // cwd = dirname(cwd);
938
+ // Object.assign(env, {
939
+ // npm_config_prefix: cwd,
940
+ // });
941
+ // }
942
+ // }
943
+ // this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, cyan(`\n\rUSER: ${userInfo().username}\n\r`));
944
+ // this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, cyan(`DIR: ${cwd}\n\r`));
945
+ // this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, cyan(`CMD: ${command.join(' ')}\n\r\n\r`));
946
+ // await new Promise((_resolve, _reject) => {
947
+ // const term = pty.spawn(command.shift()!, command, {
948
+ // name: 'xterm-color',
949
+ // cols: 80,
950
+ // rows: 30,
951
+ // cwd,
952
+ // env,
953
+ // });
954
+ // // send stdout data from the process to all clients
955
+ // term.onData((data: string) => {
956
+ // this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, data);
957
+ // });
958
+ // // send an error message to the client if the command does not exit with code 0
959
+ // term.onExit((error: { exitCode: number; signal?: number | undefined }) => {
960
+ // clearTimeout(timeoutTimer);
961
+ // if (error.exitCode === 0) {
962
+ // this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, green('\n\rOperation succeeded!.\n\r\n\r'));
963
+ // _resolve(null);
964
+ // } else {
965
+ // this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, red('\n\rOperation failed!.\n\r\n\r'));
966
+ // _reject(new Error('Operation failed. Please review log for details.'));
967
+ // }
968
+ // });
969
+ // // if the command spends to long trying to execute kill it after 5 minutes
970
+ // timeoutTimer = setTimeout(() => {
971
+ // term.kill('SIGTERM');
972
+ // }, 300000);
973
+ // });
974
+ // }
975
+ ////////////////////////////
976
+ async manage(pluginName, action, version, pluginId) {
977
+ if (pluginName === ConfigService.APP_NAME && !ConfigService.INSTALL_PATH) {
978
+ throw new Error(`Can not determine the install path for ${pluginName}`);
979
+ }
980
+ const targetDir = pluginName === ConfigService.APP_NAME ? join(ConfigService.INSTALL_PATH, ConfigService.APP_NAME) : join(this.configService.PLUGINS_INSTALL_PATH, pluginName);
981
+ this.emitProgress(pluginName, `\n\rUSER: ${userInfo().username}\n\r`, cyan);
982
+ this.emitProgress(pluginName, `DIR: ${this.configService.PLUGINS_INSTALL_PATH}\n\r`, cyan);
983
+ this.emitProgress(pluginName, `ACTION: ${action}\n\r\n\r`, cyan);
984
+ await this.removeSynologyMetadata();
985
+ try {
986
+ switch (action) {
987
+ case 'install':
988
+ await this.installPlugin(pluginName, version || 'latest', targetDir);
989
+ break;
990
+ case 'installServer':
991
+ await this.installServer(pluginName, version || 'latest', targetDir);
992
+ break;
993
+ case 'update':
994
+ await this.updatePlugin(pluginName, version || 'latest', targetDir);
995
+ break;
996
+ case 'uninstall':
997
+ await this.uninstallPlugin(pluginName, targetDir);
998
+ await sleep(2000);
999
+ break;
1000
+ default:
1001
+ throw new Error(`Unknown action: ${action}`);
1002
+ }
1003
+ await this.ensurePluginDirExists();
1004
+ if (pluginId && action === 'uninstall' && pluginName !== ConfigService.APP_NAME) {
1005
+ await this.camerasService.removeByPluginId(pluginId);
1006
+ }
1007
+ return targetDir;
1008
+ }
1009
+ catch (error) {
1010
+ if (pluginName === ConfigService.APP_NAME) {
1011
+ this.emitProgress(pluginName, 'Cleaning up npm cache, please wait...\r\n', yellow);
1012
+ await this.cleanNpmCache();
1013
+ this.emitProgress(pluginName, 'Cache cleared, please try updating camera.ui again.\r\n', yellow);
1014
+ }
1015
+ throw error;
1016
+ }
1017
+ }
1018
+ async installServer(pluginName, version, targetDir) {
1019
+ await this.removeSynologyMetadata();
1020
+ const prefix = dirname(dirname(dirname(targetDir)));
1021
+ const upgradeInstallScriptPath = join(targetDir, 'upgrade-server.sh');
1022
+ let timeoutTimer;
1023
+ const command = [upgradeInstallScriptPath, version, prefix];
1024
+ try {
1025
+ await fs.access(resolve(targetDir, 'node_modules'), fs.constants.W_OK);
1026
+ }
1027
+ catch {
1028
+ this.emitProgress(pluginName, `The user "${userInfo().username}" does not have write access to the target directory:\n\r\n\r`, yellow);
1029
+ this.emitProgress(pluginName, `${resolve(targetDir, 'node_modules')}\n\r\n\r`, yellow);
1030
+ this.emitProgress(pluginName, 'This may cause the operation to fail.\n\r', yellow);
1031
+ this.emitProgress(pluginName, 'See the docs for details on how to enable sudo mode:\n\r', yellow);
1032
+ throw new Error('Operation failed. Please review log for details.');
1033
+ }
1034
+ this.logger.log(`Running Command: ${command.join(' ')}`);
1035
+ if (!satisfies(process.version, `>=${ConfigService.MIN_NODE_VERSION}`)) {
1036
+ this.emitProgress(pluginName, `Node.js v${ConfigService.MIN_NODE_VERSION} higher is required for ${ConfigService.APP_NAME}.\n\r`, yellow);
1037
+ this.emitProgress(pluginName, `You may experience issues while running on Node.js ${process.version}.\n\r\n\r`, yellow);
1038
+ }
1039
+ // set up the environment for the call
1040
+ const env = {};
1041
+ Object.assign(env, process.env);
1042
+ Object.assign(env, {
1043
+ npm_config_install_strategy: 'shallow',
1044
+ npm_config_unsafe_perm: 'true',
1045
+ npm_config_update_notifier: 'false',
1046
+ npm_config_prefer_online: 'true',
1047
+ npm_config_foreground_scripts: 'true',
1048
+ npm_config_loglevel: 'verbose',
1049
+ npm_config_prefix: targetDir,
1050
+ });
1051
+ // set global prefix for unix based systems
1052
+ if (platform() !== 'win32') {
1053
+ if (basename(targetDir) === 'lib') {
1054
+ Object.assign(env, {
1055
+ npm_config_prefix: dirname(targetDir),
1056
+ });
1057
+ }
1058
+ }
1059
+ this.io.of('/logs').emit(`stdout/${pluginName}`, cyan(`\n\rUSER: ${userInfo().username}\n\r`));
1060
+ this.io.of('/logs').emit(`stdout/${pluginName}`, cyan(`DIR: ${targetDir}\n\r`));
1061
+ this.io.of('/logs').emit(`stdout/${pluginName}`, cyan(`CMD: ${command.join(' ')}\n\r\n\r`));
1062
+ await new Promise((_resolve, _reject) => {
1063
+ const term = pty.spawn(command.shift(), command, {
1064
+ name: 'xterm-color',
1065
+ cols: 80,
1066
+ rows: 30,
1067
+ cwd: targetDir,
1068
+ env,
1069
+ });
1070
+ // send stdout data from the process to all clients
1071
+ term.onData((data) => {
1072
+ this.io.of('/logs').emit(`stdout/${pluginName}`, data);
1073
+ });
1074
+ // send an error message to the client if the command does not exit with code 0
1075
+ term.onExit((error) => {
1076
+ clearTimeout(timeoutTimer);
1077
+ if (error.exitCode === 0) {
1078
+ this.emitProgress(pluginName, `\n\rPlugin ${pluginName}@${version} has been successfully updated.\n\r\n\r`, green);
1079
+ _resolve(null);
1080
+ }
1081
+ else {
1082
+ this.emitProgress(pluginName, `\n\rError during installation of ${pluginName}@${version}.\n\r\n\r`, red);
1083
+ _reject(new Error('Operation failed. Please review log for details.'));
1084
+ }
1085
+ });
1086
+ // if the command spends to long trying to execute kill it after 5 minutes
1087
+ timeoutTimer = setTimeout(() => {
1088
+ term.kill('SIGTERM');
1089
+ }, 300000);
1090
+ });
1091
+ }
1092
+ async installPlugin(pluginName, version, targetDir, update) {
1093
+ try {
1094
+ const npmRegistry = 'https://registry.npmjs.org';
1095
+ const packageUrl = `${npmRegistry}/${pluginName}/${version}`;
1096
+ this.emitProgress(pluginName, '> Fetching package information...');
1097
+ // Hole Paketinformationen vom npm-Registry
1098
+ const response = await fetch(packageUrl);
1099
+ if (!response.ok) {
1100
+ throw new Error(`HTTP error! status: ${response.status}`);
1101
+ }
1102
+ const packageInfo = await response.json();
1103
+ const tarballUrl = packageInfo.dist.tarball;
1104
+ const expectedShasum = packageInfo.dist.shasum;
1105
+ this.emitProgress(pluginName, ' Done\n\r\n\r', green);
1106
+ const tarballData = await this.downloadWithProgress(tarballUrl, pluginName, version);
1107
+ this.emitProgress(pluginName, ' - Done\n\r\n\r', green);
1108
+ this.emitProgress(pluginName, '> Verifying package integrity...');
1109
+ const computedShasum = createHash('sha1').update(Buffer.from(tarballData)).digest('hex');
1110
+ if (computedShasum !== expectedShasum) {
1111
+ throw new Error(`SHA checksum mismatch. Expected: ${expectedShasum}, Got: ${computedShasum}`);
1112
+ }
1113
+ this.emitProgress(pluginName, ' Done\n\r\n\r', green);
1114
+ // Erstelle das Zielverzeichnis
1115
+ await fs.ensureDir(targetDir);
1116
+ this.emitProgress(pluginName, '> Extracting package...');
1117
+ // Entpacke das Tarball direkt in das Zielverzeichnis
1118
+ await new Promise((resolve, reject) => {
1119
+ const extractStream = extract({
1120
+ cwd: targetDir,
1121
+ strip: 1,
1122
+ });
1123
+ extractStream.on('error', reject);
1124
+ extractStream.on('end', () => {
1125
+ this.emitProgress(pluginName, ' Done\n\r\n\r', green);
1126
+ resolve();
1127
+ });
1128
+ extractStream.write(Buffer.from(tarballData));
1129
+ extractStream.end();
1130
+ });
1131
+ await this.installProductionDependencies(targetDir, pluginName);
1132
+ this.emitProgress(pluginName, `\n\rPlugin ${pluginName}@${version} has been successfully ${update ? 'updated' : 'installed'}.\n\r\n\r`, green);
1133
+ }
1134
+ catch (error) {
1135
+ this.emitProgress(pluginName, `\n\rError during ${update ? 'update' : 'installation'} of ${pluginName}@${version}.\n\r\n\r`, red);
1136
+ throw error;
1137
+ }
1138
+ }
1139
+ async updatePlugin(pluginName, version, targetDir) {
1140
+ try {
1141
+ this.emitProgress(pluginName, '> Preparing update...');
1142
+ const packageJsonPath = join(targetDir, 'package.json');
1143
+ const packageJson = await fs.readJson(packageJsonPath);
1144
+ const currentVersion = packageJson.version;
1145
+ if (currentVersion === version) {
1146
+ this.emitProgress(pluginName, ' Break', red);
1147
+ this.emitProgress(pluginName, `Plugin ${pluginName} is already up to date\n\r\n\r`, green);
1148
+ return;
1149
+ }
1150
+ const tempNodeModules = join(targetDir, 'node_modules_temp');
1151
+ await fs.move(join(targetDir, 'node_modules'), tempNodeModules);
1152
+ this.emitProgress(pluginName, ' Done\n\r\n\r', green);
1153
+ let cleaningUp = false;
1154
+ try {
1155
+ await this.installPlugin(pluginName, version, targetDir, true);
1156
+ this.emitProgress(pluginName, '> Cleaning up...');
1157
+ cleaningUp = true;
1158
+ await fs.remove(tempNodeModules);
1159
+ this.emitProgress(pluginName, ' Done\n\r\n\r', green);
1160
+ }
1161
+ catch (error) {
1162
+ if (cleaningUp) {
1163
+ this.emitProgress(pluginName, ' Error\n\r\n\r', red);
1164
+ }
1165
+ this.emitProgress(pluginName, ' Restoring previous version...');
1166
+ await fs.remove(targetDir);
1167
+ await fs.move(tempNodeModules, targetDir);
1168
+ this.emitProgress(pluginName, ' Done\n\r\n\r', green);
1169
+ throw error;
1170
+ }
1171
+ }
1172
+ catch (error) {
1173
+ this.emitProgress(pluginName, `\n\rError during update of ${pluginName}@${version}.\n\r\n\r`, red);
1174
+ throw error;
1175
+ }
1176
+ }
1177
+ async uninstallPlugin(pluginName, targetDir) {
1178
+ try {
1179
+ this.emitProgress(pluginName, '> Fetching plugin information...');
1180
+ const pluginInstalled = await fs.pathExists(targetDir);
1181
+ this.emitProgress(pluginName, ' Done\n\r\n\r', green);
1182
+ if (!pluginInstalled) {
1183
+ this.emitProgress(pluginName, `Plugin ${pluginName} is not installed. Break\n\r\n\r`, red);
1184
+ return;
1185
+ }
1186
+ this.emitProgress(pluginName, '> Uninstalling plugin...');
1187
+ await fs.remove(targetDir);
1188
+ this.emitProgress(pluginName, ' Done\n\r\n\r', green);
1189
+ this.emitProgress(pluginName, `Plugin ${pluginName} has been successfully uninstalled.\n\r`, green);
1190
+ }
1191
+ catch (error) {
1192
+ this.emitProgress(pluginName, ' Error\n\r\n\r', red);
1193
+ this.emitProgress(pluginName, `\n\rError during uninstallation of ${pluginName}.\n\r\n\r`, red);
1194
+ throw error;
1195
+ }
1196
+ }
1197
+ async installProductionDependencies(packageDir, pluginName) {
1198
+ await new Promise((_resolve, _reject) => {
1199
+ this.emitProgress(pluginName, '> Installing dependencies...\n\r\n\r');
1200
+ const command = [...getNpmPath(), 'install', '--omit=dev', '--verbose'];
1201
+ const env = {};
1202
+ Object.assign(env, process.env);
1203
+ Object.assign(env, {
1204
+ // npm_config_global_style: 'false',
1205
+ npm_config_install_strategy: 'shallow',
1206
+ npm_config_unsafe_perm: 'true',
1207
+ npm_config_update_notifier: 'false',
1208
+ npm_config_prefer_online: 'true',
1209
+ npm_config_foreground_scripts: 'true',
1210
+ npm_config_loglevel: 'verbose',
1211
+ npm_config_prefix: packageDir,
1212
+ });
1213
+ // set global prefix for unix based systems
1214
+ if (platform() !== 'win32') {
1215
+ if (basename(packageDir) === 'lib') {
1216
+ packageDir = dirname(packageDir);
1217
+ Object.assign(env, {
1218
+ npm_config_prefix: packageDir,
1219
+ });
1220
+ }
1221
+ }
1222
+ this.logger.log(`Running Command: ${command.join(' ')}`);
1223
+ const term = pty.spawn(command.shift(), command, {
1224
+ name: 'xterm-color',
1225
+ cols: 80,
1226
+ rows: 30,
1227
+ cwd: packageDir,
1228
+ env,
1229
+ });
1230
+ // send stdout data from the process to all clients
1231
+ term.onData((data) => {
1232
+ this.emitProgress(pluginName, data);
1233
+ });
1234
+ // send an error message to the client if the command does not exit with code 0
1235
+ term.onExit((error) => {
1236
+ clearTimeout(timeoutTimer);
1237
+ if (error.exitCode === 0) {
1238
+ _resolve(null);
1239
+ }
1240
+ else {
1241
+ _reject(new Error('Operation failed. Please review log for details.'));
1242
+ }
1243
+ });
1244
+ // if the command spends to long trying to execute kill it after 5 minutes
1245
+ const timeoutTimer = setTimeout(() => {
1246
+ term.kill('SIGTERM');
1247
+ }, 300000);
1248
+ });
1249
+ }
1250
+ ////////////////////////////
879
1251
  async cleanNpmCache() {
880
1252
  const command = [...getNpmPath(), 'cache', 'clean', '--force'];
881
1253
  if (this.configService.config.plugins.sudo) {
@@ -905,16 +1277,6 @@ export class PluginsController {
905
1277
  this.logger.error('Failed to recreate plugin directory', error);
906
1278
  }
907
1279
  }
908
- if (!(await fs.pathExists(this.configService.PLUGINS_PJSON_FILE))) {
909
- this.logger.warn(`Plugin package.json was removed. Re-creating: ${this.configService.PLUGINS_INSTALL_PATH}`);
910
- try {
911
- await fs.ensureFile(this.configService.PLUGINS_PJSON_FILE);
912
- await fs.writeJson(this.configService.PLUGINS_PJSON_FILE, this.pluginsPackageJson(), { spaces: 2 });
913
- }
914
- catch (error) {
915
- this.logger.error('Failed to recreate plugin package.json file', error);
916
- }
917
- }
918
1280
  }
919
1281
  async getAvailablePluginVersions(pluginName) {
920
1282
  const fromCache = npmPluginCache.get(`lookup-${pluginName}`);
@@ -936,28 +1298,17 @@ export class PluginsController {
936
1298
  versions: Object.keys(pkg.versions).reverse(),
937
1299
  };
938
1300
  }
939
- getLastNodeModulesPath(fullPath) {
940
- const segments = fullPath.split(sep);
941
- const lastNodeModulesIndex = segments.lastIndexOf('node_modules');
942
- if (lastNodeModulesIndex !== -1) {
943
- return segments.slice(0, lastNodeModulesIndex + 1).join(sep);
944
- }
945
- else {
946
- return null;
947
- }
948
- }
949
- async getNpmModuleLatestVersion(npmModuleName) {
950
- try {
951
- const response = await fetchInstance(`https://registry.npmjs.org/${npmModuleName}/latest`, {
952
- method: 'GET',
953
- });
954
- const data = await response.json();
955
- return data.version;
956
- }
957
- catch {
958
- return 'latest';
959
- }
960
- }
1301
+ // private async getNpmModuleLatestVersion(npmModuleName: string): Promise<string> {
1302
+ // try {
1303
+ // const response = await fetchInstance(`https://registry.npmjs.org/${npmModuleName}/latest`, {
1304
+ // method: 'GET',
1305
+ // });
1306
+ // const data: IPackageJson = await response.json();
1307
+ // return data.version!;
1308
+ // } catch {
1309
+ // return 'latest';
1310
+ // }
1311
+ // }
961
1312
  async getPluginFromNpm(plugin) {
962
1313
  const pluginState = {
963
1314
  updateAvailable: false,
@@ -1001,15 +1352,6 @@ export class PluginsController {
1001
1352
  }
1002
1353
  return pluginState;
1003
1354
  }
1004
- pluginsPackageJson() {
1005
- return {
1006
- private: true,
1007
- name: 'plugins',
1008
- version: '1.0.0',
1009
- description: 'camera.ui plugins',
1010
- dependencies: {},
1011
- };
1012
- }
1013
1355
  async removeSynologyMetadata() {
1014
1356
  if (!this.configService.PLUGINS_INSTALL_PATH) {
1015
1357
  return;
@@ -1024,100 +1366,6 @@ export class PluginsController {
1024
1366
  this.logger.error(`Failed to remove ${offendingPath}`, error);
1025
1367
  }
1026
1368
  }
1027
- async runNpmCommand(command, cwd, pluginInfo) {
1028
- // remove synology @eaDir folders from the node_modules
1029
- await this.removeSynologyMetadata();
1030
- let timeoutTimer;
1031
- command = command.filter((x) => x.length);
1032
- let sudoEnabled = this.configService.config.plugins.sudo;
1033
- if (pluginInfo.pluginName === ConfigService.APP_NAME) {
1034
- try {
1035
- await fs.access(resolve(cwd, 'node_modules'), fs.constants.W_OK);
1036
- sudoEnabled = false;
1037
- }
1038
- catch {
1039
- sudoEnabled = true;
1040
- }
1041
- }
1042
- if (sudoEnabled) {
1043
- command.unshift('sudo', '-E', '-n');
1044
- }
1045
- else if (pluginInfo.pluginName !== ConfigService.APP_NAME) {
1046
- // do a pre-check to test for write access when not using sudo mode
1047
- try {
1048
- await fs.access(resolve(cwd, 'node_modules'), fs.constants.W_OK);
1049
- }
1050
- catch {
1051
- this.io
1052
- .of('/logs')
1053
- .emit(`stdout/${pluginInfo.pluginName}`, yellow(`The user "${userInfo().username}" does not have write access to the target directory:\n\r\n\r`));
1054
- this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, `${resolve(cwd, 'node_modules')}\n\r\n\r`);
1055
- this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, yellow('This may cause the operation to fail.\n\r'));
1056
- this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, yellow('See the docs for details on how to enable sudo mode:\n\r'));
1057
- }
1058
- }
1059
- this.logger.log(`Running Command: ${command.join(' ')}`);
1060
- if (!satisfies(process.version, `>=${ConfigService.MIN_NODE_VERSION}`)) {
1061
- this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, yellow(`Node.js v${ConfigService.MIN_NODE_VERSION} higher is required for camera.ui.\n\r`));
1062
- this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, yellow(`You may experience issues while running on Node.js ${process.version}.\n\r\n\r`));
1063
- }
1064
- // set up the environment for the call
1065
- const env = {};
1066
- Object.assign(env, process.env);
1067
- Object.assign(env, {
1068
- npm_config_global_style: pluginInfo.pluginName === ConfigService.APP_NAME ? 'true' : 'false',
1069
- npm_config_unsafe_perm: 'true',
1070
- npm_config_update_notifier: 'false',
1071
- npm_config_prefer_online: 'true',
1072
- npm_config_foreground_scripts: 'true',
1073
- npm_config_loglevel: pluginInfo.pluginName === ConfigService.APP_NAME ? 'verbose' : 'error',
1074
- });
1075
- // set global prefix for unix based systems
1076
- if (command.includes('-g') && basename(cwd) === 'lib') {
1077
- cwd = dirname(cwd);
1078
- Object.assign(env, {
1079
- npm_config_prefix: cwd,
1080
- });
1081
- }
1082
- // on windows, we want to ensure the global prefix is the same as the installation path
1083
- if (platform() === 'win32') {
1084
- Object.assign(env, {
1085
- npm_config_prefix: cwd,
1086
- });
1087
- }
1088
- this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, cyan(`\n\rUSER: ${userInfo().username}\n\r`));
1089
- this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, cyan(`DIR: ${cwd}\n\r`));
1090
- this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, cyan(`CMD: ${command.join(' ')}\n\r\n\r`));
1091
- await new Promise((_resolve, _reject) => {
1092
- const term = pty.spawn(command.shift(), command, {
1093
- name: 'xterm-color',
1094
- cols: 80,
1095
- rows: 30,
1096
- cwd,
1097
- env,
1098
- });
1099
- // send stdout data from the process to all clients
1100
- term.onData((data) => {
1101
- this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, data);
1102
- });
1103
- // send an error message to the client if the command does not exit with code 0
1104
- term.onExit((error) => {
1105
- clearTimeout(timeoutTimer);
1106
- if (error.exitCode === 0) {
1107
- this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, green('\n\rOperation succeeded!.\n\r\n\r'));
1108
- _resolve(null);
1109
- }
1110
- else {
1111
- this.io.of('/logs').emit(`stdout/${pluginInfo.pluginName}`, red('\n\rOperation failed!.\n\r\n\r'));
1112
- _reject(new Error('Operation failed. Please review log for details.'));
1113
- }
1114
- });
1115
- // if the command spends to long trying to execute kill it after 5 minutes
1116
- timeoutTimer = setTimeout(() => {
1117
- term.kill('SIGTERM');
1118
- }, 300000);
1119
- });
1120
- }
1121
1369
  async searchNpmRegistrySingle(query) {
1122
1370
  try {
1123
1371
  const fromCache = npmPluginCache.get(`lookup-${query}`);
@@ -1168,20 +1416,6 @@ export class PluginsController {
1168
1416
  return [];
1169
1417
  }
1170
1418
  }
1171
- findInstalledPath() {
1172
- let currentDir = __dirname;
1173
- while (currentDir !== parse(currentDir).root) {
1174
- const nodeModulesDir = join(currentDir, 'node_modules');
1175
- if (fs.existsSync(nodeModulesDir)) {
1176
- const moduleDir = join(nodeModulesDir, 'test'); // 'test' ist der Name Ihres Moduls
1177
- if (fs.existsSync(moduleDir)) {
1178
- return nodeModulesDir;
1179
- }
1180
- }
1181
- currentDir = dirname(currentDir);
1182
- }
1183
- return null;
1184
- }
1185
1419
  resolvePluginName(params) {
1186
1420
  if (params.scope) {
1187
1421
  const at = params.scope.startsWith('@') ? '' : '@';
@@ -1194,5 +1428,38 @@ export class PluginsController {
1194
1428
  isScopedPlugin(pluginName) {
1195
1429
  return pluginName.charAt(0) === '@' && pluginName.split('/').length > 0 && pluginName.split('/')[1]?.indexOf('camera-ui-') === 0;
1196
1430
  }
1431
+ async downloadWithProgress(url, pluginName, version) {
1432
+ const response = await fetch(url);
1433
+ if (!response.ok) {
1434
+ throw new Error(`HTTP error! status: ${response.status}`);
1435
+ }
1436
+ const contentLength = Number(response.headers.get('Content-Length'));
1437
+ let receivedLength = 0;
1438
+ const chunks = [];
1439
+ const reader = response.body.getReader();
1440
+ this.emitProgress(pluginName, `> Downloading ${pluginName}@${version}...\n\r`);
1441
+ // eslint-disable-next-line no-constant-condition
1442
+ while (true) {
1443
+ const { done, value } = await reader.read();
1444
+ if (done) {
1445
+ break;
1446
+ }
1447
+ chunks.push(value);
1448
+ receivedLength += value.length;
1449
+ const percentComplete = Math.round((receivedLength / contentLength) * 100);
1450
+ const progressBar = this.getProgressBar(percentComplete);
1451
+ this.emitProgress(pluginName, `\r${progressBar} ${percentComplete}%`, cyan);
1452
+ }
1453
+ return await new Blob(chunks).arrayBuffer();
1454
+ }
1455
+ getProgressBar(percent) {
1456
+ const width = 50;
1457
+ const complete = Math.round(width * (percent / 100));
1458
+ const incomplete = width - complete;
1459
+ return `[${'='.repeat(complete)}${'-'.repeat(incomplete)}]`;
1460
+ }
1461
+ emitProgress(pluginName, message, color) {
1462
+ this.io.of('/logs').emit(`stdout/${pluginName}`, color ? color(message) : message);
1463
+ }
1197
1464
  }
1198
1465
  //# sourceMappingURL=plugins.controller.js.map