Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85824650
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
56 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/api/mfa.js b/src/api/mfa.js
index ff5df65607..8c1677fbe3 100644
--- a/src/api/mfa.js
+++ b/src/api/mfa.js
@@ -1,46 +1,45 @@
import { promisedRequest } from './helpers.js'
export const verifyOTPCode = ({
clientId,
clientSecret,
instance,
mfaToken,
code,
}) => {
const formData = new window.FormData()
formData.append('client_id', clientId)
formData.append('client_secret', clientSecret)
formData.append('mfa_token', mfaToken)
formData.append('code', code)
formData.append('challenge_type', 'totp')
return promisedRequest({
- url: '/oauth/mfa/challenge'
+ url: '/oauth/mfa/challenge',
method: 'POST',
formData,
})
}
export const verifyRecoveryCode = ({
clientId,
clientSecret,
instance,
mfaToken,
code,
}) => {
- const url = `${instance}`
const formData = new window.FormData()
formData.append('client_id', clientId)
formData.append('client_secret', clientSecret)
formData.append('mfa_token', mfaToken)
formData.append('code', code)
formData.append('challenge_type', 'recovery')
return promisedRequest({
- url: '/oauth/mfa/challenge'
+ url: `${instance}/oauth/mfa/challenge`,
method: 'POST',
formData,
})
}
diff --git a/src/api/oauth.js b/src/api/oauth.js
new file mode 100644
index 0000000000..ce4c2f6fce
--- /dev/null
+++ b/src/api/oauth.js
@@ -0,0 +1,138 @@
+import { reduce } from 'lodash'
+
+import { paramsString, promisedRequest } from './helpers.js'
+
+import { StatusCodeError } from 'src/services/errors/errors.js'
+
+const REDIRECT_URI = `${window.location.origin}/oauth-callback`
+
+export const createApp = ({ instance }) => {
+ const formData = new window.FormData()
+
+ formData.append('client_name', 'PleromaFE')
+ formData.append('website', 'https://pleroma.social')
+ formData.append('redirect_uris', REDIRECT_URI)
+ formData.append('scopes', 'read write follow push admin')
+
+ return promisedRequest({
+ url: `${instance}/api/v1/apps`,
+ method: 'POST',
+ formData,
+ }).then(({ data, ...rest }) => ({
+ ...rest,
+ data: {
+ ...data,
+ clientId: data.client_id,
+ clientSecret: data.client_secret,
+ },
+ }))
+}
+
+export const getLoginUrl = ({ instance, clientId }) => {
+ const data = {
+ responseType: 'code',
+ clientId,
+ redirectUri: REDIRECT_URI,
+ scope: 'read write follow push admin',
+ }
+
+ return `${instance}/oauth/authorize${paramsString(data)}`
+}
+
+export const getTokenWithCredentials = ({
+ clientId,
+ clientSecret,
+ instance,
+ username,
+ password,
+}) => {
+ const formData = new window.FormData()
+
+ formData.append('client_id', clientId)
+ formData.append('client_secret', clientSecret)
+ formData.append('grant_type', 'password')
+ formData.append('username', username)
+ formData.append('password', password)
+
+ return promisedRequest({
+ url: `${instance}/oauth/token`,
+ method: 'POST',
+ formData,
+ })
+}
+
+export const getToken = ({ clientId, clientSecret, instance, code }) => {
+ const formData = new window.FormData()
+
+ formData.append('client_id', clientId)
+ formData.append('client_secret', clientSecret)
+ formData.append('grant_type', 'authorization_code')
+ formData.append('code', code)
+ formData.append('redirect_uri', `${window.location.origin}/oauth-callback`)
+
+ return promisedRequest({
+ url: `${instance}/oauth/token`,
+ method: 'POST',
+ formData,
+ })
+}
+
+export const getClientToken = ({ clientId, clientSecret, instance }) => {
+ const formData = new window.FormData()
+
+ formData.append('client_id', clientId)
+ formData.append('client_secret', clientSecret)
+ formData.append('grant_type', 'client_credentials')
+ formData.append('redirect_uri', `${window.location.origin}/oauth-callback`)
+
+ return promisedRequest({
+ url: `${instance}/oauth/token`,
+ method: 'POST',
+ formData,
+ })
+}
+export const verifyOTPCode = ({ app, instance, mfaToken, code }) => {
+ const formData = new window.FormData()
+
+ formData.append('client_id', app.client_id)
+ formData.append('client_secret', app.client_secret)
+ formData.append('mfa_token', mfaToken)
+ formData.append('code', code)
+ formData.append('challenge_type', 'totp')
+
+ return promisedRequest({
+ url: `${instance}/oauth/mfa/challenge`,
+ method: 'POST',
+ formData,
+ })
+}
+
+export const verifyRecoveryCode = ({ app, instance, mfaToken, code }) => {
+ const formData = new window.FormData()
+
+ formData.append('client_id', app.client_id)
+ formData.append('client_secret', app.client_secret)
+ formData.append('mfa_token', mfaToken)
+ formData.append('code', code)
+ formData.append('challenge_type', 'recovery')
+
+ return promisedRequest({
+ url: `${instance}/oauth/mfa/challenge`,
+ method: 'POST',
+ formData,
+ })
+}
+
+export const revokeToken = ({ app, instance, token }) => {
+ const formData = new window.FormData()
+
+ formData.append('client_id', app.clientId)
+ formData.append('client_secret', app.clientSecret)
+ formData.append('token', token)
+
+ return promisedRequest({
+ url: `${instance}/oauth/revoke`,
+ method: 'POST',
+ formData,
+ })
+}
diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js
index b16fb8d2ff..8f705f2690 100644
--- a/src/components/login_form/login_form.js
+++ b/src/components/login_form/login_form.js
@@ -1,103 +1,91 @@
import { mapActions, mapState as mapPiniaState, mapStores } from 'pinia'
import { mapState } from 'vuex'
-import oauthApi from '../../services/new_api/oauth.js'
-
import { useAuthFlowStore } from 'src/stores/auth_flow.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useOAuthStore } from 'src/stores/oauth.js'
+import { getLoginUrl, getTokenWithCredentials } from 'src/api/oauth.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: {
...mapState({
loggingIn: (state) => state.users.loggingIn,
}),
...mapPiniaState(useOAuthStore, ['clientId', 'clientSecret']),
...mapPiniaState(useInstanceStore, ['server', 'registrationOpen']),
...mapPiniaState(useAuthFlowStore, {
isTokenAuth: (store) => store.requiredToken,
isPasswordAuth: (store) => !store.requiredToken,
}),
},
methods: {
...mapActions(useAuthFlowStore, ['requireMFA', 'login']),
...mapActions(useOAuthStore, ['ensureAppToken']),
submit() {
this.isTokenAuth ? this.submitToken() : this.submitPassword()
},
submitToken() {
- const data = {
- instance: this.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.ensureAppToken().then(() => {
- const app = {
+ window.location.href = getLoginUrl({
clientId: this.clientId,
- clientSecret: this.clientSecret,
- }
- oauthApi.login({ ...app, ...data })
+ instance: this.server,
+ })
})
},
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.ensureAppToken().then(() => {
- const app = {
+ getTokenWithCredentials({
clientId: this.clientId,
clientSecret: this.clientSecret,
- }
-
- oauthApi
- .getTokenWithCredentials({
- ...app,
- instance: this.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
+ instance: this.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()
}
- this.login(result).then(() => {
- this.$router.push({ name: 'friends' })
- })
+ 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 46e38d29cf..e04218bd83 100644
--- a/src/components/mfa_form/recovery_form.js
+++ b/src/components/mfa_form/recovery_form.js
@@ -1,60 +1,60 @@
import { mapActions, mapState, mapStores } from 'pinia'
-import { verifyRecoveryCode } from 'src/api/mfa.js'
-
import { useAuthFlowStore } from 'src/stores/auth_flow.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useOAuthStore } from 'src/stores/oauth.js'
+import { verifyRecoveryCode } from 'src/api/mfa.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: {
...mapStores(useOAuthStore),
...mapState(useOAuthStore, ['clientId', 'clientSecret']),
...mapState(useAuthFlowStore, ['settings']),
...mapState(useInstanceStore, ['server']),
},
methods: {
...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 data = {
clientId: this.clientId,
clientSecret: this.clientSecret,
instance: this.server,
mfaToken: this.settings.mfa_token,
code: this.code,
}
verifyRecoveryCode(data)
.then((result) => {
this.login(result).then(() => {
this.$router.push({ name: 'friends' })
})
})
.catch((error) => {
this.error = error
this.code = null
this.focusOnCodeInput()
return
})
},
},
}
diff --git a/src/components/mfa_form/totp_form.js b/src/components/mfa_form/totp_form.js
index 4cd6355500..056098c251 100644
--- a/src/components/mfa_form/totp_form.js
+++ b/src/components/mfa_form/totp_form.js
@@ -1,63 +1,63 @@
import { mapActions, mapState, mapStores } from 'pinia'
-import { verifyOTPCode } from 'src/api/mfa.js'
-
import { useAuthFlowStore } from 'src/stores/auth_flow.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useOAuthStore } from 'src/stores/oauth.js'
+import { verifyOTPCode } from 'src/api/mfa.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: {
...mapState(useAuthFlowStore, {
authSettings: (store) => store.settings,
}),
...mapState(useInstanceStore, ['server']),
...mapStores(useOAuthStore),
},
methods: {
...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.server,
mfaToken: this.authSettings.mfa_token,
code: this.code,
}
verifyOTPCode(data)
.then(({ data: result }) => {
this.login(result).then(() => {
this.$router.push({ name: 'friends' })
})
})
.catch((error) => {
this.error = error
this.code = null
this.focusOnCodeInput()
return
})
},
},
}
diff --git a/src/components/oauth_callback/oauth_callback.js b/src/components/oauth_callback/oauth_callback.js
index 4bcb9803e2..04f79425ec 100644
--- a/src/components/oauth_callback/oauth_callback.js
+++ b/src/components/oauth_callback/oauth_callback.js
@@ -1,29 +1,27 @@
-import oauth from '../../services/new_api/oauth.js'
-
import { useInstanceStore } from 'src/stores/instance.js'
import { useOAuthStore } from 'src/stores/oauth.js'
+import { getToken } from 'src/api/oauth.js'
+
const oac = {
props: ['code'],
mounted() {
if (this.code) {
const oauthStore = useOAuthStore()
const { clientId, clientSecret } = oauthStore
- oauth
- .getToken({
- clientId,
- clientSecret,
- instance: useInstanceStore().server,
- code: this.code,
- })
- .then((result) => {
- oauthStore.setToken(result.access_token)
- this.$store.dispatch('loginUser', result.access_token)
- this.$router.push({ name: 'friends' })
- })
+ getToken({
+ clientId,
+ clientSecret,
+ instance: useInstanceStore().server,
+ code: this.code,
+ }).then(({ data: result }) => {
+ oauthStore.setToken(result.access_token)
+ this.$store.dispatch('loginUser', result.access_token)
+ this.$router.push({ name: 'friends' })
+ })
}
},
}
export default oac
diff --git a/src/modules/users.js b/src/modules/users.js
index 2eee21333a..5573928b42 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -1,871 +1,871 @@
import {
compact,
concat,
each,
isArray,
last,
map,
mergeWith,
uniq,
} from 'lodash'
-import oauthApi from '../services/new_api/oauth.js'
import {
registerPushNotifications,
unregisterPushNotifications,
} from '../services/sw/sw.js'
import {
windowHeight,
windowWidth,
} from '../services/window_utils/window_utils'
import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders.js'
import { useEmojiStore } from 'src/stores/emoji.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useInterfaceStore } from 'src/stores/interface.js'
import { useListsStore } from 'src/stores/lists.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useOAuthStore } from 'src/stores/oauth.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js'
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
+import { revokeToken } from 'src/api/oauth.js'
import {
fetchFollowers,
fetchFriends,
fetchUser,
fetchUserByName,
getCaptcha,
register,
searchUsers,
verifyCredentials,
} from 'src/api/public.js'
import {
blockUser as apiBlockUser,
muteUser as apiMuteUser,
unblockUser as apiUnblockUser,
unmuteUser as apiUnmuteUser,
fetchBlocks,
fetchDomainMutes,
fetchMutes,
fetchUserInLists,
fetchUserRelationship,
followUser,
} from 'src/api/user.js'
// TODO: Unify with mergeOrAdd in statuses.js
export const mergeOrAdd = (arr, obj, item) => {
if (!item) {
return false
}
const oldItem = obj[item.id]
if (oldItem) {
// We already have this, so only merge the new info.
mergeWith(oldItem, item, mergeArrayLength)
return { item: oldItem, new: false }
} else {
// This is a new item, prepare it
arr.push(item)
obj[item.id] = item
return { item, new: true }
}
}
const mergeArrayLength = (oldValue, newValue) => {
if (isArray(oldValue) && isArray(newValue)) {
oldValue.length = newValue.length
return mergeWith(oldValue, newValue, mergeArrayLength)
}
}
const getNotificationPermission = () => {
const Notification = window.Notification
if (!Notification) return Promise.resolve(null)
if (Notification.permission === 'default')
return Notification.requestPermission()
return Promise.resolve(Notification.permission)
}
const blockUser = (store, args) => {
const id = args.id
const expiresIn = typeof args === 'object' ? args.expiresIn : 0
const predictedRelationship = store.state.relationships[id] || { id }
store.commit('updateUserRelationship', [predictedRelationship])
store.commit('addBlockId', id)
return apiBlockUser({ id, expiresIn }).then(({ data: relationship }) => {
store.commit('updateUserRelationship', [relationship])
store.commit('addBlockId', id)
store.commit('removeStatus', { timeline: 'friends', userId: id })
store.commit('removeStatus', { timeline: 'public', userId: id })
store.commit('removeStatus', {
timeline: 'publicAndExternal',
userId: id,
})
})
}
const unblockUser = (store, id) => {
return apiUnblockUser({ id }).then(({ data: relationship }) =>
store.commit('updateUserRelationship', [relationship]),
)
}
const removeUserFromFollowers = (store, id) => {
return removeUserFromFollowers({ id }).then((relationship) =>
store.commit('updateUserRelationship', [relationship]),
)
}
const editUserNote = (store, { id, comment }) => {
return editUserNote({ id, comment }).then((relationship) =>
store.commit('updateUserRelationship', [relationship]),
)
}
const muteUser = (store, args) => {
const id = typeof args === 'object' ? args.id : args
const expiresIn = typeof args === 'object' ? args.expiresIn : 0
const predictedRelationship = store.state.relationships[id] || { id }
store.commit('updateUserRelationship', [predictedRelationship])
store.commit('addMuteId', id)
return apiMuteUser({
id,
expiresIn,
credentials: useOAuthStore().token,
}).then(({ data: relationship }) => {
store.commit('updateUserRelationship', [relationship])
store.commit('addMuteId', id)
})
}
const unmuteUser = (store, id) => {
const predictedRelationship = store.state.relationships[id] || { id }
predictedRelationship.muting = false
store.commit('updateUserRelationship', [predictedRelationship])
return apiUnmuteUser({ id }).then(({ data: relationship }) =>
store.commit('updateUserRelationship', [relationship]),
)
}
const hideReblogs = (store, userId) => {
return followUser({
id: userId,
reblogs: false,
credentials: useOAuthStore().token,
}).then(({ data: relationship }) =>
store.commit('updateUserRelationship', [relationship]),
)
}
const showReblogs = (store, userId) => {
return followUser({
id: userId,
reblogs: true,
credentials: useOAuthStore().token,
}).then(({ data: relationship }) =>
store.commit('updateUserRelationship', [relationship]),
)
}
const muteDomain = (store, domain) => {
return muteDomain({
domain,
credentials: useOAuthStore().token,
}).then(() => store.commit('addDomainMute', domain))
}
const unmuteDomain = (store, domain) => {
return unmuteDomain({
domain,
credentials: useOAuthStore().token,
}).then(() => store.commit('removeDomainMute', domain))
}
export const mutations = {
tagUser(state, { user: { id }, tag }) {
const user = state.usersObject[id]
user.tags.add(tag)
},
untagUser(state, { user: { id }, tag }) {
const user = state.usersObject[id]
user.tags.delete(tag)
},
updateRight(state, { user: { id }, right, value }) {
const user = state.usersObject[id]
const newRights = user.rights
newRights[right] = value
user.rights = newRights
},
updateUserAdminData(state, { user }) {
const { id } = user
const localUser = state.usersObject[id]
localUser.adminData = user
localUser.deactivated = !user.is_active
localUser.tags = new Set(user.tags)
},
setCurrentUser(state, user) {
state.lastLoginName = user.screen_name
state.currentUser = mergeWith(
state.currentUser || {},
user,
mergeArrayLength,
)
},
clearCurrentUser(state) {
state.currentUser = false
state.lastLoginName = false
},
beginLogin(state) {
state.loggingIn = true
},
endLogin(state) {
state.loggingIn = false
},
saveFriendIds(state, { id, friendIds }) {
const user = state.usersObject[id]
user.friendIds = uniq(concat(user.friendIds || [], friendIds))
},
saveFollowerIds(state, { id, followerIds }) {
const user = state.usersObject[id]
user.followerIds = uniq(concat(user.followerIds || [], followerIds))
},
// Because frontend doesn't have a reason to keep these stuff in memory
// outside of viewing someones user profile.
clearFriends(state, userId) {
const user = state.usersObject[userId]
if (user) {
user.friendIds = []
}
},
clearFollowers(state, userId) {
const user = state.usersObject[userId]
if (user) {
user.followerIds = []
}
},
addNewUsers(state, users) {
each(users, (user) => {
if (user.relationship) {
state.relationships[user.relationship.id] = user.relationship
}
const res = mergeOrAdd(state.users, state.usersObject, user)
const item = res.item
if (res.new && item.screen_name && !item.screen_name.includes('@')) {
state.usersByNameObject[item.screen_name.toLowerCase()] = item
}
})
},
updateUserRelationship(state, relationships) {
relationships.forEach((relationship) => {
state.relationships[relationship.id] = relationship
})
},
updateUserInLists(state, { id, inLists }) {
state.usersObject[id].inLists = inLists
},
saveBlockIds(state, blockIds) {
state.currentUser.blockIds = blockIds
},
addBlockId(state, blockId) {
if (state.currentUser.blockIds.indexOf(blockId) === -1) {
state.currentUser.blockIds.push(blockId)
}
},
setBlockIdsMaxId(state, blockIdsMaxId) {
state.currentUser.blockIdsMaxId = blockIdsMaxId
},
saveMuteIds(state, muteIds) {
state.currentUser.muteIds = muteIds
},
setMuteIdsMaxId(state, muteIdsMaxId) {
state.currentUser.muteIdsMaxId = muteIdsMaxId
},
addMuteId(state, muteId) {
if (state.currentUser.muteIds.indexOf(muteId) === -1) {
state.currentUser.muteIds.push(muteId)
}
},
saveDomainMutes(state, domainMutes) {
state.currentUser.domainMutes = domainMutes
},
addDomainMute(state, domain) {
if (state.currentUser.domainMutes.indexOf(domain) === -1) {
state.currentUser.domainMutes.push(domain)
}
},
removeDomainMute(state, domain) {
const index = state.currentUser.domainMutes.indexOf(domain)
if (index !== -1) {
state.currentUser.domainMutes.splice(index, 1)
}
},
setPinnedToUser(state, status) {
const user = state.usersObject[status.user.id]
user.pinnedStatusIds = user.pinnedStatusIds || []
const index = user.pinnedStatusIds.indexOf(status.id)
if (status.pinned && index === -1) {
user.pinnedStatusIds.push(status.id)
} else if (!status.pinned && index !== -1) {
user.pinnedStatusIds.splice(index, 1)
}
},
setUserForStatus(state, status) {
status.user = state.usersObject[status.user.id]
},
setUserForNotification(state, notification) {
if (notification.type !== 'follow') {
notification.action.user = state.usersObject[notification.action.user.id]
}
notification.from_profile = state.usersObject[notification.from_profile.id]
},
setColor(state, { user: { id }, highlighted }) {
const user = state.usersObject[id]
user.highlight = highlighted
},
signUpPending(state) {
state.signUpPending = true
state.signUpErrors = []
state.signUpNotice = {}
},
signUpSuccess(state) {
state.signUpPending = false
},
signUpFailure(state, errors) {
state.signUpPending = false
state.signUpErrors = errors
state.signUpNotice = {}
},
signUpNotice(state, notice) {
state.signUpPending = false
state.signUpErrors = []
state.signUpNotice = notice
},
}
export const getters = {
findUser: (state) => (query) => {
return state.usersObject[query]
},
findUserByName: (state) => (query) => {
return state.usersByNameObject[query.toLowerCase()]
},
findUserByUrl: (state) => (query) => {
return state.users.find(
(u) =>
u.statusnet_profile_url &&
u.statusnet_profile_url.toLowerCase() === query.toLowerCase(),
)
},
relationship: (state) => (id) => {
const rel = id && state.relationships[id]
return rel || { id, loading: true }
},
}
export const defaultState = {
loggingIn: false,
lastLoginName: false,
currentUser: false,
users: [],
usersObject: {},
usersByNameObject: {},
signUpPending: false,
signUpErrors: [],
signUpNotice: {},
relationships: {},
}
const users = {
state: defaultState,
mutations,
getters,
actions: {
fetchUserIfMissing(store, id) {
const user = store.getters.findUser(id)
if (!user) {
return store.dispatch('fetchUser', id)
} else {
return Promise.resolve(user)
}
},
updateUserAdminData(store, { userAdminData }) {
return store
.dispatch('fetchUserIfMissing', userAdminData.id)
.then((user) => {
user.adminData = userAdminData
store.commit('addNewUsers', [user])
return user
})
},
fetchUser(store, id) {
return fetchUser({
id,
credentials: useOAuthStore().token,
})
.then(({ data: user }) => {
store.commit('addNewUsers', [user])
return user
})
.catch((error) => {
if (error.statusCode === 404) {
console.warn(`User ${id} not found`)
} else {
throw error
}
})
},
fetchUserByName(store, name) {
return fetchUserByName({
name,
credentials: useOAuthStore().token,
}).then(({ data: user }) => {
store.commit('addNewUsers', [user])
return user
})
},
fetchUserRelationship(store, id) {
if (store.state.currentUser) {
fetchUserRelationship({
id,
credentials: useOAuthStore().token,
}).then(({ data: relationships }) =>
store.commit('updateUserRelationship', relationships),
)
}
},
fetchUserInLists(store, id) {
if (store.state.currentUser) {
fetchUserInLists({
id,
credentials: useOAuthStore().token,
}).then(({ data: inLists }) =>
store.commit('updateUserInLists', { id, inLists }),
)
}
},
fetchBlocks(store, args) {
const { reset } = args || {}
const maxId = store.state.currentUser.blockIdsMaxId
return fetchBlocks({
maxId,
credentials: useOAuthStore().token,
}).then(({ data: blocks }) => {
if (reset) {
store.commit('saveBlockIds', map(blocks, 'id'))
} else {
map(blocks, 'id').map((id) => store.commit('addBlockId', id))
}
if (blocks.length) {
store.commit('setBlockIdsMaxId', last(blocks).id)
}
store.commit('addNewUsers', blocks)
return blocks
})
},
blockUser(store, data) {
return blockUser(store, data)
},
unblockUser(store, data) {
return unblockUser(store, data)
},
removeUserFromFollowers(store, id) {
return removeUserFromFollowers(store, id)
},
blockUsers(store, data = []) {
return Promise.all(data.map((d) => blockUser(store, d)))
},
unblockUsers(store, data = []) {
return Promise.all(data.map((d) => unblockUser(store, d)))
},
editUserNote(store, args) {
return editUserNote(store, args)
},
fetchMutes(store, args) {
const { reset } = args || {}
const maxId = store.state.currentUser.muteIdsMaxId
return fetchMutes({
maxId,
credentials: useOAuthStore().token,
}).then(({ data: mutes }) => {
if (reset) {
store.commit('saveMuteIds', map(mutes, 'id'))
} else {
map(mutes, 'id').map((id) => store.commit('addMuteId', id))
}
if (mutes.length) {
store.commit('setMuteIdsMaxId', last(mutes).id)
}
store.commit('addNewUsers', mutes)
return mutes
})
},
muteUser(store, data) {
return muteUser(store, data)
},
unmuteUser(store, id) {
return unmuteUser(store, id)
},
hideReblogs(store, id) {
return hideReblogs(store, id)
},
showReblogs(store, id) {
return showReblogs(store, id)
},
muteUsers(store, data = []) {
return Promise.all(data.map((d) => muteUser(store, d)))
},
unmuteUsers(store, ids = []) {
return Promise.all(ids.map((d) => unmuteUser(store, d)))
},
fetchDomainMutes(store) {
return fetchDomainMutes({
credentials: useOAuthStore().token,
}).then(({ data: domainMutes }) => {
store.commit('saveDomainMutes', domainMutes)
return domainMutes
})
},
muteDomain(store, domain) {
return muteDomain(store, domain)
},
unmuteDomain(store, domain) {
return unmuteDomain(store, domain)
},
muteDomains(store, domains = []) {
return Promise.all(domains.map((domain) => muteDomain(store, domain)))
},
unmuteDomains(store, domain = []) {
return Promise.all(domain.map((domain) => unmuteDomain(store, domain)))
},
fetchFriends({ rootState, commit }, id) {
const user = rootState.users.usersObject[id]
const maxId = last(user.friendIds)
return fetchFriends({
id,
maxId,
credentials: useOAuthStore().token,
}).then(({ data: friends }) => {
commit('addNewUsers', friends)
commit('saveFriendIds', { id, friendIds: map(friends, 'id') })
return friends
})
},
fetchFollowers({ rootState, commit }, id) {
const user = rootState.users.usersObject[id]
const maxId = last(user.followerIds)
return fetchFollowers({
id,
maxId,
credentials: useOAuthStore().token,
}).then(({ data: followers }) => {
commit('addNewUsers', followers)
commit('saveFollowerIds', { id, followerIds: map(followers, 'id') })
return followers
})
},
clearFriends({ commit }, userId) {
commit('clearFriends', userId)
},
clearFollowers({ commit }, userId) {
commit('clearFollowers', userId)
},
subscribeUser({ rootState, commit }, id) {
return followUser({
id,
notify: true,
credentials: useOAuthStore().token,
}).then(({ data: relationship }) =>
commit('updateUserRelationship', [relationship]),
)
},
unsubscribeUser({ rootState, commit }, id) {
return followUser({
id,
notify: false,
credentials: useOAuthStore().token,
}).then(({ data: relationship }) =>
commit('updateUserRelationship', [relationship]),
)
},
registerPushNotifications(store) {
const token = store.state.currentUser.credentials
const vapidPublicKey = useInstanceStore().vapidPublicKey
const isEnabled = useMergedConfigStore().mergedConfig.webPushNotifications
const notificationVisibility =
useMergedConfigStore().mergedConfig.notificationVisibility
registerPushNotifications(
isEnabled,
vapidPublicKey,
token,
notificationVisibility,
)
},
unregisterPushNotifications(store) {
const token = store.state.currentUser.credentials
unregisterPushNotifications(token)
},
addNewUsers({ commit }, users) {
commit('addNewUsers', users)
},
addNewStatuses(store, { statuses }) {
const users = map(statuses, 'user')
const retweetedUsers = compact(map(statuses, 'retweeted_status.user'))
store.commit('addNewUsers', users)
store.commit('addNewUsers', retweetedUsers)
each(statuses, (status) => {
// Reconnect users to statuses
store.commit('setUserForStatus', status)
// Set pinned statuses to user
store.commit('setPinnedToUser', status)
})
each(compact(map(statuses, 'retweeted_status')), (status) => {
// Reconnect users to retweets
store.commit('setUserForStatus', status)
// Set pinned retweets to user
store.commit('setPinnedToUser', status)
})
},
addNewNotifications(store, { notifications }) {
const users = map(notifications, 'from_profile')
const targetUsers = map(notifications, 'target').filter((_) => _)
const notificationIds = notifications.map((_) => _.id)
store.commit('addNewUsers', users)
store.commit('addNewUsers', targetUsers)
const notificationsObject = store.rootState.notifications.idStore
const relevantNotifications = Object.entries(notificationsObject)
.filter(([k]) => notificationIds.includes(k))
.map(([, val]) => val)
// Reconnect users to notifications
each(relevantNotifications, (notification) => {
store.commit('setUserForNotification', notification)
})
},
searchUsers({ rootState, commit }, { query }) {
return searchUsers({
query,
credentials: useOAuthStore().token,
}).then(({ data: users }) => {
commit('addNewUsers', users)
return users
})
},
async signUp(store, userInfo) {
const oauthStore = useOAuthStore()
store.commit('signUpPending')
try {
const token = await oauthStore.ensureAppToken()
const { data } = await register({
credentials: token,
params: { ...userInfo },
})
if (data.access_token) {
store.commit('signUpSuccess')
oauthStore.setToken(data.access_token)
await store.dispatch('loginUser', data.access_token)
return 'ok'
} else {
// Request succeeded, but user cannot login yet.
store.commit('signUpNotice', data)
return 'request_sent'
}
} catch (e) {
const errors = e.message
store.commit('signUpFailure', errors)
throw e
}
},
getCaptcha(store) {
return getCaptcha({
credentials: useOAuthStore().token,
}).then(({ data }) => data)
},
logout(store) {
const oauth = useOAuthStore()
// NOTE: No need to verify the app still exists, because if it doesn't,
// the token will be invalid too
return oauth
.ensureApp()
.then((app) => {
const params = {
app,
instance: useInstanceStore().server,
token: oauth.userToken,
}
- return oauthApi.revokeToken(params)
+ return revokeToken(params)
})
.then(() => {
store.commit('clearCurrentUser')
store.dispatch('disconnectFromSocket')
oauth.clearToken()
store.dispatch('stopFetchingTimeline', 'friends')
store.dispatch('stopFetchingNotifications')
useListsStore().stopFetching()
useBookmarkFoldersStore().stopFetching()
store.dispatch('stopFetchingFollowRequests')
store.commit('clearNotifications')
store.commit('resetStatuses')
store.dispatch('resetChats')
useInterfaceStore().setLastTimeline('public-timeline')
useInterfaceStore().setLayoutWidth(windowWidth())
useInterfaceStore().setLayoutHeight(windowHeight())
})
},
loginUser(store, accessToken) {
return new Promise((resolve, reject) => {
const commit = store.commit
const dispatch = store.dispatch
commit('beginLogin')
verifyCredentials({
credentials: useOAuthStore().token,
})
.then(({ data: user }) => {
// user.credentials = userCredentials
user.credentials = accessToken
user.blockIds = []
user.muteIds = []
user.domainMutes = []
commit('setCurrentUser', user)
useSyncConfigStore()
.initSyncConfig(user)
.then(() => {
useInterfaceStore()
.applyTheme()
.catch((e) => {
console.error('Error setting theme', e)
})
})
useUserHighlightStore().initUserHighlight(user)
commit('addNewUsers', [user])
useEmojiStore().fetchEmoji()
getNotificationPermission().then((permission) =>
useInterfaceStore().setNotificationPermission(permission),
)
// Do server-side storage migrations
// Debug snippet to clean up storage and reset migrations
/*
// Reset wordfilter
Object.keys(
useSyncConfigStore().prefsStorage.simple.muteFilters
).forEach(key => {
useSyncConfigStore().unsetSimplePrefAndSave({ path: 'muteFilters.' + key, value: null })
})
// Reset flag to 0 to re-run migrations
useSyncConfigStore().setFlag({ flag: 'configMigration', value: 0 })
/**/
if (user.token) {
dispatch('setWsToken', user.token)
// Initialize the shout socket.
dispatch('initializeSocket')
}
const startPolling = () => {
// Start getting fresh posts.
dispatch('startFetchingTimeline', { timeline: 'friends' })
// Start fetching notifications
dispatch('startFetchingNotifications')
if (useInstanceCapabilitiesStore().pleromaChatMessagesAvailable) {
// Start fetching chats
dispatch('startFetchingChats')
}
}
useListsStore().startFetching()
useBookmarkFoldersStore().startFetching()
if (user.locked) {
dispatch('startFetchingFollowRequests')
}
if (useMergedConfigStore().mergedConfig.useStreamingApi) {
dispatch('fetchTimeline', {
timeline: 'friends',
sinceId: null,
})
dispatch('fetchNotifications', { sinceId: null })
dispatch('enableMastoSockets', true)
.catch((error) => {
console.error(
'Failed initializing MastoAPI Streaming socket',
error,
)
})
.then(() => {
dispatch('fetchChats', { latest: true })
setTimeout(
() => dispatch('setNotificationsSilence', false),
10000,
)
})
} else {
startPolling()
}
// Get user mutes
dispatch('fetchMutes')
useInterfaceStore().setLayoutWidth(windowWidth())
useInterfaceStore().setLayoutHeight(windowHeight())
// Fetch our friends
fetchFriends({ id: user.id }).then(({ data: friends }) =>
commit('addNewUsers', friends),
)
commit('endLogin')
resolve()
})
.catch((error) => {
console.error(error)
// Authentication failed
commit('endLogin')
// remove authentication token on client/authentication errors
if ([400, 401, 403, 422].includes(error.statusCode)) {
useOAuthStore().clearToken()
}
commit('endLogin')
if (error.tatusCode === 401) {
throw new Error('Wrong username or password', error)
} else {
throw new Error('An error occurred, please try again', error)
}
})
})
},
},
}
export default users
diff --git a/src/services/new_api/oauth.js b/src/services/new_api/oauth.js
deleted file mode 100644
index b803e21465..0000000000
--- a/src/services/new_api/oauth.js
+++ /dev/null
@@ -1,198 +0,0 @@
-import { reduce } from 'lodash'
-
-import { StatusCodeError } from 'src/services/errors/errors.js'
-
-const REDIRECT_URI = `${window.location.origin}/oauth-callback`
-
-export const getJsonOrError = async (response) => {
- if (response.ok) {
- return response.json().catch((error) => {
- throw new StatusCodeError(response.status, error, {}, response)
- })
- } else {
- throw new StatusCodeError(
- response.status,
- await response.text(),
- {},
- response,
- )
- }
-}
-
-export const createApp = (instance) => {
- const url = `${instance}/api/v1/apps`
- const form = new window.FormData()
-
- form.append('client_name', 'PleromaFE')
- form.append('website', 'https://pleroma.social')
- form.append('redirect_uris', REDIRECT_URI)
- form.append('scopes', 'read write follow push admin')
-
- return window
- .fetch(url, {
- method: 'POST',
- body: form,
- })
- .then(getJsonOrError)
- .then((app) => ({
- clientId: app.client_id,
- clientSecret: app.client_secret,
- }))
-}
-
-export const verifyAppToken = ({ instance, appToken }) => {
- return window
- .fetch(`${instance}/api/v1/apps/verify_credentials`, {
- method: 'GET',
- headers: { Authorization: `Bearer ${appToken}` },
- })
- .then(getJsonOrError)
-}
-
-const login = ({ instance, clientId }) => {
- const data = {
- response_type: 'code',
- client_id: clientId,
- redirect_uri: REDIRECT_URI,
- scope: 'read write follow push admin',
- }
-
- const dataString = reduce(
- data,
- (acc, v, k) => {
- const encoded = `${k}=${encodeURIComponent(v)}`
- if (!acc) {
- return encoded
- } else {
- return `${acc}&${encoded}`
- }
- },
- false,
- )
-
- // Do the redirect...
- const url = `${instance}/oauth/authorize?${dataString}`
-
- window.location.href = url
-}
-
-const getTokenWithCredentials = ({
- clientId,
- clientSecret,
- instance,
- username,
- password,
-}) => {
- const url = `${instance}/oauth/token`
- const form = new window.FormData()
-
- form.append('client_id', clientId)
- form.append('client_secret', clientSecret)
- form.append('grant_type', 'password')
- form.append('username', username)
- form.append('password', password)
-
- return window
- .fetch(url, {
- method: 'POST',
- body: form,
- })
- .then((data) => data.json())
-}
-
-const getToken = ({ clientId, clientSecret, instance, code }) => {
- const url = `${instance}/oauth/token`
- const form = new window.FormData()
-
- form.append('client_id', clientId)
- form.append('client_secret', clientSecret)
- form.append('grant_type', 'authorization_code')
- form.append('code', code)
- form.append('redirect_uri', `${window.location.origin}/oauth-callback`)
-
- return window
- .fetch(url, {
- method: 'POST',
- body: form,
- })
- .then((data) => data.json())
-}
-
-export const getClientToken = ({ clientId, clientSecret, instance }) => {
- const url = `${instance}/oauth/token`
- const form = new window.FormData()
-
- form.append('client_id', clientId)
- form.append('client_secret', clientSecret)
- form.append('grant_type', 'client_credentials')
- form.append('redirect_uri', `${window.location.origin}/oauth-callback`)
-
- return window
- .fetch(url, {
- method: 'POST',
- body: form,
- })
- .then(getJsonOrError)
-}
-const verifyOTPCode = ({ app, instance, mfaToken, code }) => {
- const url = `${instance}/oauth/mfa/challenge`
- const form = new window.FormData()
-
- form.append('client_id', app.client_id)
- form.append('client_secret', app.client_secret)
- form.append('mfa_token', mfaToken)
- form.append('code', code)
- form.append('challenge_type', 'totp')
-
- return window
- .fetch(url, {
- method: 'POST',
- body: form,
- })
- .then((data) => data.json())
-}
-
-const verifyRecoveryCode = ({ app, instance, mfaToken, code }) => {
- const url = `${instance}/oauth/mfa/challenge`
- const form = new window.FormData()
-
- form.append('client_id', app.client_id)
- form.append('client_secret', app.client_secret)
- form.append('mfa_token', mfaToken)
- form.append('code', code)
- form.append('challenge_type', 'recovery')
-
- return window
- .fetch(url, {
- method: 'POST',
- body: form,
- })
- .then((data) => data.json())
-}
-
-const revokeToken = ({ app, instance, token }) => {
- const url = `${instance}/oauth/revoke`
- const form = new window.FormData()
-
- form.append('client_id', app.clientId)
- form.append('client_secret', app.clientSecret)
- form.append('token', token)
-
- return window
- .fetch(url, {
- method: 'POST',
- body: form,
- })
- .then((data) => data.json())
-}
-
-const oauth = {
- login,
- getToken,
- getTokenWithCredentials,
- verifyOTPCode,
- verifyRecoveryCode,
- revokeToken,
-}
-
-export default oauth
diff --git a/src/stores/announcements.js b/src/stores/announcements.js
index f780f6dd72..4668b286ab 100644
--- a/src/stores/announcements.js
+++ b/src/stores/announcements.js
@@ -1,158 +1,160 @@
import { defineStore } from 'pinia'
import { useOAuthStore } from 'src/stores/oauth.js'
import { getAnnouncements } from 'src/api/public.js'
const FETCH_ANNOUNCEMENT_INTERVAL_MS = 1000 * 60 * 5
export const useAnnouncementsStore = defineStore('announcements', {
state: () => ({
announcements: [],
supportsAnnouncements: true,
fetchAnnouncementsTimer: undefined,
adminActions: {},
userActions: {},
}),
getters: {
unreadAnnouncementCount() {
if (!window.vuex.state.users.currentUser) {
return 0
}
const unread = this.announcements.filter(
(announcement) => !(announcement.inactive || announcement.read),
)
return unread.length
},
},
actions: {
async fetchAnnouncements() {
if (!this.supportsAnnouncements) return
const currentUser = window.vuex.state.users.currentUser
const isAdmin =
currentUser &&
currentUser.privileges.has('announcements_manage_announcements')
try {
if (currentUser) {
this.userActions = await import('src/api/user.js')
}
if (isAdmin) {
this.adminActions = await import('src/api/admin.js')
} else {
const all = await getAnnouncements({
credentials: useOAuthStore().token,
})
return all.data
}
const { data: all } = await this.adminActions.getAnnouncements({
credentials: useOAuthStore().token,
})
const { data: visible } = await getAnnouncements({
credentials: useOAuthStore().token,
})
const visibleObject = visible.reduce((a, c) => {
a[c.id] = c
return a
}, {})
const getWithinVisible = (announcement) =>
visibleObject[announcement.id]
all.forEach((announcement) => {
const visibleAnnouncement = getWithinVisible(announcement)
if (!visibleAnnouncement) {
announcement.inactive = true
} else {
announcement.read = visibleAnnouncement.read
}
})
this.announcements = all
} catch (error) {
// If and only if backend does not support announcements, it would return 404.
// In this case, silently ignores it.
if (error && error.statusCode === 404) {
this.supportsAnnouncements = false
} else {
throw error
}
}
},
markAnnouncementAsRead(id) {
- return this.userActions.dismissAnnouncement({
- id,
- credentials: useOAuthStore().token,
- }).then(() => {
- const index = this.announcements.findIndex((a) => a.id === id)
-
- if (index < 0) {
- return
- }
+ return this.userActions
+ .dismissAnnouncement({
+ id,
+ credentials: useOAuthStore().token,
+ })
+ .then(() => {
+ const index = this.announcements.findIndex((a) => a.id === id)
+
+ if (index < 0) {
+ return
+ }
- this.announcements[index].read = true
- })
+ this.announcements[index].read = true
+ })
},
startFetchingAnnouncements() {
if (this.fetchAnnouncementsTimer) {
return
}
const interval = setInterval(
() => this.fetchAnnouncements(),
FETCH_ANNOUNCEMENT_INTERVAL_MS,
)
this.fetchAnnouncementsTimer = interval
return this.fetchAnnouncements()
},
stopFetchingAnnouncements() {
const interval = this.fetchAnnouncementsTimer
this.fetchAnnouncementsTimer = undefined
clearInterval(interval)
},
postAnnouncement({ content, startsAt, endsAt, allDay }) {
return this.adminActions
.postAnnouncement({
credentials: useOAuthStore().token,
content,
startsAt,
endsAt,
allDay,
})
.then(() => {
return this.fetchAnnouncements()
})
},
editAnnouncement({ id, content, startsAt, endsAt, allDay }) {
return this.adminActions
.editAnnouncement({
id,
content,
startsAt,
endsAt,
allDay,
credentials: useOAuthStore().token,
})
.then(() => {
return this.fetchAnnouncements()
})
},
deleteAnnouncement(id) {
return this.adminActions
.deleteAnnouncement({
id,
credentials: useOAuthStore().token,
})
.then(() => {
return this.fetchAnnouncements()
})
},
},
})
diff --git a/src/stores/oauth.js b/src/stores/oauth.js
index 116169b81c..55e8e759cf 100644
--- a/src/stores/oauth.js
+++ b/src/stores/oauth.js
@@ -1,156 +1,152 @@
import { defineStore } from 'pinia'
import { useInstanceStore } from 'src/stores/instance.js'
-import {
- createApp,
- getClientToken,
- verifyAppToken,
-} from 'src/services/new_api/oauth.js'
+import { createApp, getClientToken } from 'src/api/oauth.js'
+import { verifyCredentials } from 'src/api/public.js'
// status codes about verifyAppToken (GET /api/v1/apps/verify_credentials)
const isAppTokenRejected = (error) =>
// Pleroma API docs say it returns 422 when unauthorized
error.statusCode === 422 ||
// but it actually returns 400 (as of 2.9.0)
// NOTE: don't try to match against the error message, it is translatable
error.statusCode === 400 ||
// and Mastodon docs say it returns 401
error.statusCode === 401
// status codes about getAppToken (GET /oauth/token)
const isClientDataRejected = (error) =>
// Mastodon docs say it returns 401
error.statusCode === 401 ||
// but Pleroma actually returns 400 (as of 2.9.0)
// NOTE: don't try to match against the error message, it is translatable
error.statusCode === 400
export const useOAuthStore = defineStore('oauth', {
state: () => ({
clientId: false,
clientSecret: false,
/* App token is authentication for app without any user, used mostly for
* MastoAPI's registration of new users, stored so that we can fall back to
* it on logout
*/
appToken: false,
/* User token is authentication for app with user, this is for every calls
* that need authorized user to be successful (i.e. posting, liking etc)
*/
userToken: false,
}),
getters: {
token() {
return this.userToken || this.appToken
},
},
actions: {
setClientData({ clientId, clientSecret }) {
this.clientId = clientId
this.clientSecret = clientSecret
},
setAppToken(token) {
this.appToken = token
},
setToken(token) {
this.userToken = token
},
clearToken() {
this.userToken = false
},
async createApp() {
const instance = useInstanceStore().server
- const app = await createApp(instance)
- this.setClientData(app)
- return app
+ const app = await createApp({ instance })
+ this.setClientData(app.data)
+ return app.data
},
/// Use this if you want to get the client id and secret but are not interested
/// in whether they are valid.
/// @return {{ clientId: string, clientSecret: string }} An object representing the app
async ensureApp() {
if (this.clientId && this.clientSecret) {
return {
clientId: this.clientId,
clientSecret: this.clientSecret,
}
} else {
return this.createApp()
}
},
async getAppToken() {
const instance = useInstanceStore().server
const res = await getClientToken({
clientId: this.clientId,
clientSecret: this.clientSecret,
instance,
})
- this.setAppToken(res.access_token)
+ this.setAppToken(res.data.access_token)
return res.access_token
},
/// Use this if you want to ensure the app is still valid to use.
/// @return {string} The access token to the app (not attached to any user)
async ensureAppToken() {
if (this.appToken) {
try {
- await verifyAppToken({
- instance: useInstanceStore().server,
- appToken: this.appToken,
+ await verifyCredentials({
+ credentials: this.appToken,
})
return this.appToken
} catch (e) {
if (!isAppTokenRejected(e)) {
// The server did not reject our token, but we encountered other problems. Maybe the server is down.
throw e
} else {
// The app token is rejected, so it is no longer useful.
this.setAppToken(false)
}
}
}
// appToken is not available, or is rejected: try to get a new one.
// First, make sure the client id and client secret are filled.
try {
await this.ensureApp()
} catch (e) {
console.error('Cannot create app', e)
throw e
}
// Note that at this step, the client id and secret may be invalid
// (because the backend may have already deleted the app due to no user login)
try {
return await this.getAppToken()
} catch (e) {
if (!isClientDataRejected(e)) {
// Non-credentials problem, fail fast
console.error('Cannot get app token', e)
throw e
} else {
// the client id and secret are invalid, so we should clear them
// and re-create our app
this.setClientData({
clientId: false,
clientSecret: false,
})
await this.createApp()
// try once again to get the token
return await this.getAppToken()
}
}
},
},
persist: {
afterLoad(state) {
// state.token is userToken with older name, coming from persistent state
if (state.token && !state.userToken) {
state.userToken = state.token
}
if ('token' in state) {
delete state.token
}
return state
},
},
})
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Oct 29, 5:36 PM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1798419
Default Alt Text
(56 KB)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline