💄 Rework of the authorize page
This commit is contained in:
21
app/app.vue
21
app/app.vue
@@ -2,9 +2,10 @@
|
|||||||
<naive-config>
|
<naive-config>
|
||||||
<n-dialog-provider>
|
<n-dialog-provider>
|
||||||
<n-notification-provider>
|
<n-notification-provider>
|
||||||
|
<naive-notification />
|
||||||
<n-message-provider>
|
<n-message-provider>
|
||||||
<n-loading-bar-provider>
|
<n-loading-bar-provider>
|
||||||
<nuxt-loading-indicator />
|
<naive-loading-bar navigation />
|
||||||
<nuxt-layout>
|
<nuxt-layout>
|
||||||
<nuxt-page />
|
<nuxt-page />
|
||||||
</nuxt-layout>
|
</nuxt-layout>
|
||||||
@@ -17,22 +18,4 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import "@fontsource-variable/nunito"
|
import "@fontsource-variable/nunito"
|
||||||
import { usePreferredColorScheme } from "@vueuse/core"
|
|
||||||
|
|
||||||
const { colorModePreference } = useNaiveColorMode()
|
|
||||||
const colorScheme = usePreferredColorScheme()
|
|
||||||
|
|
||||||
colorModePreference.set("system")
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
switch (colorScheme.value) {
|
|
||||||
case "dark":
|
|
||||||
colorModePreference.set("dark")
|
|
||||||
case "light":
|
|
||||||
colorModePreference.set("light")
|
|
||||||
default:
|
|
||||||
colorModePreference.set("system")
|
|
||||||
}
|
|
||||||
colorModePreference.sync()
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -36,6 +36,12 @@
|
|||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h-screen-no-header {
|
.h-layout {
|
||||||
|
/* margin of the navbar + actual navbar */
|
||||||
height: calc(100vh - 64px*2);
|
height: calc(100vh - 64px*2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.min-h-layout {
|
||||||
|
/* margin of the navbar + actual navbar */
|
||||||
|
min-height: calc(100vh - 64px*2);
|
||||||
|
}
|
||||||
6
app/components.d.ts
vendored
6
app/components.d.ts
vendored
@@ -21,6 +21,7 @@ declare module 'vue' {
|
|||||||
NCarousel: typeof import('naive-ui')['NCarousel']
|
NCarousel: typeof import('naive-ui')['NCarousel']
|
||||||
NCarouselItem: typeof import('naive-ui')['NCarouselItem']
|
NCarouselItem: typeof import('naive-ui')['NCarouselItem']
|
||||||
NChip: typeof import('naive-ui')['NChip']
|
NChip: typeof import('naive-ui')['NChip']
|
||||||
|
NCode: typeof import('naive-ui')['NCode']
|
||||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||||
NDialog: typeof import('naive-ui')['NDialog']
|
NDialog: typeof import('naive-ui')['NDialog']
|
||||||
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||||
@@ -32,6 +33,8 @@ declare module 'vue' {
|
|||||||
NImg: typeof import('naive-ui')['NImg']
|
NImg: typeof import('naive-ui')['NImg']
|
||||||
NInfiniteScroll: typeof import('naive-ui')['NInfiniteScroll']
|
NInfiniteScroll: typeof import('naive-ui')['NInfiniteScroll']
|
||||||
NInput: typeof import('naive-ui')['NInput']
|
NInput: typeof import('naive-ui')['NInput']
|
||||||
|
NList: typeof import('naive-ui')['NList']
|
||||||
|
NListItem: typeof import('naive-ui')['NListItem']
|
||||||
NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
||||||
NMenu: typeof import('naive-ui')['NMenu']
|
NMenu: typeof import('naive-ui')['NMenu']
|
||||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||||
@@ -62,6 +65,7 @@ declare global {
|
|||||||
const NCarousel: typeof import('naive-ui')['NCarousel']
|
const NCarousel: typeof import('naive-ui')['NCarousel']
|
||||||
const NCarouselItem: typeof import('naive-ui')['NCarouselItem']
|
const NCarouselItem: typeof import('naive-ui')['NCarouselItem']
|
||||||
const NChip: typeof import('naive-ui')['NChip']
|
const NChip: typeof import('naive-ui')['NChip']
|
||||||
|
const NCode: typeof import('naive-ui')['NCode']
|
||||||
const NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
const NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||||
const NDialog: typeof import('naive-ui')['NDialog']
|
const NDialog: typeof import('naive-ui')['NDialog']
|
||||||
const NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
const NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||||
@@ -73,6 +77,8 @@ declare global {
|
|||||||
const NImg: typeof import('naive-ui')['NImg']
|
const NImg: typeof import('naive-ui')['NImg']
|
||||||
const NInfiniteScroll: typeof import('naive-ui')['NInfiniteScroll']
|
const NInfiniteScroll: typeof import('naive-ui')['NInfiniteScroll']
|
||||||
const NInput: typeof import('naive-ui')['NInput']
|
const NInput: typeof import('naive-ui')['NInput']
|
||||||
|
const NList: typeof import('naive-ui')['NList']
|
||||||
|
const NListItem: typeof import('naive-ui')['NListItem']
|
||||||
const NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
const NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
||||||
const NMenu: typeof import('naive-ui')['NMenu']
|
const NMenu: typeof import('naive-ui')['NMenu']
|
||||||
const NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
const NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { keysToCamel, keysToSnake } from "~/utils/transformKeys"
|
|||||||
|
|
||||||
export const useSolarNetwork = () => {
|
export const useSolarNetwork = () => {
|
||||||
const apiBase = useSolarNetworkUrl()
|
const apiBase = useSolarNetworkUrl()
|
||||||
|
const devToken = useRuntimeConfig().public.devToken
|
||||||
|
|
||||||
// Forward cookies from the incoming request
|
// Forward cookies from the incoming request
|
||||||
const headers: HeadersInit = import.meta.server
|
const headers: HeadersInit = import.meta.server
|
||||||
@@ -18,6 +19,11 @@ export const useSolarNetwork = () => {
|
|||||||
const side = import.meta.server ? "SERVER" : "CLIENT"
|
const side = import.meta.server ? "SERVER" : "CLIENT"
|
||||||
console.log(`[useSolarNetwork] onRequest for ${request} on ${side}`)
|
console.log(`[useSolarNetwork] onRequest for ${request} on ${side}`)
|
||||||
|
|
||||||
|
if (devToken) {
|
||||||
|
options.headers = new Headers(options.headers)
|
||||||
|
options.headers.set("Authorization", `Bearer ${devToken}`)
|
||||||
|
}
|
||||||
|
|
||||||
// Transform request data from camelCase to snake_case
|
// Transform request data from camelCase to snake_case
|
||||||
if (options.body && typeof options.body === "object") {
|
if (options.body && typeof options.body === "object") {
|
||||||
options.body = keysToSnake(options.body)
|
options.body = keysToSnake(options.body)
|
||||||
|
|||||||
@@ -1,110 +1,17 @@
|
|||||||
<template>
|
|
||||||
<v-container class="d-flex align-center justify-center fill-height">
|
|
||||||
<v-card max-width="1000" rounded="lg" width="100%">
|
|
||||||
<div v-if="isLoading" class="d-flex justify-center mb-4">
|
|
||||||
<v-progress-linear indeterminate color="primary" height="4" />
|
|
||||||
</div>
|
|
||||||
<div class="pa-8">
|
|
||||||
<div class="mb-4">
|
|
||||||
<img :src="IconLight" alt="CloudyLamb" height="60" width="60" />
|
|
||||||
</div>
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="12" lg="6" class="d-flex align-start justify-start">
|
|
||||||
<div class="md:text-left h-auto">
|
|
||||||
<h2 class="text-2xl font-bold mb-1">Authorize Application</h2>
|
|
||||||
<p class="text-lg">Grant access to your Solar Network account</p>
|
|
||||||
</div>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" lg="6" class="d-flex align-center justify-stretch">
|
|
||||||
<div class="w-full d-flex flex-column md:text-right">
|
|
||||||
<div v-if="error" class="mb-4">
|
|
||||||
<v-alert
|
|
||||||
type="error"
|
|
||||||
closable
|
|
||||||
@update:model-value="error = null"
|
|
||||||
>
|
|
||||||
{{ error }}
|
|
||||||
</v-alert>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- App Info Section -->
|
|
||||||
<div v-if="clientInfo" class="mb-6">
|
|
||||||
<div class="d-flex align-center mb-4 text-left">
|
|
||||||
<div>
|
|
||||||
<h3 class="text-xl font-semibold">
|
|
||||||
{{ clientInfo.clientName || "Unknown Application" }}
|
|
||||||
</h3>
|
|
||||||
<p class="text-base">
|
|
||||||
{{
|
|
||||||
isNewApp
|
|
||||||
? "wants to access your Solar Network account"
|
|
||||||
: "wants to access your account"
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Requested Permissions -->
|
|
||||||
<v-card variant="outlined" class="pa-4 mb-4 text-left">
|
|
||||||
<h4 class="font-medium mb-2">
|
|
||||||
This will allow
|
|
||||||
{{ clientInfo.clientName || "the app" }} to
|
|
||||||
</h4>
|
|
||||||
<ul class="space-y-1">
|
|
||||||
<li
|
|
||||||
v-for="scope in requestedScopes"
|
|
||||||
:key="scope"
|
|
||||||
class="d-flex align-start"
|
|
||||||
>
|
|
||||||
<v-icon class="mt-1 mr-2" color="success" size="18"
|
|
||||||
>mdi-check</v-icon
|
|
||||||
>
|
|
||||||
<span>{{ scope }}</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</v-card>
|
|
||||||
|
|
||||||
<!-- Buttons -->
|
|
||||||
<div class="d-flex gap-3 mt-4">
|
|
||||||
<v-btn
|
|
||||||
color="primary"
|
|
||||||
:loading="isAuthorizing"
|
|
||||||
class="grow"
|
|
||||||
size="large"
|
|
||||||
@click="handleAuthorize"
|
|
||||||
>
|
|
||||||
Authorize
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
variant="outlined"
|
|
||||||
:disabled="isAuthorizing"
|
|
||||||
class="grow"
|
|
||||||
size="large"
|
|
||||||
@click="handleDeny"
|
|
||||||
>
|
|
||||||
Deny
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</div>
|
|
||||||
</v-card>
|
|
||||||
<footer-compact />
|
|
||||||
</v-container>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted } from "vue"
|
import { ref, computed, onMounted } from "vue"
|
||||||
import { useRoute } from "vue-router"
|
import { useRoute } from "vue-router"
|
||||||
import { useSolarNetwork } from "~/composables/useSolarNetwork"
|
import { useSolarNetwork } from "~/composables/useSolarNetwork"
|
||||||
|
import { CheckIcon } from "lucide-vue-next"
|
||||||
|
|
||||||
import IconLight from "~/assets/images/cloudy-lamb.png"
|
import IconLight from "~/assets/images/cloudy-lamb.png"
|
||||||
|
import type { SnCloudFile } from "~/types/api/post"
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const api = useSolarNetwork()
|
const api = useSolarNetwork()
|
||||||
|
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: "Authorize Application"
|
title: "Authorize Application"
|
||||||
})
|
})
|
||||||
@@ -112,11 +19,11 @@ useHead({
|
|||||||
// State
|
// State
|
||||||
const isLoading = ref(true)
|
const isLoading = ref(true)
|
||||||
const isAuthorizing = ref(false)
|
const isAuthorizing = ref(false)
|
||||||
const error = ref<string | null>(null)
|
|
||||||
const clientInfo = ref<{
|
const clientInfo = ref<{
|
||||||
clientName?: string
|
clientName?: string
|
||||||
homeUri?: string
|
homeUri?: string
|
||||||
picture?: { url: string }
|
picture?: SnCloudFile
|
||||||
|
background?: SnCloudFile
|
||||||
scopes?: string[]
|
scopes?: string[]
|
||||||
} | null>(null)
|
} | null>(null)
|
||||||
const isNewApp = ref(false)
|
const isNewApp = ref(false)
|
||||||
@@ -133,9 +40,7 @@ async function fetchClientInfo() {
|
|||||||
clientInfo.value = await api(`/id/auth/open/authorize?${queryString}`)
|
clientInfo.value = await api(`/id/auth/open/authorize?${queryString}`)
|
||||||
checkIfNewApp()
|
checkIfNewApp()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value =
|
message.error(err instanceof Error ? err.message : String(err))
|
||||||
(err instanceof Error ? err.message : String(err)) ||
|
|
||||||
"An error occurred while loading the authorization request"
|
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
@@ -165,9 +70,7 @@ async function handleAuthorize(authorize = true) {
|
|||||||
window.location.href = data.redirectUri
|
window.location.href = data.redirectUri
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value =
|
message.error(err instanceof Error ? err.message : String(err))
|
||||||
(err instanceof Error ? err.message : String(err)) ||
|
|
||||||
"An error occurred during authorization"
|
|
||||||
} finally {
|
} finally {
|
||||||
isAuthorizing.value = false
|
isAuthorizing.value = false
|
||||||
}
|
}
|
||||||
@@ -185,8 +88,135 @@ onMounted(() => {
|
|||||||
definePageMeta({
|
definePageMeta({
|
||||||
middleware: "auth"
|
middleware: "auth"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const apiBase = useSolarNetworkUrl()
|
||||||
|
const clientAvatar = computed(() =>
|
||||||
|
clientInfo.value?.picture
|
||||||
|
? `${apiBase}/drive/files/${clientInfo.value.picture.id}`
|
||||||
|
: undefined
|
||||||
|
)
|
||||||
|
const clientBackground = computed(() =>
|
||||||
|
clientInfo.value?.background
|
||||||
|
? `${apiBase}/drive/files/${clientInfo.value.background.id}?original=true`
|
||||||
|
: undefined
|
||||||
|
)
|
||||||
|
|
||||||
|
const pageStyle = computed(() => {
|
||||||
|
if (!clientBackground.value) return {}
|
||||||
|
return {
|
||||||
|
backgroundImage: `linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)), url('${clientBackground.value}')`,
|
||||||
|
backgroundSize: "cover",
|
||||||
|
backgroundPosition: "center",
|
||||||
|
backgroundRepeat: "no-repeat"
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<template>
|
||||||
/* Add any custom styles here */
|
<div class="fixed inset-0 transition-all duration-500" :style="pageStyle" />
|
||||||
</style>
|
<div class="relative flex items-center justify-center min-h-layout px-4">
|
||||||
|
<n-card
|
||||||
|
:class="[
|
||||||
|
'w-full',
|
||||||
|
'max-w-[1000px]',
|
||||||
|
{ 'backdrop-blur-2xl': clientBackground },
|
||||||
|
{ 'shadow-xl': clientBackground }
|
||||||
|
]"
|
||||||
|
size="large"
|
||||||
|
:style="
|
||||||
|
clientBackground ? 'background-color: rgba(255, 255, 255, 0.1)' : ''
|
||||||
|
"
|
||||||
|
:content-style="clientBackground ? 'background-color: transparent' : ''"
|
||||||
|
>
|
||||||
|
<div v-if="isLoading" class="flex justify-center p-8">
|
||||||
|
<n-spin size="large" />
|
||||||
|
</div>
|
||||||
|
<div v-else class="p-4 md:p-8">
|
||||||
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
|
<!-- Left Column: Title -->
|
||||||
|
<div class="flex flex-col items-start justify-start">
|
||||||
|
<div class="mb-4">
|
||||||
|
<img :src="IconLight" alt="CloudyLamb" height="60" width="60" />
|
||||||
|
</div>
|
||||||
|
<div class="text-left h-auto">
|
||||||
|
<h2 class="text-2xl font-bold mb-1">Authorize Application</h2>
|
||||||
|
<p class="text-lg">Grant access to your Solar Network account</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right Column: Content -->
|
||||||
|
<div class="flex flex-col items-end justify-stretch">
|
||||||
|
<div class="w-full flex flex-col md:text-right">
|
||||||
|
<div class="mb-3 h-[60px] px-[4px] pt-[8px]">
|
||||||
|
<n-avatar :src="clientAvatar" :size="52" />
|
||||||
|
</div>
|
||||||
|
<!-- App Info Section -->
|
||||||
|
<div v-if="clientInfo" class="mb-6">
|
||||||
|
<div class="flex flex-col items-end mb-4">
|
||||||
|
<h3 class="text-xl font-semibold">
|
||||||
|
{{ clientInfo.clientName || "Unknown Application" }}
|
||||||
|
</h3>
|
||||||
|
<p class="text-base">
|
||||||
|
{{
|
||||||
|
isNewApp
|
||||||
|
? "Wants to access your Solar Network account"
|
||||||
|
: "Wants to access your account"
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Requested Permissions -->
|
||||||
|
<n-card embedded class="mb-4 text-left">
|
||||||
|
<h4 class="font-medium mb-2">
|
||||||
|
This will allow
|
||||||
|
{{ clientInfo.clientName || "the app" }} to
|
||||||
|
</h4>
|
||||||
|
<n-list style="background-color: transparent" size="small">
|
||||||
|
<n-list-item
|
||||||
|
v-for="scope in requestedScopes"
|
||||||
|
:key="scope"
|
||||||
|
class="bg-transparent"
|
||||||
|
style="padding: 0"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<n-icon
|
||||||
|
class="mt-1 mr-2"
|
||||||
|
color="#18a058"
|
||||||
|
:size="16"
|
||||||
|
:component="CheckIcon"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<span>{{ scope }}</span>
|
||||||
|
</n-list-item>
|
||||||
|
</n-list>
|
||||||
|
</n-card>
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<div class="flex gap-3 mt-4">
|
||||||
|
<n-button
|
||||||
|
type="primary"
|
||||||
|
:loading="isAuthorizing"
|
||||||
|
class="grow"
|
||||||
|
size="large"
|
||||||
|
@click="handleAuthorize(true)"
|
||||||
|
>
|
||||||
|
Authorize
|
||||||
|
</n-button>
|
||||||
|
<n-button
|
||||||
|
secondary
|
||||||
|
:disabled="isAuthorizing"
|
||||||
|
class="grow"
|
||||||
|
size="large"
|
||||||
|
@click="handleDeny"
|
||||||
|
>
|
||||||
|
Deny
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex items-center justify-center h-screen-no-header px-4">
|
<div class="flex items-center justify-center h-layout px-4">
|
||||||
<n-card class="w-full max-w-[1000px]" size="large">
|
<n-card class="w-full max-w-[1000px]" size="large">
|
||||||
<div class="p-4 md:p-8">
|
<div class="p-4 md:p-8">
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
|
|||||||
@@ -191,9 +191,9 @@ async function exchangeToken() {
|
|||||||
|
|
||||||
await userStore.fetchUser()
|
await userStore.fetchUser()
|
||||||
|
|
||||||
const redirectUri = route.query.redirect_uri as string
|
const redirectUri = route.query.redirect as string
|
||||||
if (redirectUri) {
|
if (redirectUri) {
|
||||||
window.location.href = redirectUri
|
window.location.href = decodeURIComponent(redirectUri)
|
||||||
} else {
|
} else {
|
||||||
await router.push("/")
|
await router.push("/")
|
||||||
}
|
}
|
||||||
@@ -258,7 +258,7 @@ function getFactorIcon(factorType: number) {
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex items-center justify-center h-screen-no-header px-4">
|
<div class="flex flex-col gap-3 items-center justify-center h-layout px-4">
|
||||||
<n-card class="w-full max-w-[1000px]" size="large">
|
<n-card class="w-full max-w-[1000px]" size="large">
|
||||||
<div class="p-4 md:p-8">
|
<div class="p-4 md:p-8">
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
@@ -450,5 +450,21 @@ function getFactorIcon(factorType: number) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</n-card>
|
||||||
|
|
||||||
|
<n-alert
|
||||||
|
v-if="route.query.redirect"
|
||||||
|
class="w-full max-w-[1000px]"
|
||||||
|
type="info"
|
||||||
|
title="Login before you continue"
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<p>
|
||||||
|
You're requesting a page that requires authorization to access, please
|
||||||
|
login with your Solarpass and then we will redirect you to:
|
||||||
|
</p>
|
||||||
|
<n-code class="text-xs">{{ route.query.redirect }}</n-code>
|
||||||
|
</div>
|
||||||
|
</n-alert>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ export const useUserStore = defineStore("user", () => {
|
|||||||
// Actions
|
// Actions
|
||||||
async function fetchUser(reload = true): Promise<void> {
|
async function fetchUser(reload = true): Promise<void> {
|
||||||
if (currentFetchPromise.value) {
|
if (currentFetchPromise.value) {
|
||||||
console.log("[UserStore] Fetch already in progress. Waiting for existing fetch.")
|
console.log(
|
||||||
|
"[UserStore] Fetch already in progress. Waiting for existing fetch."
|
||||||
|
)
|
||||||
return currentFetchPromise.value
|
return currentFetchPromise.value
|
||||||
}
|
}
|
||||||
if (!reload && user.value) {
|
if (!reload && user.value) {
|
||||||
@@ -37,10 +39,18 @@ export const useUserStore = defineStore("user", () => {
|
|||||||
console.log(`[UserStore] Logged in as @${user.value.name}`)
|
console.log(`[UserStore] Logged in as @${user.value.name}`)
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
// Check for 401 Unauthorized error
|
// Check for 401 Unauthorized error
|
||||||
const is401Error = (e instanceof FetchError && e.statusCode === 401) ||
|
const is401Error =
|
||||||
(e && typeof e === 'object' && 'status' in e && (e as { status: number }).status === 401) ||
|
(e instanceof FetchError && e.statusCode === 401) ||
|
||||||
(e && typeof e === 'object' && 'statusCode' in e && (e as { statusCode: number }).statusCode === 401) ||
|
(e &&
|
||||||
(e instanceof Error && (e.message?.includes('401') || e.message?.includes('Unauthorized')))
|
typeof e === "object" &&
|
||||||
|
"status" in e &&
|
||||||
|
(e as { status: number }).status === 401) ||
|
||||||
|
(e &&
|
||||||
|
typeof e === "object" &&
|
||||||
|
"statusCode" in e &&
|
||||||
|
(e as { statusCode: number }).statusCode === 401) ||
|
||||||
|
(e instanceof Error &&
|
||||||
|
(e.message?.includes("401") || e.message?.includes("Unauthorized")))
|
||||||
|
|
||||||
if (is401Error) {
|
if (is401Error) {
|
||||||
error.value = "Unauthorized"
|
error.value = "Unauthorized"
|
||||||
@@ -50,6 +60,8 @@ export const useUserStore = defineStore("user", () => {
|
|||||||
user.value = null // Clear user data on error
|
user.value = null // Clear user data on error
|
||||||
console.error("Failed to fetch user... ", e)
|
console.error("Failed to fetch user... ", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`[UserStore] Logged as @${user.value!.name}`)
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
currentFetchPromise.value = null
|
currentFetchPromise.value = null
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ export default defineNuxtConfig({
|
|||||||
public: {
|
public: {
|
||||||
development: process.env.NODE_ENV == "development",
|
development: process.env.NODE_ENV == "development",
|
||||||
apiBase: process.env.NUXT_PUBLIC_API_BASE || "https://api.solian.app",
|
apiBase: process.env.NUXT_PUBLIC_API_BASE || "https://api.solian.app",
|
||||||
siteUrl: process.env.NUXT_PUBLIC_SITE_URL || "https://solian.app"
|
siteUrl: process.env.NUXT_PUBLIC_SITE_URL || "https://solian.app",
|
||||||
|
devToken: process.env.NUXT_PUBLIC_DEV_TOKEN || ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
vite: {
|
vite: {
|
||||||
@@ -82,6 +83,8 @@ export default defineNuxtConfig({
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
naiveui: {
|
naiveui: {
|
||||||
|
colorModePreference: "system",
|
||||||
|
colorModePreferenceCookieName: "fi-ColorMode",
|
||||||
themeConfig: {
|
themeConfig: {
|
||||||
...generateTailwindColorThemes(),
|
...generateTailwindColorThemes(),
|
||||||
shared: {
|
shared: {
|
||||||
|
|||||||
Reference in New Issue
Block a user