mirror of
https://github.com/apache/skywalking-booster-ui.git
synced 2025-08-02 15:31:56 +00:00
Merge branch 'main' into bugfix/traces
This commit is contained in:
commit
a351cb62fd
74
package-lock.json
generated
74
package-lock.json
generated
@ -18,7 +18,7 @@
|
|||||||
"vis-timeline": "^7.5.1",
|
"vis-timeline": "^7.5.1",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.2.45",
|
||||||
"vue-grid-layout": "^3.0.0-beta1",
|
"vue-grid-layout": "^3.0.0-beta1",
|
||||||
"vue-i18n": "^9.14.3",
|
"vue-i18n": "^9.14.5",
|
||||||
"vue-router": "^4.1.6",
|
"vue-router": "^4.1.6",
|
||||||
"vue-types": "^4.1.1"
|
"vue-types": "^4.1.1"
|
||||||
},
|
},
|
||||||
@ -1966,13 +1966,12 @@
|
|||||||
"integrity": "sha512-sZAW08CkqgvqRjUIaLRjScjObcCzN9D75yekLA21EClYAZIhi4A+GEt2z/WqOCOksTaEPLYmQyhkpXcboc0LhQ=="
|
"integrity": "sha512-sZAW08CkqgvqRjUIaLRjScjObcCzN9D75yekLA21EClYAZIhi4A+GEt2z/WqOCOksTaEPLYmQyhkpXcboc0LhQ=="
|
||||||
},
|
},
|
||||||
"node_modules/@intlify/core-base": {
|
"node_modules/@intlify/core-base": {
|
||||||
"version": "9.14.3",
|
"version": "9.14.5",
|
||||||
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.3.tgz",
|
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz",
|
||||||
"integrity": "sha512-nbJ7pKTlXFnaXPblyfiH6awAx1C0PWNNuqXAR74yRwgi5A/Re/8/5fErLY0pv4R8+EHj3ZaThMHdnuC/5OBa6g==",
|
"integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@intlify/message-compiler": "9.14.3",
|
"@intlify/message-compiler": "9.14.5",
|
||||||
"@intlify/shared": "9.14.3"
|
"@intlify/shared": "9.14.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16"
|
"node": ">= 16"
|
||||||
@ -1982,12 +1981,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@intlify/message-compiler": {
|
"node_modules/@intlify/message-compiler": {
|
||||||
"version": "9.14.3",
|
"version": "9.14.5",
|
||||||
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.3.tgz",
|
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz",
|
||||||
"integrity": "sha512-ANwC226BQdd+MpJ36rOYkChSESfPwu3Ss2Faw0RHTOknYLoHTX6V6e/JjIKVDMbzs0/H/df/rO6yU0SPiWHqNg==",
|
"integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@intlify/shared": "9.14.3",
|
"@intlify/shared": "9.14.5",
|
||||||
"source-map-js": "^1.0.2"
|
"source-map-js": "^1.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1998,10 +1996,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@intlify/shared": {
|
"node_modules/@intlify/shared": {
|
||||||
"version": "9.14.3",
|
"version": "9.14.5",
|
||||||
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.3.tgz",
|
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz",
|
||||||
"integrity": "sha512-hJXz9LA5VG7qNE00t50bdzDv8Z4q9fpcL81wj4y4duKavrv0KM8YNLTwXNEFINHjTsfrG9TXvPuEjVaAvZ7yWg==",
|
"integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==",
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16"
|
"node": ">= 16"
|
||||||
},
|
},
|
||||||
@ -13254,13 +13251,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vue-i18n": {
|
"node_modules/vue-i18n": {
|
||||||
"version": "9.14.3",
|
"version": "9.14.5",
|
||||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.3.tgz",
|
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz",
|
||||||
"integrity": "sha512-C+E0KE8ihKjdYCQx8oUkXX+8tBItrYNMnGJuzEPevBARQFUN2tKez6ZVOvBrWH0+KT5wEk3vOWjNk7ygb2u9ig==",
|
"integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@intlify/core-base": "9.14.3",
|
"@intlify/core-base": "9.14.5",
|
||||||
"@intlify/shared": "9.14.3",
|
"@intlify/shared": "9.14.5",
|
||||||
"@vue/devtools-api": "^6.5.0"
|
"@vue/devtools-api": "^6.5.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -14956,27 +14952,27 @@
|
|||||||
"integrity": "sha512-sZAW08CkqgvqRjUIaLRjScjObcCzN9D75yekLA21EClYAZIhi4A+GEt2z/WqOCOksTaEPLYmQyhkpXcboc0LhQ=="
|
"integrity": "sha512-sZAW08CkqgvqRjUIaLRjScjObcCzN9D75yekLA21EClYAZIhi4A+GEt2z/WqOCOksTaEPLYmQyhkpXcboc0LhQ=="
|
||||||
},
|
},
|
||||||
"@intlify/core-base": {
|
"@intlify/core-base": {
|
||||||
"version": "9.14.3",
|
"version": "9.14.5",
|
||||||
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.3.tgz",
|
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz",
|
||||||
"integrity": "sha512-nbJ7pKTlXFnaXPblyfiH6awAx1C0PWNNuqXAR74yRwgi5A/Re/8/5fErLY0pv4R8+EHj3ZaThMHdnuC/5OBa6g==",
|
"integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@intlify/message-compiler": "9.14.3",
|
"@intlify/message-compiler": "9.14.5",
|
||||||
"@intlify/shared": "9.14.3"
|
"@intlify/shared": "9.14.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@intlify/message-compiler": {
|
"@intlify/message-compiler": {
|
||||||
"version": "9.14.3",
|
"version": "9.14.5",
|
||||||
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.3.tgz",
|
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz",
|
||||||
"integrity": "sha512-ANwC226BQdd+MpJ36rOYkChSESfPwu3Ss2Faw0RHTOknYLoHTX6V6e/JjIKVDMbzs0/H/df/rO6yU0SPiWHqNg==",
|
"integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@intlify/shared": "9.14.3",
|
"@intlify/shared": "9.14.5",
|
||||||
"source-map-js": "^1.0.2"
|
"source-map-js": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@intlify/shared": {
|
"@intlify/shared": {
|
||||||
"version": "9.14.3",
|
"version": "9.14.5",
|
||||||
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.3.tgz",
|
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz",
|
||||||
"integrity": "sha512-hJXz9LA5VG7qNE00t50bdzDv8Z4q9fpcL81wj4y4duKavrv0KM8YNLTwXNEFINHjTsfrG9TXvPuEjVaAvZ7yWg=="
|
"integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ=="
|
||||||
},
|
},
|
||||||
"@jridgewell/gen-mapping": {
|
"@jridgewell/gen-mapping": {
|
||||||
"version": "0.3.8",
|
"version": "0.3.8",
|
||||||
@ -23097,12 +23093,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vue-i18n": {
|
"vue-i18n": {
|
||||||
"version": "9.14.3",
|
"version": "9.14.5",
|
||||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.3.tgz",
|
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz",
|
||||||
"integrity": "sha512-C+E0KE8ihKjdYCQx8oUkXX+8tBItrYNMnGJuzEPevBARQFUN2tKez6ZVOvBrWH0+KT5wEk3vOWjNk7ygb2u9ig==",
|
"integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@intlify/core-base": "9.14.3",
|
"@intlify/core-base": "9.14.5",
|
||||||
"@intlify/shared": "9.14.3",
|
"@intlify/shared": "9.14.5",
|
||||||
"@vue/devtools-api": "^6.5.0"
|
"@vue/devtools-api": "^6.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
"vis-timeline": "^7.5.1",
|
"vis-timeline": "^7.5.1",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.2.45",
|
||||||
"vue-grid-layout": "^3.0.0-beta1",
|
"vue-grid-layout": "^3.0.0-beta1",
|
||||||
"vue-i18n": "^9.14.3",
|
"vue-i18n": "^9.14.5",
|
||||||
"vue-router": "^4.1.6",
|
"vue-router": "^4.1.6",
|
||||||
"vue-types": "^4.1.1"
|
"vue-types": "^4.1.1"
|
||||||
},
|
},
|
||||||
|
@ -150,6 +150,7 @@ $theme-background: var(--theme-background);
|
|||||||
$active-background: var(--el-color-primary);
|
$active-background: var(--el-color-primary);
|
||||||
$font-size-smaller: 12px;
|
$font-size-smaller: 12px;
|
||||||
$font-size-normal: 14px;
|
$font-size-normal: 14px;
|
||||||
|
$error-color: #e66;
|
||||||
|
|
||||||
.opt:hover {
|
.opt:hover {
|
||||||
background-color: var(--sw-list-hover) !important;
|
background-color: var(--sw-list-hover) !important;
|
||||||
@ -208,9 +209,9 @@ div.vis-tooltip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.vis-item.Error {
|
.vis-item.Error {
|
||||||
background-color: #e66;
|
background-color: $error-color;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
border-color: #e66;
|
border-color: $error-color;
|
||||||
color: var(--sw-event-vis-selected) !important;
|
color: var(--sw-event-vis-selected) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,13 +13,14 @@ limitations under the License. -->
|
|||||||
<template>
|
<template>
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<span class="label">{{ t("iframeSrc") }}</span>
|
<span class="label">{{ t("iframeSrc") }}</span>
|
||||||
<el-input class="input" v-model="url" size="small" @change="changeConfig({ url: encodeURIComponent(url) })" />
|
<el-input class="input" v-model="url" size="small" @change="handleUrlChange" :class="{ error: urlError }" />
|
||||||
|
<div v-if="urlError" class="error-message">{{ urlError }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<el-button size="small" @click="cancelConfig">
|
<el-button size="small" @click="cancelConfig">
|
||||||
{{ t("cancel") }}
|
{{ t("cancel") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button size="small" type="primary" @click="applyConfig">
|
<el-button size="small" type="primary" @click="applyConfig" :disabled="!!urlError">
|
||||||
{{ t("apply") }}
|
{{ t("apply") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@ -28,24 +29,89 @@ limitations under the License. -->
|
|||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { useDashboardStore } from "@/store/modules/dashboard";
|
import { useDashboardStore } from "@/store/modules/dashboard";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
const originConfig = dashboardStore.selectedGrid;
|
const originConfig = dashboardStore.selectedGrid;
|
||||||
const widget = originConfig.widget || {};
|
const widget = originConfig.widget || {};
|
||||||
const url = ref(widget.url || "");
|
const url = ref(widget.url || "");
|
||||||
|
const urlError = ref("");
|
||||||
|
|
||||||
|
// URL validation function to prevent XSS
|
||||||
|
function validateAndSanitizeUrl(inputUrl: string): { isValid: boolean; sanitizedUrl: string; error: string } {
|
||||||
|
if (!inputUrl.trim()) {
|
||||||
|
return { isValid: true, sanitizedUrl: "", error: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create URL object to validate the URL format
|
||||||
|
const urlObj = new URL(inputUrl);
|
||||||
|
|
||||||
|
// Only allow HTTP and HTTPS protocols to prevent XSS
|
||||||
|
if (!["http:", "https:"].includes(urlObj.protocol)) {
|
||||||
|
return {
|
||||||
|
isValid: false,
|
||||||
|
sanitizedUrl: "",
|
||||||
|
error: "Only HTTP and HTTPS URLs are allowed",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional security checks
|
||||||
|
const dangerousProtocols = ["javascript:", "data:", "vbscript:", "le:"];
|
||||||
|
const lowerUrl = inputUrl.toLowerCase();
|
||||||
|
|
||||||
|
for (const protocol of dangerousProtocols) {
|
||||||
|
if (lowerUrl.includes(protocol)) {
|
||||||
|
return {
|
||||||
|
isValid: false,
|
||||||
|
sanitizedUrl: "",
|
||||||
|
error: "Dangerous protocols are not allowed",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the sanitized URL (using the URL object to normalize it)
|
||||||
|
return {
|
||||||
|
isValid: true,
|
||||||
|
sanitizedUrl: urlObj.href,
|
||||||
|
error: "",
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
isValid: false,
|
||||||
|
sanitizedUrl: "",
|
||||||
|
error: "Please enter a valid URL",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUrlChange() {
|
||||||
|
const validation = validateAndSanitizeUrl(url.value);
|
||||||
|
urlError.value = validation.error;
|
||||||
|
|
||||||
|
if (validation.isValid) {
|
||||||
|
changeConfig({ url: validation.sanitizedUrl });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function changeConfig(param: { [key: string]: string }) {
|
function changeConfig(param: { [key: string]: string }) {
|
||||||
const key = Object.keys(param)[0];
|
const key = Object.keys(param)[0];
|
||||||
if (!key) {
|
if (!key) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { selectedGrid } = dashboardStore;
|
const { selectedGrid } = dashboardStore;
|
||||||
const widget = {
|
const widget = {
|
||||||
...dashboardStore.selectedGrid.widget,
|
...dashboardStore.selectedGrid.widget,
|
||||||
[key]: decodeURIComponent(param[key]),
|
[key]: param[key], // Use the sanitized URL directly, no need for decodeURIComponent
|
||||||
};
|
};
|
||||||
dashboardStore.selectWidget({ ...selectedGrid, widget });
|
dashboardStore.selectWidget({ ...selectedGrid, widget });
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyConfig() {
|
function applyConfig() {
|
||||||
|
if (urlError.value) {
|
||||||
|
return; // Don't apply if there's a validation error
|
||||||
|
}
|
||||||
dashboardStore.setConfigPanel(false);
|
dashboardStore.setConfigPanel(false);
|
||||||
dashboardStore.setConfigs(dashboardStore.selectedGrid);
|
dashboardStore.setConfigs(dashboardStore.selectedGrid);
|
||||||
}
|
}
|
||||||
@ -76,6 +142,18 @@ limitations under the License. -->
|
|||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.url-input.error {
|
||||||
|
:deep(.el-input__inner) {
|
||||||
|
border-color: $error-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
color: $error-color;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
@ -37,6 +37,8 @@ limitations under the License. -->
|
|||||||
height="100%"
|
height="100%"
|
||||||
scrolling="no"
|
scrolling="no"
|
||||||
style="border: none"
|
style="border: none"
|
||||||
|
sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
|
||||||
|
referrerpolicy="no-referrer"
|
||||||
></iframe>
|
></iframe>
|
||||||
<div v-else class="tips">{{ t("iframeWidgetTip") }}</div>
|
<div v-else class="tips">{{ t("iframeWidgetTip") }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -69,6 +69,7 @@ limitations under the License. -->
|
|||||||
type: { type: String, default: TraceGraphType.LIST },
|
type: { type: String, default: TraceGraphType.LIST },
|
||||||
headerType: { type: String, default: "" },
|
headerType: { type: String, default: "" },
|
||||||
});
|
});
|
||||||
|
const emits = defineEmits(["select"]);
|
||||||
const appStore = useAppStoreWithOut();
|
const appStore = useAppStoreWithOut();
|
||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
const showDetail = ref<boolean>(false);
|
const showDetail = ref<boolean>(false);
|
||||||
@ -130,6 +131,7 @@ limitations under the License. -->
|
|||||||
refParentSpans.value = [];
|
refParentSpans.value = [];
|
||||||
if (props.type === TraceGraphType.TABLE) {
|
if (props.type === TraceGraphType.TABLE) {
|
||||||
currentSpan.value = i;
|
currentSpan.value = i;
|
||||||
|
emits("select", i);
|
||||||
} else {
|
} else {
|
||||||
currentSpan.value = i.data;
|
currentSpan.value = i.data;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,13 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License. -->
|
limitations under the License. -->
|
||||||
<template>
|
<template>
|
||||||
<div class="trace-table-charts">
|
<div class="trace-table-charts">
|
||||||
<Graph :data="data" :traceId="traceId" :type="TraceGraphType.TABLE" :headerType="headerType" />
|
<Graph
|
||||||
|
:data="data"
|
||||||
|
:traceId="traceId"
|
||||||
|
:type="TraceGraphType.TABLE"
|
||||||
|
:headerType="headerType"
|
||||||
|
@select="getSelectedSpan"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@ -26,6 +32,11 @@ limitations under the License. -->
|
|||||||
traceId: { type: String, default: "" },
|
traceId: { type: String, default: "" },
|
||||||
headerType: { type: String, default: "" },
|
headerType: { type: String, default: "" },
|
||||||
});
|
});
|
||||||
|
const emits = defineEmits(["select"]);
|
||||||
|
|
||||||
|
function getSelectedSpan(span: Span) {
|
||||||
|
emits("select", span);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.trace-table-charts {
|
.trace-table-charts {
|
||||||
|
Loading…
Reference in New Issue
Block a user