Fix Appwrite-Session und Collection-IDs für Ticket-System
Nutzt String-Collection-IDs, ticket.webklar.com/v1 als Default und korrigiert isAdmin sowie Session-Handling für Same-Origin-Login. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
9
package-lock.json
generated
9
package-lock.json
generated
@@ -56,7 +56,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
|
||||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.5",
|
||||||
@@ -1097,7 +1096,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
|
||||||
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
|
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
@@ -1174,7 +1172,6 @@
|
|||||||
"url": "https://github.com/sponsors/ai"
|
"url": "https://github.com/sponsors/ai"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
@@ -1785,7 +1782,6 @@
|
|||||||
"version": "18.3.1",
|
"version": "18.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0"
|
"loose-envify": "^1.1.0"
|
||||||
},
|
},
|
||||||
@@ -1797,7 +1793,6 @@
|
|||||||
"version": "18.3.1",
|
"version": "18.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.1.0",
|
"loose-envify": "^1.1.0",
|
||||||
"scheduler": "^0.23.2"
|
"scheduler": "^0.23.2"
|
||||||
@@ -1953,8 +1948,7 @@
|
|||||||
"version": "0.182.0",
|
"version": "0.182.0",
|
||||||
"resolved": "https://registry.npmjs.org/three/-/three-0.182.0.tgz",
|
"resolved": "https://registry.npmjs.org/three/-/three-0.182.0.tgz",
|
||||||
"integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==",
|
"integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/tr46": {
|
"node_modules/tr46": {
|
||||||
"version": "0.0.3",
|
"version": "0.0.3",
|
||||||
@@ -2002,7 +1996,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
||||||
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.21.3",
|
"esbuild": "^0.21.3",
|
||||||
"postcss": "^8.4.43",
|
"postcss": "^8.4.43",
|
||||||
|
|||||||
@@ -1,24 +1,61 @@
|
|||||||
import { createContext, useContext, useState, useEffect } from 'react'
|
import { createContext, useContext, useState, useEffect } from 'react'
|
||||||
import { account, databases, DATABASE_ID, COLLECTIONS, ID, Query } from '../lib/appwrite'
|
import { account, databases, DATABASE_ID, COLLECTIONS, ID, Query, hasAppwriteSession } from '../lib/appwrite'
|
||||||
|
|
||||||
const AuthContext = createContext()
|
const AuthContext = createContext()
|
||||||
|
|
||||||
// Demo mode when Appwrite is not configured
|
// Demo mode when Appwrite is not configured
|
||||||
const DEMO_MODE = !import.meta.env.VITE_APPWRITE_PROJECT_ID
|
const DEMO_MODE = !(import.meta.env.VITE_APPWRITE_PROJECT_ID || '').trim()
|
||||||
|
|
||||||
|
const PROJECT_ID = (import.meta.env.VITE_APPWRITE_PROJECT_ID || '').trim()
|
||||||
|
|
||||||
|
function clearStaleAppwriteSessions() {
|
||||||
|
if (typeof window === 'undefined' || !window.localStorage) return
|
||||||
|
try {
|
||||||
|
const raw = window.localStorage.getItem('cookieFallback')
|
||||||
|
if (!raw) return
|
||||||
|
const parsed = JSON.parse(raw)
|
||||||
|
if (typeof parsed !== 'object' || parsed === null) return
|
||||||
|
for (const key of Object.keys(parsed)) {
|
||||||
|
if (key.startsWith('a_session_') && key !== `a_session_${PROJECT_ID}`) {
|
||||||
|
delete parsed[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.localStorage.setItem('cookieFallback', JSON.stringify(parsed))
|
||||||
|
} catch {
|
||||||
|
window.localStorage.removeItem('cookieFallback')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAllLocalAppwriteSessions() {
|
||||||
|
if (typeof window === 'undefined') return
|
||||||
|
try {
|
||||||
|
window.localStorage.removeItem('cookieFallback')
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createSessionWithCleanup(email, password) {
|
||||||
|
try {
|
||||||
|
await account.deleteSessions()
|
||||||
|
} catch { /* alte Sitzung ggf. nur serverseitig */ }
|
||||||
|
clearAllLocalAppwriteSessions()
|
||||||
|
if (account.createEmailPasswordSession) {
|
||||||
|
await account.createEmailPasswordSession(email, password)
|
||||||
|
} else {
|
||||||
|
await account.createEmailSession(email, password)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Hilfsfunktion: Fügt User automatisch zur employees Collection hinzu
|
// Hilfsfunktion: Fügt User automatisch zur employees Collection hinzu
|
||||||
async function ensureEmployeeExists(user) {
|
async function ensureEmployeeExists(user) {
|
||||||
if (!user || DEMO_MODE) return
|
if (!user || DEMO_MODE) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Prüfe ob User bereits in employees Collection existiert
|
|
||||||
const response = await databases.listDocuments(
|
const response = await databases.listDocuments(
|
||||||
DATABASE_ID,
|
DATABASE_ID,
|
||||||
COLLECTIONS.EMPLOYEES,
|
COLLECTIONS.EMPLOYEES,
|
||||||
[Query.equal('userId', user.$id)]
|
[Query.equal('userId', user.$id)]
|
||||||
)
|
)
|
||||||
|
|
||||||
// Wenn User noch nicht existiert, füge ihn hinzu
|
|
||||||
if (response.documents.length === 0) {
|
if (response.documents.length === 0) {
|
||||||
await databases.createDocument(
|
await databases.createDocument(
|
||||||
DATABASE_ID,
|
DATABASE_ID,
|
||||||
@@ -28,13 +65,12 @@ async function ensureEmployeeExists(user) {
|
|||||||
userId: user.$id,
|
userId: user.$id,
|
||||||
displayName: user.name || user.email,
|
displayName: user.name || user.email,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
shortcode: '' // Kürzel wird später vom Admin hinzugefügt
|
shortcode: ''
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
console.log('✅ User automatisch zur Mitarbeiter-Liste hinzugefügt')
|
console.log('✅ User automatisch zur Mitarbeiter-Liste hinzugefügt')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Fehler ignorieren wenn Collection nicht existiert oder Permissions fehlen
|
|
||||||
if (error.code !== 404) {
|
if (error.code !== 404) {
|
||||||
console.warn('Could not add user to employees collection:', error.message)
|
console.warn('Could not add user to employees collection:', error.message)
|
||||||
}
|
}
|
||||||
@@ -51,7 +87,6 @@ export function AuthProvider({ children }) {
|
|||||||
|
|
||||||
async function checkUser() {
|
async function checkUser() {
|
||||||
if (DEMO_MODE) {
|
if (DEMO_MODE) {
|
||||||
// Check localStorage for demo session
|
|
||||||
const demoUser = localStorage.getItem('demo_user')
|
const demoUser = localStorage.getItem('demo_user')
|
||||||
if (demoUser) {
|
if (demoUser) {
|
||||||
setUser(JSON.parse(demoUser))
|
setUser(JSON.parse(demoUser))
|
||||||
@@ -59,18 +94,24 @@ export function AuthProvider({ children }) {
|
|||||||
setLoading(false)
|
setLoading(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!hasAppwriteSession()) {
|
||||||
|
setUser(null)
|
||||||
|
setLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const session = await account.get()
|
const session = await account.get()
|
||||||
setUser(session)
|
setUser(session)
|
||||||
// Automatisch zur employees Collection hinzufügen
|
|
||||||
await ensureEmployeeExists(session)
|
await ensureEmployeeExists(session)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Kein Fehler loggen beim initialen Check - das ist normal wenn nicht eingeloggt
|
|
||||||
// Nur loggen wenn es ein unerwarteter Fehler ist (nicht 401)
|
|
||||||
if (error.code !== 401 && error.code !== 404) {
|
if (error.code !== 401 && error.code !== 404) {
|
||||||
console.error('Unexpected error checking user:', error)
|
console.error('Unexpected error checking user:', error)
|
||||||
}
|
}
|
||||||
|
if (error.code === 401) {
|
||||||
|
clearAllLocalAppwriteSessions()
|
||||||
|
}
|
||||||
setUser(null)
|
setUser(null)
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
@@ -79,45 +120,45 @@ export function AuthProvider({ children }) {
|
|||||||
|
|
||||||
async function login(email, password) {
|
async function login(email, password) {
|
||||||
if (DEMO_MODE) {
|
if (DEMO_MODE) {
|
||||||
// Demo login - accept any credentials
|
|
||||||
const demoUser = { $id: 'demo', email, name: email.split('@')[0] }
|
const demoUser = { $id: 'demo', email, name: email.split('@')[0] }
|
||||||
localStorage.setItem('demo_user', JSON.stringify(demoUser))
|
localStorage.setItem('demo_user', JSON.stringify(demoUser))
|
||||||
setUser(demoUser)
|
setUser(demoUser)
|
||||||
return { success: true }
|
return { success: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Appwrite 1.5.7 / SDK 13.0 - versuche beide Methoden für Kompatibilität
|
const normalizedEmail = email.trim()
|
||||||
try {
|
const normalizedPassword = password.trim()
|
||||||
await account.createEmailSession(email, password)
|
clearStaleAppwriteSessions()
|
||||||
} catch (e) {
|
|
||||||
// Fallback für ältere API
|
await createSessionWithCleanup(normalizedEmail, normalizedPassword)
|
||||||
if (account.createEmailPasswordSession) {
|
|
||||||
await account.createEmailPasswordSession(email, password)
|
|
||||||
} else {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// User-Daten laden und automatisch zur employees Collection hinzufügen
|
|
||||||
const session = await account.get()
|
const session = await account.get()
|
||||||
setUser(session)
|
setUser(session)
|
||||||
await ensureEmployeeExists(session)
|
await ensureEmployeeExists(session)
|
||||||
|
|
||||||
return { success: true }
|
return { success: true }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Login error:', error)
|
console.error('Login error:', error)
|
||||||
let errorMessage = error.message || 'Login fehlgeschlagen'
|
let errorMessage = error.message || 'Login fehlgeschlagen'
|
||||||
|
|
||||||
// Bessere Fehlermeldungen
|
if (error.code === 429 || errorMessage.includes('Rate limit')) {
|
||||||
if (error.code === 401 || errorMessage.includes('Invalid credentials')) {
|
errorMessage = 'Zu viele Login-Versuche. Bitte 15–30 Minuten warten und es erneut versuchen.'
|
||||||
|
} else if (errorMessage.includes('missing scopes') && errorMessage.includes('account')) {
|
||||||
|
errorMessage = 'Session konnte nicht gespeichert werden. Bitte Seite neu laden und erneut versuchen.'
|
||||||
|
} else if (
|
||||||
|
error?.type === 'user_session_already_exists' ||
|
||||||
|
errorMessage.includes('session is active')
|
||||||
|
) {
|
||||||
|
errorMessage = 'Es gibt noch eine alte Sitzung. Bitte Seite neu laden und erneut anmelden.'
|
||||||
|
} else if (error.code === 401 || errorMessage.includes('Invalid credentials')) {
|
||||||
errorMessage = 'Ungültige Email oder Passwort'
|
errorMessage = 'Ungültige Email oder Passwort'
|
||||||
} else if (errorMessage.includes('User not found')) {
|
} else if (errorMessage.includes('User not found')) {
|
||||||
errorMessage = 'Benutzer nicht gefunden. Bitte registriere dich zuerst.'
|
errorMessage = 'Benutzer nicht gefunden. Bitte registriere dich zuerst.'
|
||||||
} else if (errorMessage.includes('Email/Password')) {
|
} else if (errorMessage.includes('Email/Password')) {
|
||||||
errorMessage = 'Email/Password Authentifizierung ist nicht aktiviert. Bitte aktiviere sie in deinem Appwrite Dashboard unter Auth → Providers.'
|
errorMessage = 'Email/Password Authentifizierung ist nicht aktiviert. Bitte aktiviere sie in deinem Appwrite Dashboard unter Auth → Providers.'
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: false, error: errorMessage }
|
return { success: false, error: errorMessage }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,28 +169,25 @@ export function AuthProvider({ children }) {
|
|||||||
setUser(null)
|
setUser(null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await account.deleteSession('current')
|
await account.deleteSessions()
|
||||||
setUser(null)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Logout error:', error)
|
console.error('Logout error:', error)
|
||||||
|
} finally {
|
||||||
|
clearAllLocalAppwriteSessions()
|
||||||
|
setUser(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hilfsfunktion um zu prüfen ob Benutzer Admin ist
|
const isAdmin = Boolean(user?.labels?.includes('admin'))
|
||||||
const isAdmin = () => {
|
|
||||||
if (!user) return false
|
|
||||||
// Prüfe ob Benutzer das "admin" Label hat
|
|
||||||
return user.labels?.includes('admin') || false
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = {
|
const value = {
|
||||||
user,
|
user,
|
||||||
loading,
|
loading,
|
||||||
login,
|
login,
|
||||||
logout,
|
logout,
|
||||||
isAdmin: isAdmin()
|
isAdmin
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Client, Account, Databases, Storage, ID, Query } from 'appwrite'
|
import { Client, Account, Databases, Storage, ID, Query } from 'appwrite'
|
||||||
|
|
||||||
// Debug: Zeige geladene Umgebungsvariablen (nur in Development)
|
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
console.log('🔧 Appwrite Konfiguration:')
|
console.log('🔧 Appwrite Konfiguration:')
|
||||||
console.log('Endpoint:', import.meta.env.VITE_APPWRITE_ENDPOINT || 'NICHT GESETZT')
|
console.log('Endpoint:', import.meta.env.VITE_APPWRITE_ENDPOINT || 'NICHT GESETZT')
|
||||||
@@ -8,8 +7,8 @@ if (import.meta.env.DEV) {
|
|||||||
console.log('Database ID:', import.meta.env.VITE_APPWRITE_DATABASE_ID || 'NICHT GESETZT')
|
console.log('Database ID:', import.meta.env.VITE_APPWRITE_DATABASE_ID || 'NICHT GESETZT')
|
||||||
}
|
}
|
||||||
|
|
||||||
const endpoint = import.meta.env.VITE_APPWRITE_ENDPOINT || 'https://appwrite.webklar.com/v1'
|
const endpoint = import.meta.env.VITE_APPWRITE_ENDPOINT || 'https://ticket.webklar.com/v1'
|
||||||
const projectId = import.meta.env.VITE_APPWRITE_PROJECT_ID || ''
|
const projectId = (import.meta.env.VITE_APPWRITE_PROJECT_ID || '').trim()
|
||||||
|
|
||||||
if (!projectId) {
|
if (!projectId) {
|
||||||
console.error('❌ FEHLER: VITE_APPWRITE_PROJECT_ID ist nicht gesetzt!')
|
console.error('❌ FEHLER: VITE_APPWRITE_PROJECT_ID ist nicht gesetzt!')
|
||||||
@@ -26,18 +25,35 @@ export const storage = new Storage(client)
|
|||||||
|
|
||||||
export const DATABASE_ID = import.meta.env.VITE_APPWRITE_DATABASE_ID || 'woms-database'
|
export const DATABASE_ID = import.meta.env.VITE_APPWRITE_DATABASE_ID || 'woms-database'
|
||||||
|
|
||||||
// Collection IDs - Verwende die tatsächlichen Collection IDs aus Appwrite!
|
|
||||||
export const COLLECTIONS = {
|
export const COLLECTIONS = {
|
||||||
WORKORDERS: '6943bf7d001901baa60c', // Collection ID für workorders
|
WORKORDERS: 'workorders',
|
||||||
CONFIG: 'config', // Collection ID für Admin-Konfiguration (wird erstellt)
|
CONFIG: 'config',
|
||||||
CUSTOMERS: '694bd1fb002b2e583d13', // Collection ID für customers
|
CUSTOMERS: 'customers',
|
||||||
EMPLOYEES: '695280510031c6c6153b', // Collection ID für employees
|
EMPLOYEES: 'employees',
|
||||||
WORKSHEETS: '6952dbcf0032a92e1168', // Collection ID für worksheets ✅
|
WORKSHEETS: 'worksheets',
|
||||||
USERS: 'users',
|
USERS: 'users',
|
||||||
ATTACHMENTS: 'attachments'
|
ATTACHMENTS: 'attachments'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BUCKET_ID = 'woms-attachments'
|
export const BUCKET_ID = import.meta.env.VITE_APPWRITE_BUCKET_ID || 'woms-attachments'
|
||||||
|
|
||||||
|
/** Prüft ob vermutlich eine Session existiert (cookieFallback oder Same-Origin-Cookie). */
|
||||||
|
export function hasAppwriteSession() {
|
||||||
|
if (typeof window === 'undefined' || !projectId) return false
|
||||||
|
try {
|
||||||
|
const apiHost = new URL(endpoint).hostname
|
||||||
|
if (apiHost === window.location.hostname) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
try {
|
||||||
|
const raw = window.localStorage.getItem('cookieFallback')
|
||||||
|
if (!raw) return false
|
||||||
|
const parsed = JSON.parse(raw)
|
||||||
|
return Boolean(parsed?.[`a_session_${projectId}`])
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Helper functions
|
|
||||||
export { ID, Query }
|
export { ID, Query }
|
||||||
|
|||||||
Reference in New Issue
Block a user