feat: Manage Images using Minio Service
This commit is contained in:
parent
754244345a
commit
47f09b3c4e
506
package-lock.json
generated
506
package-lock.json
generated
@ -54,6 +54,7 @@
|
||||
"leaflet": "^1.9.4",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mermaid": "^11.12.2",
|
||||
"minio": "^8.0.6",
|
||||
"ng-otp-input": "^2.0.9",
|
||||
"ng2-charts": "^8.0.0",
|
||||
"ngx-countup": "^13.2.0",
|
||||
@ -74,6 +75,7 @@
|
||||
"@types/jquery": "^3.5.33",
|
||||
"@types/leaflet": "^1.9.21",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^25.0.3",
|
||||
"baseline-browser-mapping": "^2.9.11",
|
||||
"jasmine-core": "~5.12.0",
|
||||
"karma": "~6.4.4",
|
||||
@ -4698,9 +4700,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz",
|
||||
"integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==",
|
||||
"version": "25.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
|
||||
"integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -4747,6 +4749,13 @@
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/@zxing/text-encoding": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz",
|
||||
"integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==",
|
||||
"license": "(Unlicense OR Apache-2.0)",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/abbrev": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz",
|
||||
@ -4954,6 +4963,27 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/available-typed-arrays": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"possible-typed-array-names": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@ -5013,6 +5043,29 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/block-stream2": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.1.0.tgz",
|
||||
"integrity": "sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"readable-stream": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/block-stream2/node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
|
||||
@ -5097,6 +5150,12 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/browser-or-node": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-2.1.1.tgz",
|
||||
"integrity": "sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.27.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz",
|
||||
@ -5130,6 +5189,15 @@
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-crc32": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
|
||||
"integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
@ -5262,11 +5330,28 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
|
||||
"integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.0",
|
||||
"es-define-property": "^1.0.0",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"set-function-length": "^1.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
@ -5280,7 +5365,6 @@
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
@ -6452,6 +6536,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/decode-uri-component": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
|
||||
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/define-data-property": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/delaunator": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
|
||||
@ -6591,7 +6701,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
@ -6822,7 +6931,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@ -6832,7 +6940,6 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@ -6842,7 +6949,6 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
@ -7058,6 +7164,24 @@
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/fast-xml-parser": {
|
||||
"version": "4.5.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz",
|
||||
"integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"strnum": "^1.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"fxparser": "src/cli/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||
@ -7088,6 +7212,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/filter-obj": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
|
||||
"integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
||||
@ -7140,6 +7273,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/for-each": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||
"integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-callable": "^1.2.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||
@ -7231,7 +7379,6 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@ -7246,6 +7393,15 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/generator-function": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
|
||||
"integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
@ -7280,7 +7436,6 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
@ -7305,7 +7460,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
@ -7361,7 +7515,6 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@ -7393,11 +7546,22 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/has-property-descriptors": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@ -7410,7 +7574,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
@ -7426,7 +7589,6 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
@ -7701,6 +7863,22 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arguments": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz",
|
||||
"integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
"has-tostringtag": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
@ -7714,6 +7892,18 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-callable": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
|
||||
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||
@ -7753,6 +7943,25 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-generator-function": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
|
||||
"integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.4",
|
||||
"generator-function": "^2.0.0",
|
||||
"get-proto": "^1.0.1",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"safe-regex-test": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
@ -7800,7 +8009,6 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
|
||||
"integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
@ -7815,6 +8023,21 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-typed-array": {
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
|
||||
"integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"which-typed-array": "^1.1.16"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-unicode-supported": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
|
||||
@ -8918,7 +9141,6 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@ -9076,6 +9298,67 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/minio": {
|
||||
"version": "8.0.6",
|
||||
"resolved": "https://registry.npmjs.org/minio/-/minio-8.0.6.tgz",
|
||||
"integrity": "sha512-sOeh2/b/XprRmEtYsnNRFtOqNRTPDvYtMWh+spWlfsuCV/+IdxNeKVUMKLqI7b5Dr07ZqCPuaRGU/rB9pZYVdQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"async": "^3.2.4",
|
||||
"block-stream2": "^2.1.0",
|
||||
"browser-or-node": "^2.1.1",
|
||||
"buffer-crc32": "^1.0.0",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"fast-xml-parser": "^4.4.1",
|
||||
"ipaddr.js": "^2.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"mime-types": "^2.1.35",
|
||||
"query-string": "^7.1.3",
|
||||
"stream-json": "^1.8.0",
|
||||
"through2": "^4.0.2",
|
||||
"web-encoding": "^1.1.5",
|
||||
"xml2js": "^0.5.0 || ^0.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16 || ^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/minio/node_modules/eventemitter3": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/minio/node_modules/ipaddr.js": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz",
|
||||
"integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/minio/node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/minio/node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
@ -10183,6 +10466,15 @@
|
||||
"points-on-curve": "0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/possible-typed-array-names": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
|
||||
"integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||
@ -10321,6 +10613,24 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/query-string": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
|
||||
"integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"decode-uri-component": "^0.2.2",
|
||||
"filter-obj": "^1.1.0",
|
||||
"split-on-first": "^1.0.0",
|
||||
"strict-uri-encode": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/quill": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz",
|
||||
@ -10640,7 +10950,6 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
|
||||
"integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bound": "^1.0.2",
|
||||
@ -10681,6 +10990,12 @@
|
||||
"@parcel/watcher": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz",
|
||||
"integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==",
|
||||
"license": "BlueOak-1.0.0"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
@ -10733,6 +11048,23 @@
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/set-function-length": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.1.4",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"gopd": "^1.0.1",
|
||||
"has-property-descriptors": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
@ -11180,6 +11512,15 @@
|
||||
"dev": true,
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/split-on-first": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
|
||||
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/ssri": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz",
|
||||
@ -11216,6 +11557,21 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/stream-chain": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz",
|
||||
"integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/stream-json": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz",
|
||||
"integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"stream-chain": "^2.2.5"
|
||||
}
|
||||
},
|
||||
"node_modules/streamroller": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz",
|
||||
@ -11231,6 +11587,15 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strict-uri-encode": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
|
||||
"integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
@ -11358,6 +11723,18 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strnum": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz",
|
||||
"integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/NaturalIntelligence"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stylis": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
|
||||
@ -11491,6 +11868,29 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/through2": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
|
||||
"integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"readable-stream": "3"
|
||||
}
|
||||
},
|
||||
"node_modules/through2/node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyexec": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
|
||||
@ -11744,6 +12144,19 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/util": {
|
||||
"version": "0.12.5",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
|
||||
"integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"is-arguments": "^1.0.4",
|
||||
"is-generator-function": "^1.0.7",
|
||||
"is-typed-array": "^1.1.3",
|
||||
"which-typed-array": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@ -11986,6 +12399,18 @@
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/web-encoding": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz",
|
||||
"integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"util": "^0.12.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@zxing/text-encoding": "0.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
@ -12002,6 +12427,27 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/which-typed-array": {
|
||||
"version": "1.1.19",
|
||||
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
|
||||
"integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"available-typed-arrays": "^1.0.7",
|
||||
"call-bind": "^1.0.8",
|
||||
"call-bound": "^1.0.4",
|
||||
"for-each": "^0.3.5",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-tostringtag": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
@ -12207,6 +12653,28 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xml2js": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
|
||||
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sax": ">=0.6.0",
|
||||
"xmlbuilder": "~11.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlbuilder": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
|
||||
@ -57,6 +57,7 @@
|
||||
"leaflet": "^1.9.4",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mermaid": "^11.12.2",
|
||||
"minio": "^8.0.6",
|
||||
"ng-otp-input": "^2.0.9",
|
||||
"ng2-charts": "^8.0.0",
|
||||
"ngx-countup": "^13.2.0",
|
||||
@ -77,6 +78,7 @@
|
||||
"@types/jquery": "^3.5.33",
|
||||
"@types/leaflet": "^1.9.21",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^25.0.3",
|
||||
"baseline-browser-mapping": "^2.9.11",
|
||||
"jasmine-core": "~5.12.0",
|
||||
"karma": "~6.4.4",
|
||||
|
||||
109
src/app/core/services/minio.service.ts
Normal file
109
src/app/core/services/minio.service.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { environment } from '@environments/environment';
|
||||
|
||||
export interface UploadLogoResponse {
|
||||
success: boolean;
|
||||
fileName: string;
|
||||
url: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MinioService {
|
||||
private apiUrl = `${environment.configApiUrl}/minio`; // URL de votre backend
|
||||
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
/**
|
||||
* Upload un logo de marchand vers MinIO
|
||||
*/
|
||||
uploadMerchantLogo(file: File, merchantId?: number): Observable<UploadLogoResponse> {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('bucketName', 'bo-assets');
|
||||
|
||||
// Générer un nom unique pour le logo
|
||||
const timestamp = Date.now();
|
||||
const extension = file.name.split('.').pop();
|
||||
const fileName = merchantId
|
||||
? `merchant_${merchantId}_${timestamp}.${extension}`
|
||||
: `logo_${timestamp}.${extension}`;
|
||||
|
||||
formData.append('objectName', fileName);
|
||||
|
||||
return this.http.post<UploadLogoResponse>(`${this.apiUrl}/upload-logo`, formData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère l'URL présignée pour afficher un logo
|
||||
* URL valide pour 7 jours
|
||||
*/
|
||||
getMerchantLogoUrl(logoFileName: string): Observable<string> {
|
||||
const expiry = 7 * 24 * 60 * 60; // 7 jours en secondes
|
||||
return this.http.get<{ url: string }>(
|
||||
`${this.apiUrl}/presigned-url`,
|
||||
{
|
||||
params: {
|
||||
bucketName: 'bo-assets',
|
||||
objectName: logoFileName,
|
||||
expiry: expiry.toString()
|
||||
}
|
||||
}
|
||||
).pipe(map(response => response.url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime un logo de marchand
|
||||
*/
|
||||
deleteMerchantLogo(logoFileName: string): Observable<any> {
|
||||
return this.http.delete(`${this.apiUrl}/delete`, {
|
||||
params: {
|
||||
bucketName: 'bo-assets',
|
||||
objectName: logoFileName
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide qu'un fichier est une image valide
|
||||
*/
|
||||
validateImageFile(file: File): { valid: boolean; error?: string } {
|
||||
// Vérifier le type MIME
|
||||
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml'];
|
||||
if (!allowedTypes.includes(file.type)) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Format non supporté. Utilisez JPG, PNG, GIF, WebP ou SVG.'
|
||||
};
|
||||
}
|
||||
|
||||
// Vérifier la taille (2MB max pour un logo)
|
||||
const maxSize = 2 * 1024 * 1024; // 2MB
|
||||
if (file.size > maxSize) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Le fichier est trop volumineux (max 2MB pour un logo)'
|
||||
};
|
||||
}
|
||||
|
||||
// Vérifier les dimensions si possible (optionnel)
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Prévisualise une image avant upload
|
||||
*/
|
||||
previewImage(file: File): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e: any) => resolve(e.target.result);
|
||||
reader.onerror = () => reject(new Error('Erreur lors de la lecture du fichier'));
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -180,17 +180,20 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
@if (merchant.logo) {
|
||||
<img
|
||||
[src]="merchant.logo"
|
||||
alt="Logo {{ merchant.name }}"
|
||||
class="avatar-sm rounded-circle me-2"
|
||||
onerror="this.style.display='none'"
|
||||
>
|
||||
}
|
||||
<div class="avatar-sm bg-primary bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center me-2">
|
||||
<ng-icon name="lucideStore" class="text-primary fs-12"></ng-icon>
|
||||
</div>
|
||||
<!-- Logo du marchand -->
|
||||
<div class="avatar-container me-3 position-relative">
|
||||
<ng-container *ngIf="merchant.logo;">
|
||||
<!-- Image du logo -->
|
||||
<img
|
||||
[src]="getMerchantLogoUrl(merchant.logo) | async"
|
||||
[alt]="merchant.name + ' logo'"
|
||||
class="merchant-logo avatar-lg rounded-circle"
|
||||
/>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div>
|
||||
<strong class="d-block">{{ merchant.name }}</strong>
|
||||
<small class="text-muted">{{ merchant.adresse }}</small>
|
||||
|
||||
@ -4,7 +4,7 @@ import { FormsModule } from '@angular/forms';
|
||||
import { NgIcon } from '@ng-icons/core';
|
||||
import { NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { Observable, Subject, of } from 'rxjs';
|
||||
import { catchError, takeUntil } from 'rxjs/operators';
|
||||
import { catchError, takeUntil, tap } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
Merchant,
|
||||
@ -20,6 +20,8 @@ import { MerchantConfigService } from '../merchant-config.service';
|
||||
import { RoleManagementService } from '@core/services/hub-users-roles-management.service';
|
||||
import { AuthService } from '@core/services/auth.service';
|
||||
import { UiCard } from '@app/components/ui-card';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { MinioService } from '@core/services/minio.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-merchant-config-list',
|
||||
@ -39,6 +41,14 @@ export class MerchantConfigsList implements OnInit, OnDestroy {
|
||||
protected roleService = inject(RoleManagementService);
|
||||
private cdRef = inject(ChangeDetectorRef);
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
private minioService = inject(MinioService);
|
||||
private sanitizer = inject(DomSanitizer);
|
||||
|
||||
// Cache des URLs de logos
|
||||
private logoUrlCache = new Map<string, string>();
|
||||
// Ajouter un cache pour les logos non trouvés
|
||||
private logoErrorCache = new Set<string>();
|
||||
|
||||
// Configuration
|
||||
readonly ConfigType = ConfigType;
|
||||
@ -208,6 +218,85 @@ export class MerchantConfigsList implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== AFFICHAGE DU LOGO ====================
|
||||
|
||||
/**
|
||||
* Récupère l'URL du logo avec fallback automatique
|
||||
*/
|
||||
getMerchantLogoUrl(logoFileName: string, merchantName?: string): Observable<string> {
|
||||
// Vérifier si le logo est en cache d'erreur
|
||||
if (this.logoErrorCache.has(logoFileName)) {
|
||||
const defaultLogo = this.getDefaultLogoUrl(merchantName || logoFileName);
|
||||
return of(defaultLogo);
|
||||
}
|
||||
|
||||
// Vérifier le cache normal
|
||||
if (this.logoUrlCache.has(logoFileName)) {
|
||||
return of(this.logoUrlCache.get(logoFileName)!);
|
||||
}
|
||||
|
||||
// Récupérer l'URL depuis MinIO
|
||||
return this.minioService.getMerchantLogoUrl(logoFileName).pipe(
|
||||
tap(url => {
|
||||
// Mettre en cache
|
||||
this.logoUrlCache.set(logoFileName, url);
|
||||
}),
|
||||
catchError(error => {
|
||||
// En cas d'erreur, ajouter au cache d'erreur et retourner le logo par défaut
|
||||
this.logoErrorCache.add(logoFileName);
|
||||
const defaultLogo = this.getDefaultLogoUrl(merchantName || logoFileName);
|
||||
return of(defaultLogo);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère une URL de logo par défaut basée sur les initiales
|
||||
*/
|
||||
getDefaultLogoUrl(merchantName: string): string {
|
||||
|
||||
// Créer des initiales significatives
|
||||
const initials = this.extractInitials(merchantName);
|
||||
|
||||
// Palette de couleurs agréables
|
||||
const colors = [
|
||||
'FF6B6B', '4ECDC4', '45B7D1', '96CEB4', 'FFEAA7',
|
||||
'DDA0DD', '98D8C8', 'F7DC6F', 'BB8FCE', '85C1E9'
|
||||
];
|
||||
|
||||
const colorIndex = merchantName.length % colors.length;
|
||||
const backgroundColor = colors[colorIndex];
|
||||
const textColor = 'FFFFFF'; // Blanc pour contraste
|
||||
|
||||
return `https://ui-avatars.com/api/?name=${encodeURIComponent(initials)}&background=${backgroundColor}&color=${textColor}&size=200&bold=true&font-size=0.5`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extrait les initiales de manière intelligente
|
||||
*/
|
||||
private extractInitials(name: string): string {
|
||||
// Nettoyer le nom
|
||||
const cleanedName = name.trim().toUpperCase();
|
||||
|
||||
// Extraire les mots (ignorer les articles, prépositions courtes)
|
||||
const words = cleanedName.split(/\s+/);
|
||||
|
||||
// Si un seul mot, prendre les deux premières lettres
|
||||
if (words.length === 1) {
|
||||
return words[0].substring(0, 2);
|
||||
}
|
||||
|
||||
// Prendre la première lettre des deux premiers mots significatifs
|
||||
const initials = words
|
||||
.filter(word => word.length > 2) // Ignorer les mots courts
|
||||
.slice(0, 2) // Prendre maximum 2 mots
|
||||
.map(word => word[0])
|
||||
.join('');
|
||||
|
||||
return initials || name.substring(0, 2).toUpperCase();
|
||||
}
|
||||
|
||||
|
||||
private buildSearchParams(): SearchMerchantsParams {
|
||||
const params: SearchMerchantsParams = {};
|
||||
|
||||
|
||||
@ -110,10 +110,21 @@
|
||||
<div class="profile-section">
|
||||
<div class="profile-header">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-8">
|
||||
<div class="col-md-4">
|
||||
<h2 class="mb-2">{{ merchant.name }}</h2>
|
||||
<p class="mb-0 opacity-75">{{ merchant.description || 'Aucune description' }}</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<ng-container *ngIf="merchant.logo;">
|
||||
<img
|
||||
[src]="getMerchantLogoUrl(merchant.logo) | async"
|
||||
[alt]="merchant.name"
|
||||
class="merchant-logo-large"
|
||||
/>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 text-md-end">
|
||||
@if (canEditMerchant()) {
|
||||
<button
|
||||
|
||||
@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { NgIcon } from '@ng-icons/core';
|
||||
import { NgbAlertModule, NgbPaginationModule, NgbNavModule, NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
import { catchError, Observable, of, Subject, takeUntil, tap } from 'rxjs';
|
||||
|
||||
import {
|
||||
Merchant,
|
||||
@ -22,6 +22,8 @@ import { MerchantDataAdapter } from '../merchant-data-adapter.service';
|
||||
import { RoleManagementService } from '@core/services/hub-users-roles-management.service';
|
||||
import { AuthService } from '@core/services/auth.service';
|
||||
import { UserRole } from '@core/models/dcb-bo-hub-user.model';
|
||||
import { MinioService } from '@core/services/minio.service';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
|
||||
@Component({
|
||||
selector: 'app-merchant-config-view',
|
||||
@ -180,6 +182,14 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
||||
private modalService = inject(NgbModal);
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
private minioService = inject(MinioService);
|
||||
private sanitizer = inject(DomSanitizer);
|
||||
|
||||
// Cache des URLs de logos
|
||||
private logoUrlCache = new Map<string, string>();
|
||||
// Ajouter un cache pour les logos non trouvés
|
||||
private logoErrorCache = new Set<string>();
|
||||
|
||||
readonly ConfigType = ConfigType;
|
||||
readonly Operator = Operator;
|
||||
readonly MerchantUtils = MerchantUtils;
|
||||
@ -281,6 +291,84 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== AFFICHAGE DU LOGO ====================
|
||||
|
||||
/**
|
||||
* Récupère l'URL du logo avec fallback automatique
|
||||
*/
|
||||
getMerchantLogoUrl(logoFileName: string, merchantName?: string): Observable<string> {
|
||||
// Vérifier si le logo est en cache d'erreur
|
||||
if (this.logoErrorCache.has(logoFileName)) {
|
||||
const defaultLogo = this.getDefaultLogoUrl(merchantName || logoFileName);
|
||||
return of(defaultLogo);
|
||||
}
|
||||
|
||||
// Vérifier le cache normal
|
||||
if (this.logoUrlCache.has(logoFileName)) {
|
||||
return of(this.logoUrlCache.get(logoFileName)!);
|
||||
}
|
||||
|
||||
// Récupérer l'URL depuis MinIO
|
||||
return this.minioService.getMerchantLogoUrl(logoFileName).pipe(
|
||||
tap(url => {
|
||||
// Mettre en cache
|
||||
this.logoUrlCache.set(logoFileName, url);
|
||||
}),
|
||||
catchError(error => {
|
||||
// En cas d'erreur, ajouter au cache d'erreur et retourner le logo par défaut
|
||||
this.logoErrorCache.add(logoFileName);
|
||||
const defaultLogo = this.getDefaultLogoUrl(merchantName || logoFileName);
|
||||
return of(defaultLogo);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère une URL de logo par défaut basée sur les initiales
|
||||
*/
|
||||
getDefaultLogoUrl(merchantName: string): string {
|
||||
|
||||
// Créer des initiales significatives
|
||||
const initials = this.extractInitials(merchantName);
|
||||
|
||||
// Palette de couleurs agréables
|
||||
const colors = [
|
||||
'FF6B6B', '4ECDC4', '45B7D1', '96CEB4', 'FFEAA7',
|
||||
'DDA0DD', '98D8C8', 'F7DC6F', 'BB8FCE', '85C1E9'
|
||||
];
|
||||
|
||||
const colorIndex = merchantName.length % colors.length;
|
||||
const backgroundColor = colors[colorIndex];
|
||||
const textColor = 'FFFFFF'; // Blanc pour contraste
|
||||
|
||||
return `https://ui-avatars.com/api/?name=${encodeURIComponent(initials)}&background=${backgroundColor}&color=${textColor}&size=200&bold=true&font-size=0.5`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extrait les initiales de manière intelligente
|
||||
*/
|
||||
private extractInitials(name: string): string {
|
||||
// Nettoyer le nom
|
||||
const cleanedName = name.trim().toUpperCase();
|
||||
|
||||
// Extraire les mots (ignorer les articles, prépositions courtes)
|
||||
const words = cleanedName.split(/\s+/);
|
||||
|
||||
// Si un seul mot, prendre les deux premières lettres
|
||||
if (words.length === 1) {
|
||||
return words[0].substring(0, 2);
|
||||
}
|
||||
|
||||
// Prendre la première lettre des deux premiers mots significatifs
|
||||
const initials = words
|
||||
.filter(word => word.length > 2) // Ignorer les mots courts
|
||||
.slice(0, 2) // Prendre maximum 2 mots
|
||||
.map(word => word[0])
|
||||
.join('');
|
||||
|
||||
return initials || name.substring(0, 2).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge les permissions de l'utilisateur courant
|
||||
*/
|
||||
|
||||
@ -176,16 +176,69 @@
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- ==================== MODAL CRÉATION - SECTION LOGO ==================== -->
|
||||
<!-- À ajouter dans votre modal de création de marchand -->
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Logo URL</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="https://exemple.com/logo.png"
|
||||
[(ngModel)]="newMerchant.logo"
|
||||
name="logo"
|
||||
[disabled]="creatingMerchant"
|
||||
>
|
||||
<div class="form-group logo-upload-section">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-image"></i> Logo du marchand (optionnel)
|
||||
</label>
|
||||
|
||||
<div class="logo-upload-container">
|
||||
<!-- Zone de prévisualisation -->
|
||||
<div class="logo-preview-area" *ngIf="logoPreviewUrl || !selectedLogoFile">
|
||||
<div class="logo-preview" *ngIf="logoPreviewUrl">
|
||||
<img [src]="logoPreviewUrl" alt="Prévisualisation du logo">
|
||||
<button
|
||||
type="button"
|
||||
class="btn-remove-preview"
|
||||
(click)="removeSelectedLogo()"
|
||||
title="Supprimer"
|
||||
>
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="logo-placeholder" *ngIf="!logoPreviewUrl">
|
||||
<i class="fas fa-image fa-3x"></i>
|
||||
<p>Aucun logo sélectionné</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bouton de sélection -->
|
||||
<div class="logo-upload-actions">
|
||||
<input
|
||||
type="file"
|
||||
id="logoInput"
|
||||
accept="image/*"
|
||||
(change)="onLogoSelected($event)"
|
||||
style="display: none;"
|
||||
/>
|
||||
<label for="logoInput" class="btn btn-outline-primary btn-select-logo">
|
||||
<i class="fas fa-upload"></i>
|
||||
{{ logoPreviewUrl ? 'Changer le logo' : 'Sélectionner un logo' }}
|
||||
</label>
|
||||
|
||||
<small class="text-muted d-block mt-2">
|
||||
Formats: JPG, PNG, GIF, WebP, SVG | Taille max: 2MB
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Erreur upload logo -->
|
||||
<div class="alert alert-danger mt-2" *ngIf="logoUploadError">
|
||||
<i class="fas fa-exclamation-circle"></i> {{ logoUploadError }}
|
||||
</div>
|
||||
|
||||
<!-- Indicateur d'upload -->
|
||||
<div class="upload-progress" *ngIf="uploadingLogo">
|
||||
<div class="spinner-border spinner-border-sm" role="status">
|
||||
<span class="sr-only">Upload en cours...</span>
|
||||
</div>
|
||||
<span class="ml-2">Upload du logo en cours...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
@ -517,15 +570,79 @@
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Logo URL</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
[(ngModel)]="selectedMerchantForEdit.logo"
|
||||
name="logo"
|
||||
[disabled]="updatingMerchant"
|
||||
placeholder="https://exemple.com/logo.png"
|
||||
>
|
||||
<div class="form-group logo-edit-section">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-image"></i> Logo du marchand
|
||||
</label>
|
||||
|
||||
<div class="logo-edit-container">
|
||||
<!-- Logo actuel ou nouveau -->
|
||||
<div class="logo-display-area">
|
||||
<!-- Nouveau logo sélectionné -->
|
||||
<div class="logo-preview" *ngIf="editLogoPreviewUrl">
|
||||
<img [src]="editLogoPreviewUrl" alt="Nouveau logo">
|
||||
<div class="logo-badge badge-new">Nouveau</div>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-remove-preview"
|
||||
(click)="cancelEditLogo()"
|
||||
title="Annuler"
|
||||
>
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Logo actuel -->
|
||||
<div class="logo-preview" *ngIf="!editLogoPreviewUrl && currentLogoUrl">
|
||||
<img [src]="currentLogoUrl" alt="Logo actuel">
|
||||
<div class="logo-badge badge-current">Actuel</div>
|
||||
</div>
|
||||
|
||||
<!-- Pas de logo -->
|
||||
<div class="logo-placeholder" *ngIf="!editLogoPreviewUrl && !currentLogoUrl">
|
||||
<i class="fas fa-image fa-3x"></i>
|
||||
<p>Aucun logo</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="logo-edit-actions">
|
||||
<input
|
||||
type="file"
|
||||
id="editLogoInput"
|
||||
accept="image/*"
|
||||
(change)="onEditLogoSelected($event)"
|
||||
style="display: none;"
|
||||
/>
|
||||
<label for="editLogoInput" class="btn btn-outline-primary btn-change-logo">
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
{{ currentLogoUrl ? 'Changer le logo' : 'Ajouter un logo' }}
|
||||
</label>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-danger btn-remove-logo"
|
||||
*ngIf="currentLogoUrl && !editLogoPreviewUrl"
|
||||
(click)="selectedMerchantForEdit!.logo = ''; currentLogoUrl = null"
|
||||
>
|
||||
<i class="fas fa-trash"></i>
|
||||
Supprimer le logo
|
||||
</button>
|
||||
|
||||
<small class="text-muted d-block mt-2">
|
||||
Formats: JPG, PNG, GIF, WebP, SVG | Taille max: 2MB
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Indicateur d'upload -->
|
||||
<div class="upload-progress" *ngIf="uploadingLogo">
|
||||
<div class="spinner-border spinner-border-sm" role="status">
|
||||
<span class="sr-only">Upload en cours...</span>
|
||||
</div>
|
||||
<span class="ml-2">Upload du nouveau logo en cours...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
|
||||
@ -3,7 +3,8 @@ import { CommonModule } from '@angular/common';
|
||||
import { FormsModule, ReactiveFormsModule, FormBuilder, Validators, FormArray, FormGroup } from '@angular/forms';
|
||||
import { NgIcon } from '@ng-icons/core';
|
||||
import { NgbNavModule, NgbModal, NgbModalModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { catchError, finalize, map, of, Subject, takeUntil } from 'rxjs';
|
||||
import { catchError, finalize, map, Observable, of, Subject, takeUntil, tap } from 'rxjs';
|
||||
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
||||
|
||||
import { MerchantConfigService } from './merchant-config.service';
|
||||
import { RoleManagementService } from '@core/services/hub-users-roles-management.service';
|
||||
@ -13,6 +14,8 @@ import { PageTitle } from '@app/components/page-title/page-title';
|
||||
import { MerchantConfigsList } from './merchant-config-list/merchant-config-list';
|
||||
import { MerchantConfigView } from './merchant-config-view/merchant-config-view';
|
||||
|
||||
import { MinioService } from '@core/services/minio.service';
|
||||
|
||||
import {
|
||||
CreateMerchantDto,
|
||||
MerchantUtils,
|
||||
@ -47,12 +50,17 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
||||
private modalService = inject(NgbModal);
|
||||
private authService = inject(AuthService);
|
||||
private merchantConfigService = inject(MerchantConfigService);
|
||||
private merchantSyncService = inject(MerchantSyncService);
|
||||
private dataAdapter = inject(MerchantDataAdapter);
|
||||
protected roleService = inject(RoleManagementService);
|
||||
private cdRef = inject(ChangeDetectorRef);
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
private minioService = inject(MinioService);
|
||||
private sanitizer = inject(DomSanitizer);
|
||||
|
||||
// Cache des URLs de logos
|
||||
private logoUrlCache = new Map<string, string>();
|
||||
|
||||
// Configuration
|
||||
readonly UserRole = UserRole;
|
||||
readonly ConfigType = ConfigType;
|
||||
@ -63,6 +71,20 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
||||
pageSubtitle: string = 'Administrez les marchands et leurs configurations techniques';
|
||||
badge: any = { icon: 'lucideSettings', text: 'Merchant Management' };
|
||||
|
||||
// ==================== GESTION DES LOGOS ====================
|
||||
|
||||
// Logo pour création
|
||||
selectedLogoFile: File | null = null;
|
||||
logoPreviewUrl: string | null = null;
|
||||
uploadingLogo = false;
|
||||
logoUploadError = '';
|
||||
|
||||
// Logo pour édition
|
||||
editLogoFile: File | null = null;
|
||||
editLogoPreviewUrl: string | null = null;
|
||||
currentLogoUrl: string | null = null;
|
||||
logoChanged = false;
|
||||
|
||||
// État de l'interface
|
||||
activeTab: 'list' | 'merchant-profile' = 'list';
|
||||
selectedMerchantId: number | null = null;
|
||||
@ -594,6 +616,7 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
this.resetMerchantForm();
|
||||
this.removeSelectedLogo();
|
||||
this.createMerchantError = '';
|
||||
this.openModal(this.createMerchantModal);
|
||||
}
|
||||
@ -655,9 +678,125 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
createMerchant(): void {
|
||||
this.createMerchantWithLogo();
|
||||
}
|
||||
|
||||
updateMerchant(): void {
|
||||
this.updateMerchantWithLogo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère une URL de logo par défaut basée sur les initiales
|
||||
*/
|
||||
getDefaultLogoUrl(merchantName: string): string {
|
||||
// Créer un avatar avec les initiales
|
||||
const initials = merchantName
|
||||
.split(' ')
|
||||
.map(word => word[0])
|
||||
.join('')
|
||||
.substring(0, 2)
|
||||
.toUpperCase();
|
||||
|
||||
// Utiliser un service comme UI Avatars
|
||||
return `https://ui-avatars.com/api/?name=${encodeURIComponent(initials)}&background=random&size=200&bold=true`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formate la taille du fichier
|
||||
*/
|
||||
formatFileSize(bytes: number): string {
|
||||
if (bytes === 0) return '0 B';
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si un logo existe et est valide
|
||||
*/
|
||||
hasValidLogo(merchant: Merchant): boolean {
|
||||
return !!(merchant.logo && merchant.logo.trim().length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nettoie les ressources au changement de marchand
|
||||
*/
|
||||
private cleanupLogoResources(): void {
|
||||
// Nettoyer les URLs de prévisualisation
|
||||
if (this.logoPreviewUrl && this.logoPreviewUrl.startsWith('blob:')) {
|
||||
URL.revokeObjectURL(this.logoPreviewUrl);
|
||||
}
|
||||
if (this.editLogoPreviewUrl && this.editLogoPreviewUrl.startsWith('blob:')) {
|
||||
URL.revokeObjectURL(this.editLogoPreviewUrl);
|
||||
}
|
||||
|
||||
this.logoPreviewUrl = null;
|
||||
this.editLogoPreviewUrl = null;
|
||||
this.selectedLogoFile = null;
|
||||
this.editLogoFile = null;
|
||||
}
|
||||
|
||||
// ==================== OPÉRATIONS CRUD ====================
|
||||
|
||||
createMerchant(): void {
|
||||
// ==================== GESTION DU LOGO LORS DE LA CRÉATION ====================
|
||||
|
||||
/**
|
||||
* Gère la sélection du logo lors de la création
|
||||
*/
|
||||
onLogoSelected(event: Event): void {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (!input.files || input.files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const file = input.files[0];
|
||||
|
||||
// Validation du fichier
|
||||
const validation = this.minioService.validateImageFile(file);
|
||||
if (!validation.valid) {
|
||||
this.logoUploadError = validation.error || 'Fichier invalide';
|
||||
this.selectedLogoFile = null;
|
||||
this.logoPreviewUrl = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedLogoFile = file;
|
||||
this.logoUploadError = '';
|
||||
|
||||
// Générer la prévisualisation
|
||||
this.minioService.previewImage(file).then(
|
||||
(dataUrl) => {
|
||||
this.logoPreviewUrl = dataUrl;
|
||||
this.cdRef.detectChanges();
|
||||
},
|
||||
(error) => {
|
||||
console.error('Erreur prévisualisation:', error);
|
||||
this.logoUploadError = 'Impossible de prévisualiser l\'image';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime le logo sélectionné pour la création
|
||||
*/
|
||||
removeSelectedLogo(): void {
|
||||
this.selectedLogoFile = null;
|
||||
this.logoPreviewUrl = null;
|
||||
this.logoUploadError = '';
|
||||
|
||||
// Reset l'input file
|
||||
const fileInput = document.getElementById('logoInput') as HTMLInputElement;
|
||||
if (fileInput) {
|
||||
fileInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload le logo et crée le marchand
|
||||
*/
|
||||
createMerchantWithLogo(): void {
|
||||
if (!this.canCreateMerchants) {
|
||||
this.createMerchantError = 'Vous n\'avez pas la permission de créer des marchands';
|
||||
return;
|
||||
@ -672,7 +811,41 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
||||
this.creatingMerchant = true;
|
||||
this.createMerchantError = '';
|
||||
|
||||
// Conversion pour l'API
|
||||
// Si un logo est sélectionné, l'uploader d'abord
|
||||
if (this.selectedLogoFile) {
|
||||
|
||||
this.uploadingLogo = true;
|
||||
this.minioService.uploadMerchantLogo(this.selectedLogoFile)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe({
|
||||
next: (uploadResponse) => {
|
||||
console.log('✅ Logo uploaded:', uploadResponse);
|
||||
this.uploadingLogo = false;
|
||||
|
||||
// Ajouter le nom du fichier au DTO
|
||||
this.newMerchant.logo = uploadResponse.fileName;
|
||||
|
||||
// Créer le marchand avec le logo
|
||||
this.createMerchantApiCall();
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('❌ Error uploading logo:', error);
|
||||
this.uploadingLogo = false;
|
||||
this.creatingMerchant = false;
|
||||
this.createMerchantError = 'Erreur lors de l\'upload du logo: ' + (error.message || 'Erreur inconnue');
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Pas de logo, créer directement
|
||||
this.createMerchantApiCall();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appel API pour créer le marchand
|
||||
*/
|
||||
private createMerchantApiCall(): void {
|
||||
const createDto = this.convertMerchantToBackend(this.newMerchant);
|
||||
|
||||
console.log('📤 Creating merchant:', createDto);
|
||||
@ -681,13 +854,17 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe({
|
||||
next: (createdMerchant) => {
|
||||
// Conversion de la réponse pour Angular
|
||||
const frontendMerchant = this.convertMerchantToFrontend(createdMerchant);
|
||||
|
||||
console.log('✅ Merchant created successfully:', frontendMerchant);
|
||||
this.creatingMerchant = false;
|
||||
this.modalService.dismissAll();
|
||||
this.refreshMerchantsList();
|
||||
|
||||
// Reset le formulaire et le logo
|
||||
this.resetMerchantForm();
|
||||
this.removeSelectedLogo();
|
||||
|
||||
this.cdRef.detectChanges();
|
||||
},
|
||||
error: (error) => {
|
||||
@ -699,14 +876,69 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
// Mise à jour COMPLÈTE du merchant
|
||||
updateMerchant(): void {
|
||||
// ==================== GESTION DU LOGO LORS DE L'ÉDITION ====================
|
||||
|
||||
/**
|
||||
* Gère la sélection du nouveau logo lors de l'édition
|
||||
*/
|
||||
onEditLogoSelected(event: Event): void {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (!input.files || input.files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const file = input.files[0];
|
||||
|
||||
// Validation du fichier
|
||||
const validation = this.minioService.validateImageFile(file);
|
||||
if (!validation.valid) {
|
||||
this.updateMerchantError = validation.error || 'Fichier invalide';
|
||||
this.editLogoFile = null;
|
||||
this.editLogoPreviewUrl = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.editLogoFile = file;
|
||||
this.logoChanged = true;
|
||||
this.updateMerchantError = '';
|
||||
|
||||
// Générer la prévisualisation
|
||||
this.minioService.previewImage(file).then(
|
||||
(dataUrl) => {
|
||||
this.editLogoPreviewUrl = dataUrl;
|
||||
this.cdRef.detectChanges();
|
||||
},
|
||||
(error) => {
|
||||
console.error('Erreur prévisualisation:', error);
|
||||
this.updateMerchantError = 'Impossible de prévisualiser l\'image';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Annule le changement de logo lors de l'édition
|
||||
*/
|
||||
cancelEditLogo(): void {
|
||||
this.editLogoFile = null;
|
||||
this.editLogoPreviewUrl = null;
|
||||
this.logoChanged = false;
|
||||
|
||||
// Reset l'input file
|
||||
const fileInput = document.getElementById('editLogoInput') as HTMLInputElement;
|
||||
if (fileInput) {
|
||||
fileInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le marchand avec le nouveau logo
|
||||
*/
|
||||
updateMerchantWithLogo(): void {
|
||||
if (!this.selectedMerchantForEdit) {
|
||||
this.updateMerchantError = 'Aucun marchand sélectionné pour modification';
|
||||
return;
|
||||
}
|
||||
|
||||
// Validation des données complètes
|
||||
const validation = this.validateMerchantUpdate(this.selectedMerchantForEdit);
|
||||
if (!validation.isValid) {
|
||||
this.updateMerchantError = validation.errors.join(', ');
|
||||
@ -716,9 +948,56 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
||||
this.updatingMerchant = true;
|
||||
this.updateMerchantError = '';
|
||||
|
||||
// Conversion pour l'API avec TOUTES les données
|
||||
const merchantId = this.selectedMerchantForEdit.id!;
|
||||
const updateDto = this.convertUpdateMerchantToBackend(this.selectedMerchantForEdit, this.selectedMerchantForEdit);
|
||||
// Si un nouveau logo est sélectionné
|
||||
if (this.editLogoFile && this.logoChanged) {
|
||||
this.uploadingLogo = true;
|
||||
const merchantId = this.selectedMerchantForEdit.id!;
|
||||
|
||||
this.minioService.uploadMerchantLogo(this.editLogoFile, merchantId)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe({
|
||||
next: (uploadResponse) => {
|
||||
console.log('✅ New logo uploaded:', uploadResponse);
|
||||
this.uploadingLogo = false;
|
||||
|
||||
// Supprimer l'ancien logo si différent
|
||||
const oldLogo = this.selectedMerchantForEdit!.logo;
|
||||
if (oldLogo && oldLogo !== uploadResponse.fileName) {
|
||||
this.minioService.deleteMerchantLogo(oldLogo).subscribe({
|
||||
next: () => console.log('🗑️ Old logo deleted'),
|
||||
error: (err) => console.error('⚠️ Error deleting old logo:', err)
|
||||
});
|
||||
}
|
||||
|
||||
// Mettre à jour le logo dans le DTO
|
||||
this.selectedMerchantForEdit!.logo = uploadResponse.fileName;
|
||||
|
||||
// Mettre à jour le marchand
|
||||
this.updateMerchantApiCall();
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('❌ Error uploading new logo:', error);
|
||||
this.uploadingLogo = false;
|
||||
this.updatingMerchant = false;
|
||||
this.updateMerchantError = 'Erreur lors de l\'upload du logo: ' + (error.message || 'Erreur inconnue');
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Pas de changement de logo, mettre à jour directement
|
||||
this.updateMerchantApiCall();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appel API pour mettre à jour le marchand
|
||||
*/
|
||||
private updateMerchantApiCall(): void {
|
||||
const merchantId = this.selectedMerchantForEdit!.id!;
|
||||
const updateDto = this.convertUpdateMerchantToBackend(
|
||||
this.selectedMerchantForEdit!,
|
||||
this.selectedMerchantForEdit!
|
||||
);
|
||||
|
||||
console.log('📤 Updating merchant with full data:', updateDto);
|
||||
|
||||
@ -726,7 +1005,6 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe({
|
||||
next: (updatedMerchant) => {
|
||||
// Conversion pour Angular
|
||||
const frontendMerchant = this.convertMerchantToFrontend(updatedMerchant);
|
||||
|
||||
this.updatingMerchant = false;
|
||||
@ -737,15 +1015,22 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
||||
// Mettre à jour le cache
|
||||
if (this.selectedMerchantId) {
|
||||
this.merchantProfiles[this.selectedMerchantId] = frontendMerchant;
|
||||
|
||||
// Invalider le cache de l'URL du logo
|
||||
if (frontendMerchant.logo) {
|
||||
this.logoUrlCache.delete(frontendMerchant.logo);
|
||||
}
|
||||
}
|
||||
|
||||
// Mettre à jour le marchand de l'utilisateur si nécessaire
|
||||
if (this.isMerchantUser && this.userMerchantId === merchantId) {
|
||||
this.userMerchant = frontendMerchant;
|
||||
}
|
||||
|
||||
this.successMessage = 'Marchand modifié avec succès';
|
||||
|
||||
|
||||
// Reset les états du logo
|
||||
this.cancelEditLogo();
|
||||
|
||||
this.cdRef.detectChanges();
|
||||
},
|
||||
error: (error) => {
|
||||
@ -757,6 +1042,47 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== AFFICHAGE DU LOGO ====================
|
||||
|
||||
/**
|
||||
* Récupère l'URL du logo pour affichage
|
||||
*/
|
||||
getMerchantLogoUrl(logoFileName: string): Observable<string> {
|
||||
// Vérifier le cache
|
||||
if (this.logoUrlCache.has(logoFileName)) {
|
||||
return of(this.logoUrlCache.get(logoFileName)!);
|
||||
}
|
||||
|
||||
// Récupérer l'URL depuis MinIO
|
||||
return this.minioService.getMerchantLogoUrl(logoFileName).pipe(
|
||||
tap(url => {
|
||||
// Mettre en cache
|
||||
this.logoUrlCache.set(logoFileName, url);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge le logo pour l'édition
|
||||
*/
|
||||
loadMerchantLogoForEdit(merchant: Merchant): void {
|
||||
if (!merchant.logo) {
|
||||
this.currentLogoUrl = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.getMerchantLogoUrl(merchant.logo).subscribe({
|
||||
next: (url) => {
|
||||
this.currentLogoUrl = url;
|
||||
this.cdRef.detectChanges();
|
||||
},
|
||||
error: (error) => {
|
||||
console.error('Error loading logo:', error);
|
||||
this.currentLogoUrl = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Validation complète pour la mise à jour
|
||||
validateMerchantUpdate(merchant: UpdateMerchantDto): { isValid: boolean; errors: string[] } {
|
||||
const errors: string[] = [];
|
||||
@ -891,19 +1217,30 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
||||
};
|
||||
}
|
||||
|
||||
private resetMerchantForm(): void {
|
||||
this.newMerchant = this.getDefaultMerchantForm();
|
||||
console.log('🔄 Merchant form reset');
|
||||
}
|
||||
|
||||
/**
|
||||
* Override de populateEditForm pour charger le logo
|
||||
*/
|
||||
private populateEditForm(merchant: Merchant): void {
|
||||
this.selectedMerchantForEdit = {
|
||||
...merchant,
|
||||
configs: merchant.configs.map(config => ({ ...config })),
|
||||
technicalContacts: merchant.technicalContacts.map(contact => ({ ...contact }))
|
||||
};
|
||||
|
||||
// Charger le logo actuel
|
||||
this.loadMerchantLogoForEdit(merchant);
|
||||
this.editLogoFile = null;
|
||||
this.editLogoPreviewUrl = null;
|
||||
this.logoChanged = false;
|
||||
}
|
||||
|
||||
private resetMerchantForm(): void {
|
||||
this.newMerchant = this.getDefaultMerchantForm();
|
||||
this.removeSelectedLogo();
|
||||
console.log('🔄 Merchant form reset');
|
||||
}
|
||||
|
||||
private refreshMerchantsList(): void {
|
||||
if (this.merchantConfigsList && typeof this.merchantConfigsList.refreshData === 'function') {
|
||||
console.log('🔄 Refreshing merchants list...');
|
||||
@ -1025,8 +1362,14 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
||||
return !this.isMerchantUser && this.activeTab === 'list';
|
||||
}
|
||||
|
||||
// ==================== NETTOYAGE ====================
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
|
||||
// Nettoyer les logos
|
||||
this.cleanupLogoResources();
|
||||
this.logoUrlCache.clear();
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@ export const environment = {
|
||||
production: true,
|
||||
localServiceTestApiUrl: "https://backoffice.dcb.pixpay.sn/api/v1",
|
||||
iamApiUrl: "https://api-user-service.dcb.pixpay.sn/api/v1",
|
||||
configApiUrl: "https://api-merchant-config-service.dcb.pixpay.sn/api/v1",
|
||||
apiCoreUrl: "https://api-core-service.dcb.pixpay.sn/api/v1",
|
||||
reportingApiUrl: "https://api-reporting-service.dcb.pixpay.sn/api/v1/",
|
||||
configApiUrl: 'https://api-merchant-config-service.dcb.pixpay.sn/api/v1',
|
||||
apiCoreUrl: 'https://api-core-service.dcb.pixpay.sn/api/v1',
|
||||
reportingApiUrl: 'https://api-reporting-service.dcb.pixpay.sn/api/v1/'
|
||||
};
|
||||
|
||||
@ -4,5 +4,5 @@ export const environment = {
|
||||
iamApiUrl: "https://api-user-service.dcb.pixpay.sn/api/v1",
|
||||
configApiUrl: "https://api-merchant-config-service.dcb.pixpay.sn/api/v1",
|
||||
apiCoreUrl: "https://api-core-service.dcb.pixpay.sn/api/v1",
|
||||
reportingApiUrl: "https://api-reporting-service.dcb.pixpay.sn/api/v1/",
|
||||
reportingApiUrl: "https://api-reporting-service.dcb.pixpay.sn/api/v1/"
|
||||
};
|
||||
|
||||
@ -4,5 +4,5 @@ export const environment = {
|
||||
iamApiUrl: "http://localhost:3001/api/v1",
|
||||
configApiUrl: "http://localhost:3000/api/v1",
|
||||
apiCoreUrl: "https://api-core-service.dcb.pixpay.sn/api/v1",
|
||||
reportingApiUrl: "https://api-reporting-service.dcb.pixpay.sn/api/v1/",
|
||||
reportingApiUrl: "https://api-reporting-service.dcb.pixpay.sn/api/v1/"
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user