mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-07 14:57:12 -04:00
Merge 5035cb888c
into 07eea0f484
This commit is contained in:
commit
b0a2b12d98
6 changed files with 92 additions and 21 deletions
30
.devcontainer/devcontainer.json
Normal file
30
.devcontainer/devcontainer.json
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||||
|
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
|
||||||
|
{
|
||||||
|
"name": "Node.js & TypeScript",
|
||||||
|
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm",
|
||||||
|
|
||||||
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
|
// "features": {},
|
||||||
|
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
// "forwardPorts": [],
|
||||||
|
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
// "postCreateCommand": "yarn install",
|
||||||
|
|
||||||
|
// Configure tool-specific properties.
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"Vue.volar",
|
||||||
|
"Lokalise.i18n-ally",
|
||||||
|
"dbaeumer.vscode-eslint"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
|
// "remoteUser": "root"
|
||||||
|
}
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -13,6 +13,7 @@ dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
coverage
|
coverage
|
||||||
*.local
|
*.local
|
||||||
|
.pnpm-store
|
||||||
|
|
||||||
/cypress/videos/
|
/cypress/videos/
|
||||||
/cypress/screenshots/
|
/cypress/screenshots/
|
||||||
|
|
8
components.d.ts
vendored
8
components.d.ts
vendored
|
@ -98,6 +98,7 @@ declare module '@vue/runtime-core' {
|
||||||
IconMdiEye: typeof import('~icons/mdi/eye')['default']
|
IconMdiEye: typeof import('~icons/mdi/eye')['default']
|
||||||
IconMdiEyeOff: typeof import('~icons/mdi/eye-off')['default']
|
IconMdiEyeOff: typeof import('~icons/mdi/eye-off')['default']
|
||||||
IconMdiHeart: typeof import('~icons/mdi/heart')['default']
|
IconMdiHeart: typeof import('~icons/mdi/heart')['default']
|
||||||
|
IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
|
||||||
IconMdiSearch: typeof import('~icons/mdi/search')['default']
|
IconMdiSearch: typeof import('~icons/mdi/search')['default']
|
||||||
IconMdiTranslate: typeof import('~icons/mdi/translate')['default']
|
IconMdiTranslate: typeof import('~icons/mdi/translate')['default']
|
||||||
IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default']
|
IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default']
|
||||||
|
@ -135,13 +136,20 @@ declare module '@vue/runtime-core' {
|
||||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||||
NDivider: typeof import('naive-ui')['NDivider']
|
NDivider: typeof import('naive-ui')['NDivider']
|
||||||
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
||||||
|
NForm: typeof import('naive-ui')['NForm']
|
||||||
|
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||||
NH1: typeof import('naive-ui')['NH1']
|
NH1: typeof import('naive-ui')['NH1']
|
||||||
NH3: typeof import('naive-ui')['NH3']
|
NH3: typeof import('naive-ui')['NH3']
|
||||||
NIcon: typeof import('naive-ui')['NIcon']
|
NIcon: typeof import('naive-ui')['NIcon']
|
||||||
|
NImage: typeof import('naive-ui')['NImage']
|
||||||
|
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||||
NLayout: typeof import('naive-ui')['NLayout']
|
NLayout: typeof import('naive-ui')['NLayout']
|
||||||
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
|
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
|
||||||
NMenu: typeof import('naive-ui')['NMenu']
|
NMenu: typeof import('naive-ui')['NMenu']
|
||||||
|
NProgress: typeof import('naive-ui')['NProgress']
|
||||||
|
NSlider: typeof import('naive-ui')['NSlider']
|
||||||
NSpace: typeof import('naive-ui')['NSpace']
|
NSpace: typeof import('naive-ui')['NSpace']
|
||||||
|
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||||
NTable: typeof import('naive-ui')['NTable']
|
NTable: typeof import('naive-ui')['NTable']
|
||||||
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
|
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
|
||||||
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
|
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
|
||||||
|
|
|
@ -444,7 +444,7 @@ tools:
|
||||||
otp-generator:
|
otp-generator:
|
||||||
title: OTP-Code-Generator
|
title: OTP-Code-Generator
|
||||||
description: >-
|
description: >-
|
||||||
Generiere und validiere zeitbasierte OTPs (Einmalpasswörter) für
|
Generiere und validiere zeitbasierte und ereignisgesteuertes OTPs (Einmalpasswörter) für
|
||||||
Multi-Faktor-Authentifizierung.
|
Multi-Faktor-Authentifizierung.
|
||||||
url-encoder:
|
url-encoder:
|
||||||
title: Kodieren/Decodieren von URL-formatierten Zeichenfolgen
|
title: Kodieren/Decodieren von URL-formatierten Zeichenfolgen
|
||||||
|
|
|
@ -383,7 +383,7 @@ tools:
|
||||||
|
|
||||||
otp-generator:
|
otp-generator:
|
||||||
title: OTP code generator
|
title: OTP code generator
|
||||||
description: Generate and validate time-based OTP (one time password) for multi-factor authentication.
|
description: Generate and validate time-based and event-based OTP (one time password) for multi-factor authentication.
|
||||||
|
|
||||||
url-encoder:
|
url-encoder:
|
||||||
title: Encode/decode URL-formatted strings
|
title: Encode/decode URL-formatted strings
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { useTimestamp } from '@vueuse/core';
|
import { useTimestamp } from '@vueuse/core';
|
||||||
import { useThemeVars } from 'naive-ui';
|
import { useThemeVars } from 'naive-ui';
|
||||||
import { useQRCode } from '../qr-code-generator/useQRCode';
|
import { useQRCode } from '../qr-code-generator/useQRCode';
|
||||||
import { base32toHex, buildKeyUri, generateSecret, generateTOTP, getCounterFromTime } from './otp.service';
|
import { base32toHex, buildKeyUri, generateHOTP, generateSecret, generateTOTP, getCounterFromTime } from './otp.service';
|
||||||
import TokenDisplay from './token-display.vue';
|
import TokenDisplay from './token-display.vue';
|
||||||
import { useStyleStore } from '@/stores/style.store';
|
import { useStyleStore } from '@/stores/style.store';
|
||||||
import InputCopyable from '@/components/InputCopyable.vue';
|
import InputCopyable from '@/components/InputCopyable.vue';
|
||||||
|
@ -19,6 +19,16 @@ function refreshSecret() {
|
||||||
secret.value = generateSecret();
|
secret.value = generateSecret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const counter = ref(0);
|
||||||
|
|
||||||
|
const [hotpValues] = computedRefreshable(
|
||||||
|
() =>
|
||||||
|
Object.fromEntries(
|
||||||
|
Array.from({ length: 10 }, (_, i) => [+counter.value + i, generateHOTP({ key: secret.value, counter: +counter.value + i })]),
|
||||||
|
),
|
||||||
|
{ throttle: 500 },
|
||||||
|
);
|
||||||
|
|
||||||
const [tokens] = computedRefreshable(
|
const [tokens] = computedRefreshable(
|
||||||
() => ({
|
() => ({
|
||||||
previous: generateTOTP({ key: secret.value, now: now.value - 30000 }),
|
previous: generateTOTP({ key: secret.value, now: now.value - 30000 }),
|
||||||
|
@ -68,23 +78,6 @@ const secretValidationRules = [
|
||||||
</c-tooltip>
|
</c-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</c-input-text>
|
</c-input-text>
|
||||||
|
|
||||||
<div>
|
|
||||||
<TokenDisplay :tokens="tokens" />
|
|
||||||
|
|
||||||
<n-progress :percentage="(100 * interval) / 30" :color="theme.primaryColor" :show-indicator="false" />
|
|
||||||
<div style="text-align: center">
|
|
||||||
Next in {{ String(Math.floor(30 - interval)).padStart(2, '0') }}s
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div mt-4 flex flex-col items-center justify-center gap-3>
|
|
||||||
<n-image :src="qrcode" />
|
|
||||||
<c-button :href="keyUri" target="_blank">
|
|
||||||
Open Key URI in new tab
|
|
||||||
</c-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="max-width: 350px">
|
|
||||||
<InputCopyable
|
<InputCopyable
|
||||||
label="Secret in hexadecimal"
|
label="Secret in hexadecimal"
|
||||||
:value="base32toHex(secret)"
|
:value="base32toHex(secret)"
|
||||||
|
@ -93,11 +86,41 @@ const secretValidationRules = [
|
||||||
mb-5
|
mb-5
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div mt-4 flex flex-col items-center justify-center gap-3>
|
||||||
|
<n-image :src="qrcode" />
|
||||||
|
<c-button :href="keyUri" target="_blank">
|
||||||
|
Open Key URI in new tab
|
||||||
|
</c-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="max-width: 350px">
|
||||||
|
<div>
|
||||||
|
<c-input-text
|
||||||
|
v-model:value="counter"
|
||||||
|
label="Start-value for HOTP counter"
|
||||||
|
placeholder="Start counter for HOTP at..."
|
||||||
|
type="number"
|
||||||
|
mb-5
|
||||||
|
mt-5
|
||||||
|
/>
|
||||||
|
<InputCopyable
|
||||||
|
v-for="(value, currentCounter) in hotpValues" :key="currentCounter"
|
||||||
|
:value="value"
|
||||||
|
readonly
|
||||||
|
:label="`HOTP ${currentCounter}:`"
|
||||||
|
label-position="left"
|
||||||
|
label-width="90px"
|
||||||
|
label-align="right"
|
||||||
|
placeholder="HOTP will be displayed here"
|
||||||
|
mb-1
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="max-width: 350px">
|
||||||
<InputCopyable
|
<InputCopyable
|
||||||
label="Epoch"
|
label="Epoch"
|
||||||
:value="Math.floor(now / 1000).toString()"
|
:value="Math.floor(now / 1000).toString()"
|
||||||
readonly
|
readonly
|
||||||
mb-5
|
|
||||||
placeholder="Epoch in sec will be displayed here"
|
placeholder="Epoch in sec will be displayed here"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -122,6 +145,15 @@ const secretValidationRules = [
|
||||||
label-align="right"
|
label-align="right"
|
||||||
label="Padded hex:"
|
label="Padded hex:"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<TokenDisplay :tokens="tokens" mt-5 />
|
||||||
|
|
||||||
|
<n-progress :percentage="(100 * interval) / 30" :color="theme.primaryColor" :show-indicator="false" />
|
||||||
|
<div style="text-align: center">
|
||||||
|
Next in {{ String(Math.floor(30 - interval)).padStart(2, '0') }}s
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue