mirror of
https://github.com/Bubka/2FAuth.git
synced 2025-03-06 19:21:25 +01:00
Sync app settings at login/registration instead of page load
This commit is contained in:
parent
4ef76e0ae9
commit
f3945463b7
@ -19,7 +19,19 @@ public function index()
|
|||||||
{
|
{
|
||||||
event(new ScanForNewReleaseCalled);
|
event(new ScanForNewReleaseCalled);
|
||||||
|
|
||||||
$settings = Settings::all()->toJson();
|
// We only share necessary and acceptable values with the HTML front-end.
|
||||||
|
// But all the properties have to be pushed to init the appSetting store state correctly,
|
||||||
|
// so we set them to null, they will be fed later by the front-end
|
||||||
|
$appSettings = Settings::all();
|
||||||
|
$publicSettings = $appSettings->only([
|
||||||
|
'disableRegistration',
|
||||||
|
'enableSso',
|
||||||
|
'useSsoOnly'
|
||||||
|
]);
|
||||||
|
$settings = $appSettings->map(function (mixed $item, string $key) {
|
||||||
|
return null;
|
||||||
|
})->merge($publicSettings)->toJson();
|
||||||
|
|
||||||
$proxyAuth = config('auth.defaults.guard') === 'reverse-proxy-guard' ? true : false;
|
$proxyAuth = config('auth.defaults.guard') === 'reverse-proxy-guard' ? true : false;
|
||||||
$proxyLogoutUrl = config('2fauth.config.proxyLogoutUrl') ? config('2fauth.config.proxyLogoutUrl') : false;
|
$proxyLogoutUrl = config('2fauth.config.proxyLogoutUrl') ? config('2fauth.config.proxyLogoutUrl') : false;
|
||||||
$subdir = config('2fauth.config.appSubdirectory') ? '/' . config('2fauth.config.appSubdirectory') : '';
|
$subdir = config('2fauth.config.appSubdirectory') ? '/' . config('2fauth.config.appSubdirectory') : '';
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import appSettingService from '@/services/appSettingService'
|
import appSettingService from '@/services/appSettingService'
|
||||||
import { useAppSettingsStore } from '@/stores/appSettings'
|
|
||||||
import { useNotifyStore } from '@/stores/notify'
|
import { useNotifyStore } from '@/stores/notify'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -9,13 +8,11 @@ import { useNotifyStore } from '@/stores/notify'
|
|||||||
*/
|
*/
|
||||||
export async function useAppSettingsUpdater(setting, value, returnValidationError = false) {
|
export async function useAppSettingsUpdater(setting, value, returnValidationError = false) {
|
||||||
|
|
||||||
// const appSettings = useAppSettingsStore()
|
|
||||||
let data = null
|
let data = null
|
||||||
let error = null
|
let error = null
|
||||||
|
|
||||||
await appSettingService.update(setting, value, { returnError: true })
|
await appSettingService.update(setting, value, { returnError: true })
|
||||||
.then(response => {
|
.then(response => {
|
||||||
// appSettings[setting] = value
|
|
||||||
data = value
|
data = value
|
||||||
useNotifyStore().success({ type: 'is-success', text: trans('settings.forms.setting_saved') })
|
useNotifyStore().success({ type: 'is-success', text: trans('settings.forms.setting_saved') })
|
||||||
})
|
})
|
||||||
|
@ -79,7 +79,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<!-- email link -->
|
<!-- email link -->
|
||||||
<button type="button" id="btnEmailMenu" @click="showMenu = !showMenu" class="button is-text is-like-text has-text-grey" style="width: 100%;">
|
<button type="button" id="btnEmailMenu" @click="showMenu = !showMenu" class="button is-text is-like-text has-text-grey" style="width: 100%;">
|
||||||
<span v-if="appSettings.latestRelease && appSettings.checkForUpdate" class="release-flag"></span>
|
<span v-if="user.isAdmin && appSettings.latestRelease && appSettings.checkForUpdate" class="release-flag"></span>
|
||||||
<span class="mx-2 has-ellipsis">{{ user.email }}</span>
|
<span class="mx-2 has-ellipsis">{{ user.email }}</span>
|
||||||
<FontAwesomeIcon v-if="!showMenu" :icon="['fas', 'bars']" class="mr-2" />
|
<FontAwesomeIcon v-if="!showMenu" :icon="['fas', 'bars']" class="mr-2" />
|
||||||
<!-- <button v-else class="delete ml-3"></button> -->
|
<!-- <button v-else class="delete ml-3"></button> -->
|
||||||
|
39
resources/js/router/index.js
vendored
39
resources/js/router/index.js
vendored
@ -12,35 +12,36 @@ import noEmptyError from './middlewares/noEmptyError'
|
|||||||
import noRegistration from './middlewares/noRegistration'
|
import noRegistration from './middlewares/noRegistration'
|
||||||
import setReturnTo from './middlewares/setReturnTo'
|
import setReturnTo from './middlewares/setReturnTo'
|
||||||
import skipIfAuthProxy from './middlewares/skipIfAuthProxy'
|
import skipIfAuthProxy from './middlewares/skipIfAuthProxy'
|
||||||
|
import syncAppSettings from './middlewares/syncAppSettings'
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(window.appConfig.subdirectory ? window.appConfig.subdirectory : '/'),
|
history: createWebHistory(window.appConfig.subdirectory ? window.appConfig.subdirectory : '/'),
|
||||||
routes: [
|
routes: [
|
||||||
{ path: '/start', name: 'start', component: () => import('../views/Start.vue'), meta: { middlewares: [authGuard, setReturnTo], watchedByKicker: true } },
|
{ path: '/start', name: 'start', component: () => import('../views/Start.vue'), meta: { middlewares: [authGuard, syncAppSettings, setReturnTo], watchedByKicker: true } },
|
||||||
{ path: '/capture', name: 'capture', component: () => import('../views/twofaccounts/Capture.vue'), meta: { middlewares: [authGuard, setReturnTo], watchedByKicker: true } },
|
{ path: '/capture', name: 'capture', component: () => import('../views/twofaccounts/Capture.vue'), meta: { middlewares: [authGuard, syncAppSettings, setReturnTo], watchedByKicker: true } },
|
||||||
|
|
||||||
{ path: '/accounts', name: 'accounts', component: () => import('../views/twofaccounts/Accounts.vue'), meta: { middlewares: [authGuard, starter, setReturnTo], watchedByKicker: true }, alias: '/' },
|
{ path: '/accounts', name: 'accounts', component: () => import('../views/twofaccounts/Accounts.vue'), meta: { middlewares: [authGuard, syncAppSettings, starter, setReturnTo], watchedByKicker: true }, alias: '/' },
|
||||||
{ path: '/account/create', name: 'createAccount', component: () => import('../views/twofaccounts/CreateUpdate.vue'), meta: { middlewares: [authGuard, setReturnTo], watchedByKicker: true } },
|
{ path: '/account/create', name: 'createAccount', component: () => import('../views/twofaccounts/CreateUpdate.vue'), meta: { middlewares: [authGuard, syncAppSettings, setReturnTo], watchedByKicker: true } },
|
||||||
{ path: '/account/import', name: 'importAccounts', component: () => import('../views/twofaccounts/Import.vue'), meta: { middlewares: [authGuard, setReturnTo], watchedByKicker: true } },
|
{ path: '/account/import', name: 'importAccounts', component: () => import('../views/twofaccounts/Import.vue'), meta: { middlewares: [authGuard, syncAppSettings, setReturnTo], watchedByKicker: true } },
|
||||||
{ path: '/account/:twofaccountId/edit', name: 'editAccount', component: () => import('../views/twofaccounts/CreateUpdate.vue'), meta: { middlewares: [authGuard, setReturnTo], watchedByKicker: true }, props: true },
|
{ path: '/account/:twofaccountId/edit', name: 'editAccount', component: () => import('../views/twofaccounts/CreateUpdate.vue'), meta: { middlewares: [authGuard, syncAppSettings, setReturnTo], watchedByKicker: true }, props: true },
|
||||||
{ path: '/account/:twofaccountId/qrcode', name: 'showQRcode', component: () => import('../views/twofaccounts/QRcode.vue'), meta: { middlewares: [authGuard, setReturnTo], watchedByKicker: true } },
|
{ path: '/account/:twofaccountId/qrcode', name: 'showQRcode', component: () => import('../views/twofaccounts/QRcode.vue'), meta: { middlewares: [authGuard, syncAppSettings, setReturnTo], watchedByKicker: true } },
|
||||||
|
|
||||||
{ path: '/groups', name: 'groups', component: () => import('../views/groups/Groups.vue'), meta: { middlewares: [authGuard, setReturnTo], watchedByKicker: true }, props: true },
|
{ path: '/groups', name: 'groups', component: () => import('../views/groups/Groups.vue'), meta: { middlewares: [authGuard, syncAppSettings, setReturnTo], watchedByKicker: true }, props: true },
|
||||||
{ path: '/group/create', name: 'createGroup', component: () => import('../views/groups/CreateUpdate.vue'), meta: { middlewares: [authGuard, setReturnTo], watchedByKicker: true } },
|
{ path: '/group/create', name: 'createGroup', component: () => import('../views/groups/CreateUpdate.vue'), meta: { middlewares: [authGuard, syncAppSettings, setReturnTo], watchedByKicker: true } },
|
||||||
{ path: '/group/:groupId/edit', name: 'editGroup', component: () => import('../views/groups/CreateUpdate.vue'), meta: { middlewares: [authGuard, setReturnTo], watchedByKicker: true }, props: true },
|
{ path: '/group/:groupId/edit', name: 'editGroup', component: () => import('../views/groups/CreateUpdate.vue'), meta: { middlewares: [authGuard, syncAppSettings, setReturnTo], watchedByKicker: true }, props: true },
|
||||||
|
|
||||||
{ path: '/settings/options', name: 'settings.options', component: () => import('../views/settings/Options.vue'), meta: { middlewares: [authGuard], watchedByKicker: true, showAbout: true } },
|
{ path: '/settings/options', name: 'settings.options', component: () => import('../views/settings/Options.vue'), meta: { middlewares: [authGuard, syncAppSettings], watchedByKicker: true, showAbout: true } },
|
||||||
{ path: '/settings/account', name: 'settings.account', component: () => import('../views/settings/Account.vue'), meta: { middlewares: [authGuard], watchedByKicker: true, showAbout: true } },
|
{ path: '/settings/account', name: 'settings.account', component: () => import('../views/settings/Account.vue'), meta: { middlewares: [authGuard, syncAppSettings], watchedByKicker: true, showAbout: true } },
|
||||||
{ path: '/settings/oauth', name: 'settings.oauth.tokens', component: () => import('../views/settings/OAuth.vue'), meta: { middlewares: [authGuard], watchedByKicker: true, showAbout: true, props: true } },
|
{ path: '/settings/oauth', name: 'settings.oauth.tokens', component: () => import('../views/settings/OAuth.vue'), meta: { middlewares: [authGuard, syncAppSettings], watchedByKicker: true, showAbout: true, props: true } },
|
||||||
{ path: '/settings/webauthn/:credentialId/edit', name: 'settings.webauthn.editCredential', component: () => import('../views/settings/Credentials/Edit.vue'), meta: { middlewares: [authGuard], watchedByKicker: true, showAbout: true }, props: true },
|
{ path: '/settings/webauthn/:credentialId/edit', name: 'settings.webauthn.editCredential', component: () => import('../views/settings/Credentials/Edit.vue'), meta: { middlewares: [authGuard, syncAppSettings], watchedByKicker: true, showAbout: true }, props: true },
|
||||||
{ path: '/settings/webauthn', name: 'settings.webauthn.devices', component: () => import('../views/settings/WebAuthn.vue'), meta: { middlewares: [authGuard], watchedByKicker: true, showAbout: true } },
|
{ path: '/settings/webauthn', name: 'settings.webauthn.devices', component: () => import('../views/settings/WebAuthn.vue'), meta: { middlewares: [authGuard, syncAppSettings], watchedByKicker: true, showAbout: true } },
|
||||||
|
|
||||||
{ path: '/admin/app', name: 'admin.appSetup', component: () => import('../views/admin/AppSetup.vue'), meta: { middlewares: [authGuard, adminOnly], watchedByKicker: true, showAbout: true } },
|
{ path: '/admin/app', name: 'admin.appSetup', component: () => import('../views/admin/AppSetup.vue'), meta: { middlewares: [authGuard, adminOnly], watchedByKicker: true, showAbout: true } },
|
||||||
{ path: '/admin/auth', name: 'admin.auth', component: () => import('../views/admin/Auth.vue'), meta: { middlewares: [authGuard, adminOnly], watchedByKicker: true, showAbout: true } },
|
{ path: '/admin/auth', name: 'admin.auth', component: () => import('../views/admin/Auth.vue'), meta: { middlewares: [authGuard, adminOnly], watchedByKicker: true, showAbout: true } },
|
||||||
{ path: '/admin/users', name: 'admin.users', component: () => import('../views/admin/Users.vue'), meta: { middlewares: [authGuard, adminOnly], watchedByKicker: true, showAbout: true } },
|
{ path: '/admin/users', name: 'admin.users', component: () => import('../views/admin/Users.vue'), meta: { middlewares: [authGuard, syncAppSettings, adminOnly], watchedByKicker: true, showAbout: true } },
|
||||||
{ path: '/admin/users/create', name: 'admin.createUser', component: () => import('../views/admin/users/Create.vue'), meta: { middlewares: [authGuard, adminOnly], watchedByKicker: true, showAbout: true } },
|
{ path: '/admin/users/create', name: 'admin.createUser', component: () => import('../views/admin/users/Create.vue'), meta: { middlewares: [authGuard, syncAppSettings, adminOnly], watchedByKicker: true, showAbout: true } },
|
||||||
{ path: '/admin/users/:userId/manage', name: 'admin.manageUser', component: () => import('../views/admin/users/Manage.vue'), meta: { middlewares: [authGuard, adminOnly], watchedByKicker: true, showAbout: true }, props: true },
|
{ path: '/admin/users/:userId/manage', name: 'admin.manageUser', component: () => import('../views/admin/users/Manage.vue'), meta: { middlewares: [authGuard, syncAppSettings, adminOnly], watchedByKicker: true, showAbout: true }, props: true },
|
||||||
{ path: '/admin/logs/:userId/access', name: 'admin.logs.access', component: () => import('../views/admin/logs/Access.vue'), meta: { middlewares: [authGuard, adminOnly], watchedByKicker: true, showAbout: true }, props: true },
|
{ path: '/admin/logs/:userId/access', name: 'admin.logs.access', component: () => import('../views/admin/logs/Access.vue'), meta: { middlewares: [authGuard, syncAppSettings, adminOnly], watchedByKicker: true, showAbout: true }, props: true },
|
||||||
|
|
||||||
{ path: '/login', name: 'login', component: () => import('../views/auth/Login.vue'), meta: { middlewares: [skipIfAuthProxy, setReturnTo], showAbout: true } },
|
{ path: '/login', name: 'login', component: () => import('../views/auth/Login.vue'), meta: { middlewares: [skipIfAuthProxy, setReturnTo], showAbout: true } },
|
||||||
{ path: '/register', name: 'register', component: () => import('../views/auth/Register.vue'), meta: { middlewares: [skipIfAuthProxy, noRegistration, setReturnTo], showAbout: true } },
|
{ path: '/register', name: 'register', component: () => import('../views/auth/Register.vue'), meta: { middlewares: [skipIfAuthProxy, noRegistration, setReturnTo], showAbout: true } },
|
||||||
|
12
resources/js/router/middlewares/syncAppSettings.js
vendored
Normal file
12
resources/js/router/middlewares/syncAppSettings.js
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Retrieve app settings from the backend, only if the store is not synced yet
|
||||||
|
*/
|
||||||
|
export default function syncAppSettings({ to, next, nextMiddleware, stores }) {
|
||||||
|
const { appSettings, user } = stores
|
||||||
|
|
||||||
|
if (user.isAdmin && ! appSettings.isSynced ) {
|
||||||
|
appSettings.fetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
nextMiddleware()
|
||||||
|
}
|
13
resources/js/stores/appSettings.js
vendored
13
resources/js/stores/appSettings.js
vendored
@ -9,20 +9,25 @@ export const useAppSettingsStore = defineStore({
|
|||||||
return { ...window.appSettings }
|
return { ...window.appSettings }
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
getters: {
|
||||||
|
// Tells if all properties have been fetched from the backend.
|
||||||
|
// Here we test useEncryption but we could have test any other property
|
||||||
|
// appart from the ones pushed by Laravel in the html template.
|
||||||
|
isSynced: (state) => state.useEncryption != null,
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
/**
|
/**
|
||||||
* Fetches the appSetting collection from the backend
|
* Fetches the appSetting collection from the backend
|
||||||
*/
|
*/
|
||||||
async fetch() {
|
async fetch() {
|
||||||
appSettingService.getAll({ returnError: true })
|
appSettingService.getAll({ returnError: true }).then(response => {
|
||||||
.then(response => {
|
|
||||||
response.data.forEach(setting => {
|
response.data.forEach(setting => {
|
||||||
this[setting.key] = setting.value
|
this[setting.key] = setting.value
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
useNotifyStore().alert({ text: trans('errors.data_cannot_be_refreshed_from_server') })
|
useNotifyStore().alert({ text: trans('errors.failed_to_retrieve_app_settings') })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
2
resources/js/stores/user.js
vendored
2
resources/js/stores/user.js
vendored
@ -6,6 +6,7 @@ import { useColorMode } from '@vueuse/core'
|
|||||||
import { useTwofaccounts } from '@/stores/twofaccounts'
|
import { useTwofaccounts } from '@/stores/twofaccounts'
|
||||||
import { useGroups } from '@/stores/groups'
|
import { useGroups } from '@/stores/groups'
|
||||||
import { useNotifyStore } from '@/stores/notify'
|
import { useNotifyStore } from '@/stores/notify'
|
||||||
|
import { useAppSettingsStore } from '@/stores/appSettings'
|
||||||
|
|
||||||
export const useUserStore = defineStore({
|
export const useUserStore = defineStore({
|
||||||
id: 'user',
|
id: 'user',
|
||||||
@ -98,6 +99,7 @@ export const useUserStore = defineStore({
|
|||||||
this.$reset()
|
this.$reset()
|
||||||
this.initDataStores()
|
this.initDataStores()
|
||||||
this.applyUserPrefs()
|
this.applyUserPrefs()
|
||||||
|
useAppSettingsStore().$reset()
|
||||||
router.push({ name: 'login' })
|
router.push({ name: 'login' })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -27,8 +27,6 @@
|
|||||||
activeForm.value = 'webauthn'
|
activeForm.value = 'webauthn'
|
||||||
}
|
}
|
||||||
else activeForm.value = 'legacy'
|
else activeForm.value = 'legacy'
|
||||||
|
|
||||||
// showWebauthnForm && appSettings.useSsoOnly != true
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -47,6 +45,7 @@
|
|||||||
*/
|
*/
|
||||||
function LegacysignIn(e) {
|
function LegacysignIn(e) {
|
||||||
notify.clear()
|
notify.clear()
|
||||||
|
isBusy.value = true
|
||||||
|
|
||||||
form.post('/user/login', {returnError: true}).then(async (response) => {
|
form.post('/user/login', {returnError: true}).then(async (response) => {
|
||||||
await user.loginAs({
|
await user.loginAs({
|
||||||
@ -69,6 +68,9 @@
|
|||||||
notify.error(error)
|
notify.error(error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.finally(() => {
|
||||||
|
isBusy.value = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -195,7 +197,7 @@
|
|||||||
<form id="frmLegacyLogin" @submit.prevent="LegacysignIn" @keydown="form.onKeydown($event)">
|
<form id="frmLegacyLogin" @submit.prevent="LegacysignIn" @keydown="form.onKeydown($event)">
|
||||||
<FormField v-model="form.email" fieldName="email" :fieldError="form.errors.get('email')" inputType="email" label="auth.forms.email" autocomplete="username" autofocus />
|
<FormField v-model="form.email" fieldName="email" :fieldError="form.errors.get('email')" inputType="email" label="auth.forms.email" autocomplete="username" autofocus />
|
||||||
<FormPasswordField v-model="form.password" fieldName="password" :fieldError="form.errors.get('password')" label="auth.forms.password" autocomplete="current-password" />
|
<FormPasswordField v-model="form.password" fieldName="password" :fieldError="form.errors.get('password')" label="auth.forms.password" autocomplete="current-password" />
|
||||||
<FormButtons :isBusy="form.isBusy" caption="auth.sign_in" submitId="btnSignIn"/>
|
<FormButtons :isBusy="isBusy" caption="auth.sign_in" submitId="btnSignIn"/>
|
||||||
</form>
|
</form>
|
||||||
<div class="nav-links">
|
<div class="nav-links">
|
||||||
<p>{{ $t('auth.forms.forgot_your_password') }}
|
<p>{{ $t('auth.forms.forgot_your_password') }}
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
email: response.data.email,
|
email: response.data.email,
|
||||||
preferences: response.data.preferences,
|
preferences: response.data.preferences,
|
||||||
isAdmin: response.data.is_admin ?? false,
|
isAdmin: response.data.is_admin ?? false,
|
||||||
|
// TODO : add 'id' to the response
|
||||||
})
|
})
|
||||||
user.applyTheme()
|
user.applyTheme()
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
import { useBusStore } from '@/stores/bus'
|
import { useBusStore } from '@/stores/bus'
|
||||||
import { useTwofaccounts } from '@/stores/twofaccounts'
|
import { useTwofaccounts } from '@/stores/twofaccounts'
|
||||||
import { useGroups } from '@/stores/groups'
|
import { useGroups } from '@/stores/groups'
|
||||||
import { useAppSettingsStore } from '@/stores/appSettings'
|
|
||||||
import { useDisplayablePassword } from '@/composables/helpers'
|
import { useDisplayablePassword } from '@/composables/helpers'
|
||||||
import { useSortable, moveArrayElement } from '@vueuse/integrations/useSortable'
|
import { useSortable, moveArrayElement } from '@vueuse/integrations/useSortable'
|
||||||
|
|
||||||
@ -24,7 +23,6 @@
|
|||||||
const notify = useNotifyStore()
|
const notify = useNotifyStore()
|
||||||
const user = useUserStore()
|
const user = useUserStore()
|
||||||
const bus = useBusStore()
|
const bus = useBusStore()
|
||||||
const appSettings = useAppSettingsStore()
|
|
||||||
const { copy, copied } = useClipboard({ legacy: true })
|
const { copy, copied } = useClipboard({ legacy: true })
|
||||||
const twofaccounts = useTwofaccounts()
|
const twofaccounts = useTwofaccounts()
|
||||||
const groups = useGroups()
|
const groups = useGroups()
|
||||||
@ -407,7 +405,7 @@
|
|||||||
<div class="tfa-text has-ellipsis">
|
<div class="tfa-text has-ellipsis">
|
||||||
<img v-if="account.icon && user.preferences.showAccountsIcons" role="presentation" class="tfa-icon" :src="$2fauth.config.subdirectory + '/storage/icons/' + account.icon" alt="">
|
<img v-if="account.icon && user.preferences.showAccountsIcons" role="presentation" class="tfa-icon" :src="$2fauth.config.subdirectory + '/storage/icons/' + account.icon" alt="">
|
||||||
<img v-else-if="account.icon == null && user.preferences.showAccountsIcons" role="presentation" class="tfa-icon" :src="$2fauth.config.subdirectory + '/storage/noicon.svg'" alt="">
|
<img v-else-if="account.icon == null && user.preferences.showAccountsIcons" role="presentation" class="tfa-icon" :src="$2fauth.config.subdirectory + '/storage/noicon.svg'" alt="">
|
||||||
{{ account.service ? account.service : $t('twofaccounts.no_service') }}<FontAwesomeIcon class="has-text-danger is-size-5 ml-2" v-if="appSettings.useEncryption && account.account === $t('errors.indecipherable')" :icon="['fas', 'exclamation-circle']" />
|
{{ account.service ? account.service : $t('twofaccounts.no_service') }}<FontAwesomeIcon class="has-text-danger is-size-5 ml-2" v-if="account.account === $t('errors.indecipherable')" :icon="['fas', 'exclamation-circle']" />
|
||||||
<span class="has-ellipsis is-family-primary is-size-6 is-size-7-mobile has-text-grey ">{{ account.account }}</span>
|
<span class="has-ellipsis is-family-primary is-size-6 is-size-7-mobile has-text-grey ">{{ account.account }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -74,4 +74,5 @@
|
|||||||
'qrcode_has_invalid_checksum' => 'QR code has invalid checksum',
|
'qrcode_has_invalid_checksum' => 'QR code has invalid checksum',
|
||||||
'no_readable_qrcode' => 'No readable QR code',
|
'no_readable_qrcode' => 'No readable QR code',
|
||||||
'failed_icon_store_database_toggling' => 'Migration of icons failed. The setting has been restored to its previous value.',
|
'failed_icon_store_database_toggling' => 'Migration of icons failed. The setting has been restored to its previous value.',
|
||||||
|
'failed_to_retrieve_app_settings' => 'Failed to retrieve application settings'
|
||||||
];
|
];
|
Loading…
Reference in New Issue
Block a user