@camera.ui/server 0.0.14-alpha.4 → 0.0.14-test.0
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.
- package/dist/interface/@tanstack.D3xDk1KT.0.0.14-test.0.js +1 -0
- package/dist/interface/{@vee-validate.DH43ss6R.0.0.14-alpha.4.js → @vee-validate.DOKkv4ps.0.0.14-test.0.js} +1 -1
- package/dist/interface/@vueform.DcIdpPOs.0.0.14-test.0.js +1 -0
- package/dist/interface/@vueuse.xVzoI_1-.0.0.14-test.0.js +1 -0
- package/dist/interface/Account.jxvpz8eb.0.0.14-test.0.js +1 -0
- package/dist/interface/Adminpanel.BA690upJ.0.0.14-test.0.js +2 -0
- package/dist/interface/Appearance.BcaSOMXL.0.0.14-test.0.js +1 -0
- package/dist/interface/Backup.DDqrILQi.0.0.14-test.0.js +1 -0
- package/dist/interface/Camera.BSCHhORz.0.0.14-test.0.js +1 -0
- package/dist/interface/Cameras.Be5nO7Hm.0.0.14-test.0.js +1 -0
- package/dist/interface/Camview.Cnw0fe2B.0.0.14-test.0.js +1 -0
- package/dist/interface/Config.CLbloVcn.0.0.14-test.0.js +1 -0
- package/dist/interface/Console.CIk5blhj.0.0.14-test.0.js +4 -0
- package/dist/interface/FirstSteps.BqFBy47s.0.0.14-test.0.js +1 -0
- package/dist/interface/{NewCamera.CVpitp4W.0.0.14-alpha.4.js → NewCamera.DqRRXyiS.0.0.14-test.0.js} +1 -1
- package/dist/interface/Plugin.4EKvTIAN.0.0.14-test.0.js +1 -0
- package/dist/interface/Plugins.B3vJM8oE.0.0.14-test.0.js +1 -0
- package/dist/interface/Recordings.CctqQst2.0.0.14-test.0.js +1 -0
- package/dist/interface/Recordings.ClfFPNWt.0.0.14-test.0.js +1 -0
- package/dist/interface/Settings.CsDJzySp.0.0.14-test.0.js +1 -0
- package/dist/interface/{System.DLJuO_7K.0.0.14-alpha.4.js → System.BjX529ZT.0.0.14-test.0.js} +1 -1
- package/dist/interface/User.9czjeM6_.0.0.14-test.0.js +1 -0
- package/dist/interface/VConfigEditor.vue_vue_type_script_setup_true_lang.CwKlBpDN.0.0.14-test.0.js +2 -0
- package/dist/interface/VConsole.CpjLpdxp.0.0.14-test.0.js +1 -0
- package/dist/interface/VPlayer.DEmVXyrd.0.0.14-test.0.js +1 -0
- package/dist/interface/VPluginCard.DY48Dnpd.0.0.14-test.0.js +4 -0
- package/dist/interface/{ace-builds.DFZjdKko.0.0.14-alpha.4.js → ace-builds.Cwc9IOBs.0.0.14-test.0.js} +1 -1
- package/dist/interface/assets/VPluginCard.0.0.14-test.0.C2GrhESv.css +1 -0
- package/dist/interface/dnd-core.CDzinE8l.0.0.14-test.0.js +1 -0
- package/dist/interface/{draggabilly.B1sGLlkC.0.0.14-alpha.4.js → draggabilly.DnzlD6V8.0.0.14-test.0.js} +1 -1
- package/dist/interface/engine.io-client.1WcLFu-h.0.0.14-test.0.js +1 -0
- package/dist/interface/ev-emitter._Fda3r0p.0.0.14-test.0.js +1 -0
- package/dist/interface/fast-deep-equal.LulmGQ-C.0.0.14-test.0.js +1 -0
- package/dist/interface/{highlight.js.0YB-0mi8.0.0.14-alpha.4.js → highlight.js.CAXmjaYi.0.0.14-test.0.js} +1 -1
- package/dist/interface/index.html +33 -33
- package/dist/interface/linkify-it.AzfKWZxp.0.0.14-test.0.js +1 -0
- package/dist/interface/main.5hpHRJeM.0.0.14-test.0.js +9 -0
- package/dist/interface/{markdown-it.BKNgIiXo.0.0.14-alpha.4.js → markdown-it.DdFR_UFF.0.0.14-test.0.js} +1 -1
- package/dist/interface/{nprogress.CyIg_Gxi.0.0.14-alpha.4.js → nprogress.C9__1SbO.0.0.14-test.0.js} +1 -1
- package/dist/interface/{pinia.CecoA7CB.0.0.14-alpha.4.js → pinia.BKfmzk9I.0.0.14-test.0.js} +1 -1
- package/dist/interface/plugins.DEMI-w_e.0.0.14-test.0.js +1 -0
- package/dist/interface/{qrcode.DendPh0T.0.0.14-alpha.4.js → qrcode.q_ZJ6wJi.0.0.14-test.0.js} +1 -1
- package/dist/interface/react-dnd-touch-backend.Bf0iLHP3.0.0.14-test.0.js +1 -0
- package/dist/interface/{rxjs.CRxlDfq_.0.0.14-alpha.4.js → rxjs.DvH6tcR4.0.0.14-test.0.js} +1 -1
- package/dist/interface/socket.io-client.BtCQqdbX.0.0.14-test.0.js +1 -0
- package/dist/interface/socket.io-parser.DMpQBlxp.0.0.14-test.0.js +1 -0
- package/dist/interface/sw.js +1 -1
- package/dist/interface/system.RCM_IcfZ.0.0.14-test.0.js +1 -0
- package/dist/interface/ui.Cr2152-8.0.0.14-test.0.js +1 -0
- package/dist/interface/{unidragger.D3rkYrUG.0.0.14-alpha.4.js → unidragger.BoLik_pQ.0.0.14-test.0.js} +1 -1
- package/dist/interface/v-calendar.wkuuwQ83.0.0.14-test.0.js +1 -0
- package/dist/interface/{vee-validate.pvxJMmMq.0.0.14-alpha.4.js → vee-validate.tc9vGU36.0.0.14-test.0.js} +1 -1
- package/dist/interface/{vue-i18n.CozUMf0M.0.0.14-alpha.4.js → vue-i18n.BYF0s9mP.0.0.14-test.0.js} +1 -1
- package/dist/interface/vue-inline-svg.CPSPBSLN.0.0.14-test.0.js +1 -0
- package/dist/interface/{vue-router.BvzGucu2.0.0.14-alpha.4.js → vue-router.CMjsArp9.0.0.14-test.0.js} +1 -1
- package/dist/interface/{vue3-apexcharts.CVBbHYHw.0.0.14-alpha.4.js → vue3-apexcharts.jPAcekva.0.0.14-test.0.js} +1 -1
- package/dist/interface/vue3-dnd.DsZHuyII.0.0.14-test.0.js +1 -0
- package/dist/interface/vue3-toastify.CuQ6p2fJ.0.0.14-test.0.js +1 -0
- package/dist/interface/{vuetify.h38bTLGK.0.0.14-alpha.4.js → vuetify.CEVDhY59.0.0.14-test.0.js} +1 -1
- package/dist/server/package.json +3 -2
- package/dist/server/src/api/controllers/plugins.controller.d.ts +11 -9
- package/dist/server/src/api/controllers/plugins.controller.js +513 -246
- package/dist/server/src/api/controllers/plugins.controller.js.map +1 -1
- package/dist/server/src/api/routes/system.routes.js +1 -1
- package/dist/server/src/plugins/index.js +1 -1
- package/dist/server/src/plugins/index.js.map +1 -1
- package/dist/server/src/services/config/index.d.ts +0 -2
- package/dist/server/src/services/config/index.js +3 -20
- package/dist/server/src/services/config/index.js.map +1 -1
- package/package.json +3 -2
- package/upgrade-server.sh +95 -0
- package/dist/interface/@tanstack.DxR9TqNT.0.0.14-alpha.4.js +0 -1
- package/dist/interface/@vueform.BEK-4lp8.0.0.14-alpha.4.js +0 -1
- package/dist/interface/@vueuse.0r_4ERfz.0.0.14-alpha.4.js +0 -1
- package/dist/interface/Account.BKb5WCug.0.0.14-alpha.4.js +0 -1
- package/dist/interface/Adminpanel.CV_YvWCC.0.0.14-alpha.4.js +0 -2
- package/dist/interface/Appearance.DHt3Aut6.0.0.14-alpha.4.js +0 -1
- package/dist/interface/Backup.rY0lEVR6.0.0.14-alpha.4.js +0 -1
- package/dist/interface/Camera.CtLcCWEv.0.0.14-alpha.4.js +0 -1
- package/dist/interface/Cameras.CFeTbskA.0.0.14-alpha.4.js +0 -1
- package/dist/interface/Camview.BtrGE5XA.0.0.14-alpha.4.js +0 -1
- package/dist/interface/Config.qRLVQl_r.0.0.14-alpha.4.js +0 -1
- package/dist/interface/Console.5Xw8RJIu.0.0.14-alpha.4.js +0 -4
- package/dist/interface/FirstSteps.Bc-g53Gw.0.0.14-alpha.4.js +0 -1
- package/dist/interface/Plugin.DQMR2bcj.0.0.14-alpha.4.js +0 -1
- package/dist/interface/Plugins.BlnUOFH6.0.0.14-alpha.4.js +0 -1
- package/dist/interface/Recordings.YR4raWR-.0.0.14-alpha.4.js +0 -1
- package/dist/interface/Recordings.a9uscpmX.0.0.14-alpha.4.js +0 -1
- package/dist/interface/Settings.Bqtc0cQ3.0.0.14-alpha.4.js +0 -1
- package/dist/interface/User.LrBOLpvr.0.0.14-alpha.4.js +0 -1
- package/dist/interface/VConfigEditor.vue_vue_type_script_setup_true_lang.D7BKvf1P.0.0.14-alpha.4.js +0 -2
- package/dist/interface/VConsole.B6n_q1I4.0.0.14-alpha.4.js +0 -1
- package/dist/interface/VPlayer.Cse-EfS2.0.0.14-alpha.4.js +0 -1
- package/dist/interface/VPluginCard.BXBkPqIn.0.0.14-alpha.4.js +0 -4
- package/dist/interface/assets/VPluginCard.0.0.14-alpha.4.B4G51MYn.css +0 -1
- package/dist/interface/dnd-core.BM-WmXXD.0.0.14-alpha.4.js +0 -1
- package/dist/interface/engine.io-client.C1WzcoRG.0.0.14-alpha.4.js +0 -1
- package/dist/interface/ev-emitter.BTd_mWjj.0.0.14-alpha.4.js +0 -1
- package/dist/interface/fast-deep-equal.HwPw-pxY.0.0.14-alpha.4.js +0 -1
- package/dist/interface/linkify-it.BFvjiZc3.0.0.14-alpha.4.js +0 -1
- package/dist/interface/main.V0WK2QMW.0.0.14-alpha.4.js +0 -9
- package/dist/interface/plugins.CXiJ27uA.0.0.14-alpha.4.js +0 -1
- package/dist/interface/react-dnd-touch-backend.C5ABPVRb.0.0.14-alpha.4.js +0 -1
- package/dist/interface/socket.io-client.DRrdPiqk.0.0.14-alpha.4.js +0 -1
- package/dist/interface/socket.io-parser.CgdzaAil.0.0.14-alpha.4.js +0 -1
- package/dist/interface/system.CTiWjzrS.0.0.14-alpha.4.js +0 -1
- package/dist/interface/ui.DS0tO89R.0.0.14-alpha.4.js +0 -1
- package/dist/interface/v-calendar.BsChHPaY.0.0.14-alpha.4.js +0 -1
- package/dist/interface/vue-inline-svg.Dt8SW6Et.0.0.14-alpha.4.js +0 -1
- package/dist/interface/vue3-dnd.10LbMhwv.0.0.14-alpha.4.js +0 -1
- package/dist/interface/vue3-toastify.RTQ6RfPx.0.0.14-alpha.4.js +0 -1
- /package/dist/interface/{@babel.l0sNRNKZ.0.0.14-alpha.4.js → @babel.l0sNRNKZ.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{@intlify.YW_pt133.0.0.14-alpha.4.js → @intlify.YW_pt133.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{@mdi.jq8isxax.0.0.14-alpha.4.js → @mdi.jq8isxax.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{@popperjs.BQBsAJpH.0.0.14-alpha.4.js → @popperjs.BQBsAJpH.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{@react-dnd.CcwRs9xb.0.0.14-alpha.4.js → @react-dnd.CcwRs9xb.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{@socket.io.Dkula2eQ.0.0.14-alpha.4.js → @socket.io.Dkula2eQ.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{@vue.CIFymae3.0.0.14-alpha.4.js → @vue.CIFymae3.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{@xterm.B_cEcIAu.0.0.14-alpha.4.js → @xterm.CwCrcWv8.0.0.14-test.0.js} +0 -0
- /package/dist/interface/assets/{@vueform.0.0.14-alpha.4.h7tNX9p6.css → @vueform.0.0.14-test.0.h7tNX9p6.css} +0 -0
- /package/dist/interface/assets/{@xterm.0.0.14-alpha.4.CGrrDQr5.css → @xterm.0.0.14-test.0.CGrrDQr5.css} +0 -0
- /package/dist/interface/assets/{Account.0.0.14-alpha.4.DlOUWknb.css → Account.0.0.14-test.0.DlOUWknb.css} +0 -0
- /package/dist/interface/assets/{Adminpanel.0.0.14-alpha.4.BHh3IMeu.css → Adminpanel.0.0.14-test.0.BHh3IMeu.css} +0 -0
- /package/dist/interface/assets/{Backup.0.0.14-alpha.4.BIwaSlv3.css → Backup.0.0.14-test.0.BIwaSlv3.css} +0 -0
- /package/dist/interface/assets/{Camera.0.0.14-alpha.4.B5hAZmeQ.css → Camera.0.0.14-test.0.B5hAZmeQ.css} +0 -0
- /package/dist/interface/assets/{Cameras.0.0.14-alpha.4.D7bnvH42.css → Cameras.0.0.14-test.0.D7bnvH42.css} +0 -0
- /package/dist/interface/assets/{Camview.0.0.14-alpha.4.cgjDTjXy.css → Camview.0.0.14-test.0.cgjDTjXy.css} +0 -0
- /package/dist/interface/assets/{Config.0.0.14-alpha.4.CncRTcjC.css → Config.0.0.14-test.0.CncRTcjC.css} +0 -0
- /package/dist/interface/assets/{Console.0.0.14-alpha.4.DWrfRWs9.css → Console.0.0.14-test.0.DWrfRWs9.css} +0 -0
- /package/dist/interface/assets/{FirstSteps.0.0.14-alpha.4.LZWhAsRR.css → FirstSteps.0.0.14-test.0.LZWhAsRR.css} +0 -0
- /package/dist/interface/assets/{NewCamera.0.0.14-alpha.4.DJ3tUBT_.css → NewCamera.0.0.14-test.0.DJ3tUBT_.css} +0 -0
- /package/dist/interface/assets/{Plugin.0.0.14-alpha.4.BGZE7hpr.css → Plugin.0.0.14-test.0.BGZE7hpr.css} +0 -0
- /package/dist/interface/assets/{Plugins.0.0.14-alpha.4.Cwu0aGUO.css → Plugins.0.0.14-test.0.Cwu0aGUO.css} +0 -0
- /package/dist/interface/assets/{Settings.0.0.14-alpha.4.BfSP_zcY.css → Settings.0.0.14-test.0.BfSP_zcY.css} +0 -0
- /package/dist/interface/assets/{User.0.0.14-alpha.4.BHWFyck9.css → User.0.0.14-test.0.BHWFyck9.css} +0 -0
- /package/dist/interface/assets/{VConsole.0.0.14-alpha.4.BZT1WyS-.css → VConsole.0.0.14-test.0.BZT1WyS-.css} +0 -0
- /package/dist/interface/assets/{VPlayer.0.0.14-alpha.4.H4sGdlHU.css → VPlayer.0.0.14-test.0.H4sGdlHU.css} +0 -0
- /package/dist/interface/assets/{highlight.0.0.14-alpha.4.DgoK_Xib.css → highlight.0.0.14-test.0.DgoK_Xib.css} +0 -0
- /package/dist/interface/assets/{logo-256.0.0.14-alpha.4.BCQk9H1-.png → logo-256.0.0.14-test.0.BCQk9H1-.png} +0 -0
- /package/dist/interface/assets/{logo-512.0.0.14-alpha.4.DLKVOU2S.png → logo-512.0.0.14-test.0.DLKVOU2S.png} +0 -0
- /package/dist/interface/assets/{logo.0.0.14-alpha.4.DmEiRk0z.png → logo.0.0.14-test.0.DmEiRk0z.png} +0 -0
- /package/dist/interface/assets/{main.0.0.14-alpha.4.BPl82sqN.css → main.0.0.14-test.0.BPl82sqN.css} +0 -0
- /package/dist/interface/assets/{mode-json.0.0.14-alpha.4.Br7xisR1.js → mode-json.0.0.14-test.0.Br7xisR1.js} +0 -0
- /package/dist/interface/assets/{mode-yaml.0.0.14-alpha.4.CIYL8U3d.js → mode-yaml.0.0.14-test.0.CIYL8U3d.js} +0 -0
- /package/dist/interface/assets/{v-calendar.0.0.14-alpha.4.CtSuDYZU.css → v-calendar.0.0.14-test.0.CtSuDYZU.css} +0 -0
- /package/dist/interface/assets/{vue3-toastify.0.0.14-alpha.4.DTY1DMRs.css → vue3-toastify.0.0.14-test.0.DTY1DMRs.css} +0 -0
- /package/dist/interface/assets/{vuetify.0.0.14-alpha.4.DPyVfAPv.css → vuetify.0.0.14-test.0.DPyVfAPv.css} +0 -0
- /package/dist/interface/{axios.BI2i94Vb.0.0.14-alpha.4.js → axios.BI2i94Vb.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{compare-versions.DqEOhma6.0.0.14-alpha.4.js → compare-versions.DqEOhma6.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{dijkstrajs.D_NXgYpA.0.0.14-alpha.4.js → dijkstrajs.D_NXgYpA.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{engine.io-parser.BiEtp6m2.0.0.14-alpha.4.js → engine.io-parser.BiEtp6m2.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{entities.C20TfXL6.0.0.14-alpha.4.js → entities.C20TfXL6.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{get-size.D1dS-IaP.0.0.14-alpha.4.js → get-size.D1dS-IaP.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{mdurl.k9Sl0PQj.0.0.14-alpha.4.js → mdurl.k9Sl0PQj.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{punycode.js.Dj65hjkv.0.0.14-alpha.4.js → punycode.js.Dj65hjkv.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{react-dnd-html5-backend.1HQjlkwI.0.0.14-alpha.4.js → react-dnd-html5-backend.1HQjlkwI.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{redux.CuxFRhX_.0.0.14-alpha.4.js → redux.CuxFRhX_.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{tslib.9k7AwCpt.0.0.14-alpha.4.js → tslib.9k7AwCpt.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{uc.micro.kMc2yuuw.0.0.14-alpha.4.js → uc.micro.kMc2yuuw.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{utils.BvINh1mj.0.0.14-alpha.4.js → utils.BvINh1mj.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{uuid.DvF23Exx.0.0.14-alpha.4.js → uuid.DvF23Exx.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{vue.l0sNRNKZ.0.0.14-alpha.4.js → vue.l0sNRNKZ.0.0.14-test.0.js} +0 -0
- /package/dist/interface/{zod.BaF2dDCd.0.0.14-alpha.4.js → zod.BaF2dDCd.0.0.14-test.0.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,
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
}
|
|
949
|
-
|
|
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
|