semaphore/src/routes/_actions/addInstance.js

132 lines
4.9 KiB
JavaScript
Raw Normal View History

import { getAccessTokenFromAuthCode, registerApplication, generateAuthLink } from '../_api/oauth.js'
import { getInstanceInfo } from '../_api/instance.js'
import { goto } from '../../../__sapper__/client.js'
import { DEFAULT_THEME, switchToTheme } from '../_utils/themeEngine.js'
import { store } from '../_store/store.js'
import { updateVerifyCredentialsForInstance } from './instances.js'
import { updateCustomEmojiForInstance } from './emoji.js'
import { database } from '../_database/database.js'
import { DOMAIN_BLOCKS } from '../_static/blocks.js'
2018-01-27 21:38:57 +00:00
const GENERIC_ERROR = `
Is this a valid Mastodon instance? Is a browser extension
blocking the request? Are you in private browsing mode?
If you believe this is a problem with your instance, please send
2023-01-22 09:08:20 +00:00
<a href="https://github.com/NickColley/semaphore/blob/main/docs/Admin-Guide.md"
target="_blank" rel="noopener">this link</a> to the administrator of your instance.`
function createKnownError (message) {
2019-08-03 21:49:37 +01:00
const err = new Error(message)
err.knownError = true
return err
}
function getRedirectUri () {
return `${location.origin}/settings/instances/add`
}
2018-02-09 06:29:29 +00:00
async function redirectToOauth () {
let { instanceNameInSearch, loggedInInstances } = store.get()
instanceNameInSearch = instanceNameInSearch.replace(/^https?:\/\//, '').replace(/\/+$/, '').toLowerCase()
if (Object.keys(loggedInInstances).includes(instanceNameInSearch)) {
throw createKnownError(`You've already logged in to ${instanceNameInSearch}`)
}
2019-08-03 21:49:37 +01:00
const instanceHostname = new URL(`http://${instanceNameInSearch}`).hostname
if (DOMAIN_BLOCKS.some(domain => new RegExp(`(?:\\.|^)${domain}$`, 'i').test(instanceHostname))) {
throw createKnownError('This service is blocked')
2018-01-27 21:38:57 +00:00
}
const redirectUri = getRedirectUri()
const registrationPromise = registerApplication(instanceNameInSearch, redirectUri)
try {
const instanceInfo = await getInstanceInfo(instanceNameInSearch)
await database.setInstanceInfo(instanceNameInSearch, instanceInfo) // cache for later
} catch (err) {
// We get a 401 in limited federation mode, so we can just skip setting the instance info in that case.
// It will be fetched automatically later.
if (err.status !== 401) {
throw err // this is a good way to test for typos in the instance name or some other problem
}
}
2019-08-03 21:49:37 +01:00
const instanceData = await registrationPromise
2018-01-27 21:38:57 +00:00
store.set({
currentRegisteredInstanceName: instanceNameInSearch,
2018-01-27 21:38:57 +00:00
currentRegisteredInstance: instanceData
})
store.save()
2019-08-03 21:49:37 +01:00
const oauthUrl = generateAuthLink(
instanceNameInSearch,
2018-01-27 21:38:57 +00:00
instanceData.client_id,
redirectUri
2018-01-27 21:38:57 +00:00
)
// setTimeout to allow the browser to *actually* save the localStorage data (fixes Safari bug apparently)
setTimeout(() => {
document.location.href = oauthUrl
}, 200)
2018-01-27 21:38:57 +00:00
}
2018-02-09 06:29:29 +00:00
export async function logInToInstance () {
2018-02-15 02:15:14 +00:00
store.set({
logInToInstanceLoading: true,
logInToInstanceError: null
})
2018-01-27 21:38:57 +00:00
try {
await redirectToOauth()
} catch (err) {
console.error(err)
2019-08-03 21:49:37 +01:00
const error = `${err.message || err.name}. ` +
(err.knownError ? '' : (navigator.onLine ? GENERIC_ERROR : 'Are you offline?'))
2019-08-03 21:49:37 +01:00
const { instanceNameInSearch } = store.get()
2018-02-18 22:31:28 +00:00
store.set({
logInToInstanceError: error,
logInToInstanceErrorForText: instanceNameInSearch
2018-02-18 22:31:28 +00:00
})
2018-01-27 21:38:57 +00:00
} finally {
store.set({ logInToInstanceLoading: false })
2018-01-27 21:38:57 +00:00
}
}
2018-02-09 06:29:29 +00:00
async function registerNewInstance (code) {
2019-08-03 21:49:37 +01:00
const { currentRegisteredInstanceName, currentRegisteredInstance } = store.get()
const redirectUri = getRedirectUri()
2019-08-03 21:49:37 +01:00
const instanceData = await getAccessTokenFromAuthCode(
2018-01-27 21:38:57 +00:00
currentRegisteredInstanceName,
currentRegisteredInstance.client_id,
currentRegisteredInstance.client_secret,
code,
redirectUri
2018-01-27 21:38:57 +00:00
)
2019-08-03 21:49:37 +01:00
const { loggedInInstances, loggedInInstancesInOrder, instanceThemes } = store.get()
instanceThemes[currentRegisteredInstanceName] = DEFAULT_THEME
2018-01-27 21:38:57 +00:00
loggedInInstances[currentRegisteredInstanceName] = instanceData
if (!loggedInInstancesInOrder.includes(currentRegisteredInstanceName)) {
loggedInInstancesInOrder.push(currentRegisteredInstanceName)
}
store.set({
instanceNameInSearch: '',
currentRegisteredInstanceName: null,
currentRegisteredInstance: null,
2022-11-18 17:32:31 +00:00
loggedInInstances,
2018-01-27 21:38:57 +00:00
currentInstance: currentRegisteredInstanceName,
2022-11-18 17:32:31 +00:00
loggedInInstancesInOrder,
instanceThemes
2018-01-27 21:38:57 +00:00
})
store.save()
2019-08-03 21:49:37 +01:00
const { enableGrayscale } = store.get()
switchToTheme(DEFAULT_THEME, enableGrayscale)
2018-02-28 07:18:07 +00:00
// fire off these requests so they're cached
/* no await */ updateVerifyCredentialsForInstance(currentRegisteredInstanceName)
/* no await */ updateCustomEmojiForInstance(currentRegisteredInstanceName)
2018-01-27 21:38:57 +00:00
goto('/')
}
2018-02-09 06:29:29 +00:00
export async function handleOauthCode (code) {
2018-01-27 21:38:57 +00:00
try {
store.set({ logInToInstanceLoading: true })
2018-01-27 21:38:57 +00:00
await registerNewInstance(code)
} catch (err) {
store.set({ logInToInstanceError: `${err.message || err.name}. Failed to connect to instance.` })
2018-01-27 21:38:57 +00:00
} finally {
store.set({ logInToInstanceLoading: false })
2018-01-27 21:38:57 +00:00
}
}