TT#122202 ProxiedFeature - As SubscriberAdmin, I want to be able to access the "Customer Details" page in v1 from within v2

AC:
Can click on a main menu point to open Subscriber management
Can see a page opening and loading the page from v1 within v2

Note, to test it locally:
1. you need to create the "quasar.conf.dev.js" file with content like

  module.exports = {
      public: '',
      publicPath: '/v2/',
      proxyAPI2localhost: true,
      proxyAPIFromURL: 'https://dev-web3-trunk.mgm.sipwise.com'
  }

2. "src/config.js" should have

  baseHttpUrl: '',

3. to login into system you have to use "username@domain" login !!!

Change-Id: I3af7caeb242a52e573d87c50fe05c9462f95bfb3
mr9.5.2
Sergii Leonenko 4 years ago
parent ccdf8971bb
commit 31f00c6aa0

2
.gitignore vendored

@ -37,3 +37,5 @@ yarn-error.log*
/junit.xml
/test/jest/coverage
quasar.conf.dev.js

@ -0,0 +1,7 @@
module.exports = {
public: '',
publicPath: '/v2/',
proxyAPI2localhost: true,
proxyAPIFromURL: 'https://{{sipwiseVoipPlatformIPorFDQN}}'
}

@ -1,3 +1,4 @@
/* eslint-env node */
/*
* This file runs in a Node context (it's NOT transpiled by Babel), so use only
* the ES6 features that are supported by your Node version. https://node.green/
@ -5,9 +6,18 @@
// Configuration for your app
// https://quasar.dev/quasar-cli/quasar-conf-js
/* eslint-env node */
module.exports = function (ctx) {
let devServerConfig = {}
try {
devServerConfig = (ctx.dev) ? require('./quasar.conf.dev.js') : {}
} catch (e) {
if (e.code === 'MODULE_NOT_FOUND') {
devServerConfig = {}
} else {
throw e
}
}
module.exports = function (/* ctx */) {
return {
// https://quasar.dev/quasar-cli/supporting-ts
supportTS: false,
@ -88,7 +98,24 @@ module.exports = function (/* ctx */) {
devServer: {
https: false,
port: 8080,
open: true // opens browser window automatically
open: true, // opens browser window automatically,
public: devServerConfig.public,
publicPath: devServerConfig.publicPath,
...(!devServerConfig.proxyAPI2localhost ? {} : {
https: true,
publicPath: devServerConfig.publicPath || '/v2/',
proxy: {
[`!${devServerConfig.publicPath || '/v2/'}`]: {
target: devServerConfig.proxyAPIFromURL,
secure: false
}
},
before: function (app, server, compiler) {
app.get('/', function (req, res) {
res.redirect(301, devServerConfig.publicPath || '/v2/')
})
}
})
},
// https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-framework

@ -5,6 +5,7 @@ import config from 'src/config'
Vue.use({
install (Vue, options) {
Vue.$config = config
Vue.prototype.$config = config
}
})

@ -48,7 +48,9 @@ export default {
'isRtcEngineUiVisible',
'isPbxEnabled',
'hasFaxCapability',
'hasSubscriberProfileAttribute'
'hasSubscriberProfileAttribute',
'getCustomerId',
'isOldCSCProxyingAllowed'
]),
items () {
return [
@ -200,6 +202,12 @@ export default {
icon: 'devices',
label: this.$t('Registered Devices'),
visible: true
},
{
to: '/customer/' + this.getCustomerId + '/details',
icon: 'far fa-address-card',
label: this.$t('Customer Details'),
visible: this.isOldCSCProxyingAllowed
}
]
}

@ -178,6 +178,7 @@
"Custom Announcement": "Custom Announcement",
"Custom Announcements": "Custom Announcements",
"Custom sound": "Custom sound",
"Customer Details": "Customer Details",
"Daily": "Daily",
"Default": "Default",
"Default For Subscribers": "Default For Subscribers",

@ -5,6 +5,7 @@ import localeIt from './it.json'
import localeEs from './es.json'
import localeDe from './de.json'
import localeRu from './ru.json'
import { i18n } from 'src/boot/i18n'
export const defaultLocale = 'en-US'
@ -43,3 +44,25 @@ function patchKeysForFallback (messages = {}) {
})
return messages
}
/**
* It converts language code from V2 (new CSC) to V1 UI (old Panel CSC) format
* @param {string} lang
* @returns {string}
*/
export function convertLangV2toV1 (lang) {
return lang === 'en-US' ? 'en' : lang
}
/**
* It converts language code from V1 (old Panel CSC) to V2 UI (new CSC) format
* @param {string} lang
* @returns {string}
*/
export function convertLangV1toV2 (lang) {
return ['en', 'i-default'].includes(lang) ? 'en-US' : lang
}
export function getCurrentLangAsV1Format () {
return convertLangV2toV1(i18n.locale)
}

@ -0,0 +1,159 @@
<template>
<div
class="proxy-iframe-wrapper flex flex-center"
>
<iframe
v-show="loaded"
ref="proxyIframe"
class="proxy-iframe"
:src="finalSrc"
@load="loadedEvent"
/>
<q-spinner
v-if="!loaded"
color="primary"
size="xl"
/>
</div>
</template>
<script>
import _ from 'lodash'
import {
mapActions,
mapState
} from 'vuex'
import { showGlobalError } from 'src/helpers/ui'
import { getCurrentLangAsV1Format } from 'src/i18n'
export default {
name: 'Proxy',
data () {
return {
loaded: false
}
},
computed: {
...mapState('user', [
'currentPathIframe'
]),
language () {
return getCurrentLangAsV1Format()
},
finalSrc () {
let url = null
if (_.isString(this.$config.baseHttpUrl) && _.trim(this.$config.baseHttpUrl) !== '') {
url = new URL(this.$config.baseHttpUrl)
} else {
url = new URL(location.origin)
}
url.searchParams.set('framed', '1')
url.searchParams.set('lang', this.language)
if (this.$route?.meta?.proxyRewrite) {
return this.$route?.meta?.proxyRewrite({
route: this.$route,
url: url
}).toString()
} else {
url.pathname = this.$route.path
return url.toString()
}
}
},
watch: {
currentPathIframe (path) {
const routeData = this.$router.resolve(path)
if (!routeData?.route?.meta?.proxy && !routeData?.route?.meta?.proxyReverseInvisible) {
this.$router.push({
path: path
})
}
}
},
mounted () {
window.addEventListener('message', this.trackMessagesFromV1, false)
},
beforeDestroy () {
window.removeEventListener('message', this.trackMessagesFromV1, false)
},
methods: {
...mapActions('user', [
'logout'
]),
loadedEvent () {
try {
const domEl = this.$refs.proxyIframe.contentWindow.document.getElementById('login_page_v1')
if (domEl !== null) {
this.logout()
return
}
this.injectDarkUITheme()
} catch (err) {
console.debug('Session expiration detection is disabled')
console.debug(err)
} finally {
this.loaded = true
}
},
injectDarkUITheme () {
const framedWindow = this.$refs.proxyIframe.contentWindow
const $ = framedWindow.$
const darkThemeCSS = `
body {
background: #3b3440; /* $darkBase */
color: white;
overflow: auto;
}
h1, h2, h3, h4, h5, h6 { color: white; }
.table-hover, .table-highlight { color: black; }
.table-hover tbody tr:hover > td,
.table-hover tbody tr:hover > th { background: none; }
.table-highlight.table-bordered tbody tr td,
.table-highlight.table-bordered tbody tr th { background-color: #F0F3F7; }
.table-highlight.table-bordered thead tr,
table.ngcp-datatable tfoot tr { background: silver; }
.table-highlight.table-bordered thead th,
table.ngcp-datatable tfoot td {
text-shadow: none;
color: black;
border-left: 1px solid #F1F1F1;
border-right: 1px solid #CCC;
box-shadow: none;
}
.ngcp-modal,
.jquery-msgbox { color: #333; }
.modal-header { background: silver; border-color: white; }
.modal-header h3 { color: black; text-shadow: none; }
.accordion-inner { border-top: none; }
`
$(`<style>${darkThemeCSS}</style>`).appendTo(framedWindow.document.head)
},
trackMessagesFromV1 (event) {
if (event?.data?.origin === 'ngcp-panel') {
if (event?.data?.error) {
showGlobalError(event.data.error)
}
}
if (event?.data?.origin === 'ngcp-panel-beforeunload') {
this.loaded = false
}
}
}
}
</script>
<style lang="sass" rel="stylesheet/sass">
.proxy-iframe-wrapper
height: calc(100vh - 100px)
width: 100%
.proxy-iframe
border: none
height: calc(100vh - 100px)
width: 100%
</style>

@ -355,6 +355,21 @@ export default function routes (app) {
}
}
},
{
path: '/customer/*',
component: () => import('pages/Proxy'),
meta: {
title: i18n.t('Customer Details'),
subtitle: i18n.t('Customer Details')
},
async beforeEnter (routeTo, routeFrom, next) {
if (app.store.getters['user/isOldCSCProxyingAllowed']) {
next()
} else {
next('/')
}
}
},
{
path: '*',
component: CscPageError404,

@ -173,6 +173,9 @@ export default {
getSubscriber (state) {
return state.subscriber
},
getCustomerId (state) {
return state.subscriber?.customer_id
},
isPasswordChanging (state) {
return state.changePasswordState === RequestState.requesting
},
@ -199,6 +202,9 @@ export default {
},
hasSubscriberProfileAttributes: (state) => (attributes) => {
return state.profile ? state.profile.attributes.some(item => attributes.includes(item)) : true
},
isOldCSCProxyingAllowed (state, getters) {
return getters.isAdmin && state.platformInfo?.csc_v2_mode === 'mixed' && !!getters.getCustomerId
}
},
mutations: {

@ -5,7 +5,7 @@ FROM docker.mgm.sipwise.com/sipwise-bullseye:latest
# is updated with the current date. It will force refresh of all
# of the base images and things like `apt-get update` won't be using
# old cached versions when the Dockerfile is built.
ENV REFRESHED_AT 2021-07-07
ENV REFRESHED_AT 2021-07-08
ENV DEBIAN_FRONTEND noninteractive
ENV DISPLAY=:0

Loading…
Cancel
Save