feat: Cloud code merge (#131)

* changes while merge code with cloud

* cloud merge changes

* segment and function name change

* fix: api endpoint changed

* build fix

* fix: api endpoint changed

* Update organizations.ts

* fix: minor fix

* fix: user page ui

---------

Co-authored-by: Hengfei Yang <hengfei.yang@gmail.com>
This commit is contained in:
Bhargav
2023-02-21 20:21:39 +08:00
committed by GitHub
co-authored by Hengfei Yang
parent 46cb48b3ea
commit 6975f89103
26 changed files with 1008 additions and 604 deletions
+191 -45
View File
@@ -27,103 +27,239 @@
<q-separator />
<div>
<q-form ref="addAlertForm" @submit="onSubmit">
<q-input v-model="formData.name" :label="t('alerts.name')" color="input-border" bg-color="input-bg"
class="q-py-sm showLabelOnTop" stack-label outlined filled dense v-bind:readonly="beingUpdated"
v-bind:disable="beingUpdated" :rules="[(val: any) => !!val || 'Field is required!']" tabindex="0" />
<q-input
v-model="formData.name"
:label="t('alerts.name')"
color="input-border"
bg-color="input-bg"
class="q-py-sm showLabelOnTop"
stack-label
outlined
filled
dense
v-bind:readonly="beingUpdated"
v-bind:disable="beingUpdated"
:rules="[(val: any) => !!val || 'Field is required!']"
tabindex="0"
/>
<q-select v-model="formData.stream_name" :options="indexOptions" :label="t('alerts.stream_name')"
color="input-border" bg-color="input-bg" class="q-py-sm showLabelOnTop" stack-label outlined filled dense
<q-select
v-model="formData.stream_name"
:options="indexOptions"
:label="t('alerts.stream_name')"
color="input-border"
bg-color="input-bg"
class="q-py-sm showLabelOnTop"
stack-label
outlined
filled
dense
@update:model-value="updateAlert(formData.stream_name)"
:rules="[(val: any) => !!val || 'Field is required!']" />
:rules="[(val: any) => !!val || 'Field is required!']"
/>
<div class="q-gutter-sm">
<q-radio v-bind:readonly="beingUpdated" v-bind:disable="beingUpdated" v-model="formData.isScheduled"
:checked="formData.isScheduled" val="true" :label="t('alerts.scheduled')" />
<q-radio v-bind:readonly="beingUpdated" v-bind:disable="beingUpdated" v-model="formData.isScheduled"
:checked="!formData.isScheduled" val="false" :label="t('alerts.realTime')" />
<q-radio
v-bind:readonly="beingUpdated"
v-bind:disable="beingUpdated"
v-model="formData.isScheduled"
:checked="formData.isScheduled"
val="true"
:label="t('alerts.scheduled')"
/>
<q-radio
v-bind:readonly="beingUpdated"
v-bind:disable="beingUpdated"
v-model="formData.isScheduled"
:checked="!formData.isScheduled"
val="false"
:label="t('alerts.realTime')"
/>
</div>
<!--<q-toggle v-model="formData.isScheduled" :label="t('alerts.isScheduled')" color="input-border" bg-color="input-bg"
class="q-py-md showLabelOnTop" stack-label outlined filled dense />-->
<div v-if="formData.isScheduled === 'true'" class="q-py-sm showLabelOnTop text-bold text-h7">
<div
v-if="formData.isScheduled === 'true'"
class="q-py-sm showLabelOnTop text-bold text-h7"
>
{{ t("alerts.sql") }}:
</div>
<div v-show="formData.isScheduled === 'true'" ref="editorRef" id="editor" :label="t('alerts.sql')" stack-label
style="border: 1px solid #dbdbdb; border-radius: 5px" @keyup="editorUpdate" @focusout="updateCondtions"
class="q-py-sm showLabelOnTop" resize :rules="[(val: any) => !!val || 'Field is required!']"></div>
<div
v-show="formData.isScheduled === 'true'"
ref="editorRef"
id="editor"
:label="t('alerts.sql')"
stack-label
style="border: 1px solid #dbdbdb; border-radius: 5px"
@keyup="editorUpdate"
@focusout="updateCondtions"
class="q-py-sm showLabelOnTop"
resize
:rules="[(val: any) => !!val || 'Field is required!']"
></div>
<div class="q-py-sm showLabelOnTop text-bold text-h7">
{{ t("alerts.condition") }}:
</div>
<div class="col-8 row justify-left align-center q-gutter-sm">
<div class="" style="minwidth: 100px">
<q-select v-model="formData.condition.column" :options="triggerCols" dense filled
:rules="[(val: any) => !!val || 'Field is required!']"></q-select>
<q-select
v-model="formData.condition.column"
:options="triggerCols"
dense
filled
:rules="[(val: any) => !!val || 'Field is required!']"
></q-select>
</div>
<div class="" style="minwidth: 100px">
<q-select v-model="formData.condition.operator" :options="triggerOperators" dense filled
:rules="[(val: any) => !!val || 'Field is required!']"></q-select>
<q-select
v-model="formData.condition.operator"
:options="triggerOperators"
dense
filled
:rules="[(val: any) => !!val || 'Field is required!']"
></q-select>
</div>
<div class="" style="width: 80px">
<q-input v-model="formData.condition.value" dense filled
:rules="[(val: any) => !!val || 'Field is required!']"></q-input>
<q-input
v-model="formData.condition.value"
dense
filled
:rules="[(val: any) => !!val || 'Field is required!']"
></q-input>
</div>
</div>
<div v-if="formData.isScheduled === 'true'" class="q-py-sm showLabelOnTop text-bold text-h7">
<div
v-if="formData.isScheduled === 'true'"
class="q-py-sm showLabelOnTop text-bold text-h7"
>
{{ t("alerts.duration") }}:
</div>
<div v-if="formData.isScheduled === 'true'" class="col-8 row justify-left align-center q-gutter-sm">
<div
v-if="formData.isScheduled === 'true'"
class="col-8 row justify-left align-center q-gutter-sm"
>
<div class="" style="width: 80px">
<q-input v-model="formData.duration.value" type="number" dense filled min="0"
:rules="[(val: any) => !!val || 'Field is required!']"></q-input>
<q-input
v-model="formData.duration.value"
type="number"
dense
filled
min="0"
:rules="[(val: any) => !!val || 'Field is required!']"
></q-input>
</div>
<div class="" style="minwidth: 100px">
<q-select v-model="formData.duration.unit" :options="relativePeriods" dense filled
:rules="[(val: any) => !!val || 'Field is required!']"></q-select>
<q-select
v-model="formData.duration.unit"
:options="relativePeriods"
dense
filled
:rules="[(val: any) => !!val || 'Field is required!']"
></q-select>
</div>
</div>
<div v-if="formData.isScheduled === 'true'" class="q-py-sm showLabelOnTop text-bold text-h7">
<div
v-if="formData.isScheduled === 'true'"
class="q-py-sm showLabelOnTop text-bold text-h7"
>
{{ t("alerts.interval") }}:
</div>
<div v-if="formData.isScheduled === 'true'" class="col-8 row justify-left align-center q-gutter-sm">
<div
v-if="formData.isScheduled === 'true'"
class="col-8 row justify-left align-center q-gutter-sm"
>
<div class="" style="width: 80px">
<q-input v-model="formData.frequency.value" type="number" dense filled min="0"
:rules="[(val: any) => !!val || 'Field is required!']"></q-input>
<q-input
v-model="formData.frequency.value"
type="number"
dense
filled
min="0"
:rules="[(val: any) => !!val || 'Field is required!']"
></q-input>
</div>
<div class="" style="minwidth: 100px">
<q-select v-model="formData.frequency.unit" :options="relativePeriods" dense filled
:rules="[(val: any) => !!val || 'Field is required!']"></q-select>
<q-select
v-model="formData.frequency.unit"
:options="relativePeriods"
dense
filled
:rules="[(val: any) => !!val || 'Field is required!']"
></q-select>
</div>
</div>
<div v-if="formData.isScheduled === 'true'" class="q-py-sm showLabelOnTop text-bold text-h7">
<div
v-if="formData.isScheduled === 'true'"
class="q-py-sm showLabelOnTop text-bold text-h7"
>
{{ t("alerts.delayNotificationUntil") }}:
</div>
<div v-if="formData.isScheduled === 'true'" class="col-8 row justify-left align-center q-gutter-sm">
<div
v-if="formData.isScheduled === 'true'"
class="col-8 row justify-left align-center q-gutter-sm"
>
<div class="" style="width: 80px">
<q-input v-model="formData.time_between_alerts.value" type="number" dense filled min="0"></q-input>
<q-input
v-model="formData.time_between_alerts.value"
type="number"
dense
filled
min="0"
></q-input>
</div>
<div class="" style="minwidth: 100px">
<q-select v-model="formData.time_between_alerts.unit" :options="relativePeriods" dense filled></q-select>
<q-select
v-model="formData.time_between_alerts.unit"
:options="relativePeriods"
dense
filled
></q-select>
</div>
</div>
<q-input v-model="formData.destination" :label="t('alerts.destination')" color="input-border" bg-color="input-bg"
class="q-py-sm showLabelOnTop" stack-label outlined filled dense
:rules="[(val: any) => !!val || 'Field is required!']" tabindex="0" />
<q-input
v-model="formData.destination"
:label="t('alerts.destination')"
color="input-border"
bg-color="input-bg"
class="q-py-sm showLabelOnTop"
stack-label
outlined
filled
dense
:rules="[(val: any) => !!val || 'Field is required!']"
tabindex="0"
/>
<div class="flex justify-center q-mt-lg">
<q-btn v-close-popup class="q-mb-md text-bold no-border" :label="t('alerts.cancel')" text-color="light-text"
padding="sm md" color="accent" no-caps @click="$emit('cancel:hideform')" />
<q-btn :label="t('alerts.save')" class="q-mb-md text-bold no-border q-ml-md" color="secondary" padding="sm xl"
type="submit" no-caps />
<q-btn
v-close-popup
class="q-mb-md text-bold no-border"
:label="t('alerts.cancel')"
text-color="light-text"
padding="sm md"
color="accent"
no-caps
@click="$emit('cancel:hideform')"
/>
<q-btn
:label="t('alerts.save')"
class="q-mb-md text-bold no-border q-ml-md"
color="secondary"
padding="sm xl"
type="submit"
no-caps
/>
</div>
</q-form>
</div>
</div>
</div>
</template>
<script lang="ts">
@@ -136,6 +272,7 @@ import { useQuasar } from "quasar";
import IndexService from "../../services/index";
import { Parser } from "node-sql-parser";
import segment from "../../services/segment_analytics";
const defaultValue: any = () => {
return {
@@ -167,7 +304,7 @@ const defaultValue: any = () => {
let callAlert: Promise<{ data: any }>;
export default defineComponent({
name: "ComponentAddUpdateUser",
name: "ComponentAddUpdateAlert",
props: {
modelValue: {
type: Object,
@@ -457,6 +594,15 @@ export default defineComponent({
message: err.response.data.error,
});
});
segment.track("Button Click", {
button: "Save Alert",
user_org: this.store.state.selectedOrganization.identifier,
user_id: this.store.state.userInfo.email,
stream_name: this.formData.stream_name,
alert_name: this.formData.name,
page: "Add/Update Alert",
});
});
},
},
@@ -18,9 +18,9 @@
<div class="row items-center no-wrap">
<div class="col">
<div v-if="beingUpdated" class="text-h6">
{{ t("jstransform.updateTitle") }}
{{ t("function.updateTitle") }}
</div>
<div v-else class="text-h6">{{ t("jstransform.addTitle") }}</div>
<div v-else class="text-h6">{{ t("function.addTitle") }}</div>
</div>
</div>
@@ -29,7 +29,7 @@
<q-form ref="addJSTransformForm" @submit="onSubmit">
<q-toggle
v-model="formData.ingest"
:label="t('jstransform.showQuery')"
:label="t('function.showQuery')"
color="input-border"
bg-color="input-bg"
class="q-py-md showLabelOnTop"
@@ -44,7 +44,7 @@
v-if="formData.ingest"
v-model="formData.stream_name"
:options="indexOptions"
:label="t('jstransform.stream_name')"
:label="t('function.stream_name')"
color="input-border"
bg-color="input-bg"
class="q-py-md showLabelOnTop"
@@ -56,7 +56,7 @@
<q-input
v-model="formData.name"
:label="t('jstransform.name')"
:label="t('function.name')"
color="input-border"
bg-color="input-bg"
class="q-py-md showLabelOnTop"
@@ -75,7 +75,7 @@
<div
ref="editorRef"
id="editor"
:label="t('jstransform.jsfunction')"
:label="t('function.jsfunction')"
stack-label
style="border: 1px solid #dbdbdb; border-radius: 5px"
@keyup="editorUpdate"
@@ -93,14 +93,14 @@
outlined
filled
dense
:label="t('jstransform.jsfunction')"
:label="t('function.jsfunction')"
:rules="[(val) => !!val || 'Field is required!']"
/> -->
<q-input
v-if="formData.ingest"
v-model="formData.order"
:label="t('jstransform.order')"
:label="t('function.order')"
color="input-border"
bg-color="input-bg"
class="q-py-md showLabelOnTop"
@@ -116,7 +116,7 @@
<q-btn
v-close-popup
class="q-mb-md text-bold no-border"
:label="t('jstransform.cancel')"
:label="t('function.cancel')"
text-color="light-text"
padding="sm md"
color="accent"
@@ -124,7 +124,7 @@
@click="$emit('cancel:hideform')"
/>
<q-btn
:label="t('jstransform.save')"
:label="t('function.save')"
class="q-mb-md text-bold no-border q-ml-md"
color="secondary"
padding="sm xl"
@@ -147,6 +147,7 @@ import { useQuasar } from "quasar";
import IndexService from "../../services/index";
import { update } from "plotly.js";
import segment from "../../services/segment_analytics";
const defaultValue: any = () => {
return {
@@ -161,7 +162,7 @@ const defaultValue: any = () => {
let callTransform: Promise<{ data: any }>;
export default defineComponent({
name: "ComponentAddUpdateUser",
name: "ComponentAddUpdateFunction",
props: {
modelValue: {
type: Object,
@@ -372,6 +373,16 @@ end`;
message: res.data.message,
});
});
segment.track("Button Click", {
button: "Save Function",
user_org: this.store.state.selectedOrganization.identifier,
user_id: this.store.state.userInfo.email,
stream_name: this.formData.stream_name,
function_name: this.formData.name,
is_ingest_fn: this.formData.ingest,
page: "Add/Update Function",
});
});
},
},
@@ -0,0 +1,254 @@
<!-- Copyright 2022 Zinc Labs Inc. and Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http:www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<template>
<q-layout view="hHh lpR fFf">
<q-page-container>
<q-page class="fullscreen bg-grey-7 flex flex-center">
<q-card square class="my-card shadow-24 bg-white text-white">
<q-card-section class="bg-primary">
<div class="text-h5 q-my-md">ZincObserve</div>
</q-card-section>
<q-card>
<q-card-section class="bg-white">
<q-form ref="loginform" class="q-gutter-md" @submit.prevent="">
<q-input
v-model="name"
data-cy="login-user-id"
label="Email *"
@blur="getOrganizations"
>
<template #prepend>
<q-icon name="email" />
</template>
</q-input>
<q-input
v-model="password"
data-cy="login-password"
type="password"
:label="t('login.password') + ' *'"
>
<template #prepend>
<q-icon name="lock" />
</template>
</q-input>
<q-select
v-model="org_identifier"
:options="organizations"
label="Select Organization *"
:display-value="`${
org_identifier ? org_identifier.label : '-- Select --'
}`"
>
<template #prepend>
<q-icon name="corporate_fare" />
</template>
</q-select>
<q-card-actions class="q-px-lg q-mt-md q-mb-xl">
<q-btn
data-cy="login-sign-in"
unelevated
size="lg"
class="full-width"
color="primary"
type="submit"
:label="t('login.signIn')"
:loading="submitting"
no-caps
@click="onSignIn()"
/>
</q-card-actions>
</q-form>
</q-card-section>
</q-card>
</q-card>
</q-page>
</q-page-container>
</q-layout>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import { useStore } from "vuex";
import { useQuasar } from "quasar";
import { useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
import authService from "../../services/auth";
import {
useLocalToken,
getBasicAuth,
b64EncodeUnicode,
useLocalUserInfo,
useLocalCurrentUser,
useLocalOrganization,
} from "../../utils/zincutils";
import { getDefaultOrganization, redirectUser } from "../../utils/common";
export default defineComponent({
name: "PageLogin",
setup() {
const store = useStore();
const router = useRouter();
const $q = useQuasar();
const { t } = useI18n();
const name = ref("");
const org_identifier = ref();
const organizations = ref([]);
const password = ref("");
const confirmpassword = ref("");
const email = ref("");
const loginform = ref();
const submitting = ref(false);
const onSignIn = () => {
if (org_identifier.value == undefined) {
$q.notify({
color: "negative",
message: "Organization is required",
});
return false;
}
if (name.value == "" || password.value == "") {
$q.notify({
position: "top",
color: "warning",
textColor: "white",
icon: "warning",
message: "Please input",
});
} else {
submitting.value = true;
try {
authService
.sign_in_user(org_identifier.value.identifier, {
name: name.value,
password: password.value,
})
.then(async (res: any) => {
if (res.data.status == true) {
const authToken = getBasicAuth(name.value, password.value);
useLocalToken(authToken);
const userInfo = {
given_name: name.value,
auth_time: Math.floor(Date.now() / 1000),
name: name.value,
exp: Math.floor(
(new Date().getTime() + 1000 * 60 * 60 * 24 * 30) / 1000
),
family_name: "",
email: name.value,
role: res.data.role,
};
const encodedUserInfo = b64EncodeUnicode(
JSON.stringify(userInfo)
);
useLocalUserInfo(encodedUserInfo);
store.dispatch("setUserInfo", encodedUserInfo);
useLocalCurrentUser(JSON.stringify(userInfo));
store.dispatch("setCurrentUser", userInfo);
const organizations = await getDefaultOrganization(
userInfo,
org_identifier.value.identifier
);
store.dispatch("setOrganizations", organizations);
const selectedOrgs = useLocalOrganization(org_identifier.value);
store.dispatch("setSelectedOrganization", selectedOrgs);
const redirectURI =
window.sessionStorage.getItem("redirectURI");
window.sessionStorage.removeItem("redirectURI");
redirectUser(redirectURI);
} else {
submitting.value = false;
loginform.value.resetValidation();
$q.notify({
color: "negative",
message: res.data.message,
});
}
})
.catch((e: Error) => {
submitting.value = false;
loginform.value.resetValidation();
$q.notify({
color: "negative",
message: "Invalid username or password",
});
console.log(e);
});
} catch (e) {
submitting.value = false;
loginform.value.resetValidation();
$q.notify({
color: "negative",
message: "Please fill all the fields and try again.",
});
console.log(e);
}
}
};
const getOrganizations = () => {
authService
.get_organization_by_username(name.value)
.then((res) => {
organizations.value = res.data;
org_identifier.value = res.data[0];
})
.catch((e) => {
console.log(e);
});
};
return {
t,
name,
password,
confirmpassword,
org_identifier,
organizations,
email,
loginform,
submitting,
onSignIn,
tab: ref("signin"),
innerTab: ref("signup"),
getOrganizations,
};
},
methods: {
selected(item: any) {
this.$q.notify(`Selected suggestion "${item.label}"`);
},
},
});
</script>
<style lang="scss">
.my-card {
width: 400px;
}
</style>
+8
View File
@@ -165,6 +165,7 @@ import { useI18n } from "vue-i18n";
import { useStore } from "vuex";
import { useQuasar, date, format } from "quasar";
import indexService from "../../services/index";
import segment from "../../services/segment_analytics";
const defaultValue: any = () => {
return {
@@ -281,6 +282,13 @@ export default defineComponent({
)
.then((res) => {
getSchema();
segment.track("Button Click", {
button: "Update Settings",
user_org: store.state.selectedOrganization.identifier,
user_id: store.state.userInfo.email,
stream_name: indexData.value.name,
page: "Schema Details",
});
dismiss();
});
};
+4 -4
View File
@@ -198,7 +198,7 @@ import {
getUserInfo,
} from "../utils/zincutils";
import { ref } from "vue";
import { ref, defineComponent } from "vue";
import { useStore } from "vuex";
import { useRouter } from "vue-router";
import organizationService from "../services/organizations";
@@ -206,7 +206,7 @@ import config from "../aws-exports";
import Tracker from "@openreplay/tracker";
import configService from "../services/config";
export default {
export default defineComponent({
name: "MainLayout",
components: {
@@ -315,7 +315,7 @@ export default {
];
const local = ref(getLocale());
const selectedLanguage = ref(langList.find((l) => l.code == local.value));
const selectedLanguage:any = ref(langList.find((l) => l.code == local.value));
if (user.picture == "") {
user.picture = "/src/assets/images/common/profile.svg";
@@ -467,7 +467,7 @@ export default {
zoBackendUrl,
};
},
};
});
</script>
<style lang="scss">
+1 -1
View File
@@ -190,7 +190,7 @@
"refreshStats": "Refresh Stats",
"updateSettings": "Update Settings"
},
"jstransform": {
"function": {
"header": "Functions",
"add": "Create new function",
"search": "Search Function",
+1 -1
View File
@@ -190,7 +190,7 @@
"refreshStats": "İstatistikleri Yenile",
"updateSettings": "Ayarları Güncelle"
},
"jstransform": {
"function": {
"header": "Fonksiyonlar",
"add": "Yeni işlev oluştur",
"search": "Arama İşlevi",
+1 -1
View File
@@ -190,7 +190,7 @@
"refreshStats": "刷新统计",
"updateSettings": "更新设置"
},
"jstransform": {
"function": {
"header": "函数",
"add": "创建新函数",
"search": "搜索函数",
+1 -2
View File
@@ -32,7 +32,6 @@ import config from "./aws-exports";
import SearchPlugin from "./plugins/index";
const app = createApp(App);
const router = createRouter(store);
if (config.enableAnalytics == "true") {
@@ -45,7 +44,7 @@ if (config.enableAnalytics == "true") {
tracingOrigins: [
"localhost",
"alpha1.cloud.zinclabs.dev",
"cloud.zincsearch.com",
"observe.zinc.dev",
/^\//,
],
}),
+1
View File
@@ -237,6 +237,7 @@ export default defineComponent({
startTime: "00:00",
endTime: "23:59",
},
userChangedValue: false,
},
};
},
+29 -1
View File
@@ -133,6 +133,8 @@ import indexService from "../../services/index";
import searchService from "../../services/search";
import TransformService from "../../services/jstransform";
import { useLocalLogsObj } from "../../utils/zincutils";
import segment from "../../services/segment_analytics";
import config from "../../aws-exports";
export default defineComponent({
name: "PageSearch",
@@ -146,6 +148,20 @@ export default defineComponent({
if (this.searchObj.loading == false) {
this.searchObj.runQuery = true;
}
if (config.isZincObserveCloud == "true") {
segment.track("Button Click", {
button: "Search Data",
user_org: this.store.state.selectedOrganization.identifier,
user_id: this.store.state.userInfo.email,
stream_name: this.searchObj.data.stream.selectedStream.value,
show_query: this.searchObj.meta.showQuery,
show_histogram: this.searchObj.meta.showHistogram,
sqlMode: this.searchObj.meta.sqlMode,
showFields: this.searchObj.meta.showFields,
page: "Search Logs",
});
}
},
getMoreData() {
if (
@@ -161,6 +177,16 @@ export default defineComponent({
) {
this.searchObj.loading = true;
this.getQueryData();
if (config.isZincObserveCloud == "true") {
segment.track("Button Click", {
button: "Get More Data",
user_org: this.store.state.selectedOrganization.identifier,
user_id: this.store.state.userInfo.email,
stream_name: this.searchObj.data.stream.selectedStream.value,
page: "Search Logs",
});
}
}
},
},
@@ -256,7 +282,9 @@ export default defineComponent({
searchObj.data.streamResults = res.data;
if (res.data.list.length > 0) {
// getQueryTransform();
if (config.isZincObserveCloud == "true") {
getQueryTransform();
}
//extract stream data from response
loadStreamLists();
+2 -2
View File
@@ -80,7 +80,7 @@ export default defineComponent({
{
label: "between",
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: "between('${1:params}','${1:params}') ",
insertText: "between '${1:params}' and '${1:params}' ",
range: range,
insertTextRules:
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
@@ -88,7 +88,7 @@ export default defineComponent({
{
label: "not between",
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: "not between('${1:params}','${1:params}') ",
insertText: "not between '${1:params}' and '${1:params}' ",
range: range,
insertTextRules:
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
+21
View File
@@ -146,6 +146,8 @@ import QueryEditor from "./QueryEditor.vue";
import SyntaxGuide from "./SyntaxGuide.vue";
import { Parser } from "node-sql-parser";
import segment from "../../services/segment_analytics";
import config from "../../aws-exports";
export default defineComponent({
name: "ComponentSearchSearchBar",
@@ -232,6 +234,25 @@ export default defineComponent({
const updateDateTime = (value: object) => {
searchObj.data.datetime = value;
if (config.isZincObserveCloud == "true" && value.userChangedValue) {
let dateTimeVal;
if (value.tab === "relative") {
dateTimeVal = value.relative;
} else {
dateTimeVal = value.absolute;
}
segment.track("Button Click", {
button: "Date Change",
tab: value.tab,
value: dateTimeVal,
//user_org: this.store.state.selectedOrganization.identifier,
//user_id: this.store.state.userInfo.email,
stream_name: searchObj.data.stream.selectedStream.value,
page: "Search Logs",
});
}
};
const udpateQuery = () => {
+2 -2
View File
@@ -26,7 +26,7 @@ import LoginCallback from "../views/LoginCallback.vue";
import MemberSubscription from "../views/MemberSubscription.vue";
import Search from "../views/Search.vue";
import LogStream from "../views/LogStream.vue";
import JSTransform from "../views/JSTransform.vue";
import Functions from "../views/Functions.vue";
import Alerts from "../views/Alerts.vue";
import Ingestion from "../views/Ingestion.vue";
import Error404 from "../views/Error404.vue";
@@ -153,7 +153,7 @@ export default function (store: any) {
{
path: "functions",
name: "functions",
component: JSTransform,
component: Functions,
meta: {
keepAlive: true,
},
+19 -8
View File
@@ -10,7 +10,7 @@
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// limitations under the License.
import http from "./http";
@@ -21,20 +21,31 @@ var alerts = {
sort_by: string,
desc: boolean,
name: string,
org_identifier: string,
org_identifier: string
) => {
return http().get(
`/api/${org_identifier}/alerts?page_num=${page_num}&page_size=${page_size}&sort_by=${sort_by}&desc=${desc}&name=${name}`
);
},
create: (org_identifier: string, stream_name: string,data: any) => {
return http().post(`/api/${org_identifier}/${stream_name}/alerts/${data.name}`, data);
create: (org_identifier: string, stream_name: string, data: any) => {
return http().post(
`/api/${org_identifier}/${stream_name}/alerts/${data.name}`,
data
);
},
get_with_name: (org_identifier: string, stream_name: string, alert_name: string) => {
return http().get(`/api/${org_identifier}/${stream_name}/alerts/${alert_name}`);
get_with_name: (
org_identifier: string,
stream_name: string,
alert_name: string
) => {
return http().get(
`/api/${org_identifier}/${stream_name}/alerts/${alert_name}`
);
},
delete: (org_identifier: string,stream_name: string, alert_name: string) => {
return http().delete(`/api/${org_identifier}/${stream_name}/alerts/${alert_name}`);
delete: (org_identifier: string, stream_name: string, alert_name: string) => {
return http().delete(
`/api/${org_identifier}/${stream_name}/alerts/${alert_name}`
);
},
};
+8 -22
View File
@@ -10,11 +10,10 @@
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// limitations under the License.
/* eslint-disable @typescript-eslint/no-explicit-any */
import http from "./http";
import config from "../aws-exports";
const organizations = {
os_list: (
@@ -44,38 +43,25 @@ const organizations = {
return http().post("/api/organizations", data);
},
add_members: (data: any, orgIdentifier: string) => {
return http().post(`/api/organizations/${orgIdentifier}/members`, data);
return http().post(`api/${orgIdentifier}/organizations/members`, data);
},
process_subscription: (s: string) => {
return http().get(`/api/organizations/member_subscription/${s}`);
return http().get(`api/organizations/member_subscription/${s}`);
},
get_associated_members: (orgIdentifier: string) => {
return http().get(`/api/organizations/associated_members/${orgIdentifier}`);
return http().get(`api/${orgIdentifier}/organizations/associated_members`);
},
update_member_role: (data: any, orgIdentifier: string) => {
if (config.isZincObserveCloud === "true") {
return http().put(`/api/organizations/${orgIdentifier}/member`, data);
} else {
return http().put(`/api/${orgIdentifier}/users/${encodeURIComponent(data.email)}`, { role: data.role });
}
return http().put(`api/${orgIdentifier}/organizations/member`, data);
},
verify_identifier: (name: string) => {
return http().get(`/api/organizations/verify_identifier/${name}`);
return http().get(`api/organizations/verify_identifier/${name}`);
},
get_organization_passcode: (orgIdentifier: string) => {
if (config.isZincObserveCloud === "true") {
return http().get(`/api/organizations/passcode/${orgIdentifier}`);
}
else {
return http().get(`/api/${orgIdentifier}/organizations/passcode`);
}
return http().get(`/api/${orgIdentifier}/organizations/passcode`);
},
update_organization_passcode: (orgIdentifier: string) => {
if (config.isZincObserveCloud === "true") {
return http().put(`/api/organizations/passcode/${orgIdentifier}`, {});
} else {
return http().put(`/api/${orgIdentifier}/organizations/passcode`, {});
}
return http().put(`api/${orgIdentifier}/organizations/passcode`, {});
},
get_organization_summary: (orgIdentifier: string) => {
return http().get(`/api/${orgIdentifier}/summary`);
+7
View File
@@ -46,6 +46,7 @@ export default createStore({
}
},
streamFields: [],
quotaThresholdMsg: "",
zoConfig: {},
},
mutations: {
@@ -95,6 +96,9 @@ export default createStore({
setCurrentPanelsData(state, payload) {
state.currentPanelsData = payload
},
setQuotaThresholdMsg(state, payload) {
state.quotaThresholdMsg = payload;
},
setConfig(state, payload) {
state.zoConfig = payload
}
@@ -142,6 +146,9 @@ export default createStore({
setCurrentPanelsData(context, payload) {
context.commit('setCurrentPanelsData', payload);
},
setQuotaThresholdMsg(context, payload) {
context.commit('setQuotaThresholdMsg', payload);
},
setConfig(context, payload) {
context.commit('setConfig', payload);
}
-1
View File
@@ -60,7 +60,6 @@ export default defineComponent({
pageData,
};
},
// methods: {},
});
</script>
+98 -54
View File
@@ -139,12 +139,14 @@ import { useI18n } from "vue-i18n";
import QTablePagination from "../components/shared/grid/Pagination.vue";
import alertsService from "../services/alerts";
import AddTransform from "../components/alerts/add.vue"
import NoData from "../components/shared/grid/NoData.vue"
import AddTransform from "../components/alerts/add.vue";
import NoData from "../components/shared/grid/NoData.vue";
import ConfirmDialog from "../components/ConfirmDialog.vue";
import segment from "../services/segment_analytics";
import config from "../aws-exports";
export default defineComponent({
name: "PageJSTransform",
name: "PageAlerts",
components: { QTablePagination, AddTransform, NoData, ConfirmDialog },
emits: [
"updated:fields",
@@ -200,7 +202,7 @@ export default defineComponent({
sortable: true,
style: "width: 10vw;word-break: break-all;",
},
{
name: "actions",
field: "actions",
@@ -211,37 +213,50 @@ export default defineComponent({
]);
const getAlerts = () => {
const dismiss = $q.notify({
spinner: true,
message: "Please wait while loading alerts...",
});
const dismiss = $q.notify({
spinner: true,
message: "Please wait while loading alerts...",
});
alertsService.list(1, 100000, "name", false, "", store.state.selectedOrganization.identifier).then((res) => {
alertsService
.list(
1,
100000,
"name",
false,
"",
store.state.selectedOrganization.identifier
)
.then((res) => {
var counter = 1;
resultTotal.value = res.data.list.length;
alerts.value = res.data.list.map((data: any) => {
if (data.is_real_time){
data.query.sql= "--"
if (data.is_real_time) {
data.query.sql = "--";
}
return {
"#": counter <= 9 ? `0${counter++}` : counter++,
name: data.name,
sql: data.query.sql,
stream_name: (data.stream) ? data.stream : "--",
condition_str: data.condition.column + " " + data.condition.operator + " "+data.condition.value,
stream_name: data.stream ? data.stream : "--",
condition_str:
data.condition.column +
" " +
data.condition.operator +
" " +
data.condition.value,
actions: "",
duration: {
value: data.duration,
unit: "Minutes"
unit: "Minutes",
},
frequency: {
value: data.frequency,
unit: "Minutes"
unit: "Minutes",
},
time_between_alerts: {
value: data.time_between_alerts,
unit: "Minutes"
unit: "Minutes",
},
destination: data.destination,
condition: data.condition,
@@ -251,15 +266,14 @@ export default defineComponent({
dismiss();
})
.catch((err) => {
dismiss();
$q.notify({
type: "negative",
message: "Error while pulling alerts.",
timeout: 2000,
});
.catch((err) => {
dismiss();
$q.notify({
type: "negative",
message: "Error while pulling alerts.",
timeout: 2000,
});
});
};
if (alerts.value == "" || alerts.value == undefined) {
@@ -295,51 +309,78 @@ export default defineComponent({
const addTransform = () => {
showAddAlertDialog.value = true;
}
};
const showAddUpdateFn = (props: any) => {
formData.value = props.row;
let action;
if (!props.row) {
isUpdated.value = false;
action = "Add Alert";
} else {
isUpdated.value = true;
action = "Update Alert";
}
addTransform();
}
if (config.enableAnalytics == "true") {
segment.track("Button Click", {
button: action,
user_org: store.state.selectedOrganization.identifier,
user_id: store.state.userInfo.email,
page: "Alerts",
});
}
};
const refreshList = () => {
showAddAlertDialog.value = false;
getAlerts();
}
};
const hideForm = () => {
showAddAlertDialog.value = false;
}
};
const deleteFn = () => {
alertsService.delete(store.state.selectedOrganization.identifier, selectedDelete.value.stream_name
, selectedDelete.value.name).then((res: any) => {
if (res.data.code == 200) {
$q.notify({
type: "positive",
message: res.data.message,
timeout: 2000,
});
getAlerts();
} else {
$q.notify({
type: "negative",
message: res.data.message,
timeout: 2000,
});
}
})
}
alertsService
.delete(
store.state.selectedOrganization.identifier,
selectedDelete.value.stream_name,
selectedDelete.value.name
)
.then((res: any) => {
if (res.data.code == 200) {
$q.notify({
type: "positive",
message: res.data.message,
timeout: 2000,
});
getAlerts();
} else {
$q.notify({
type: "negative",
message: res.data.message,
timeout: 2000,
});
}
});
if (config.enableAnalytics == "true") {
segment.track("Button Click", {
button: "Delete Alert",
user_org: store.state.selectedOrganization.identifier,
user_id: store.state.userInfo.email,
alert_name: selectedDelete.value.name,
page: "Alerts",
});
}
};
const showDeleteDialogFn = (props: any) => {
selectedDelete.value = props.row;
confirmDelete.value = true;
}
};
return {
t,
@@ -382,18 +423,21 @@ export default defineComponent({
},
computed: {
selectedOrg() {
return this.store.state.selectedOrganization.identifier
}
return this.store.state.selectedOrganization.identifier;
},
},
watch: {
selectedOrg(newVal: any, oldVal: any) {
if ((newVal != oldVal || this.alerts.value == undefined) && this.router.currentRoute.value.name == "transform") {
this.resultTotal = 0
if (
(newVal != oldVal || this.alerts.value == undefined) &&
this.router.currentRoute.value.name == "transform"
) {
this.resultTotal = 0;
this.alerts = [];
this.getAlerts();
}
}
}
},
},
});
</script>
-20
View File
@@ -101,15 +101,6 @@
<add-update-organization @updated="updateDashboardList" />
</q-dialog>
<q-dialog
v-model="showJoinOrganizationDialog"
position="right"
full-height
maximized
>
Organization not available
<!-- <join-organization v-model="organization" @updated="joinOrganization" /> -->
</q-dialog>
<q-dialog v-model="showOrgAPIKeyDialog">
<q-card>
<q-card-section>
@@ -171,7 +162,6 @@ export default defineComponent({
const $q = useQuasar();
const organization = ref({});
const showAddDashboardDialog = ref(false);
const showJoinOrganizationDialog = ref(false);
const showOrgAPIKeyDialog = ref(false);
const organizationAPIKey = ref("");
const qTable: any = ref(null);
@@ -315,7 +305,6 @@ export default defineComponent({
role: props.row.role,
member_lists: [],
};
showJoinOrganizationDialog.value = true;
};
const removeDashboard = async (props: any) => {
const dashboardId = props.key;
@@ -348,7 +337,6 @@ export default defineComponent({
organization,
columns,
showAddDashboardDialog,
showJoinOrganizationDialog,
showOrgAPIKeyDialog,
organizationAPIKey,
addDashboard,
@@ -390,14 +378,6 @@ export default defineComponent({
onRowClick(evt, row) {
this.routeToViewD(row);
},
joinOrganization() {
this.$q.notify({
type: "positive",
message: "Request completed successfully.",
timeout: 5000,
});
this.showJoinOrganizationDialog = false;
},
getAPIKey(org_identifier: string) {
const dismiss: any = this.$q.notify({
message: "Wait while processing your request...",
@@ -39,7 +39,7 @@
size="sm"
round
flat
:title="t('jstransform.add')"
:title="t('function.add')"
@click="showAddUpdateFn(props)"
></q-btn>
<q-btn
@@ -50,7 +50,7 @@
size="sm"
round
flat
:title="t('jstransform.delete')"
:title="t('function.delete')"
@click="showDeleteDialogFn(props)"
></q-btn>
</q-td>
@@ -68,7 +68,7 @@
</template>
<template #top="scope">
<div class="q-table__title">
{{ t("jstransform.header") }}
{{ t("function.header") }}
</div>
<q-input
v-model="filterQuery"
@@ -76,7 +76,7 @@
filled
dense
class="q-ml-auto q-mb-xs no-border"
:placeholder="t('jstransform.search')"
:placeholder="t('function.search')"
>
<template #prepend>
<q-icon name="search" class="cursor-pointer" />
@@ -87,13 +87,13 @@
padding="sm lg"
color="secondary"
no-caps
:label="t(`jstransform.add`)"
:label="t(`function.add`)"
@click="showAddUpdateFn({})"
/>
<QTablePagination
:scope="scope"
:pageTitle="t('jstransform.header')"
:pageTitle="t('function.header')"
:position="'top'"
:resultTotal="resultTotal"
:perPageOptions="perPageOptions"
@@ -139,12 +139,14 @@ import { useI18n } from "vue-i18n";
import QTablePagination from "../components/shared/grid/Pagination.vue";
import jsTransformService from "../services/jstransform";
import AddTransform from "../components/jstransform/add.vue"
import NoData from "../components/shared/grid/NoData.vue"
import AddTransform from "../components/functions/add.vue";
import NoData from "../components/shared/grid/NoData.vue";
import ConfirmDialog from "../components/ConfirmDialog.vue";
import segment from "../services/segment_analytics";
import config from "../aws-exports";
export default defineComponent({
name: "PageJSTransform",
name: "PageFunctions",
components: { QTablePagination, AddTransform, NoData, ConfirmDialog },
emits: [
"updated:fields",
@@ -157,13 +159,13 @@ export default defineComponent({
const $q = useQuasar();
const router = useRouter();
const jsTransforms: any = ref([]);
const formData:any = ref({});
const formData: any = ref({});
const showAddJSTransformDialog: any = ref(false);
const qTable: any = ref(null);
const selectedDelete: any = ref(null);
const isUpdated: any = ref(false);
const confirmDelete = ref<boolean>(false);
const columns:any = ref<QTableProps["columns"]>([
const columns: any = ref<QTableProps["columns"]>([
{
name: "#",
label: "#",
@@ -173,14 +175,14 @@ export default defineComponent({
{
name: "name",
field: "name",
label: t("jstransform.name"),
label: t("function.name"),
align: "left",
sortable: true,
},
{
name: "stream_name",
field: "stream_name",
label: t("jstransform.stream_name"),
label: t("function.stream_name"),
align: "left",
sortable: true,
},
@@ -189,33 +191,42 @@ export default defineComponent({
field: (row: any) => {
return row.ingest ? "Ingest" : "Query";
},
label: t("jstransform.type"),
label: t("function.type"),
align: "left",
sortable: true,
},
{
name: "order",
field: "order",
label: t("jstransform.order"),
label: t("function.order"),
align: "left",
sortable: true,
},
{
name: "actions",
field: "actions",
label: t("jstransform.actions"),
label: t("function.actions"),
align: "center",
sortable: false,
},
]);
const getJSTransforms = () => {
const dismiss = $q.notify({
spinner: true,
message: "Please wait while loading functions...",
});
const dismiss = $q.notify({
spinner: true,
message: "Please wait while loading functions...",
});
jsTransformService.list(1, 100000, "name", false, "", store.state.selectedOrganization.identifier).then((res) => {
jsTransformService
.list(
1,
100000,
"name",
false,
"",
store.state.selectedOrganization.identifier
)
.then((res) => {
var counter = 1;
resultTotal.value = res.data.list.length;
jsTransforms.value = res.data.list.map((data: any) => {
@@ -223,9 +234,9 @@ export default defineComponent({
"#": counter <= 9 ? `0${counter++}` : counter++,
name: data.name,
function: data.function,
order: (data.order) ? data.order : 1,
stream_name: (data.stream_name) ? data.stream_name : "--",
ingest: (data.stream_name) ? true : false,
order: data.order ? data.order : 1,
stream_name: data.stream_name ? data.stream_name : "--",
ingest: data.stream_name ? true : false,
actions: "",
};
});
@@ -245,7 +256,7 @@ export default defineComponent({
if (jsTransforms.value == "" || jsTransforms.value == undefined) {
getJSTransforms();
}
interface OptionType {
label: String;
value: number | String;
@@ -275,70 +286,99 @@ export default defineComponent({
const addTransform = () => {
showAddJSTransformDialog.value = true;
}
};
const showAddUpdateFn = (props: any) => {
formData.value = props.row;
let action;
if (!props.row) {
isUpdated.value = false;
action = "Add Function";
} else {
isUpdated.value = true;
action = "Update Function";
}
addTransform();
}
segment.track("Button Click", {
button: action,
user_org: store.state.selectedOrganization.identifier,
user_id: store.state.userInfo.email,
page: "Functions",
});
};
const refreshList = () => {
showAddJSTransformDialog.value = false;
getJSTransforms();
}
};
const hideForm = () => {
showAddJSTransformDialog.value = false;
}
};
const deleteFn = () => {
if (selectedDelete.value.ingest) {
jsTransformService.delete_stream_function(store.state.selectedOrganization.identifier, selectedDelete.value.stream_name, selectedDelete.value.name).then((res: any) => {
if (res.data.code == 200) {
$q.notify({
type: "positive",
message: res.data.message,
timeout: 2000,
});
getJSTransforms();
} else {
$q.notify({
type: "negative",
message: res.data.message,
timeout: 2000,
});
}
})
jsTransformService
.delete_stream_function(
store.state.selectedOrganization.identifier,
selectedDelete.value.stream_name,
selectedDelete.value.name
)
.then((res: any) => {
if (res.data.code == 200) {
$q.notify({
type: "positive",
message: res.data.message,
timeout: 2000,
});
getJSTransforms();
} else {
$q.notify({
type: "negative",
message: res.data.message,
timeout: 2000,
});
}
});
} else {
jsTransformService
.delete(
store.state.selectedOrganization.identifier,
selectedDelete.value.name
)
.then((res: any) => {
if (res.data.code == 200) {
$q.notify({
type: "positive",
message: res.data.message,
timeout: 2000,
});
getJSTransforms();
} else {
$q.notify({
type: "negative",
message: res.data.message,
timeout: 2000,
});
}
});
}
else {
jsTransformService.delete(store.state.selectedOrganization.identifier, selectedDelete.value.name).then((res: any) => {
if (res.data.code == 200) {
$q.notify({
type: "positive",
message: res.data.message,
timeout: 2000,
});
getJSTransforms();
} else {
$q.notify({
type: "negative",
message: res.data.message,
timeout: 2000,
});
}
})
}
}
segment.track("Button Click", {
button: "Delete Function",
user_org: store.state.selectedOrganization.identifier,
user_id: store.state.userInfo.email,
function_name: selectedDelete.value.name,
is_ingest_func: selectedDelete.value.ingest,
page: "Functions",
});
};
const showDeleteDialogFn = (props: any) => {
selectedDelete.value = props.row;
confirmDelete.value = true;
}
};
return {
t,
@@ -381,18 +421,21 @@ export default defineComponent({
},
computed: {
selectedOrg() {
return this.store.state.selectedOrganization.identifier
}
return this.store.state.selectedOrganization.identifier;
},
},
watch: {
selectedOrg(newVal: any, oldVal: any) {
if ((newVal != oldVal || this.jsTransforms.value == undefined) && this.router.currentRoute.value.name=="function") {
this.resultTotal=0
if (
(newVal != oldVal || this.jsTransforms.value == undefined) &&
this.router.currentRoute.value.name == "function"
) {
this.resultTotal = 0;
this.jsTransforms = [];
this.getJSTransforms();
}
}
}
},
},
});
</script>
+28 -25
View File
@@ -27,8 +27,13 @@
<q-separator />
<q-card-actions align="center">
<q-btn no-caps color="primary" flat>{{ t("home.view") }}
<router-link exact :to="{ name: 'logstreams' }" class="absolute full-width full-height"></router-link>
<q-btn no-caps color="primary" flat
>{{ t("home.view") }}
<router-link
exact
:to="{ name: 'logstreams' }"
class="absolute full-width full-height"
></router-link>
</q-btn>
</q-card-actions>
</q-card>
@@ -42,8 +47,13 @@
</q-card-section>
<q-card-actions align="center">
<q-btn no-caps color="primary" flat>{{ t("home.view") }}
<router-link exact :to="{ name: 'functions' }" class="absolute full-width full-height"></router-link>
<q-btn no-caps color="primary" flat
>{{ t("home.view") }}
<router-link
exact
:to="{ name: 'functions' }"
class="absolute full-width full-height"
></router-link>
</q-btn>
</q-card-actions>
</q-card>
@@ -57,8 +67,13 @@
</q-card-section>
<q-card-actions align="center">
<q-btn no-caps color="primary" flat>{{ t("home.view") }}
<router-link exact :to="{ name: 'alerts' }" class="absolute full-width full-height"></router-link>
<q-btn no-caps color="primary" flat
>{{ t("home.view") }}
<router-link
exact
:to="{ name: 'alerts' }"
class="absolute full-width full-height"
></router-link>
</q-btn>
</q-card-actions>
</q-card>
@@ -74,9 +89,12 @@
<q-separator />
<q-card-actions align="center">
<q-btn no-caps color="primary" @click="() => $router.push('/ingestion/fluentbit')" flat>{{
t("home.findIngestion")
}}
<q-btn
no-caps
color="primary"
@click="() => $router.push('/ingestion/fluentbit')"
flat
>{{ t("home.findIngestion") }}
</q-btn>
</q-card-actions>
</q-card>
@@ -88,19 +106,8 @@
</q-card-section>
<q-separator />
<!--<q-card-actions align="center">
<q-btn no-caps color="primary" @click="() => $router.push('/ingestion/fluentbit')" flat>{{ t('home.findIngestion')
}}
</q-btn>
</q-card-actions>-->
</q-card>
</div>
<!--<div class="row">
<div class="col-6">
<q-btn label="Find how to ingest logs" color="primary" @click="() => $router.push('/ingestion/fluentbit')" />
</div>
</div>-->
</q-page>
</template>
@@ -208,7 +215,7 @@ export default defineComponent({
summary,
no_data_ingest,
getSummary,
isZincObserveCloud
isZincObserveCloud,
};
},
computed: {
@@ -229,16 +236,12 @@ export default defineComponent({
<style scoped lang="scss">
.pointer-to-demo {
// width: 100%;
// height: 100%;
margin-left: auto;
margin-right: 0;
}
.pointer-description {
// vertical-align: middle;
display: flex;
align-items: center;
}
</style>
+25 -1
View File
@@ -147,6 +147,7 @@ import organizationsService from "../services/organizations";
// import { config } from "../constants/config";
import config from "../aws-exports";
import ConfirmDialog from "../components/ConfirmDialog.vue";
import segment from "../services/segment_analytics";
export default defineComponent({
name: "PageIngestion",
@@ -182,6 +183,14 @@ export default defineComponent({
ingestiontab.value = router.currentRoute.value.name;
router.push({ path: "/ingestion/" + router.currentRoute.value.name });
}
segment.track("Button Click", {
button: router.currentRoute.value.name,
user_org: store.state.selectedOrganization.identifier,
user_id: store.state.userInfo.email,
page: "Ingestion",
});
getOrganizationPasscode();
});
@@ -219,6 +228,14 @@ export default defineComponent({
timeout: 5000,
});
});
segment.track("Button Click", {
button: "Copy to Clipboard",
ingestion: router.currentRoute.value.name,
user_org: store.state.selectedOrganization.identifier,
user_id: store.state.userInfo.email,
page: "Ingestion",
});
};
const updatePasscode = () => {
@@ -251,6 +268,13 @@ export default defineComponent({
timeout: 5000,
});
});
segment.track("Button Click", {
button: "Update Passcode",
user_org: store.state.selectedOrganization.identifier,
user_id: store.state.userInfo.email,
page: "Ingestion",
});
};
const showUpdateDialogFn = () => {
@@ -327,4 +351,4 @@ export default defineComponent({
}
}
}
</style>
</style>
+17
View File
@@ -141,6 +141,8 @@ import QTablePagination from "../components/shared/grid/Pagination.vue";
import indexService from "../services/index";
import SchemaIndex from "../components/logstream/schema.vue";
import NoData from "../components/shared/grid/NoData.vue";
import segment from "../services/segment_analytics";
import config from "../aws-exports";
export default defineComponent({
name: "PageLogStream",
@@ -256,6 +258,13 @@ export default defineComponent({
});
});
}
segment.track("Button Click", {
button: "Refresh Streams",
user_org: store.state.selectedOrganization.identifier,
user_id: store.state.userInfo.email,
page: "Streams",
});
};
getLogStream();
@@ -265,6 +274,14 @@ export default defineComponent({
schemaData.value.schema = props.row.schema;
schemaData.value.stream_type = props.row.stream_type;
showIndexSchemaDialog.value = true;
segment.track("Button Click", {
button: "Actions",
user_org: store.state.selectedOrganization.identifier,
user_id: store.state.userInfo.email,
stream_name: props.row.name,
page: "Streams",
});
};
const perPageOptions: any = [
+7 -232
View File
@@ -14,241 +14,16 @@
-->
<template>
<q-layout view="hHh lpR fFf">
<q-page-container>
<q-page class="fullscreen bg-grey-7 flex flex-center">
<q-card square class="my-card shadow-24 bg-white text-white">
<q-card-section class="bg-primary">
<div class="text-h5 q-my-md">ZincObserve</div>
</q-card-section>
<q-card>
<q-card-section class="bg-white">
<q-form ref="loginform" class="q-gutter-md" @submit.prevent="">
<q-input
v-model="name"
data-cy="login-user-id"
label="Email *"
@blur="getOrganizations"
>
<template #prepend>
<q-icon name="email" />
</template>
</q-input>
<q-input
v-model="password"
data-cy="login-password"
type="password"
:label="t('login.password') + ' *'"
>
<template #prepend>
<q-icon name="lock" />
</template>
</q-input>
<q-select
v-model="org_identifier"
:options="organizations"
label="Select Organization *"
:display-value="`${
org_identifier ? org_identifier.label : '-- Select --'
}`"
>
<template #prepend>
<q-icon name="corporate_fare" />
</template>
</q-select>
<q-card-actions class="q-px-lg q-mt-md q-mb-xl">
<q-btn
data-cy="login-sign-in"
unelevated
size="lg"
class="full-width"
color="primary"
type="submit"
:label="t('login.signIn')"
:loading="submitting"
no-caps
@click="onSignIn()"
/>
</q-card-actions>
</q-form>
</q-card-section>
</q-card>
</q-card>
</q-page>
</q-page-container>
</q-layout>
<login></login>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import { useStore } from "vuex";
import { useQuasar } from "quasar";
import { useRouter } from "vue-router";
import Login from "../components/login/LoginOpensource.vue";
import { useI18n } from "vue-i18n";
import authService from "../services/auth";
import {
useLocalToken,
getBasicAuth,
b64EncodeUnicode,
useLocalUserInfo,
useLocalCurrentUser,
useLocalOrganization,
} from "../utils/zincutils";
import { getDefaultOrganization, redirectUser } from "../utils/common";
export default defineComponent({
name: "PageLogin",
setup() {
const store = useStore();
const router = useRouter();
const $q = useQuasar();
const { t } = useI18n();
const name = ref("");
const org_identifier = ref();
const organizations = ref([]);
const password = ref("");
const confirmpassword = ref("");
const email = ref("");
const loginform = ref();
const submitting = ref(false);
const onSignIn = () => {
if (org_identifier.value == undefined) {
$q.notify({
color: "negative",
message: "Organization is required",
});
return false;
}
if (name.value == "" || password.value == "") {
$q.notify({
position: "top",
color: "warning",
textColor: "white",
icon: "warning",
message: "Please input",
});
} else {
submitting.value = true;
try {
authService
.sign_in_user(org_identifier.value.identifier, {
name: name.value,
password: password.value,
})
.then(async (res: any) => {
if (res.data.status == true) {
const authToken = getBasicAuth(name.value, password.value);
useLocalToken(authToken);
const userInfo = {
given_name: name.value,
auth_time: Math.floor(Date.now() / 1000),
name: name.value,
exp: Math.floor(
(new Date().getTime() + 1000 * 60 * 60 * 24 * 30) / 1000
),
family_name: "",
email: name.value,
role: res.data.role,
};
const encodedUserInfo = b64EncodeUnicode(
JSON.stringify(userInfo)
);
useLocalUserInfo(encodedUserInfo);
store.dispatch("setUserInfo", encodedUserInfo);
useLocalCurrentUser(JSON.stringify(userInfo));
store.dispatch("setCurrentUser", userInfo);
const organizations = await getDefaultOrganization(
userInfo,
org_identifier.value.identifier
);
store.dispatch("setOrganizations", organizations);
const selectedOrgs = useLocalOrganization(org_identifier.value);
store.dispatch("setSelectedOrganization", selectedOrgs);
const redirectURI =
window.sessionStorage.getItem("redirectURI");
window.sessionStorage.removeItem("redirectURI");
redirectUser(redirectURI);
} else {
submitting.value = false;
loginform.value.resetValidation();
$q.notify({
color: "negative",
message: res.data.message,
});
}
})
.catch((e: Error) => {
submitting.value = false;
loginform.value.resetValidation();
$q.notify({
color: "negative",
message: "Invalid username or password",
});
console.log(e);
});
} catch (e) {
submitting.value = false;
loginform.value.resetValidation();
$q.notify({
color: "negative",
message: "Please fill all the fields and try again.",
});
console.log(e);
}
}
};
const getOrganizations = () => {
authService
.get_organization_by_username(name.value)
.then((res) => {
organizations.value = res.data;
org_identifier.value = res.data[0];
})
.catch((e) => {
console.log(e);
});
};
return {
t,
name,
password,
confirmpassword,
org_identifier,
organizations,
email,
loginform,
submitting,
onSignIn,
tab: ref("signin"),
innerTab: ref("signup"),
getOrganizations,
};
export default {
name: "LoginPage",
components: {
Login,
},
methods: {
selected(item: any) {
this.$q.notify(`Selected suggestion "${item.label}"`);
},
},
});
};
</script>
<style lang="scss">
.my-card {
width: 400px;
}
</style>
+147 -100
View File
@@ -26,23 +26,6 @@
:filter="filterQuery"
:filter-method="filterData"
>
<!--
<template v-slot:body-cell-actions="props">
<q-td :props="props">
<q-btn
v-if="props.row.email != store.state.userInfo.email"
icon="perm_identity"
class="iconHoverBtn"
dense
unelevated
size="sm"
color="blue-5"
@click="updateUser(props)"
:title="t('organization.invite')"
></q-btn>
</q-td>
</template>
-->
<template #no-data>
<NoData></NoData>
</template>
@@ -84,7 +67,8 @@
/>
<q-btn
v-if="
props.row.isLoggedinUser ||
(config.isZincObserveCloud == 'true' &&
props.row.isLoggedinUser) ||
currentUserRole == 'root' ||
(currentUserRole == 'admin' && props.row.role !== 'root')
"
@@ -118,12 +102,12 @@
</q-input>
<div
class="col-5 q-pr-sm"
class="col-6 q-pr-sm"
v-if="
currentUserRole == 'admin' && config.isZincObserveCloud == 'true'
"
>
<div class="row invite-user">
<div class="row invite-user" style="width: 82%; display:inline-flex;">
<q-input
v-model="userEmail"
class="col-9 q-pl-md"
@@ -143,9 +127,11 @@
/>
</div>
</div>
<label class="inputHint">{{ t("user.inviteByEmailHint") }}</label>
<q-btn
v-if="currentUserRole == 'admin'"
v-if="
currentUserRole == 'admin' &&
config.isZincObserveCloud == 'true'
"
class="col-1 text-bold no-border"
padding="sm 0"
color="secondary"
@@ -153,9 +139,13 @@
:label="t(`user.sendInvite`)"
@click="inviteUser()"
:disable="userEmail == ''"
style="padding: 7px 9px;min-width: 0px;min-height: 0px;display: block;float: right;top: 1px;"
/>
<label class="inputHint" style="display: block">{{
t("user.inviteByEmailHint")
}}</label>
</div>
<div v-else class="col-6">
<div v-else-if="config.isZincObserveCloud !== 'true'" class="col-6">
<q-btn
v-if="currentUserRole == 'admin' || currentUserRole == 'root'"
class="q-ml-md q-mb-xs text-bold no-border"
@@ -200,6 +190,7 @@
</q-dialog>
<q-dialog
v-if="config.isZincObserveCloud !== 'true'"
v-model="showAddUserDialog"
position="right"
full-height
@@ -249,11 +240,12 @@ import { useI18n } from "vue-i18n";
import config from "../aws-exports";
import QTablePagination from "../components/shared/grid/Pagination.vue";
import usersService from "../services/users";
import UpdateUserRole from "../components/users/UpdateRole.vue"
import AddUser from "../components/users/add.vue"
import UpdateUserRole from "../components/users/UpdateRole.vue";
import AddUser from "../components/users/add.vue";
import NoData from "../components/shared/grid/NoData.vue";
import { validateEmail } from "../utils/zincutils";
import organizationsService from "../services/organizations";
import segment from "../services/segment_analytics";
export default defineComponent({
name: "PageUser",
@@ -321,43 +313,58 @@ export default defineComponent({
align: "left",
},
]);
const userEmail: any = ref('');
const options = [t("user.admin"), t("user.member")]
const userEmail: any = ref("");
const options = [t("user.admin"), t("user.member")];
const selectedRole = ref(options[0]);
const currentUserRole = ref('')
const currentUserRole = ref("");
const getOrgMembers = () => {
const dismiss = $q.notify({
spinner: true,
message: "Please wait while loading users...",
});
usersService.orgUsers(0, 1000, "email", false, "", store.state.selectedOrganization.identifier).then((res) => {
resultTotal.value = res.data.data.length;
// columns.value = columns.value.filter((v: any) => v.name !== "actions");
let counter = 1;
orgMembers.value = res.data.data.map((data: any) => {
if (store.state.userInfo.email == data.email) {
currentUserRole.value = data.role
usersService
.orgUsers(
0,
1000,
"email",
false,
"",
store.state.selectedOrganization.identifier
)
.then((res) => {
resultTotal.value = res.data.data.length;
if (config.isZincObserveCloud == 'true') {
columns.value = columns.value.filter((v: any) => v.name !== "actions");
}
let counter = 1;
orgMembers.value = res.data.data.map((data: any) => {
if (store.state.userInfo.email == data.email) {
currentUserRole.value = data.role;
}
return {
"#": counter <= 9 ? `0${counter++}` : counter++,
email: data.email,
first_name: data.first_name,
last_name: data.last_name,
role: data.role,
member_created: date.formatDate(parseInt(data.member_created), "YYYY-MM-DDTHH:mm:ssZ"),
member_updated: date.formatDate(parseInt(data.member_updated), "YYYY-MM-DDTHH:mm:ssZ"),
org_member_id: data.org_member_id,
isLoggedinUser: store.state.userInfo.email == data.email
};
return {
"#": counter <= 9 ? `0${counter++}` : counter++,
email: data.email,
first_name: data.first_name,
last_name: data.last_name,
role: data.role,
member_created: date.formatDate(
parseInt(data.member_created),
"YYYY-MM-DDTHH:mm:ssZ"
),
member_updated: date.formatDate(
parseInt(data.member_updated),
"YYYY-MM-DDTHH:mm:ssZ"
),
org_member_id: data.org_member_id,
isLoggedinUser: store.state.userInfo.email == data.email,
};
});
dismiss();
});
dismiss();
});
};
if (orgMembers.value.length == 0) {
@@ -393,7 +400,7 @@ export default defineComponent({
const updateUser = (props: any) => {
selectedUser.value = props.row;
showUpdateUserDialog.value = true;
}
};
const addUser = (props: any, is_updated: boolean) => {
isUpdated.value = is_updated;
@@ -404,7 +411,15 @@ export default defineComponent({
}
showAddUserDialog.value = true;
}
segment.track("Button Click", {
button: "Actions",
user_org: store.state.selectedOrganization.identifier,
user_id: store.state.userInfo.email,
update_user: props.row.email,
page: "Users",
});
};
const updateMember = (data: any) => {
if (data.data != undefined) {
@@ -415,7 +430,7 @@ export default defineComponent({
});
showUpdateUserDialog.value = false;
}
}
};
const addMember = (res: any, data: any, operationType: string) => {
showAddUserDialog.value = false;
@@ -444,15 +459,20 @@ export default defineComponent({
});
}
}
}
};
const deleteUser = (props: any) => {
confirmDelete.value = true;
};
const inviteUser = () => {
const emailArray = userEmail.value.split(';').filter((email: any) => email).map((email: any) => email.trim())
const validationArray = emailArray.map((email: any) => validateEmail(email))
const emailArray = userEmail.value
.split(";")
.filter((email: any) => email)
.map((email: any) => email.trim());
const validationArray = emailArray.map((email: any) =>
validateEmail(email)
);
if (!validationArray.includes(false)) {
const dismiss = $q.notify({
@@ -461,10 +481,11 @@ export default defineComponent({
timeout: 2000,
});
organizationsService.add_members(
{ member_lists: emailArray, role: selectedRole.value },
store.state.selectedOrganization.identifier
)
organizationsService
.add_members(
{ member_lists: emailArray, role: selectedRole.value },
store.state.selectedOrganization.identifier
)
.then((res: { data: any }) => {
const data = res.data;
@@ -486,60 +507,78 @@ export default defineComponent({
// this.$emit("updated");
dismiss();
}).catch(error => {
})
.catch((error) => {
dismiss();
$q.notify({
type: "negative",
message: error.message,
timeout: 5000,
});
})
});
userEmail.value = "";
segment.track("Button Click", {
button: "Invite User",
user_org: store.state.selectedOrganization.identifier,
user_id: store.state.userInfo.email,
page: "Users",
});
} else {
$q.notify({
type: "negative",
message: `Please enter correct email id.`,
});
}
}
};
const updateUserRole = (row: any) => {
const dismiss = $q.notify({
spinner: true,
message: "Please wait...",
timeout: 2000,
});
organizationsService.update_member_role(
{
id: parseInt(row.orgMemberId),
role: row.role,
email: row.email,
organization_id: parseInt(store.state.selectedOrganization.id),
},
store.state.selectedOrganization.identifier
).then((res: { data: any }) => {
if (res.data.error_members != null) {
const message = `Error while updating organization member`;
$q.notify({
type: "negative",
message: message,
timeout: 15000,
});
} else {
$q.notify({
type: "positive",
message: "Organization member updated successfully.",
timeout: 3000,
});
}
dismiss();
}).catch(error => {
dismiss();
console.log(error);
organizationsService
.update_member_role(
{
id: parseInt(row.orgMemberId ? row.orgMemberId : row.org_member_id),
role: row.role,
email: row.email,
organization_id: parseInt(store.state.selectedOrganization.id),
},
store.state.selectedOrganization.identifier
)
.then((res: { data: any }) => {
if (res.data.error_members != null) {
const message = `Error while updating organization member`;
$q.notify({
type: "negative",
message: message,
timeout: 15000,
});
} else {
$q.notify({
type: "positive",
message: "Organization member updated successfully.",
timeout: 3000,
});
}
dismiss();
})
.catch((error) => {
dismiss();
console.log(error);
});
segment.track("Button Click", {
button: "Update Role",
user_org: store.state.selectedOrganization.identifier,
user_id: store.state.userInfo.email,
update_user: row.email,
page: "Users",
});
}
};
return {
t,
qTable,
@@ -572,7 +611,12 @@ export default defineComponent({
var filtered = [];
terms = terms.toLowerCase();
for (var i = 0; i < rows.length; i++) {
if (rows[i]["first_name"]?.toLowerCase().includes(terms) || rows[i]["last_name"]?.toLowerCase().includes(terms) || rows[i]["email"]?.toLowerCase().includes(terms) || rows[i]["role"].toLowerCase().includes(terms)) {
if (
rows[i]["first_name"]?.toLowerCase().includes(terms) ||
rows[i]["last_name"]?.toLowerCase().includes(terms) ||
rows[i]["email"]?.toLowerCase().includes(terms) ||
rows[i]["role"].toLowerCase().includes(terms)
) {
filtered.push(rows[i]);
}
}
@@ -583,22 +627,25 @@ export default defineComponent({
options,
inviteUser,
currentUserRole,
updateUserRole
updateUserRole,
};
},
computed: {
selectedOrg() {
return this.store.state.selectedOrganization.identifier
}
return this.store.state.selectedOrganization.identifier;
},
},
watch: {
selectedOrg(newVal: any, oldVal: any) {
this.orgData = newVal;
if ((newVal != oldVal || this.orgMembers.value == undefined) && this.router.currentRoute.value.name == "users") {
if (
(newVal != oldVal || this.orgMembers.value == undefined) &&
this.router.currentRoute.value.name == "users"
) {
this.getOrgMembers();
}
}
}
},
},
});
</script>