Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85803534
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
31 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index 1b133a089e..eb31f24185 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -1,467 +1,468 @@
/* global process */
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import vClickOutside from 'click-outside-vue3'
import VueVirtualScroller from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome'
import App from '../App.vue'
import routes from './routes'
import VBodyScrollLock from 'src/directives/body_scroll_lock'
import { windowWidth, windowHeight } from '../services/window_utils/window_utils'
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
import { applyConfig } from '../services/style_setter/style_setter.js'
import FaviconService from '../services/favicon_service/favicon_service.js'
import { initServiceWorker, updateFocus } from '../services/sw/sw.js'
import { useOAuthStore } from 'src/stores/oauth'
import { useI18nStore } from 'src/stores/i18n'
import { useInterfaceStore } from 'src/stores/interface'
import { useAnnouncementsStore } from 'src/stores/announcements'
+import { useAuthFlowStore } from 'src/stores/auth_flow'
let staticInitialResults = null
const parsedInitialResults = () => {
if (!document.getElementById('initial-results')) {
return null
}
if (!staticInitialResults) {
staticInitialResults = JSON.parse(document.getElementById('initial-results').textContent)
}
return staticInitialResults
}
const decodeUTF8Base64 = (data) => {
const rawData = atob(data)
const array = Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)))
const text = new TextDecoder().decode(array)
return text
}
const preloadFetch = async (request) => {
const data = parsedInitialResults()
if (!data || !data[request]) {
return window.fetch(request)
}
const decoded = decodeUTF8Base64(data[request])
const requestData = JSON.parse(decoded)
return {
ok: true,
json: () => requestData,
text: () => requestData
}
}
const getInstanceConfig = async ({ store }) => {
try {
const res = await preloadFetch('/api/v1/instance')
if (res.ok) {
const data = await res.json()
const textlimit = data.max_toot_chars
const vapidPublicKey = data.pleroma.vapid_public_key
store.dispatch('setInstanceOption', { name: 'textlimit', value: textlimit })
store.dispatch('setInstanceOption', { name: 'accountApprovalRequired', value: data.approval_required })
store.dispatch('setInstanceOption', { name: 'birthdayRequired', value: !!data.pleroma.metadata.birthday_required })
store.dispatch('setInstanceOption', { name: 'birthdayMinAge', value: data.pleroma.metadata.birthday_min_age || 0 })
if (vapidPublicKey) {
store.dispatch('setInstanceOption', { name: 'vapidPublicKey', value: vapidPublicKey })
}
} else {
throw (res)
}
} catch (error) {
console.error('Could not load instance config, potentially fatal')
console.error(error)
}
}
const getBackendProvidedConfig = async () => {
try {
const res = await window.fetch('/api/pleroma/frontend_configurations')
if (res.ok) {
const data = await res.json()
return data.pleroma_fe
} else {
throw (res)
}
} catch (error) {
console.error('Could not load backend-provided frontend config, potentially fatal')
console.error(error)
}
}
const getStaticConfig = async () => {
try {
const res = await window.fetch('/static/config.json')
if (res.ok) {
return res.json()
} else {
throw (res)
}
} catch (error) {
console.warn('Failed to load static/config.json, continuing without it.')
console.warn(error)
return {}
}
}
const setSettings = async ({ apiConfig, staticConfig, store }) => {
const overrides = window.___pleromafe_dev_overrides || {}
const env = window.___pleromafe_mode.NODE_ENV
// This takes static config and overrides properties that are present in apiConfig
let config = {}
if (overrides.staticConfigPreference && env === 'development') {
console.warn('OVERRIDING API CONFIG WITH STATIC CONFIG')
config = Object.assign({}, apiConfig, staticConfig)
} else {
config = Object.assign({}, staticConfig, apiConfig)
}
const copyInstanceOption = (name) => {
store.dispatch('setInstanceOption', { name, value: config[name] })
}
copyInstanceOption('theme')
copyInstanceOption('style')
copyInstanceOption('palette')
copyInstanceOption('embeddedToS')
copyInstanceOption('nsfwCensorImage')
copyInstanceOption('background')
copyInstanceOption('hidePostStats')
copyInstanceOption('hideBotIndication')
copyInstanceOption('hideUserStats')
copyInstanceOption('hideFilteredStatuses')
copyInstanceOption('logo')
store.dispatch('setInstanceOption', {
name: 'logoMask',
value: typeof config.logoMask === 'undefined'
? true
: config.logoMask
})
store.dispatch('setInstanceOption', {
name: 'logoMargin',
value: typeof config.logoMargin === 'undefined'
? 0
: config.logoMargin
})
copyInstanceOption('logoLeft')
- store.commit('authFlow/setInitialStrategy', config.loginMethod)
+ useAuthFlowStore().setInitialStrategy(config.loginMethod)
copyInstanceOption('redirectRootNoLogin')
copyInstanceOption('redirectRootLogin')
copyInstanceOption('showInstanceSpecificPanel')
copyInstanceOption('minimalScopesMode')
copyInstanceOption('hideMutedPosts')
copyInstanceOption('collapseMessageWithSubject')
copyInstanceOption('scopeCopy')
copyInstanceOption('subjectLineBehavior')
copyInstanceOption('postContentType')
copyInstanceOption('alwaysShowSubjectInput')
copyInstanceOption('showFeaturesPanel')
copyInstanceOption('hideSitename')
copyInstanceOption('sidebarRight')
}
const getTOS = async ({ store }) => {
try {
const res = await window.fetch('/static/terms-of-service.html')
if (res.ok) {
const html = await res.text()
store.dispatch('setInstanceOption', { name: 'tos', value: html })
} else {
throw (res)
}
} catch (e) {
console.warn("Can't load TOS\n", e)
}
}
const getInstancePanel = async ({ store }) => {
try {
const res = await preloadFetch('/instance/panel.html')
if (res.ok) {
const html = await res.text()
store.dispatch('setInstanceOption', { name: 'instanceSpecificPanelContent', value: html })
} else {
throw (res)
}
} catch (e) {
console.warn("Can't load instance panel\n", e)
}
}
const getStickers = async ({ store }) => {
try {
const res = await window.fetch('/static/stickers.json')
if (res.ok) {
const values = await res.json()
const stickers = (await Promise.all(
Object.entries(values).map(async ([name, path]) => {
const resPack = await window.fetch(path + 'pack.json')
let meta = {}
if (resPack.ok) {
meta = await resPack.json()
}
return {
pack: name,
path,
meta
}
})
)).sort((a, b) => {
return a.meta.title.localeCompare(b.meta.title)
})
store.dispatch('setInstanceOption', { name: 'stickers', value: stickers })
} else {
throw (res)
}
} catch (e) {
console.warn("Can't load stickers\n", e)
}
}
const getAppSecret = async ({ store }) => {
const oauth = useOAuthStore()
if (oauth.userToken) {
store.commit('setBackendInteractor', backendInteractorService(oauth.getToken))
}
}
const resolveStaffAccounts = ({ store, accounts }) => {
const nicknames = accounts.map(uri => uri.split('/').pop())
store.dispatch('setInstanceOption', { name: 'staffAccounts', value: nicknames })
}
const getNodeInfo = async ({ store }) => {
try {
const res = await preloadFetch('/nodeinfo/2.1.json')
if (res.ok) {
const data = await res.json()
const metadata = data.metadata
const features = metadata.features
store.dispatch('setInstanceOption', { name: 'name', value: metadata.nodeName })
store.dispatch('setInstanceOption', { name: 'registrationOpen', value: data.openRegistrations })
store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: features.includes('media_proxy') })
store.dispatch('setInstanceOption', { name: 'safeDM', value: features.includes('safe_dm_mentions') })
store.dispatch('setInstanceOption', { name: 'shoutAvailable', value: features.includes('chat') })
store.dispatch('setInstanceOption', { name: 'pleromaChatMessagesAvailable', value: features.includes('pleroma_chat_messages') })
store.dispatch('setInstanceOption', { name: 'pleromaCustomEmojiReactionsAvailable', value: features.includes('pleroma_custom_emoji_reactions') })
store.dispatch('setInstanceOption', { name: 'pleromaBookmarkFoldersAvailable', value: features.includes('pleroma:bookmark_folders') })
store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') })
store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') })
store.dispatch('setInstanceOption', { name: 'editingAvailable', value: features.includes('editing') })
store.dispatch('setInstanceOption', { name: 'pollLimits', value: metadata.pollLimits })
store.dispatch('setInstanceOption', { name: 'mailerEnabled', value: metadata.mailerEnabled })
store.dispatch('setInstanceOption', { name: 'quotingAvailable', value: features.includes('quote_posting') })
store.dispatch('setInstanceOption', { name: 'groupActorAvailable', value: features.includes('pleroma:group_actors') })
const uploadLimits = metadata.uploadLimits
store.dispatch('setInstanceOption', { name: 'uploadlimit', value: parseInt(uploadLimits.general) })
store.dispatch('setInstanceOption', { name: 'avatarlimit', value: parseInt(uploadLimits.avatar) })
store.dispatch('setInstanceOption', { name: 'backgroundlimit', value: parseInt(uploadLimits.background) })
store.dispatch('setInstanceOption', { name: 'bannerlimit', value: parseInt(uploadLimits.banner) })
store.dispatch('setInstanceOption', { name: 'fieldsLimits', value: metadata.fieldsLimits })
store.dispatch('setInstanceOption', { name: 'restrictedNicknames', value: metadata.restrictedNicknames })
store.dispatch('setInstanceOption', { name: 'postFormats', value: metadata.postFormats })
const suggestions = metadata.suggestions
store.dispatch('setInstanceOption', { name: 'suggestionsEnabled', value: suggestions.enabled })
store.dispatch('setInstanceOption', { name: 'suggestionsWeb', value: suggestions.web })
const software = data.software
store.dispatch('setInstanceOption', { name: 'backendVersion', value: software.version })
store.dispatch('setInstanceOption', { name: 'backendRepository', value: software.repository })
store.dispatch('setInstanceOption', { name: 'pleromaBackend', value: software.name === 'pleroma' })
const priv = metadata.private
store.dispatch('setInstanceOption', { name: 'private', value: priv })
const frontendVersion = window.___pleromafe_commit_hash
store.dispatch('setInstanceOption', { name: 'frontendVersion', value: frontendVersion })
const federation = metadata.federation
store.dispatch('setInstanceOption', {
name: 'tagPolicyAvailable',
value: typeof federation.mrf_policies === 'undefined'
? false
: metadata.federation.mrf_policies.includes('TagPolicy')
})
store.dispatch('setInstanceOption', { name: 'federationPolicy', value: federation })
store.dispatch('setInstanceOption', {
name: 'federating',
value: typeof federation.enabled === 'undefined'
? true
: federation.enabled
})
const accountActivationRequired = metadata.accountActivationRequired
store.dispatch('setInstanceOption', { name: 'accountActivationRequired', value: accountActivationRequired })
const accounts = metadata.staffAccounts
resolveStaffAccounts({ store, accounts })
} else {
throw (res)
}
} catch (e) {
console.warn('Could not load nodeinfo')
console.warn(e)
}
}
const setConfig = async ({ store }) => {
// apiConfig, staticConfig
const configInfos = await Promise.all([getBackendProvidedConfig({ store }), getStaticConfig()])
const apiConfig = configInfos[0]
const staticConfig = configInfos[1]
getAppSecret({ store })
await setSettings({ store, apiConfig, staticConfig })
}
const checkOAuthToken = async ({ store }) => {
const oauth = useOAuthStore()
if (oauth.getUserToken) {
return store.dispatch('loginUser', oauth.getUserToken)
}
return Promise.resolve()
}
const afterStoreSetup = async ({ pinia, store, storageError, i18n }) => {
const app = createApp(App)
// Must have app use pinia before we do anything that touches the store
// https://pinia.vuejs.org/core-concepts/plugins.html#Introduction
// "Plugins are only applied to stores created after the plugins themselves, and after pinia is passed to the app, otherwise they won't be applied."
app.use(pinia)
const waitForAllStoresToLoad = async () => {
// the stores that do not persist technically do not need to be awaited here,
// but that involves either hard-coding the stores in some place (prone to errors)
// or writing another vite plugin to analyze which stores needs persisting (++load time)
const allStores = import.meta.glob('../stores/*.js', { eager: true })
if (process.env.NODE_ENV === 'development') {
// do some checks to avoid common errors
if (!Object.keys(allStores).length) {
throw new Error('No stores are available. Check the code in src/boot/after_store.js')
}
}
await Promise.all(
Object.entries(allStores)
.map(async ([name, mod]) => {
const isStoreName = name => name.startsWith('use')
if (process.env.NODE_ENV === 'development') {
if (Object.keys(mod).filter(isStoreName).length !== 1) {
throw new Error('Each store file must export exactly one store as a named export. Check your code in src/stores/')
}
}
const storeFuncName = Object.keys(mod).find(isStoreName)
if (storeFuncName && typeof mod[storeFuncName] === 'function') {
const p = mod[storeFuncName]().$persistLoaded
if (!(p instanceof Promise)) {
throw new Error(`${name} store's $persistLoaded is not a Promise. The persist plugin is not applied.`)
}
await p
} else {
throw new Error(`Store module ${name} does not export a 'use...' function`)
}
}))
}
try {
await waitForAllStoresToLoad()
} catch (e) {
console.error('Cannot load stores:', e)
storageError = e
}
if (storageError) {
useInterfaceStore().pushGlobalNotice({ messageKey: 'errors.storage_unavailable', level: 'error' })
}
useInterfaceStore().setLayoutWidth(windowWidth())
useInterfaceStore().setLayoutHeight(windowHeight())
FaviconService.initFaviconService()
initServiceWorker(store)
window.addEventListener('focus', () => updateFocus())
const overrides = window.___pleromafe_dev_overrides || {}
const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin
store.dispatch('setInstanceOption', { name: 'server', value: server })
await setConfig({ store })
try {
await useInterfaceStore().applyTheme().catch((e) => { console.error('Error setting theme', e) })
} catch (e) {
window.splashError(e)
return Promise.reject(e)
}
applyConfig(store.state.config, i18n.global)
// Now we can try getting the server settings and logging in
// Most of these are preloaded into the index.html so blocking is minimized
await Promise.all([
checkOAuthToken({ store }),
getInstancePanel({ store }),
getNodeInfo({ store }),
getInstanceConfig({ store })
]).catch(e => Promise.reject(e))
// Start fetching things that don't need to block the UI
store.dispatch('fetchMutes')
store.dispatch('loadDrafts')
useAnnouncementsStore().startFetchingAnnouncements()
getTOS({ store })
getStickers({ store })
const router = createRouter({
history: createWebHistory(),
routes: routes(store),
scrollBehavior: (to, _from, savedPosition) => {
if (to.matched.some(m => m.meta.dontScroll)) {
return false
}
return savedPosition || { left: 0, top: 0 }
}
})
useI18nStore().setI18n(i18n)
app.use(router)
app.use(store)
app.use(i18n)
// Little thing to get out of invalid theme state
window.resetThemes = () => {
useInterfaceStore().resetThemeV3()
useInterfaceStore().resetThemeV3Palette()
useInterfaceStore().resetThemeV2()
}
app.use(vClickOutside)
app.use(VBodyScrollLock)
app.use(VueVirtualScroller)
app.component('FAIcon', FontAwesomeIcon)
app.component('FALayers', FontAwesomeLayers)
// remove after vue 3.3
app.config.unwrapInjectedRef = true
app.mount('#app')
return app
}
export default afterStoreSetup
diff --git a/src/components/auth_form/auth_form.js b/src/components/auth_form/auth_form.js
index a86a3dca25..243cbf574c 100644
--- a/src/components/auth_form/auth_form.js
+++ b/src/components/auth_form/auth_form.js
@@ -1,27 +1,28 @@
import { h, resolveComponent } from 'vue'
import LoginForm from '../login_form/login_form.vue'
import MFARecoveryForm from '../mfa_form/recovery_form.vue'
import MFATOTPForm from '../mfa_form/totp_form.vue'
-import { mapGetters } from 'vuex'
+import { mapState } from 'pinia'
+import { useAuthFlowStore } from 'src/stores/auth_flow'
const AuthForm = {
name: 'AuthForm',
render () {
return h(resolveComponent(this.authForm))
},
computed: {
authForm () {
if (this.requiredTOTP) { return 'MFATOTPForm' }
if (this.requiredRecovery) { return 'MFARecoveryForm' }
return 'LoginForm'
},
- ...mapGetters('authFlow', ['requiredTOTP', 'requiredRecovery'])
+ ...mapState(useAuthFlowStore, ['requiredTOTP', 'requiredRecovery'])
},
components: {
MFARecoveryForm,
MFATOTPForm,
LoginForm
}
}
export default AuthForm
diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js
index ed3f5dfc68..9566aa9035 100644
--- a/src/components/login_form/login_form.js
+++ b/src/components/login_form/login_form.js
@@ -1,100 +1,98 @@
-import { mapState, mapGetters, mapActions, mapMutations } from 'vuex'
-import { mapStores } from 'pinia'
+import { mapState } from 'vuex'
+import { mapStores, mapActions, mapState as mapPiniaState } from 'pinia'
import oauthApi from '../../services/new_api/oauth.js'
import { useOAuthStore } from 'src/stores/oauth.js'
+import { useAuthFlowStore } from 'src/stores/auth_flow.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faTimes
} from '@fortawesome/free-solid-svg-icons'
library.add(
faTimes
)
const LoginForm = {
data: () => ({
user: {},
error: false
}),
computed: {
isPasswordAuth () { return this.requiredPassword },
isTokenAuth () { return this.requiredToken },
...mapStores(useOAuthStore),
...mapState({
registrationOpen: state => state.instance.registrationOpen,
instance: state => state.instance,
loggingIn: state => state.users.loggingIn,
}),
- ...mapGetters(
- 'authFlow', ['requiredPassword', 'requiredToken', 'requiredMFA']
- )
+ ...mapPiniaState(useAuthFlowStore, ['requiredPassword', 'requiredToken', 'requiredMFA'])
},
methods: {
- ...mapMutations('authFlow', ['requireMFA']),
- ...mapActions({ login: 'authFlow/login' }),
+ ...mapActions(useAuthFlowStore, ['requireMFA', 'login']),
submit () {
this.isTokenAuth ? this.submitToken() : this.submitPassword()
},
submitToken () {
const data = {
instance: this.instance.server,
commit: this.$store.commit
}
// NOTE: we do not really need the app token, but obtaining a token and
// calling verify_credentials is the only way to ensure the app still works.
this.oauthStore.ensureAppToken()
.then(() => {
const app = {
clientId: this.oauthStore.clientId,
clientSecret: this.oauthStore.clientSecret,
}
oauthApi.login({ ...app, ...data })
})
},
submitPassword () {
this.error = false
// NOTE: we do not really need the app token, but obtaining a token and
// calling verify_credentials is the only way to ensure the app still works.
this.oauthStore.ensureAppToken().then(() => {
const app = {
clientId: this.oauthStore.clientId,
clientSecret: this.oauthStore.clientSecret,
}
oauthApi.getTokenWithCredentials(
{
...app,
instance: this.instance.server,
username: this.user.username,
password: this.user.password
}
).then((result) => {
if (result.error) {
if (result.error === 'mfa_required') {
this.requireMFA({ settings: result })
} else if (result.identifier === 'password_reset_required') {
this.$router.push({ name: 'password-reset', params: { passwordResetRequested: true } })
} else {
this.error = result.error
this.focusOnPasswordInput()
}
return
}
this.login(result).then(() => {
this.$router.push({ name: 'friends' })
})
})
})
},
clearError () { this.error = false },
focusOnPasswordInput () {
const passwordInput = this.$refs.passwordInput
passwordInput.focus()
passwordInput.setSelectionRange(0, passwordInput.value.length)
}
}
}
export default LoginForm
diff --git a/src/components/mfa_form/recovery_form.js b/src/components/mfa_form/recovery_form.js
index 2d0f4fdff5..84479b1ec3 100644
--- a/src/components/mfa_form/recovery_form.js
+++ b/src/components/mfa_form/recovery_form.js
@@ -1,64 +1,64 @@
import mfaApi from '../../services/new_api/mfa.js'
-import { mapState, mapGetters, mapActions, mapMutations } from 'vuex'
-import { mapStores } from 'pinia'
+import { mapState } from 'vuex'
+import { mapStores, mapActions, mapState as mapPiniaState } from 'pinia'
import { useOAuthStore } from 'src/stores/oauth.js'
+import { useAuthFlowStore } from 'src/stores/auth_flow.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faTimes
} from '@fortawesome/free-solid-svg-icons'
library.add(
faTimes
)
export default {
data: () => ({
code: null,
error: false
}),
computed: {
- ...mapGetters({
- authSettings: 'authFlow/settings'
+ ...mapPiniaState(useAuthFlowStore, {
+ authSettings: store => store.settings
}),
...mapStores(useOAuthStore),
...mapState({
instance: 'instance',
})
},
methods: {
- ...mapMutations('authFlow', ['requireTOTP', 'abortMFA']),
- ...mapActions({ login: 'authFlow/login' }),
+ ...mapActions(useAuthFlowStore, ['requireTOTP', 'abortMFA', 'login']),
clearError () { this.error = false },
focusOnCodeInput () {
const codeInput = this.$refs.codeInput
codeInput.focus()
codeInput.setSelectionRange(0, codeInput.value.length)
},
submit () {
const { clientId, clientSecret } = this.oauthStore
const data = {
clientId,
clientSecret,
instance: this.instance.server,
mfaToken: this.authSettings.mfa_token,
code: this.code
}
mfaApi.verifyRecoveryCode(data).then((result) => {
if (result.error) {
this.error = result.error
this.code = null
this.focusOnCodeInput()
return
}
this.login(result).then(() => {
this.$router.push({ name: 'friends' })
})
})
}
}
}
diff --git a/src/components/mfa_form/totp_form.js b/src/components/mfa_form/totp_form.js
index 857d055ff1..e369d8a5d0 100644
--- a/src/components/mfa_form/totp_form.js
+++ b/src/components/mfa_form/totp_form.js
@@ -1,64 +1,64 @@
import mfaApi from '../../services/new_api/mfa.js'
-import { mapState, mapGetters, mapActions, mapMutations } from 'vuex'
-import { mapStores } from 'pinia'
+import { mapState } from 'vuex'
+import { mapStores, mapActions, mapState as mapPiniaState } from 'pinia'
import { useOAuthStore } from 'src/stores/oauth.js'
+import { useAuthFlowStore } from 'src/stores/auth_flow.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faTimes
} from '@fortawesome/free-solid-svg-icons'
library.add(
faTimes
)
export default {
data: () => ({
code: null,
error: false
}),
computed: {
- ...mapGetters({
- authSettings: 'authFlow/settings'
+ ...mapPiniaState(useAuthFlowStore, {
+ authSettings: store => store.settings
}),
...mapStores(useOAuthStore),
...mapState({
instance: 'instance',
})
},
methods: {
- ...mapMutations('authFlow', ['requireRecovery', 'abortMFA']),
- ...mapActions({ login: 'authFlow/login' }),
+ ...mapActions(useAuthFlowStore, ['requireRecovery', 'abortMFA', 'login']),
clearError () { this.error = false },
focusOnCodeInput () {
const codeInput = this.$refs.codeInput
codeInput.focus()
codeInput.setSelectionRange(0, codeInput.value.length)
},
submit () {
const { clientId, clientSecret } = this.oauthStore
const data = {
clientId,
clientSecret,
instance: this.instance.server,
mfaToken: this.authSettings.mfa_token,
code: this.code
}
mfaApi.verifyOTPCode(data).then((result) => {
if (result.error) {
this.error = result.error
this.code = null
this.focusOnCodeInput()
return
}
this.login(result).then(() => {
this.$router.push({ name: 'friends' })
})
})
}
}
}
diff --git a/src/modules/auth_flow.js b/src/modules/auth_flow.js
deleted file mode 100644
index dfa673db97..0000000000
--- a/src/modules/auth_flow.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import { useOAuthStore } from 'src/stores/oauth.js'
-
-const PASSWORD_STRATEGY = 'password'
-const TOKEN_STRATEGY = 'token'
-
-// MFA strategies
-const TOTP_STRATEGY = 'totp'
-const RECOVERY_STRATEGY = 'recovery'
-
-// initial state
-const state = {
- settings: {},
- strategy: PASSWORD_STRATEGY,
- initStrategy: PASSWORD_STRATEGY // default strategy from config
-}
-
-const resetState = (state) => {
- state.strategy = state.initStrategy
- state.settings = {}
-}
-
-// getters
-const getters = {
- settings: (state) => {
- return state.settings
- },
- requiredPassword: (state) => {
- return state.strategy === PASSWORD_STRATEGY
- },
- requiredToken: (state) => {
- return state.strategy === TOKEN_STRATEGY
- },
- requiredTOTP: (state) => {
- return state.strategy === TOTP_STRATEGY
- },
- requiredRecovery: (state) => {
- return state.strategy === RECOVERY_STRATEGY
- }
-}
-
-// mutations
-const mutations = {
- setInitialStrategy (state, strategy) {
- if (strategy) {
- state.initStrategy = strategy
- state.strategy = strategy
- }
- },
- requirePassword (state) {
- state.strategy = PASSWORD_STRATEGY
- },
- requireToken (state) {
- state.strategy = TOKEN_STRATEGY
- },
- requireMFA (state, { settings }) {
- state.settings = settings
- state.strategy = TOTP_STRATEGY // default strategy of MFA
- },
- requireRecovery (state) {
- state.strategy = RECOVERY_STRATEGY
- },
- requireTOTP (state) {
- state.strategy = TOTP_STRATEGY
- },
- abortMFA (state) {
- resetState(state)
- }
-}
-
-// actions
-const actions = {
-
- async login ({ state, dispatch }, { access_token: accessToken }) {
- useOAuthStore().setToken(accessToken)
- await dispatch('loginUser', accessToken, { root: true })
- resetState(state)
- }
-}
-
-export default {
- namespaced: true,
- state,
- getters,
- mutations,
- actions
-}
diff --git a/src/modules/index.js b/src/modules/index.js
index 7f0aa3706a..fd233585d6 100644
--- a/src/modules/index.js
+++ b/src/modules/index.js
@@ -1,25 +1,23 @@
import instance from './instance.js'
import statuses from './statuses.js'
import notifications from './notifications.js'
import users from './users.js'
import api from './api.js'
import config from './config.js'
import profileConfig from './profileConfig.js'
import adminSettings from './adminSettings.js'
-import authFlow from './auth_flow.js'
import drafts from './drafts.js'
import chats from './chats.js'
export default {
instance,
statuses,
notifications,
users,
api,
config,
profileConfig,
adminSettings,
- authFlow,
drafts,
chats
}
diff --git a/src/stores/auth_flow.js b/src/stores/auth_flow.js
new file mode 100644
index 0000000000..e41a9bc5e0
--- /dev/null
+++ b/src/stores/auth_flow.js
@@ -0,0 +1,69 @@
+import { useOAuthStore } from 'src/stores/oauth.js'
+import { defineStore } from 'pinia'
+
+const PASSWORD_STRATEGY = 'password'
+const TOKEN_STRATEGY = 'token'
+
+// MFA strategies
+const TOTP_STRATEGY = 'totp'
+const RECOVERY_STRATEGY = 'recovery'
+
+export const useAuthFlowStore = defineStore('authFlow', {
+ // initial state
+ state: () => ({
+ settings: {},
+ strategy: PASSWORD_STRATEGY,
+ initStrategy: PASSWORD_STRATEGY // default strategy from config
+ }),
+ // getters
+ getters: {
+ requiredPassword: (state) => {
+ return state.strategy === PASSWORD_STRATEGY
+ },
+ requiredToken: (state) => {
+ return state.strategy === TOKEN_STRATEGY
+ },
+ requiredTOTP: (state) => {
+ return state.strategy === TOTP_STRATEGY
+ },
+ requiredRecovery: (state) => {
+ return state.strategy === RECOVERY_STRATEGY
+ },
+ },
+ actions: {
+ setInitialStrategy (strategy) {
+ if (strategy) {
+ this.initStrategy = strategy
+ this.strategy = strategy
+ }
+ },
+ requirePassword () {
+ this.strategy = PASSWORD_STRATEGY
+ },
+ requireToken () {
+ this.strategy = TOKEN_STRATEGY
+ },
+ requireMFA ({ settings }) {
+ this.settings = settings
+ this.strategy = TOTP_STRATEGY // default strategy of MFA
+ },
+ requireRecovery () {
+ this.strategy = RECOVERY_STRATEGY
+ },
+ requireTOTP () {
+ this.strategy = TOTP_STRATEGY
+ },
+ abortMFA () {
+ this.resetState()
+ },
+ resetState () {
+ this.strategy = this.initStrategy
+ this.settings = {}
+ },
+ async login ({ access_token: accessToken }) {
+ useOAuthStore().setToken(accessToken)
+ await window.vuex.dispatch('loginUser', accessToken, { root: true })
+ this.resetState()
+ }
+ }
+})
\ No newline at end of file
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Oct 9, 8:43 AM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1784587
Default Alt Text
(31 KB)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline
Log In to Comment