File encryption

 Shared login status across sites
This commit is contained in:
2025-07-26 01:37:23 +08:00
parent 081f3f609e
commit 0486c0d0e5
23 changed files with 670 additions and 65 deletions

View File

@@ -37,9 +37,6 @@ const userStore = useUserStore()
const route = useRoute()
const router = useRouter()
// Initialize user state on component mount
userStore.initialize()
const hideUserMenu = computed(() => {
return ['captcha', 'spells', 'login', 'create-account'].includes(route.name as string)
})

View File

@@ -42,8 +42,8 @@ router.beforeEach(async (to, from, next) => {
const userStore = useUserStore()
// Initialize user state if not already initialized
if (!userStore.user && localStorage.getItem('authToken')) {
await userStore.initialize()
if (!userStore.user) {
await userStore.fetchUser(false)
}
if (to.matched.some((record) => record.meta.requiresAuth) && !userStore.isAuthenticated) {

View File

@@ -1,3 +1,22 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useServicesStore = defineStore('services', () => {})
export const useServicesStore = defineStore('services', () => {
const services = ref<Record<string, string>>({})
async function fetchServices() {
try {
const response = await fetch('/cgi/.well-known/services')
if (!response.ok) {
throw new Error('Network response was not ok')
}
const data = await response.json()
services.value = data
} catch (error) {
console.error('Failed to fetch services:', error)
services.value = {}
}
}
return { services, fetchServices }
})

View File

@@ -1,5 +1,5 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { ref, computed, watch } from 'vue'
export const useUserStore = defineStore('user', () => {
// State
@@ -11,19 +11,13 @@ export const useUserStore = defineStore('user', () => {
const isAuthenticated = computed(() => !!user.value)
// Actions
async function fetchUser() {
const token = localStorage.getItem('authToken')
if (!token) {
return // No token, no need to fetch
}
async function fetchUser(reload = true) {
if (!reload && user.value) return // Skip fetching if already loaded and not forced to
isLoading.value = true
error.value = null
try {
const response = await fetch('/api/accounts/me', {
headers: {
'Authorization': `Bearer ${token}`
}
credentials: 'include',
})
if (!response.ok) {
@@ -50,9 +44,21 @@ export const useUserStore = defineStore('user', () => {
// router.push('/login')
}
async function initialize() {
await fetchUser()
}
watch(
user,
(_) => {
// Broadcast user changes to other subapps
window.parent.postMessage(
{
type: 'DY:LOGIN_STATUS_CHANGE',
data: user.value != null,
},
'*',
)
console.log(`[SYNC] Message sent to parent: Login status changed to ${status}`)
},
{ immediate: true, deep: true },
)
return {
user,
@@ -61,6 +67,5 @@ export const useUserStore = defineStore('user', () => {
isAuthenticated,
fetchUser,
logout,
initialize
}
})
})

View File

@@ -32,7 +32,3 @@ async function fetchVersion() {
onMounted(() => fetchVersion())
</script>
<style scoped>
/* Add any specific styles here if needed, though Tailwind should handle most. */
</style>

View File

@@ -30,7 +30,6 @@ onMounted(async () => {
const fp = await FingerprintJS.load()
const result = await fp.get()
deviceId.value = result.visitorId
localStorage.setItem('deviceId', deviceId.value)
})
const selectedFactor = computed(() => {
@@ -214,7 +213,6 @@ async function exchangeToken() {
}
const { token } = await response.json()
localStorage.setItem('authToken', token)
await userStore.fetchUser()
const redirectUri = route.query.redirect_uri as string