<template> <div> <BaseButton v-if="!enabled" id="generate-backup-codes" class="mx-2" :icon="mdiContentSaveCheck" type="button" color="info" :class="{ 'icon-loading-small': generatingCodes }" :disabled="generatingCodes" label=" Generate backup codes" @click="generateBackupCodes" /> <template v-else> <template v-if="!haveCodes"> {{ `Backup codes have been generated. ${used} of ${total} codes have been used.` }} </template> <template v-else> <div> These are your backup codes. Please save and/or print them as you will not be able to read the codes again later <ul> <li v-for="code in codes" :key="code" class="backup-code"> {{ code }} </li> </ul> <BaseButton :href="downloadUrl" class="mt-2 mb-2" :download="downloadFilename" rounded-full small :icon="mdiContentSave" :label="'Save backup codes'"> </BaseButton> <BaseButton @click="printCodes" rounded-full small :icon="mdiContentSave" :label="'Print backup codes'"> </BaseButton> <!-- <button class="button" @click="printCodes"> {{ t('twofactor_backupcodes', 'Print backup codes') }} </button> --> </div> </template> <div class="mt-4 max-w-xl text-sm text-gray-600"> <BaseButton class="mt-2 mb-2" :icon="mdiContentSaveCheck" type="button" color="info" :disabled="generatingCodes" label="Regenerate backup codes" @click="generateBackupCodes" /> </div> <p> <em> 'twofactor_backupcodes', `If you regenerate backup codes, you automatically invalidate old codes.` </em> </p> </template> </div> </template> <script setup lang="ts"> import { computed, ref, Ref } from 'vue'; import { ComputedRef } from 'vue'; import { MainService } from '@/Stores/main'; import Notification from '@/utils/toast'; import { mdiContentSaveCheck, mdiContentSave } from '@mdi/js'; import BaseButton from '@/Components/BaseButton.vue'; // import { useI18n } from 'vue-i18n'; // import { confirmPassword } from '@nextcloud/password-confirmation' // import '@nextcloud/password-confirmation/dist/style.css' // import { print } from '../service/PrintService.js' // const { t } = useI18n(); const generatingCodes: Ref<boolean> = ref(false); const mainService = MainService(); const props = defineProps({ backupState: { type: Object, default: () => ({}), }, }); if (props.backupState.enabled) { mainService.backupcodesEnabled = true; mainService.total = props.backupState.total; mainService.used = props.backupState.used; } const enabled = computed(() => mainService.backupcodesEnabled); const total = computed(() => mainService.total); const used = computed(() => mainService.used); const codes: ComputedRef<string[]> = computed(() => mainService.codes); const haveCodes = computed(() => { return codes && codes.value.length > 0; }); const downloadFilename = computed(() => { return 'tethys-backup-codes.txt'; }); const downloadUrl = computed(() => { if (!codes) { return ''; } return ( 'data:text/plain,' + encodeURIComponent( codes.value.reduce((prev, code) => { return prev + code + '\r\n'; }, ''), ) ); }); const print = (data: any) => { const name = 'Tethys'; // const newTab = window.open('', `${name} backup codes`) const newTab = window.open('about:blank', `${name} backup codes`); if (newTab) { newTab.document.write('<h1>' + `${name} backup codes` + '</h1>'); newTab.document.write('<pre>' + data + '</pre>'); newTab.print(); newTab.close(); } }; const getPrintData = (codes: string[]) => { if (!codes) { return ''; } return codes.reduce((prev, code) => { return prev + code + '<br>'; }, ''); }; const printCodes = () => { const data = getPrintData(codes.value); print(data); }; const generateBackupCodes = async () => { // Hide old codes generatingCodes.value = true; try { await mainService.generate(); generatingCodes.value = false; } catch (err) { Notification.showTemporary('An error occurred while generating your backup codes'); generatingCodes.value = false; throw err; } // this.$store.dispatch('generate').then(data => { // this.generatingCodes = false // }).catch(err => { // OC.Notification.showTemporary(t('twofactor_backupcodes', 'An error occurred while generating your backup codes')) // this.generatingCodes = false // throw err // }) }; </script> <style scoped> .backup-code { font-family: monospace; letter-spacing: 0.02em; font-size: 1.2em; } </style>