From 301fb75344d9a10637ad1993d1f69039dc8fa63b Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Thu, 11 May 2023 14:15:31 +0200 Subject: [PATCH 01/34] extracted bottom navigation in own component --- src/App.vue | 248 ++++++++--------- src/components/Navigation/Navigation.vue | 329 +++++++++++++++++++++++ 2 files changed, 453 insertions(+), 124 deletions(-) create mode 100644 src/components/Navigation/Navigation.vue diff --git a/src/App.vue b/src/App.vue index a0bced0..581479e 100644 --- a/src/App.vue +++ b/src/App.vue @@ -26,8 +26,9 @@ + - + WebAPI Access Token @@ -174,7 +174,6 @@ - Share - + --> + { router.push('/'); // generate a new access token on application start and validate it - regenerateAccessToken(); + // regenerateAccessToken(); }); -// Dialog for settings -const dialog = ref(false); +// // Dialog for settings +// const dialog = ref(false); /** * Access token @@ -301,123 +302,122 @@ const rules = { number: (v: string) => /\d/.test(v) || 'Must include at least one number', }; -const tokenVisibility = ref(false); -const isSessionActive = ref(false); -const streamData = ref(false); -const streamRegexAndCaptureAreaSettings = ref(false); - -/** - * Start the session - */ -function startSession() { - isSessionActive.value = true; - // TODO: Start session functionality - useNotifications().createNotification({ - title: 'Session started', - }); -} - -/** - * Stop the session - */ -function stopSession() { - isSessionActive.value = false; - // TODO: Stop session functionality - useNotifications().createNotification({ - title: 'Session stopped', - type: 'info', - }); -} - -/** - * Function which will toggle the visibility of the access token - */ -function toggleTokenVisibility() { - tokenVisibility.value = !tokenVisibility.value; -} - -/** - * Function which will copy the access token to the clipboard - */ -async function copyToClipboard() { - try { - await navigator.clipboard.writeText(accessToken.value); - useNotifications().createSuccessNotification({ - title: 'Copied access token to clipboard', - }); - } catch (err) { - useNotifications().createErrorNotification({ - title: 'Unable to copy access token to clipboard', - }); - } -} - -/** - * Function which will generate a random token - */ -function generateRandomToken(): string { - const buffer = new Uint8Array(32); - crypto.getRandomValues(buffer); - const characterSet = - 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*_+-=?'; - const token = Array.from(buffer) - .map((x: number) => characterSet[x % characterSet.length]) - .join(''); - if ( - rules.lowercase(token) === true && - rules.uppercase(token) === true && - rules.special(token) === true && - rules.number(token) === true && - rules.min(token) === true - ) { - return token; - } else { - return generateRandomToken(); - } -} - -/** - * Function which will regenerate a new access token - */ -async function regenerateAccessToken() { - // TODO: Regenerate access token functionality - accessToken.value = await generateRandomToken(); - if (!validateAccessToken()) { - regenerateAccessToken(); - } -} -const isAccessTokenValid = ref(false); - -function validateAccessToken() { - const isValid = Object.values(rules).every( - (rule) => rule(accessToken.value) === true - ); - - if (isAccessTokenValid.value === isValid) { - return isValid; - } - - isAccessTokenValid.value = isValid; - - if (!isValid && isSessionActive.value) { - stopSession(); - useNotifications().createErrorNotification({ - title: 'Session stopped', - message: 'The access token is invalid', - }); - } else if (!isValid) { - useNotifications().createErrorNotification({ - title: 'The access token is invalid', - }); - } else { - useNotifications().createSuccessNotification({ - title: 'The access token is valid', - }); - } - - return isValid; -} - +// const tokenVisibility = ref(false); +// // const isSessionActive = ref(false); +// const streamData = ref(false); +// const streamRegexAndCaptureAreaSettings = ref(false); + +// /** +// * Start the session +// */ +// function startSession() { +// isSessionActive.value = true; +// // TODO: Start session functionality +// useNotifications().createNotification({ +// title: 'Session started', +// }); +// } + +// /** +// * Stop the session +// */ +// function stopSession() { +// isSessionActive.value = false; +// // TODO: Stop session functionality +// useNotifications().createNotification({ +// title: 'Session stopped', +// type: 'info', +// }); +// } + +// /** +// * Function which will toggle the visibility of the access token +// */ +// function toggleTokenVisibility() { +// tokenVisibility.value = !tokenVisibility.value; +// } + +// /** +// * Function which will copy the access token to the clipboard +// */ +// async function copyToClipboard() { +// try { +// await navigator.clipboard.writeText(accessToken.value); +// useNotifications().createSuccessNotification({ +// title: 'Copied access token to clipboard', +// }); +// } catch (err) { +// useNotifications().createErrorNotification({ +// title: 'Unable to copy access token to clipboard', +// }); +// } +// } + +// /** +// * Function which will generate a random token +// */ +// function generateRandomToken(): string { +// const buffer = new Uint8Array(32); +// crypto.getRandomValues(buffer); +// const characterSet = +// 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*_+-=?'; +// const token = Array.from(buffer) +// .map((x: number) => characterSet[x % characterSet.length]) +// .join(''); +// if ( +// rules.lowercase(token) === true && +// rules.uppercase(token) === true && +// rules.special(token) === true && +// rules.number(token) === true && +// rules.min(token) === true +// ) { +// return token; +// } else { +// return generateRandomToken(); +// } +// } + +// /** +// * Function which will regenerate a new access token +// */ +// async function regenerateAccessToken() { +// // TODO: Regenerate access token functionality +// accessToken.value = await generateRandomToken(); +// if (!validateAccessToken()) { +// regenerateAccessToken(); +// } +// } +// const isAccessTokenValid = ref(false); + +// function validateAccessToken() { +// const isValid = Object.values(rules).every( +// (rule) => rule(accessToken.value) === true +// ); + +// if (isAccessTokenValid.value === isValid) { +// return isValid; +// } + +// isAccessTokenValid.value = isValid; + +// if (!isValid && isSessionActive.value) { +// stopSession(); +// useNotifications().createErrorNotification({ +// title: 'Session stopped', +// message: 'The access token is invalid', +// }); +// } else if (!isValid) { +// useNotifications().createErrorNotification({ +// title: 'The access token is invalid', +// }); +// } else { +// useNotifications().createSuccessNotification({ +// title: 'The access token is valid', +// }); +// } + +// return isValid; +// } From 200c332c5dc5d43ae78ff6a074e86c968ec6dbca Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Thu, 11 May 2023 14:17:12 +0200 Subject: [PATCH 02/34] removed old code from app.vue --- src/App.vue | 333 ----------------------- src/components/Navigation/Navigation.vue | 8 +- 2 files changed, 7 insertions(+), 334 deletions(-) diff --git a/src/App.vue b/src/App.vue index 581479e..69edbb7 100644 --- a/src/App.vue +++ b/src/App.vue @@ -27,188 +27,6 @@ - - import { onMounted, ref } from 'vue'; import { useRouter } from 'vue-router'; -import { isRunning } from '@/composables/useRunning'; import useNotifications from '@/composables/useNotificationSystem'; import ToastNotification from '@/components/ToastNotification/ToastNotification.vue'; import Navigation from '@/components/Navigation/Navigation.vue'; @@ -273,151 +90,7 @@ const router = useRouter(); onMounted(() => { // navigate to the default route router.push('/'); - - // generate a new access token on application start and validate it - // regenerateAccessToken(); }); - -// // Dialog for settings -// const dialog = ref(false); - -/** - * Access token - */ -const accessToken = ref(''); - -/** - * Rules for the access token - */ -const rules = { - required: (value: string) => - !!value || 'An access token is required to start a session', - min: (v: string) => v.length >= 8 || 'Min 8 characters', - uppercase: (v: string) => - /[A-Z]/.test(v) || 'Must include at least one uppercase letter', - lowercase: (v: string) => - /[a-z]/.test(v) || 'Must include at least one lowercase letter', - special: (v: string) => - /[\W_]/.test(v) || 'Must include at least one special character', - number: (v: string) => /\d/.test(v) || 'Must include at least one number', -}; - -// const tokenVisibility = ref(false); -// // const isSessionActive = ref(false); -// const streamData = ref(false); -// const streamRegexAndCaptureAreaSettings = ref(false); - -// /** -// * Start the session -// */ -// function startSession() { -// isSessionActive.value = true; -// // TODO: Start session functionality -// useNotifications().createNotification({ -// title: 'Session started', -// }); -// } - -// /** -// * Stop the session -// */ -// function stopSession() { -// isSessionActive.value = false; -// // TODO: Stop session functionality -// useNotifications().createNotification({ -// title: 'Session stopped', -// type: 'info', -// }); -// } - -// /** -// * Function which will toggle the visibility of the access token -// */ -// function toggleTokenVisibility() { -// tokenVisibility.value = !tokenVisibility.value; -// } - -// /** -// * Function which will copy the access token to the clipboard -// */ -// async function copyToClipboard() { -// try { -// await navigator.clipboard.writeText(accessToken.value); -// useNotifications().createSuccessNotification({ -// title: 'Copied access token to clipboard', -// }); -// } catch (err) { -// useNotifications().createErrorNotification({ -// title: 'Unable to copy access token to clipboard', -// }); -// } -// } - -// /** -// * Function which will generate a random token -// */ -// function generateRandomToken(): string { -// const buffer = new Uint8Array(32); -// crypto.getRandomValues(buffer); -// const characterSet = -// 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*_+-=?'; -// const token = Array.from(buffer) -// .map((x: number) => characterSet[x % characterSet.length]) -// .join(''); -// if ( -// rules.lowercase(token) === true && -// rules.uppercase(token) === true && -// rules.special(token) === true && -// rules.number(token) === true && -// rules.min(token) === true -// ) { -// return token; -// } else { -// return generateRandomToken(); -// } -// } - -// /** -// * Function which will regenerate a new access token -// */ -// async function regenerateAccessToken() { -// // TODO: Regenerate access token functionality -// accessToken.value = await generateRandomToken(); -// if (!validateAccessToken()) { -// regenerateAccessToken(); -// } -// } -// const isAccessTokenValid = ref(false); - -// function validateAccessToken() { -// const isValid = Object.values(rules).every( -// (rule) => rule(accessToken.value) === true -// ); - -// if (isAccessTokenValid.value === isValid) { -// return isValid; -// } - -// isAccessTokenValid.value = isValid; - -// if (!isValid && isSessionActive.value) { -// stopSession(); -// useNotifications().createErrorNotification({ -// title: 'Session stopped', -// message: 'The access token is invalid', -// }); -// } else if (!isValid) { -// useNotifications().createErrorNotification({ -// title: 'The access token is invalid', -// }); -// } else { -// useNotifications().createSuccessNotification({ -// title: 'The access token is valid', -// }); -// } - -// return isValid; -// } + From 976477c9f2131210e57ea496bd1a04e0fae3ee10 Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Thu, 11 May 2023 14:20:46 +0200 Subject: [PATCH 03/34] extracted SettingsPrompt from Navigation component --- src/components/Navigation/Navigation.vue | 275 ++++++++-------- .../SettingsPrompt/SettingsPrompt.vue | 298 ++++++++++++++++++ 2 files changed, 436 insertions(+), 137 deletions(-) create mode 100644 src/components/SettingsPrompt/SettingsPrompt.vue diff --git a/src/components/Navigation/Navigation.vue b/src/components/Navigation/Navigation.vue index a31a31c..21a5240 100644 --- a/src/components/Navigation/Navigation.vue +++ b/src/components/Navigation/Navigation.vue @@ -27,7 +27,8 @@ Regex - + + WebAPI Access Token @@ -136,7 +137,7 @@ - + Share - + --> From 56f648fd2939ebecca60f1c521a67d5c7f632bdf Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Thu, 11 May 2023 14:21:28 +0200 Subject: [PATCH 04/34] removed unsude code form Navigation component --- src/components/Navigation/Navigation.vue | 291 ----------------------- 1 file changed, 291 deletions(-) diff --git a/src/components/Navigation/Navigation.vue b/src/components/Navigation/Navigation.vue index 21a5240..5a52aa2 100644 --- a/src/components/Navigation/Navigation.vue +++ b/src/components/Navigation/Navigation.vue @@ -28,303 +28,12 @@ - diff --git a/src/components/NotificantionPrompt/NotificantionPrompt.vue b/src/components/NotificantionPrompt/NotificantionPrompt.vue new file mode 100644 index 0000000..0240fc9 --- /dev/null +++ b/src/components/NotificantionPrompt/NotificantionPrompt.vue @@ -0,0 +1,72 @@ + + + + + From 0f678ca598a480cbfda175dac6f9dedd3e503fa0 Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Thu, 11 May 2023 14:27:02 +0200 Subject: [PATCH 06/34] removed unused code etc. --- src/App.vue | 37 +----------- .../NotificantionPrompt.vue | 56 +++++++++---------- 2 files changed, 28 insertions(+), 65 deletions(-) diff --git a/src/App.vue b/src/App.vue index 138ae6d..2353871 100644 --- a/src/App.vue +++ b/src/App.vue @@ -29,50 +29,15 @@ - diff --git a/src/components/NotificantionPrompt/NotificantionPrompt.vue b/src/components/NotificantionPrompt/NotificantionPrompt.vue index 352d66a..87d54a7 100644 --- a/src/components/NotificantionPrompt/NotificantionPrompt.vue +++ b/src/components/NotificantionPrompt/NotificantionPrompt.vue @@ -31,7 +31,7 @@ - - diff --git a/src/components/Notifications/NotificantionPromptContainer/NotificantionPromptContainer.vue b/src/components/Notifications/NotificantionPromptContainer/NotificantionPromptContainer.vue new file mode 100644 index 0000000..055e9cb --- /dev/null +++ b/src/components/Notifications/NotificantionPromptContainer/NotificantionPromptContainer.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/src/components/Notifications/NotificationAnchorPosition.ts b/src/components/Notifications/NotificationAnchorPosition.ts new file mode 100644 index 0000000..1ab11dc --- /dev/null +++ b/src/components/Notifications/NotificationAnchorPosition.ts @@ -0,0 +1,6 @@ +export enum NotificationAnchorPosition { + TOP_LEFT = 'top-left', + TOP_RIGHT = 'top-right', + BOTTOM_LEFT = 'bottom-left', + BOTTOM_RIGHT = 'bottom-right', +} diff --git a/src/components/ToastNotification/ToastNotification.vue b/src/components/Notifications/NotificationPrompt/NotificationPrompt.vue similarity index 70% rename from src/components/ToastNotification/ToastNotification.vue rename to src/components/Notifications/NotificationPrompt/NotificationPrompt.vue index 52f187a..c4cbdb9 100644 --- a/src/components/ToastNotification/ToastNotification.vue +++ b/src/components/Notifications/NotificationPrompt/NotificationPrompt.vue @@ -1,28 +1,36 @@ diff --git a/src/composables/useClipboard/useClipboard.test.ts b/src/composables/useClipboard/useClipboard.test.ts new file mode 100644 index 0000000..9bca2ac --- /dev/null +++ b/src/composables/useClipboard/useClipboard.test.ts @@ -0,0 +1,10 @@ +import { describe, it, expect } from 'vitest'; +import useClipboard from '@/composables/useClipboard/useClipboard'; + +describe('useClipboard Composable', () => { + it('clipboardText is empty on initialization', () => { + const { clipboardText } = useClipboard(); + + expect(clipboardText.value).toBe(''); + }); +}); diff --git a/src/composables/useClipboard/useClipboard.ts b/src/composables/useClipboard/useClipboard.ts index 764be32..e99f458 100644 --- a/src/composables/useClipboard/useClipboard.ts +++ b/src/composables/useClipboard/useClipboard.ts @@ -20,35 +20,17 @@ export default function useClipboard() { * @param text * @returns */ - const writeClipboardText = async (text: string) => { - if (!supported) { - useNotificationSystem().createErrorNotification({ - title: 'Clipboard not supported', - message: 'Your browser does not support the clipboard API.', - }); - return; - } - - if (!text) { - useNotificationSystem().createErrorNotification({ - title: 'Empty clipboard', - message: 'Cannot copy empty text to clipboard.', - }); - return; + const writeClipboardText = async (text: string): Promise => { + if (!supported || !text) { + return false; } try { await navigator.clipboard.writeText(text); clipboardText.value = text; - useNotificationSystem().createSuccessNotification({ - title: 'Copied to clipboard', - message: 'The text has been copied to your clipboard.', - }); + return true; } catch (error) { - useNotificationSystem().createErrorNotification({ - title: 'Failed to copy to clipboard', - message: 'Failed to copy the text to your clipboard.', - }); + return false; } }; From 255cc69cba81497da566f7f2e82b403c4b00dc11 Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Fri, 12 May 2023 13:01:02 +0200 Subject: [PATCH 25/34] reimplementing token generation --- package.json | 2 +- .../useNotificationSystem.ts | 19 ------ .../useTokenGenerator/useTokenGenerator.ts | 59 +++++++++++-------- 3 files changed, 37 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index 9a6325d..34c0a3e 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "vuetify-loader": "^2.0.0-alpha.9" }, "browser": { - "crypto": true + "crypto": false }, "dependencies": { "@electron/remote": "^2.0.9", diff --git a/src/composables/useNotificationSystem/useNotificationSystem.ts b/src/composables/useNotificationSystem/useNotificationSystem.ts index ec32f82..d6b5a4f 100644 --- a/src/composables/useNotificationSystem/useNotificationSystem.ts +++ b/src/composables/useNotificationSystem/useNotificationSystem.ts @@ -26,7 +26,6 @@ export default function useNotificationSystem() { notifications.value.push( ...[ { - // TODO: this is redundant (we already have a function creating a random string. Reuse that after refactoring. id: generateToken(), ..._options, }, @@ -103,24 +102,6 @@ export default function useNotificationSystem() { }; } -// TODO: this is redundant (we already have a function creating a random string. Reuse that after refactoring. -/** - * Create a unique id - * @returns a unique id as a string - */ -// function createUUID(): string { -// let dt = new Date().getTime(); -// var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( -// /[xy]/g, -// function (c) { -// var r = (dt + Math.random() * 16) % 16 | 0; -// dt = Math.floor(dt / 16); -// return (c == 'x' ? r : (r & 0x3) | 0x8).toString(16); -// } -// ); -// return uuid; -// } - /** * Notification interface */ diff --git a/src/composables/useTokenGenerator/useTokenGenerator.ts b/src/composables/useTokenGenerator/useTokenGenerator.ts index 8c4221e..bbeffdd 100644 --- a/src/composables/useTokenGenerator/useTokenGenerator.ts +++ b/src/composables/useTokenGenerator/useTokenGenerator.ts @@ -42,9 +42,43 @@ export default function useTokenGenerator() { alphabet: string = characterSet ): string => { // https://github.com/josephg/gentoken/tree/master - const genToken = require('@josephg/gentoken'); + // const genToken = require('@josephg/gentoken'); - const generatedToken = genToken(lenght, alphabet); + // const generatedToken = genToken(lenght); + // const generatedToken = randomBytes(length) + // .toString('base64') + // .replace(/\+/g, '-') + // .replace(/\//g, '_') + // .replace(/=/g, ''); + // // remove base64 padding, replace unsafe chars + let generatedToken = ''; + + // Check if running in Node.js + if (typeof process !== 'undefined' && process?.versions?.node) { + console.log('Running in Node.js'); + // Check for Node.js crypto support + try { + const { randomBytes } = require('crypto'); + const sourceBytes = randomBytes(lenght); + generatedToken = Array.from(sourceBytes) + .map((x: any) => alphabet[x % alphabet.length]) + .join(''); + } catch (err) { + console.log('No Node', err); + } + } else { + console.log('Running in browser'); + // Test for browser crypto support + try { + const sourceBytes = new Uint8Array(lenght); + window.crypto.getRandomValues(sourceBytes); + generatedToken = Array.from(sourceBytes) + .map((x) => alphabet[x % alphabet.length]) + .join(''); + } catch (err) { + console.log('No Browser', err); + } + } if ( rules.lowercase(generatedToken) && @@ -57,27 +91,6 @@ export default function useTokenGenerator() { } else { return generateToken(); } - // const sourceBytes = randomBytes(len); - // const generatedToken = Array.from(sourceBytes) - // .map((xs: number) => alphabet[xs % alphabet.length]) - // .join(''); - - // const buffer = new Uint8Array(32); - // crypto.getRandomValues(buffer); - // const generatedToken = Array.from(buffer) - // .map((x) => characterSet[x % characterSet.length]) - // .join(''); - // if ( - // rules.lowercase(generatedToken) && - // rules.uppercase(generatedToken) && - // rules.special(generatedToken) && - // rules.number(generatedToken) && - // rules.min(generatedToken) - // ) { - // return generatedToken; - // } else { - // return generateToken(); - // } }; return { From 024d97b51d7e7713f60fe5bd5ad68ce3abd28a91 Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Fri, 12 May 2023 13:08:31 +0200 Subject: [PATCH 26/34] removing unused core etc. --- .../MainVideoStream/MainVideoStream.vue | 2 +- src/components/Navigation/Navigation.vue | 8 +-- .../SettingsPrompt/SettingsPrompt.vue | 8 ++- .../useNotificationSystem.ts | 3 +- .../useTokenGenerator/useTokenGenerator.ts | 49 ++++++------------- 5 files changed, 25 insertions(+), 45 deletions(-) diff --git a/src/components/MainVideoStream/MainVideoStream.vue b/src/components/MainVideoStream/MainVideoStream.vue index 4a4188c..9926c8b 100644 --- a/src/components/MainVideoStream/MainVideoStream.vue +++ b/src/components/MainVideoStream/MainVideoStream.vue @@ -37,7 +37,7 @@ - + diff --git a/src/components/SettingsPrompt/SettingsPrompt.vue b/src/components/SettingsPrompt/SettingsPrompt.vue index e2c7a25..f0fb422 100644 --- a/src/components/SettingsPrompt/SettingsPrompt.vue +++ b/src/components/SettingsPrompt/SettingsPrompt.vue @@ -275,4 +275,10 @@ async function copyToClipboard(text: string) { } - + diff --git a/src/composables/useNotificationSystem/useNotificationSystem.ts b/src/composables/useNotificationSystem/useNotificationSystem.ts index d6b5a4f..6006a7d 100644 --- a/src/composables/useNotificationSystem/useNotificationSystem.ts +++ b/src/composables/useNotificationSystem/useNotificationSystem.ts @@ -1,6 +1,5 @@ import { ref } from 'vue'; import useTokenGenerator from '@/composables/useTokenGenerator/useTokenGenerator'; -const { generateToken } = useTokenGenerator(); /** * notifications list @@ -11,7 +10,7 @@ const notifications = ref([]); * Notification System Composable */ export default function useNotificationSystem() { - // const { generateToken } = useTokenGenerator(); + const { generateToken } = useTokenGenerator(); /** * Create a notification diff --git a/src/composables/useTokenGenerator/useTokenGenerator.ts b/src/composables/useTokenGenerator/useTokenGenerator.ts index bbeffdd..5ee6fc8 100644 --- a/src/composables/useTokenGenerator/useTokenGenerator.ts +++ b/src/composables/useTokenGenerator/useTokenGenerator.ts @@ -35,49 +35,29 @@ export default function useTokenGenerator() { }; /** - * Generate a random token + * Generate a random token using crypto module in Node.js or browser depending on the environment (Node.js or browser) - browser for application and Node.js for tests and GitHub Actions */ const generateToken = ( lenght: number = 32, alphabet: string = characterSet ): string => { - // https://github.com/josephg/gentoken/tree/master - // const genToken = require('@josephg/gentoken'); - - // const generatedToken = genToken(lenght); - // const generatedToken = randomBytes(length) - // .toString('base64') - // .replace(/\+/g, '-') - // .replace(/\//g, '_') - // .replace(/=/g, ''); - // // remove base64 padding, replace unsafe chars let generatedToken = ''; - // Check if running in Node.js + // Checks if the environment is Node.js if (typeof process !== 'undefined' && process?.versions?.node) { - console.log('Running in Node.js'); - // Check for Node.js crypto support - try { - const { randomBytes } = require('crypto'); - const sourceBytes = randomBytes(lenght); - generatedToken = Array.from(sourceBytes) - .map((x: any) => alphabet[x % alphabet.length]) - .join(''); - } catch (err) { - console.log('No Node', err); - } + // generate random bytes using crypto module in Node.js + const { randomBytes } = require('crypto'); + const sourceBytes = randomBytes(lenght); + generatedToken = Array.from(sourceBytes) + .map((x: any) => alphabet[x % alphabet.length]) + .join(''); } else { - console.log('Running in browser'); - // Test for browser crypto support - try { - const sourceBytes = new Uint8Array(lenght); - window.crypto.getRandomValues(sourceBytes); - generatedToken = Array.from(sourceBytes) - .map((x) => alphabet[x % alphabet.length]) - .join(''); - } catch (err) { - console.log('No Browser', err); - } + // generate random bytes using crypto module in browser + const sourceBytes = new Uint8Array(lenght); + window.crypto.getRandomValues(sourceBytes); + generatedToken = Array.from(sourceBytes) + .map((x) => alphabet[x % alphabet.length]) + .join(''); } if ( @@ -94,6 +74,7 @@ export default function useTokenGenerator() { }; return { + characterSet, minTokenLenght, rules, generateToken, From 43c559e24d73547663c88eda434dcbb5aa866a9b Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Fri, 12 May 2023 13:19:31 +0200 Subject: [PATCH 27/34] removed settings --- package.json | 4 ---- tsconfig.json | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/package.json b/package.json index 34c0a3e..df4659d 100644 --- a/package.json +++ b/package.json @@ -54,12 +54,8 @@ "vue-tsc": "^1.4.4", "vuetify-loader": "^2.0.0-alpha.9" }, - "browser": { - "crypto": false - }, "dependencies": { "@electron/remote": "^2.0.9", - "@josephg/gentoken": "^1.0.0", "@mdi/font": "^7.2.96", "@types/webfontloader": "^1.6.35", "@vue/test-utils": "^2.3.2", diff --git a/tsconfig.json b/tsconfig.json index 1c33d59..f4ea4ac 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,8 +13,7 @@ "allowSyntheticDefaultImports": true, "skipLibCheck": true, "paths": { - "@/*": ["./src/*"], - "crypto": ["node_modules/crypto-js"] + "@/*": ["./src/*"] } }, "include": ["src"], From 319985095f67b4a610905f77532e7ed37fcc44d6 Mon Sep 17 00:00:00 2001 From: Kevin Beier Date: Fri, 12 May 2023 19:43:05 +0200 Subject: [PATCH 28/34] show error after closing and repoening --- .../SettingsPrompt/SettingsPrompt.vue | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/components/SettingsPrompt/SettingsPrompt.vue b/src/components/SettingsPrompt/SettingsPrompt.vue index f0fb422..66e2272 100644 --- a/src/components/SettingsPrompt/SettingsPrompt.vue +++ b/src/components/SettingsPrompt/SettingsPrompt.vue @@ -8,6 +8,7 @@ persistent scrim="#000000" width="700px" + no-click-animation > @@ -102,7 +92,6 @@ @click="regenerateAccessToken" > - Generate new token @@ -160,11 +149,12 @@ import { Ref, ref, watch } from 'vue'; * Composables */ const { writeClipboardText } = useClipboard(); -const { rules, generateValidToken } = useTokenGenerator(); +const { defaultRules, generateValidToken } = useTokenGenerator(); /** * Data */ +const accessToken = ref(''); const isAccessTokenValid = ref(false); const errorMessage: Ref = ref([]); const tokenVisibility = ref(false); @@ -176,20 +166,29 @@ const streamRegexAndCaptureAreaSettings = ref(false); * Dialog visibility */ const dialog = ref(false); + watch( () => dialog.value, (value) => { if (value) { // generate token if empty - if (accessToken.value === '') regenerateAccessToken(); + if (accessToken.value === '') { + regenerateAccessToken(); + return; + } else { + // try to validate token if not empty + errorMessage.value = Object.values(defaultRules) + .map((rule) => rule(accessToken.value)) + .filter((value) => typeof value === 'string') as string[]; + } } else { // reset validation state - const isValid = Object.values(rules).every( + const isValid = Object.values(defaultRules).every( (rule) => rule(accessToken.value) === true ); if (isAccessTokenValid.value === isValid) { - errorMessage.value = Object.values(rules) + errorMessage.value = Object.values(defaultRules) .map((rule) => rule(accessToken.value)) .filter((value) => typeof value === 'string') as string[]; } @@ -197,10 +196,12 @@ watch( } ); -/** - * Access token - */ -const accessToken = ref(''); +watch( + () => accessToken.value, + (value) => { + validate(); + } +); /** * Start the session @@ -242,30 +243,46 @@ async function regenerateAccessToken() { /** * Function which will validate the access token and notifies the user */ -function validateAccessToken() { - const isValid = Object.values(rules).every( +function validate() { + const isValid = Object.values(defaultRules).every( (rule) => rule(accessToken.value) === true ); - if (isAccessTokenValid.value === isValid) { - return isValid; - } - isAccessTokenValid.value = isValid; + errorMessage.value = Object.values(defaultRules) + .map((rule) => rule(accessToken.value)) + .filter((value) => typeof value === 'string') as string[]; + if (!isValid && isSessionActive.value) { stopSession(); useNotificationSystem().createErrorNotification({ title: 'Session stopped', message: 'The access token is invalid', }); - } else if (isValid) { + } else if (isValid && errorMessage.value.length === 0) { useNotificationSystem().createSuccessNotification({ title: 'The access token is valid', }); } +} - return isValid; +/** + * Function which will validate the access token and returns a boolean + * @returns boolean + */ +function validateAccessToken() { + // check if the access token is valid via the defaultRules + const isValid = Object.values(defaultRules).every( + (rule) => rule(accessToken.value) === true + ); + + if (isAccessTokenValid.value === isValid) { + return true; + } + + isAccessTokenValid.value = isValid; + return false; } /** diff --git a/src/composables/useClipboard/useClipboard.test.ts b/src/composables/useClipboard/useClipboard.test.ts index dc2a57c..727d3aa 100644 --- a/src/composables/useClipboard/useClipboard.test.ts +++ b/src/composables/useClipboard/useClipboard.test.ts @@ -20,6 +20,9 @@ describe('useClipboard Composable', () => { const text = 'Hello, world!'; const result = await writeClipboardText(text); + /** + * It is currently not possible to write into the clipboard when run in a GitHub Action therefore always true + */ if (result) { expect(result).toBe(true); expect(clipboardText.value).toBe(text); @@ -35,6 +38,9 @@ describe('useClipboard Composable', () => { const result = await readClipboardText(); + /** + * It is currently not possible to read the clipboard when run in a GitHub Action therefore always true + */ if (result) { expect(result).toBe(text); } else { diff --git a/src/composables/useTokenGenerator/useTokenGenerator.test.ts b/src/composables/useTokenGenerator/useTokenGenerator.test.ts index 390699d..fc92566 100644 --- a/src/composables/useTokenGenerator/useTokenGenerator.test.ts +++ b/src/composables/useTokenGenerator/useTokenGenerator.test.ts @@ -2,7 +2,8 @@ import { describe, it, expect } from 'vitest'; import useTokenGenerator from '@/composables/useTokenGenerator/useTokenGenerator'; describe('useTokenGenerator Composable', () => { - const { generateValidToken, rules, minTokenLenght } = useTokenGenerator(); + const { generateValidToken, defaultRules, minTokenLenght } = + useTokenGenerator(); it('generateToken returns a string', () => { const token = generateValidToken(); @@ -18,12 +19,12 @@ describe('useTokenGenerator Composable', () => { const runs = 100; for (let index = 0; index < runs; index++) { const token = generateValidToken(); - expect(rules.required(token)).toBe(true); - expect(rules.min(token)).toBe(true); - expect(rules.uppercase(token)).toBe(true); - expect(rules.lowercase(token)).toBe(true); - expect(rules.special(token)).toBe(true); - expect(rules.number(token)).toBe(true); + expect(defaultRules.required(token)).toBe(true); + expect(defaultRules.min(token)).toBe(true); + expect(defaultRules.uppercase(token)).toBe(true); + expect(defaultRules.lowercase(token)).toBe(true); + expect(defaultRules.special(token)).toBe(true); + expect(defaultRules.number(token)).toBe(true); } }); }); diff --git a/src/composables/useTokenGenerator/useTokenGenerator.ts b/src/composables/useTokenGenerator/useTokenGenerator.ts index 919617b..b4f9a09 100644 --- a/src/composables/useTokenGenerator/useTokenGenerator.ts +++ b/src/composables/useTokenGenerator/useTokenGenerator.ts @@ -16,9 +16,9 @@ export default function useTokenGenerator() { const minTokenLenght = ref(8); /** - * Rules for the access token + * Default Rules for the access token */ - const rules = { + const defaultRules = { required: (value: string) => !!value || 'An access token is required to start a session', min: (v: string) => @@ -31,7 +31,7 @@ export default function useTokenGenerator() { special: (v: string) => /[\W_]/.test(v) || 'Must include at least one special character', number: (v: string) => - /[0-9]*/.test(v) || 'Must include at least one number', + /[0-9]+/.test(v) || 'Must include at least one number', }; /** @@ -62,36 +62,20 @@ export default function useTokenGenerator() { return generatedToken; }; - /** - * Generate multiple tokens using the generateToken function, but they are not guaranteed to be valid tokens (they may not meet the rules) - * @param count - * @returns - */ - const generateMultipleTokens = (count: number = 16): string[] => { - const tokens: string[] = []; - for (let i = 0; i < count; i++) { - const token = generateToken(); - tokens.push(token); - } - return tokens; - }; - /** * Generate a valid token using the rules defined in the rules object * @returns a valid token */ const generateValidToken = (): string => { - const tokens = generateMultipleTokens(); - for (const token of tokens) { - if ( - rules.lowercase(token) === true && - rules.uppercase(token) === true && - rules.special(token) === true && - rules.number(token) === true && - rules.min(token) === true - ) { - return token; - } + const token = generateToken(); + if ( + defaultRules.lowercase(token) === true && + defaultRules.uppercase(token) === true && + defaultRules.special(token) === true && + defaultRules.number(token) === true && + defaultRules.min(token) === true + ) { + return token; } return generateValidToken(); }; @@ -99,9 +83,8 @@ export default function useTokenGenerator() { return { characterSet, minTokenLenght, - rules, + defaultRules, generateToken, - generateMultipleTokens, generateValidToken, }; }