chore: upgrade to Vue3/Vuetify3

This commit is contained in:
2024-02-05 16:48:02 +01:00
parent 171e1039a7
commit ef3b5460ad
65 changed files with 3153 additions and 9032 deletions
-48
View File
@@ -1,48 +0,0 @@
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'
import { setContext } from '@apollo/client/link/context'
import { useAuth } from './auth'
const apiUrl = process.env.graphqlApi || '/query'
const cache = new InMemoryCache()
const httpLink = createHttpLink({
uri: apiUrl
})
const getToken = (options) => {
const { getTokenSilently } = useAuth()
return getTokenSilently(options)
}
const authLink = setContext((_, { headers }) => {
return getToken()
.then(token => ({
headers: {
...headers,
authorization: token ? `Bearer ${token}` : ''
}
})).catch(() => {
return {}
})
})
const link = authLink.concat(httpLink)
const instance = new ApolloClient({
connectToDevTools: true,
link,
cache,
defaultOptions: {
query: {
fetchPolicy: 'cache-and-network'
},
watchQuery: {
fetchPolicy: 'cache-and-network'
}
}
})
export default function (_, inject) {
inject('apollo', instance)
}
+138
View File
@@ -0,0 +1,138 @@
import {
ApolloClient,
ApolloLink,
createHttpLink,
from,
InMemoryCache,
split,
} from '@apollo/client/core'
import { WebSocketLink } from '@apollo/client/link/ws'
import { setContext } from '@apollo/client/link/context'
import { getMainDefinition } from '@apollo/client/utilities'
import { SentryLink } from 'apollo-link-sentry'
import * as Sentry from '@sentry/browser'
import { type GetTokenSilentlyOptions } from '@auth0/auth0-spa-js'
import { DefaultApolloClient, provideApolloClient } from '@vue/apollo-composable'
import type { Auth0VueClient } from '@auth0/auth0-vue'
import { defineNuxtPlugin, useNuxtApp } from '#app'
import { envConfig } from '~/utils/environment'
// The side effect of patching fetch() has to occur before configuring Apollo Client
// https://github.com/getsentry/sentry-javascript/issues/2860#issuecomment-684514367
import './sentry'
const apiUrl = envConfig(window.location.hostname).apiUrl
const wsUrl = apiUrl.replace(/^http/, 'ws')
const cache = new InMemoryCache({
typePolicies: {
},
})
const getToken = async (options: GetTokenSilentlyOptions) => {
const nuxtApp = useNuxtApp()
const auth0: Auth0VueClient = nuxtApp.$auth0 as Auth0VueClient
return await auth0.getAccessTokenSilently(options).catch((err) => {
Sentry.captureException(err, {
extra: {
function: 'getTokenSilently',
},
})
return undefined
})
}
const httpLink = createHttpLink({
uri: apiUrl,
})
const wsLink = new WebSocketLink({
uri: wsUrl,
options: {
reconnect: true,
lazy: true,
connectionParams: () => {
return getToken({}).then((token) => ({
authToken: token,
}))
},
},
})
const authLink = setContext(async (_, { headers }) => {
return await getToken({}).then((token) => ({
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
}))
})
const traceHeaders = setContext((_, { headers }) => {
const th = Sentry.getCurrentHub().traceHeaders()
return {
headers: {
...headers,
...th,
},
}
})
const sentryTransactionLink = new ApolloLink((operation, forward) => {
const existingTransaction = Sentry.getCurrentHub()
.getScope()
?.getTransaction()
// Create a transaction if one does not exist in order to work around
// https://github.com/getsentry/sentry-javascript/issues/3169
// https://github.com/getsentry/sentry-javascript/issues/4072
const transaction =
existingTransaction ??
Sentry.startTransaction({
name: `Apollo Request: ${operation.operationName}`,
})
Sentry.getCurrentHub().configureScope((scope) => scope.setSpan(transaction))
operation.setContext({ tracing: { transaction } })
return forward(operation).map((data) => {
operation.getContext().tracing?.transaction?.finish()
return data
})
})
const link = sentryTransactionLink.concat(
traceHeaders.concat(
from([
new SentryLink({}),
split(
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
authLink.concat(wsLink),
authLink.concat(httpLink),
),
]),
),
)
const instance = new ApolloClient({
connectToDevTools: true,
link,
cache,
defaultOptions: {
query: {
fetchPolicy: 'cache-first',
},
watchQuery: {
fetchPolicy: 'cache-and-network',
},
},
})
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.provide(DefaultApolloClient[Symbol.toStringTag], instance)
provideApolloClient(instance)
})
-141
View File
@@ -1,141 +0,0 @@
import {
createAuth0Client,
Auth0Client,
GetTokenSilentlyOptions,
IdToken,
LogoutOptions,
RedirectLoginOptions,
User
} from '@auth0/auth0-spa-js'
import { Ref, ref } from 'vue'
/** Define a default action to perform after authentication */
const DEFAULT_REDIRECT_CALLBACK = (_: any) =>
window.history.replaceState({}, document.title, window.location.pathname)
interface Client {
loading: Ref<boolean>
isAuthenticated: Ref<boolean>
user: Ref<User | undefined>
auth0Client?: Promise<Auth0Client>
error: Ref
handleRedirectCallback: () => Promise<void>
loginWithRedirect: (o: RedirectLoginOptions) => Promise<void>
getIdTokenClaims: () => Promise<void | IdToken | undefined>
getTokenSilently: (o: GetTokenSilentlyOptions) => Promise<string | void>
logout: (o: LogoutOptions) => Promise<void>
}
let instance: Client
const params = (new URL(window.location.href)).searchParams
const domain = params.get('domain') || 'unbound.eu.auth0.com'
export const useAuth = (onRedirectCallback: (appState?: any) => void = DEFAULT_REDIRECT_CALLBACK) => {
if (instance) {
return instance
}
const options = {
domain,
clientId: 'orQfnvCPUR5C3mJkKoiWLQHOVQsBn60e',
authorizationParams: {
audience: 'http://dancefinder.unbound.se',
redirect_uri: window.location.origin
}
}
instance = {
loading: ref(true),
isAuthenticated: ref(false),
user: ref(undefined),
auth0Client: undefined,
error: ref(null),
/** Handles the callback when logging in using a redirect */
handleRedirectCallback: () => {
instance.loading.value = true
return instance.auth0Client!.then(client => client.handleRedirectCallback())
.then(() => {
return instance.auth0Client?.then(client => client.getUser())
.then((user) => {
instance.user.value = user
instance.isAuthenticated.value = true
instance.error.value = null
}
)
})
.catch((e) => {
instance.error.value = e
})
.finally(() => {
instance.loading.value = false
})
},
/** Authenticates the user using the redirect method */
loginWithRedirect: (o: RedirectLoginOptions) => {
return instance.auth0Client!.then(client => client.loginWithRedirect(o))
.catch((e) => {
instance.error.value = e
})
},
/** Returns all the claims present in the ID token */
getIdTokenClaims: () => {
return instance.auth0Client!.then(client => client.getIdTokenClaims())
},
/** Returns the access token. If the token is invalid or missing, a new one is retrieved */
getTokenSilently: (o: GetTokenSilentlyOptions) => {
return instance.auth0Client!.then((client) => {
return client.getTokenSilently(o).catch((e) => {
instance.error.value = e
})
})
},
/** Logs the user out and removes their session on the authorization server */
logout: (o: LogoutOptions) => {
return instance.auth0Client!.then(client => client.logout(o))
}
}
const fetchUser = () => {
return instance.auth0Client!.then(client => client.isAuthenticated())
.then((a) => {
instance.auth0Client?.then(client => client.getUser()
.then((u) => {
instance.isAuthenticated.value = a
instance.user.value = u
instance.error.value = null
}))
})
}
// Create a new instance of the SDK client using members of the given options object
instance.auth0Client = createAuth0Client(options)
instance.auth0Client
.then((client) => {
instance.loading.value = true
// If the user is returning to the app after authentication..
if (
window.location.search.includes('state=') && (
window.location.search.includes('code=') ||
window.location.search.includes('error=')
)
) {
// handle the redirect and retrieve tokens
return client.handleRedirectCallback()
.then((result) => {
// Notify subscribers that the redirect callback has happened, passing the appState
// (useful for retrieving any pre-authentication state)
onRedirectCallback(result.appState)
// Initialize our internal authentication state
return fetchUser()
})
} else {
return fetchUser()
}
})
.catch((e) => {
instance.error.value = e
})
.finally(() => {
instance.loading.value = false
})
return instance
}
+10
View File
@@ -0,0 +1,10 @@
import { createAuth0 } from '@auth0/auth0-vue'
import { defineNuxtPlugin } from '#app'
import { envConfig } from '~/utils/environment'
export default defineNuxtPlugin((nuxtApp) => {
const options = envConfig(window.location.hostname).auth
const auth0 = createAuth0(options)
nuxtApp.vueApp.use(auth0)
nuxtApp.provide('auth0', auth0)
})
-4
View File
@@ -1,4 +0,0 @@
import Vue from 'vue'
import hooks from '@u3u/vue-hooks'
Vue.use(hooks)
-14
View File
@@ -1,14 +0,0 @@
let i18n = null
let localePath = null
export const useTranslation = () => {
return {
t: i18n.t.bind(i18n),
localePath
}
}
export default ({ app }) => {
i18n = app.i18n
localePath = app.localePath
}
-10
View File
@@ -1,10 +0,0 @@
import { createPersistedState } from 'pinia-plugin-persistedstate'
import { defineNuxtPlugin } from '#app'
export default defineNuxtPlugin(({ $pinia }) => {
$pinia.use(
createPersistedState({
key: id => `dancefinder_${id}`
})
)
})
+21
View File
@@ -0,0 +1,21 @@
import * as Sentry from '@sentry/browser'
import { defineNuxtPlugin } from '#app'
import { envConfig } from '~/utils/environment'
const env = envConfig(window.location.hostname)
Sentry.init({
enabled: env.sentryEnabled,
dsn: 'https://da2e8d42185a4013909d49955432a116@o365290.ingest.sentry.io/5187660',
integrations: [
new Sentry.BrowserTracing({ traceFetch: false }),
Sentry.replayIntegration(),
],
environment: env.name,
tracesSampleRate: env.tracesSampleRate,
replaysSessionSampleRate: env.replaysSessionSampleRate,
replaysOnErrorSampleRate: env.replaysOnErrorSampleRate,
})
export default defineNuxtPlugin(() => {
})
-4
View File
@@ -1,4 +0,0 @@
import Vue from 'vue'
import Vuetify from 'vuetify'
Vue.use(Vuetify)
+14
View File
@@ -0,0 +1,14 @@
import { createVuetify } from 'vuetify'
import { defineNuxtPlugin } from '#app'
import '@mdi/font/css/materialdesignicons.css'
import 'vuetify/styles'
export default defineNuxtPlugin((app) => {
const vuetify = createVuetify({
theme: {
defaultTheme: 'light',
},
})
app.vueApp.use(vuetify)
})