fix(pwa,auth): serve manifest and icons from apps/web/public and prevent Firebase uninitialized crash

This commit is contained in:
2026-09-28 11:49:46 +05:00
parent 5c253b169e
commit 7d324a6140
11 changed files with 123 additions and 27 deletions
+37 -25
View File
@@ -1,4 +1,5 @@
import { jwtDecode } from 'jwt-decode' import { jwtDecode } from 'jwt-decode'
import { getApps } from 'firebase/app'
import { getAuth, signInWithPopup, GoogleAuthProvider, signOut, onAuthStateChanged } from 'firebase/auth' import { getAuth, signInWithPopup, GoogleAuthProvider, signOut, onAuthStateChanged } from 'firebase/auth'
interface User { interface User {
@@ -15,8 +16,6 @@ export const useAuth = () => {
sameSite: 'lax' sameSite: 'lax'
}) })
const isInitializing = useState('auth_initializing', () => true)
const user = computed<User | null>(() => { const user = computed<User | null>(() => {
if (!token.value) return null if (!token.value) return null
try { try {
@@ -30,6 +29,10 @@ export const useAuth = () => {
const isAdmin = computed(() => user.value?.role === 'admin') const isAdmin = computed(() => user.value?.role === 'admin')
const googleSignIn = async () => { const googleSignIn = async () => {
if (!getApps().length) {
alert('Ошибка: Firebase не настроен. Проверьте переменные окружения FIREBASE_API_KEY в Coolify.')
return
}
const auth = getAuth() const auth = getAuth()
const provider = new GoogleAuthProvider() const provider = new GoogleAuthProvider()
const result = await signInWithPopup(auth, provider) const result = await signInWithPopup(auth, provider)
@@ -46,8 +49,10 @@ export const useAuth = () => {
const logout = async () => { const logout = async () => {
try { try {
const auth = getAuth() if (getApps().length) {
await signOut(auth) const auth = getAuth()
await signOut(auth)
}
} catch (e) { } catch (e) {
console.warn('[Firebase] Sign out failed or not initialized', e) console.warn('[Firebase] Sign out failed or not initialized', e)
} finally { } finally {
@@ -58,37 +63,44 @@ export const useAuth = () => {
const initAuth = () => { const initAuth = () => {
if (process.server) return if (process.server) return
if (!getApps().length) {
console.warn('[Firebase] Firebase App not initialized, skipping onAuthStateChanged.')
return
}
const auth = getAuth() try {
const unsubscribe = onAuthStateChanged(auth, async (firebaseUser) => { const auth = getAuth()
// If user is authenticated in Firebase but we lost our backend token, try to restore const unsubscribe = onAuthStateChanged(auth, async (firebaseUser) => {
if (firebaseUser && !token.value) { // If user is authenticated in Firebase but we lost our backend token, try to restore
try { if (firebaseUser && !token.value) {
const idToken = await firebaseUser.getIdToken() try {
const res = await $fetch<{ token: string; user: User }>('/api/auth/google', { const idToken = await firebaseUser.getIdToken()
method: 'POST', const res = await $fetch<{ token: string; user: User }>('/api/auth/google', {
body: { idToken } method: 'POST',
}) body: { idToken }
token.value = res.token })
token.value = res.token
if (useRoute().path === '/login') { if (useRoute().path === '/login') {
await navigateTo('/') await navigateTo('/')
}
} catch (e) {
console.error('Failed to restore backend auth from Firebase', e)
} }
} catch (e) {
console.error('Failed to restore backend auth from Firebase', e)
} }
} })
isInitializing.value = false
}) onUnmounted(() => unsubscribe())
return unsubscribe } catch (err) {
console.warn('[Firebase] Auth initialization skipped:', err)
}
} }
return { return {
token,
user, user,
token,
isAuthenticated, isAuthenticated,
isAdmin, isAdmin,
isInitializing,
googleSignIn, googleSignIn,
logout, logout,
initAuth initAuth
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<rect width="32" height="32" rx="6" fill="#0f172a"/>
<text x="16" y="22" font-size="18" text-anchor="middle" fill="#3b82f6" font-family="system-ui">N</text>
</svg>

After

Width:  |  Height:  |  Size: 252 B

+34
View File
@@ -0,0 +1,34 @@
{
"id": "/",
"name": "XFlow",
"short_name": "XFlow",
"description": "XFlow Intelligent Unified Platform",
"lang": "ru",
"categories": ["productivity", "business"],
"theme_color": "#090A0F",
"background_color": "#090A0F",
"display": "standalone",
"orientation": "portrait",
"start_url": "/",
"scope": "/",
"icons": [
{
"src": "/pwa-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
+34
View File
@@ -0,0 +1,34 @@
{
"id": "/",
"name": "XFlow",
"short_name": "XFlow",
"description": "XFlow Intelligent Unified Platform",
"lang": "ru",
"categories": ["productivity", "business"],
"theme_color": "#090A0F",
"background_color": "#090A0F",
"display": "standalone",
"orientation": "portrait",
"start_url": "/",
"scope": "/",
"icons": [
{
"src": "/pwa-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">
<rect width="512" height="512" fill="#0f172a"/>
<text x="256" y="350" font-size="280" text-anchor="middle" fill="#3b82f6" font-family="system-ui" font-weight="bold">N</text>
</svg>

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" viewBox="0 0 192 192">
<rect width="192" height="192" rx="24" fill="#0f172a"/>
<text x="96" y="128" font-size="96" text-anchor="middle" fill="#3b82f6" font-family="system-ui" font-weight="bold">N</text>
</svg>

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">
<rect width="512" height="512" rx="64" fill="#0f172a"/>
<text x="256" y="350" font-size="280" text-anchor="middle" fill="#3b82f6" font-family="system-ui" font-weight="bold">N</text>
</svg>

After

Width:  |  Height:  |  Size: 281 B