From 193ad3e11583db45c6a92441375e735e14645f6d Mon Sep 17 00:00:00 2001 From: Daniil Lohvinov Date: Thu, 7 Mar 2024 21:49:33 +0200 Subject: [PATCH 1/4] feat: working on recaptcha in progress [WTEL-4251] --- .../wt-omni-widget-window.vue | 62 +++++++++++++++++-- src/app/css/_main.scss | 4 ++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/app/components/wt-omni-widget-window/wt-omni-widget-window.vue b/src/app/components/wt-omni-widget-window/wt-omni-widget-window.vue index 1b423ae..5f956d2 100644 --- a/src/app/components/wt-omni-widget-window/wt-omni-widget-window.vue +++ b/src/app/components/wt-omni-widget-window/wt-omni-widget-window.vue @@ -5,10 +5,16 @@ :channel="channel" @close="$emit('close')" > - + + + + + + + +
+ + {{ state }} @@ -18,6 +24,13 @@ import CallWrapper from '../../../modules/call/components/wt-omni-widget-call-wr import WidgetChannel from '../../enums/WidgetChannel.enum'; import WtOmniWidgetHeader from './wt-omni-widget-window-header/wt-omni-widget-window-header.vue'; +const script = document.createElement('script'); +script.src = `https://www.google.com/recaptcha/api.js?render=${sitekey}`; +// script.async = true; +document.head.appendChild(script); + +import axios from 'axios'; + export default { name: 'wt-omni-widget-window', components: { @@ -25,6 +38,10 @@ export default { ChatWrapper, CallWrapper, }, + data: () => ({ + state: 'empty', + sitekeyv2, + }), props: { channel: { type: String, // WidgetChannel.enum @@ -43,6 +60,43 @@ export default { } }, }, + methods: { + onVerify(state, response) { + // console.info('response', state, response); + // this.state = state; + }, + }, + mounted() { + const button = document.getElementById('cptch'); + button.addEventListener('click', () => { + window.grecaptcha.ready(() => { + window.grecaptcha.execute(sitekey, { action: 'homepage' }) + .then(async (token) => { + this.onVerify('verified', token); + const res = await axios.post('https://dev.webitel.com/api/recaptcha', { + response: token, + version: '3', + }); + console.info(res.data.success, res.data.score); + }); + }); + }); + + const button2 = document.getElementById('cptch2'); + button2.addEventListener('click', () => { + window.grecaptcha.render(document.getElementById('g-recaptcha-v2'), { + sitekey: sitekeyv2, + callback: async (response) => { + this.onVerify('verified v2', response); + const res = await axios.post('https://dev.webitel.com/api/recaptcha', { + version: '2', + response, + }); + console.info(res.data); + }, + }); + }); + }, }; diff --git a/src/app/css/_main.scss b/src/app/css/_main.scss index e6bfa09..18caf08 100644 --- a/src/app/css/_main.scss +++ b/src/app/css/_main.scss @@ -4,6 +4,10 @@ @import 'variables/scroll'; @import 'styleguide/placeholder/placeholder'; +// https://developers.google.com/recaptcha/docs/faq#id-like-to-hide-the-recaptcha-badge.-what-is-allowed +.grecaptcha-badge { + visibility: hidden; +} .wt-omni-widget--reset-styles, .wt-omni-widget--reset-styles * { From 43a9fc71195011e42c46c8466b49dc24a9814cbc Mon Sep 17 00:00:00 2001 From: Daniil Lohvinov Date: Fri, 8 Mar 2024 21:54:43 +0200 Subject: [PATCH 2/4] refactor: reCAPTCHA refactor [WTEL-4251] --- .../wt-omni-widget-window.vue | 59 ++----------------- src/install.js | 3 + src/main.js | 3 + .../components/wt-omni-widget-call-start.vue | 15 ++++- .../wt-omni-widget-chat-wrapper.vue | 25 +++++--- .../reCAPTCHA-verification/api/reCAPTCHA.js | 30 ++++++++++ .../scripts/reCAPTCHify.js | 15 +++++ 7 files changed, 87 insertions(+), 63 deletions(-) create mode 100644 src/modules/reCAPTCHA-verification/api/reCAPTCHA.js create mode 100644 src/modules/reCAPTCHA-verification/scripts/reCAPTCHify.js diff --git a/src/app/components/wt-omni-widget-window/wt-omni-widget-window.vue b/src/app/components/wt-omni-widget-window/wt-omni-widget-window.vue index 5f956d2..863d4a8 100644 --- a/src/app/components/wt-omni-widget-window/wt-omni-widget-window.vue +++ b/src/app/components/wt-omni-widget-window/wt-omni-widget-window.vue @@ -5,16 +5,10 @@ :channel="channel" @close="$emit('close')" > - - - - - - - -
- - {{ state }} + @@ -24,13 +18,6 @@ import CallWrapper from '../../../modules/call/components/wt-omni-widget-call-wr import WidgetChannel from '../../enums/WidgetChannel.enum'; import WtOmniWidgetHeader from './wt-omni-widget-window-header/wt-omni-widget-window-header.vue'; -const script = document.createElement('script'); -script.src = `https://www.google.com/recaptcha/api.js?render=${sitekey}`; -// script.async = true; -document.head.appendChild(script); - -import axios from 'axios'; - export default { name: 'wt-omni-widget-window', components: { @@ -40,7 +27,6 @@ export default { }, data: () => ({ state: 'empty', - sitekeyv2, }), props: { channel: { @@ -60,43 +46,6 @@ export default { } }, }, - methods: { - onVerify(state, response) { - // console.info('response', state, response); - // this.state = state; - }, - }, - mounted() { - const button = document.getElementById('cptch'); - button.addEventListener('click', () => { - window.grecaptcha.ready(() => { - window.grecaptcha.execute(sitekey, { action: 'homepage' }) - .then(async (token) => { - this.onVerify('verified', token); - const res = await axios.post('https://dev.webitel.com/api/recaptcha', { - response: token, - version: '3', - }); - console.info(res.data.success, res.data.score); - }); - }); - }); - - const button2 = document.getElementById('cptch2'); - button2.addEventListener('click', () => { - window.grecaptcha.render(document.getElementById('g-recaptcha-v2'), { - sitekey: sitekeyv2, - callback: async (response) => { - this.onVerify('verified v2', response); - const res = await axios.post('https://dev.webitel.com/api/recaptcha', { - version: '2', - response, - }); - console.info(res.data); - }, - }); - }); - }, }; diff --git a/src/install.js b/src/install.js index e916e2c..6b819e5 100644 --- a/src/install.js +++ b/src/install.js @@ -11,6 +11,8 @@ import eventBus from '@webitel/ui-sdk/src/scripts/eventBus'; import globalConfigMixin from './app/mixins/globalConfigMixin'; +import { initializeReCAPTCHA } from './modules/reCAPTCHA-verification/api/reCAPTCHA'; + import './app/assets/icons/sprite'; import './app/css/fonts/_fonts.scss'; @@ -56,6 +58,7 @@ export default class WtOmniWidget { async mountApp({ selector, config }) { await this.setConfig(config); + await initializeReCAPTCHA(config.reCAPTCHA.sitekey); Instance.$mount(selector); } diff --git a/src/main.js b/src/main.js index 80d611c..22cf631 100644 --- a/src/main.js +++ b/src/main.js @@ -23,4 +23,7 @@ export default new WtOmniWidget('#wt-omni-widget', { url: 'wss://dev.webitel.com/sip', id: 'dania-webchat', }, + reCAPTCHA: { + sitekey: '', + }, }); diff --git a/src/modules/call/components/wt-omni-widget-call-start.vue b/src/modules/call/components/wt-omni-widget-call-start.vue index da8281f..afa708d 100644 --- a/src/modules/call/components/wt-omni-widget-call-start.vue +++ b/src/modules/call/components/wt-omni-widget-call-start.vue @@ -22,8 +22,9 @@ icon="call" icon-size="sm" color="success" - @click="() => makeCall({ initWithMuted })" + @click="call" > + err: {{ err }} @@ -31,6 +32,7 @@ diff --git a/src/modules/chat/components/wt-omni-widget-chat-wrapper.vue b/src/modules/chat/components/wt-omni-widget-chat-wrapper.vue index 0abccea..d537518 100644 --- a/src/modules/chat/components/wt-omni-widget-chat-wrapper.vue +++ b/src/modules/chat/components/wt-omni-widget-chat-wrapper.vue @@ -10,6 +10,7 @@ why there's d: contents; and this weird wrapper? + err: {{ err }} @@ -25,6 +26,7 @@ import WidgetChannel from '../../../app/enums/WidgetChannel.enum'; import MessageClient from '../../../app/websocket/MessageClient'; import ChatContent from './wt-omni-widget-chat-content/wt-omni-widget-window-content.vue'; import ChatFooter from './wt-omni-widget-chat-footer/wt-omni-widget-window-footer.vue'; +import reCAPTCHify from '../../reCAPTCHA-verification/scripts/reCAPTCHify'; export default { name: 'wt-omni-widget-chat-wrapper', @@ -40,9 +42,12 @@ export default { required: true, }, }, + data: () => ({ + err: null, + }), computed: { ...mapState('chat', { - client: state => state.messageClient, + client: (state) => state.messageClient, }), }, methods: { @@ -89,15 +94,21 @@ export default { }, }, - created() { + async created() { if (this.isPreviewMode) { this.initPreviewMode(); } else { - this.initSession(); - window.addEventListener('beforeunload', async (e) => { - await this.closeSession(); - delete e.returnValue; // page will always reload - }); + try { + await reCAPTCHify(() => { + this.initSession(); + window.addEventListener('beforeunload', async (e) => { + await this.closeSession(); + delete e.returnValue; // page will always reload + }); + }); + } catch (err) { + this.err = err; + } } }, }; diff --git a/src/modules/reCAPTCHA-verification/api/reCAPTCHA.js b/src/modules/reCAPTCHA-verification/api/reCAPTCHA.js new file mode 100644 index 0000000..a0145e0 --- /dev/null +++ b/src/modules/reCAPTCHA-verification/api/reCAPTCHA.js @@ -0,0 +1,30 @@ +import instance from '../../../app/api/instance.axios'; + +let sitekey = null; + +export const initializeReCAPTCHA = async (_sitekey) => { + sitekey = _sitekey; + + const script = document.createElement('script'); + script.src = `https://www.google.com/recaptcha/api.js?render=${sitekey}`; + script.async = true; + document.head.appendChild(script); +}; + +const executeReCAPTCHA = ({ action }) => window.grecaptcha.execute(sitekey, { action }); + +const verifyReCAPTCHA = (token) => { + const url = 'https://dev.webitel.com/api/recaptcha'; // FIXME + // if throws error, then isn't verified + return instance.post(url, { + response: token, + version: '3', + }); +}; + +const reCAPTCHA = { + executeReCAPTCHA, + verifyReCAPTCHA, +}; + +export default reCAPTCHA; diff --git a/src/modules/reCAPTCHA-verification/scripts/reCAPTCHify.js b/src/modules/reCAPTCHA-verification/scripts/reCAPTCHify.js new file mode 100644 index 0000000..b40b5b5 --- /dev/null +++ b/src/modules/reCAPTCHA-verification/scripts/reCAPTCHify.js @@ -0,0 +1,15 @@ +import reCAPTCHA from '../api/reCAPTCHA'; + +const reCAPTCHify = async (callback, { + action = 'default_action', +} = {}) => { + try { + const token = await reCAPTCHA.executeReCAPTCHA({ action }); + const response = await reCAPTCHA.verifyReCAPTCHA(token); + return callback(response); + } catch (err) { + throw err || new Error('unknown reCAPTCHA error'); + } +}; + +export default reCAPTCHify; From c713592b90a1baa00fc3f820531ff3d15bb8fe2c Mon Sep 17 00:00:00 2001 From: Daniil Lohvinov Date: Tue, 12 Mar 2024 15:50:36 +0200 Subject: [PATCH 3/4] refactor: complete captcha refactor [WTEL-4251] --- src/app/components/utils/index.js | 2 + src/app/components/utils/wt-snack-bar.vue | 141 ++++++++++++++++++ .../wt-omni-widget-popup.vue | 7 + .../wt-omni-widget-window.vue | 2 + src/app/locale/en/en.js | 5 + src/app/locale/ru/ru.js | 5 + src/app/locale/ua/ua.js | 5 + src/install.js | 19 ++- src/main.js | 3 +- .../components/wt-omni-widget-appointment.vue | 15 +- .../components/wt-omni-widget-call-start.vue | 10 +- .../wt-omni-widget-chat-wrapper.vue | 55 +++---- .../reCAPTCHA-verification/api/reCAPTCHA.js | 18 ++- 13 files changed, 244 insertions(+), 43 deletions(-) create mode 100644 src/app/components/utils/wt-snack-bar.vue diff --git a/src/app/components/utils/index.js b/src/app/components/utils/index.js index 68a6332..9cacc81 100644 --- a/src/app/components/utils/index.js +++ b/src/app/components/utils/index.js @@ -4,6 +4,7 @@ import WtIconBtn from './wt-icon-btn.vue'; import WtInput from './wt-input.vue'; import WtLabel from './wt-label.vue'; import WtTextarea from './wt-textarea.vue'; +import WtSnackBar from './wt-snack-bar.vue'; const Components = { WtButton, @@ -11,6 +12,7 @@ const Components = { WtInput, WtLabel, WtTextarea, + WtSnackBar, }; Object.keys(Components).forEach((name) => { diff --git a/src/app/components/utils/wt-snack-bar.vue b/src/app/components/utils/wt-snack-bar.vue new file mode 100644 index 0000000..874304a --- /dev/null +++ b/src/app/components/utils/wt-snack-bar.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/src/app/components/wt-omni-widget-popup/wt-omni-widget-popup.vue b/src/app/components/wt-omni-widget-popup/wt-omni-widget-popup.vue index 54c236c..fa13160 100644 --- a/src/app/components/wt-omni-widget-popup/wt-omni-widget-popup.vue +++ b/src/app/components/wt-omni-widget-popup/wt-omni-widget-popup.vue @@ -2,6 +2,7 @@ diff --git a/src/modules/call/components/wt-omni-widget-call-start.vue b/src/modules/call/components/wt-omni-widget-call-start.vue index afa708d..db116f5 100644 --- a/src/modules/call/components/wt-omni-widget-call-start.vue +++ b/src/modules/call/components/wt-omni-widget-call-start.vue @@ -24,7 +24,6 @@ color="success" @click="call" > - err: {{ err }} @@ -32,6 +31,7 @@