Made general rules for processing json db
This commit is contained in:
parent
c558565b11
commit
90a8766b15
7
client/demo/POST.mods.json
Normal file
7
client/demo/POST.mods.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"POST": {
|
||||
"constellation/devices": {},
|
||||
"constellation/connect": {},
|
||||
"constellation/block": {}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,17 @@
|
|||
{
|
||||
"GET": {
|
||||
"login": {},
|
||||
"me": {},
|
||||
"logout": {},
|
||||
"config": {},
|
||||
"restart": {},
|
||||
"can-send-email": {
|
||||
"data": {
|
||||
"canSendEmail": true
|
||||
}
|
||||
},
|
||||
"get-backup": {
|
||||
"_comment": "TODO: Not Implemented"
|
||||
},
|
||||
"constellation/devices": {
|
||||
"data": [
|
||||
"constellation": {
|
||||
"devices": [
|
||||
{
|
||||
"nickname": "admin",
|
||||
"deviceName": "phone",
|
||||
|
@ -51,18 +48,14 @@
|
|||
"blocked": false,
|
||||
"fingerprint": "..."
|
||||
}
|
||||
]
|
||||
},
|
||||
"constellation/restart": {},
|
||||
"constellation/reset": {},
|
||||
"constellation/config": {
|
||||
"data": "pki:\n ca: /config/ca.crt\n cert: /config/cosmos.crt\n key: /config/cosmos.key\n blocklist: []\nstatic_host_map:\n 192.168.201.1:\n - vpn.domain.com:4242\nlighthouse:\n am_lighthouse: true\n interval: 60\n hosts: []\nlisten:\n host: 0.0.0.0\n port: 4242\npunchy:\n punch: true\n respond: true\nrelay:\n am_relay: true\n use_relays: true\n relays: []\ntun:\n disabled: false\n dev: nebula1\n drop_local_broadcast: false\n drop_multicast: false\n tx_queue: 500\n mtu: 1300\n routes: []\n unsafe_routes: []\nlogging:\n level: info\n format: text\nfirewall:\n outbound_action: drop\n inbound_action: drop\n conntrack:\n tcp_timeout: 12m\n udp_timeout: 3m\n default_timeout: 10m\n outbound:\n - port: any\n proto: any\n host: any\n inbound:\n - port: any\n proto: any\n host: any\n"
|
||||
},
|
||||
"constellation/logs": {
|
||||
"data": "Some logs..."
|
||||
],
|
||||
"restart": {},
|
||||
"reset": {},
|
||||
"config": ["$return", "pki:\n ca: /config/ca.crt\n cert: /config/cosmos.crt\n key: /config/cosmos.key\n blocklist: []\nstatic_host_map:\n 192.168.201.1:\n - vpn.domain.com:4242\nlighthouse:\n am_lighthouse: true\n interval: 60\n hosts: []\nlisten:\n host: 0.0.0.0\n port: 4242\npunchy:\n punch: true\n respond: true\nrelay:\n am_relay: true\n use_relays: true\n relays: []\ntun:\n disabled: false\n dev: nebula1\n drop_local_broadcast: false\n drop_multicast: false\n tx_queue: 500\n mtu: 1300\n routes: []\n unsafe_routes: []\nlogging:\n level: info\n format: text\nfirewall:\n outbound_action: drop\n inbound_action: drop\n conntrack:\n tcp_timeout: 12m\n udp_timeout: 3m\n default_timeout: 10m\n outbound:\n - port: any\n proto: any\n host: any\n inbound:\n - port: any\n proto: any\n host: any\n"],
|
||||
"logs": ["$return", "Some logs..."]
|
||||
},
|
||||
"servapps": {
|
||||
"data": [
|
||||
"list": [
|
||||
{
|
||||
"Id": "a03ea9a3408b2831198f2192c628090193e9d5e8e4c4515f2c94d1cc26f6a7f5",
|
||||
"Names": [
|
||||
|
@ -1389,10 +1382,9 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"servapps/demo/logs": {
|
||||
"data": [
|
||||
],
|
||||
"demo": {
|
||||
"logs": [
|
||||
{
|
||||
"streamType": 1,
|
||||
"size": 157,
|
||||
|
@ -3893,10 +3885,12 @@
|
|||
"size": 127,
|
||||
"output": "2023-05-08T15:18:09.061182900Z [15:18:09] [INF] [3] Emby.Server.Implementations.ScheduledTasks.TaskManager: ExecuteQueuedTasks"
|
||||
}
|
||||
]
|
||||
],
|
||||
"secure": {},
|
||||
"manage": {}
|
||||
}
|
||||
},
|
||||
"volumes": {
|
||||
"data": {
|
||||
"Volumes": [
|
||||
{
|
||||
"CreatedAt": "2023-05-02T13:26:13Z",
|
||||
|
@ -4053,10 +4047,8 @@
|
|||
}
|
||||
],
|
||||
"Warnings": null
|
||||
}
|
||||
},
|
||||
"networks": {
|
||||
"data": [
|
||||
"networks": [
|
||||
{
|
||||
"Name": "test2",
|
||||
"Id": "6675d361c126d013dbc4cba07c140821879718af3d52f15a07aa94dabbffe124",
|
||||
|
@ -4222,30 +4214,6 @@
|
|||
"Options": {},
|
||||
"Labels": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
"servapps/demo/secure": {},
|
||||
"api/newDB": {},
|
||||
"servapps/demo/manage": {}
|
||||
},
|
||||
"POST": {
|
||||
"constellation/devices": {
|
||||
"data": {
|
||||
"CA": "-----BEGIN NEBULA CERTIFICATE-----\\....\n+dfE+ikL8jUh/n+C+....\\....\nZon/Dw==\n-----END NEBULA CERTIFICATE-----\n",
|
||||
"Config": "constellation_api_key: ...\nconstellation_device_name: test\nconstellation_local_dns_overwrite: true\nconstellation_local_dns_overwrite_address: 192.168.201.1\nconstellation_public_hostname: \"\"\nfirewall:\n conntrack:\n default_timeout: 10m\n tcp_timeout: 12m\n udp_timeout: 3m\n inbound:\n - host: any\n port: any\n proto: any\n inbound_action: drop\n outbound:\n - host: any\n port: any\n proto: any\n outbound_action: drop\nlighthouse:\n am_lighthouse: false\n hosts:\n - 192.168.201.1\n interval: 60\nlisten:\n host: 0.0.0.0\n port: \"4242\"\nlogging:\n format: text\n level: info\npki:\n blocklist: []\n ca: |\n -----BEGIN NEBULA CERTIFICATE-----\n ...\n +dfE+ikL8jUh/n+C+...\n .\n Zon/Dw==\n -----END NEBULA CERTIFICATE-----\n cert: |\n -----BEGIN NEBULA CERTIFICATE-----\n CmIKBHRlc3QSCoeSo4UMgP7//..\n ...+QwZSiBxLdKhjkCH+.../..\n ./hfL+....\n ..==\n -----END NEBULA CERTIFICATE-----\n key: |\n -----BEGIN NEBULA X25519 PRIVATE KEY-----\n nS39dWX7uo1rhTvP2yl2XonGx3fWEkpk+43thNrMu7U=\n -----END NEBULA X25519 PRIVATE KEY-----\npunchy:\n punch: true\n respond: true\nrelay:\n am_relay: false\n relays:\n - 192.168.201.1\n use_relays: true\nstatic_host_map:\n 192.168.201.1:\n - vpn.domain.com:4242\ntun:\n dev: nebula1\n disabled: false\n drop_local_broadcast: false\n drop_multicast: false\n mtu: 1300\n routes: []\n tx_queue: 500\n unsafe_routes: []\n",
|
||||
"DeviceName": "test",
|
||||
"IP": "192.168.201.7/24",
|
||||
"IsLighthouse": false,
|
||||
"IsRelay": true,
|
||||
"LighthousesList": [],
|
||||
"Nickname": "admin",
|
||||
"Port": "4242",
|
||||
"PrivateKey": "-----BEGIN NEBULA CERTIFICATE-----\\...//w8o3ZaFqQYwhdGFuAY6IGXmYRCr3z932Y....w\\..==\n-----END NEBULA CERTIFICATE-----\n",
|
||||
"PublicHostname": "",
|
||||
"PublicKey": "-----BEGIN NEBULA X25519 PRIVATE KEY-----\nnS39dWX...hTvP......+43thNrMu7U=\n-----END NEBULA X25519 PRIVATE KEY-----\n"
|
||||
}
|
||||
},
|
||||
"constellation/connect": {},
|
||||
"constellation/block": {}
|
||||
}
|
||||
],
|
||||
"newDB": {}
|
||||
}
|
|
@ -1,11 +1,87 @@
|
|||
import { Request, Response } from "express"
|
||||
import { resolve } from "path"
|
||||
import { readFileSync } from "fs"
|
||||
import jsonServer from "json-server"
|
||||
import traverse from "traverse"
|
||||
|
||||
const PROXY_PORT = process.env.PROXY_PORT || 9000
|
||||
|
||||
const proxyServer = jsonServer.create()
|
||||
const router = jsonServer.router("db.json")
|
||||
const middlewares = jsonServer.defaults()
|
||||
const definedDB: Record<string, any> = JSON.parse(readFileSync(resolve(__dirname, "db.json"), {
|
||||
encoding: "utf8"
|
||||
}))
|
||||
|
||||
const autoDB = traverse(definedDB)
|
||||
.paths()
|
||||
.map(pathArray => ({
|
||||
path: pathArray.join("."),
|
||||
data: pathArray.reduce((db, pathSegment) => db[pathSegment], definedDB)
|
||||
}))
|
||||
.map(dbEntry => (dbEntry.data = isObject(dbEntry.data) ? dbEntry.data : ["$return", dbEntry.data], dbEntry))
|
||||
.reduce((acc, dbEntry) => (acc[dbEntry.path] = dbEntry.data, acc), {} as Record<string, any>)
|
||||
const db = Object.assign({}, autoDB, definedDB)
|
||||
const dbPaths = Object.keys(db)
|
||||
|
||||
// TODO: Reference types do not work at the ["$return", data] level
|
||||
// A possible solution is to replace filter with map and custom behavior for get, post and put
|
||||
// defining them to interact with the parent object
|
||||
|
||||
const definedRoutes: Record<string, string> = JSON.parse(readFileSync(resolve(__dirname, "routes.json"), {
|
||||
encoding: "utf8"
|
||||
}))
|
||||
|
||||
const autoRoutes = dbPaths
|
||||
.filter(path => path.includes("."))
|
||||
.map(path => ({
|
||||
origin: "/" + path.replace(/\./g, "/"),
|
||||
destination: "/" + path
|
||||
}))
|
||||
.reduce((acc, bind) => (acc[bind.origin] = bind.destination, acc), {} as Record<string, string>)
|
||||
const routes = Object.assign({}, autoRoutes, definedRoutes)
|
||||
|
||||
const proxyServer = jsonServer.create()
|
||||
const middlewares = jsonServer.defaults()
|
||||
const rewriter = jsonServer.rewriter(routes)
|
||||
const router = jsonServer.router(db, {
|
||||
foreignKeySuffix: "Id"
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
router.render = (req: Request, res: Response) => {
|
||||
const reqPath = req.path.replace("/", "")
|
||||
const matchedPath = dbPaths
|
||||
.map(path => path.replace(/\./g, "/"))
|
||||
.find(path => path !== reqPath && reqPath.startsWith(path))
|
||||
console.log(matchedPath)
|
||||
|
||||
if (typeof res.locals.data["_comment"] === "string" && res.locals.data["_comment"].toLowerCase().includes("todo"))
|
||||
return res.sendStatus(404)
|
||||
else if (Array.isArray(res.locals.data.list))
|
||||
res.locals.data = res.locals.data.list;
|
||||
else if (res.locals.data[0] === "$return")
|
||||
res.locals.data = res.locals.data[1]
|
||||
|
||||
switch (req.method) {
|
||||
case "GET":
|
||||
res.locals.result = res.locals.data
|
||||
break
|
||||
case "PUT": case "POST": default:
|
||||
res.locals.result = req.body
|
||||
break
|
||||
}
|
||||
|
||||
return res.jsonp({
|
||||
data: res.locals.result,
|
||||
status: "ok"
|
||||
})
|
||||
}
|
||||
|
||||
proxyServer.use(jsonServer.bodyParser)
|
||||
proxyServer.use(middlewares)
|
||||
|
||||
proxyServer.use("/api", rewriter)
|
||||
proxyServer.use("/api", router)
|
||||
proxyServer.listen(PROXY_PORT, () => console.info(`JSON proxy is running on ${PROXY_PORT} port`))
|
||||
|
||||
function isObject(value: unknown) {
|
||||
return typeof value === 'object' && value !== null
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
{
|
||||
"/servapps/:container": "/servapps?data.Id=:container",
|
||||
"/servapps/:container/logs": "/servapps/demo/logs",
|
||||
"/volume/:name": "/volumes?data.Volumes.Name=:name",
|
||||
"/network/:name": "/networks?data.Name=:name",
|
||||
"/volume/:name": "/volumes/Volumes?Volumes.Name=:name",
|
||||
"/network/:name": "/networks?Name=:name",
|
||||
"/servapps/:id/secure/:res": "/servapps/demo/secure",
|
||||
"/servapps/:id/manage/:action": "/servapps/demo/manage",
|
||||
"/servapps/:id/update": "/servapps/:id",
|
||||
"/servapps/:id/networks": "/servapps/:id?_embed=data.NetworkSettings.Networks"
|
||||
"/servapps/:id/networks": "/servapps/:id?_embed=NetworkSettings.Networks",
|
||||
"/servapps/:containerId/network/:networkId": "/servapps/:containerId/networks"
|
||||
}
|
40
package-lock.json
generated
40
package-lock.json
generated
|
@ -51,6 +51,7 @@
|
|||
"simplebar": "^5.3.8",
|
||||
"simplebar-react": "^2.4.1",
|
||||
"timeago.js": "^4.0.2",
|
||||
"traverse": "^0.6.7",
|
||||
"web-vitals": "^3.0.2",
|
||||
"whiskers": "^0.4.0",
|
||||
"yup": "^0.32.11"
|
||||
|
@ -72,6 +73,7 @@
|
|||
"@types/lodash.merge": "^4.6.9",
|
||||
"@types/node": "^20.10.2",
|
||||
"@types/react-color": "^3.0.10",
|
||||
"@types/traverse": "^0.6.35",
|
||||
"@types/webpack-bundle-analyzer": "^4.6.3",
|
||||
"babel-loader": "^9.1.3",
|
||||
"babel-plugin-direct-import": "^1.0.0",
|
||||
|
@ -3726,6 +3728,12 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/traverse": {
|
||||
"version": "0.6.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/traverse/-/traverse-0.6.35.tgz",
|
||||
"integrity": "sha512-ZZBG4X4CTVqKLpPvDtqxyCO7VsVAfOsvbJAdbM90uYrXRv27fAjbV8eVUtdBLivC6x6Gcx6n6Kvjbai3IX1rWw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/unist": {
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz",
|
||||
|
@ -4069,12 +4077,6 @@
|
|||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ajv-keywords": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
|
@ -4084,6 +4086,12 @@
|
|||
"ajv": "^6.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ansi-html-community": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
|
||||
|
@ -11187,9 +11195,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/json-server": {
|
||||
|
@ -14777,12 +14785,6 @@
|
|||
"ajv": "^8.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/seek-bzip": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz",
|
||||
|
@ -16152,6 +16154,14 @@
|
|||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/traverse": {
|
||||
"version": "0.6.7",
|
||||
"resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz",
|
||||
"integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/trim-newlines": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
"simplebar": "^5.3.8",
|
||||
"simplebar-react": "^2.4.1",
|
||||
"timeago.js": "^4.0.2",
|
||||
"traverse": "^0.6.7",
|
||||
"web-vitals": "^3.0.2",
|
||||
"whiskers": "^0.4.0",
|
||||
"yup": "^0.32.11"
|
||||
|
@ -83,9 +84,9 @@
|
|||
],
|
||||
"scripts": {
|
||||
"client": "vite",
|
||||
"webpack:setup-env": "cross-env withReport=$npm_config_with_report analyzeDeps=$npm_config_analyze_deps",
|
||||
"webpack:setup-env": "cross-env useProduction=$npm_config_use_production withReport=$npm_config_with_report analyzeDeps=$npm_config_analyze_deps",
|
||||
"webpack:build": "npm run webpack:setup-env -- webpack --config webpack.prod.js --progress",
|
||||
"webpack:start": "npm run webpack:setup-env -- webpack serve --open --config webpack.dev.js",
|
||||
"webpack:serve": "npm run webpack:setup-env -- webpack serve --open --config webpack.dev.js",
|
||||
"start": "env COSMOS_CONFIG_FOLDER=/mnt/e/work/Cosmos-Server/zz_test_config/ CONFIG_FILE=./config_dev.json EZ=UTC ACME_STAGING=true build/cosmos",
|
||||
"build": "sh build.sh",
|
||||
"dev": "npm run build && npm run start",
|
||||
|
@ -124,6 +125,7 @@
|
|||
"@types/lodash.merge": "^4.6.9",
|
||||
"@types/node": "^20.10.2",
|
||||
"@types/react-color": "^3.0.10",
|
||||
"@types/traverse": "^0.6.35",
|
||||
"@types/webpack-bundle-analyzer": "^4.6.3",
|
||||
"babel-loader": "^9.1.3",
|
||||
"babel-plugin-direct-import": "^1.0.0",
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"ts-node": {
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
}
|
||||
},
|
||||
"include": ["client/src/**/*"],
|
||||
"exclude": ["client/**/*.demo*"]
|
||||
}
|
|
@ -3,6 +3,7 @@ const { DuplicatesPlugin } = require("inspectpack/plugin")
|
|||
const { join } = require("path")
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin")
|
||||
|
||||
const withReport = process.env.withReport ? true : false
|
||||
const analyzeDeps = process.env.analyzeDeps ? true : false
|
||||
|
||||
|
|
|
@ -2,10 +2,11 @@ const { DefinePlugin } = require("webpack")
|
|||
const { merge } = require("webpack-merge")
|
||||
const { resolve } = require("path")
|
||||
const webpackCommon = require("./webpack.common.js")
|
||||
const webpackProd = require("./webpack.prod.js")
|
||||
|
||||
module.exports = merge(webpackCommon, {
|
||||
module.exports = merge(process.env.useProduction ? webpackProd : webpackCommon, {
|
||||
mode: "development",
|
||||
devtool: "inline-source-map",
|
||||
devtool: !process.env.useProduction ? "inline-source-map" : undefined,
|
||||
target: "web",
|
||||
devServer: {
|
||||
port: 3000,
|
||||
|
|
|
@ -28,11 +28,6 @@ const cacheGroups = {
|
|||
|
||||
module.exports = merge(webpackCommon, {
|
||||
mode: "production",
|
||||
plugins: [
|
||||
new IgnorePlugin({
|
||||
resourceRegExp: /[\d\D]*.demo[\d\D]*/
|
||||
})
|
||||
],
|
||||
optimization: {
|
||||
chunkIds: 'total-size',
|
||||
moduleIds: 'size',
|
||||
|
@ -53,10 +48,10 @@ module.exports = merge(webpackCommon, {
|
|||
terserOptions: {
|
||||
mangle: true,
|
||||
compress: true,
|
||||
sourceMap: false,
|
||||
format: {
|
||||
comments: false
|
||||
},
|
||||
sourceMap: false
|
||||
}
|
||||
}
|
||||
}),
|
||||
new ImageMinimizerPlugin({
|
||||
|
|
Loading…
Reference in a new issue