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:
Webklar Deploy
2026-05-22 17:58:11 +00:00
parent 5fbb2fb4b5
commit 4f4de7f290
3 changed files with 109 additions and 62 deletions

View File

@@ -1,24 +1,61 @@
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()
// 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
async function ensureEmployeeExists(user) {
if (!user || DEMO_MODE) return
try {
// Prüfe ob User bereits in employees Collection existiert
const response = await databases.listDocuments(
DATABASE_ID,
COLLECTIONS.EMPLOYEES,
[Query.equal('userId', user.$id)]
)
// Wenn User noch nicht existiert, füge ihn hinzu
if (response.documents.length === 0) {
await databases.createDocument(
DATABASE_ID,
@@ -28,13 +65,12 @@ async function ensureEmployeeExists(user) {
userId: user.$id,
displayName: user.name || 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')
}
} catch (error) {
// Fehler ignorieren wenn Collection nicht existiert oder Permissions fehlen
if (error.code !== 404) {
console.warn('Could not add user to employees collection:', error.message)
}
@@ -51,7 +87,6 @@ export function AuthProvider({ children }) {
async function checkUser() {
if (DEMO_MODE) {
// Check localStorage for demo session
const demoUser = localStorage.getItem('demo_user')
if (demoUser) {
setUser(JSON.parse(demoUser))
@@ -59,18 +94,24 @@ export function AuthProvider({ children }) {
setLoading(false)
return
}
if (!hasAppwriteSession()) {
setUser(null)
setLoading(false)
return
}
try {
const session = await account.get()
setUser(session)
// Automatisch zur employees Collection hinzufügen
await ensureEmployeeExists(session)
} 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) {
console.error('Unexpected error checking user:', error)
}
if (error.code === 401) {
clearAllLocalAppwriteSessions()
}
setUser(null)
} finally {
setLoading(false)
@@ -79,45 +120,45 @@ export function AuthProvider({ children }) {
async function login(email, password) {
if (DEMO_MODE) {
// Demo login - accept any credentials
const demoUser = { $id: 'demo', email, name: email.split('@')[0] }
localStorage.setItem('demo_user', JSON.stringify(demoUser))
setUser(demoUser)
return { success: true }
}
try {
// Appwrite 1.5.7 / SDK 13.0 - versuche beide Methoden für Kompatibilität
try {
await account.createEmailSession(email, password)
} catch (e) {
// Fallback für ältere API
if (account.createEmailPasswordSession) {
await account.createEmailPasswordSession(email, password)
} else {
throw e
}
}
// User-Daten laden und automatisch zur employees Collection hinzufügen
const normalizedEmail = email.trim()
const normalizedPassword = password.trim()
clearStaleAppwriteSessions()
await createSessionWithCleanup(normalizedEmail, normalizedPassword)
const session = await account.get()
setUser(session)
await ensureEmployeeExists(session)
return { success: true }
} catch (error) {
console.error('Login error:', error)
let errorMessage = error.message || 'Login fehlgeschlagen'
// Bessere Fehlermeldungen
if (error.code === 401 || errorMessage.includes('Invalid credentials')) {
if (error.code === 429 || errorMessage.includes('Rate limit')) {
errorMessage = 'Zu viele Login-Versuche. Bitte 1530 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'
} else if (errorMessage.includes('User not found')) {
errorMessage = 'Benutzer nicht gefunden. Bitte registriere dich zuerst.'
} else if (errorMessage.includes('Email/Password')) {
errorMessage = 'Email/Password Authentifizierung ist nicht aktiviert. Bitte aktiviere sie in deinem Appwrite Dashboard unter Auth → Providers.'
}
return { success: false, error: errorMessage }
}
}
@@ -128,28 +169,25 @@ export function AuthProvider({ children }) {
setUser(null)
return
}
try {
await account.deleteSession('current')
setUser(null)
await account.deleteSessions()
} catch (error) {
console.error('Logout error:', error)
} finally {
clearAllLocalAppwriteSessions()
setUser(null)
}
}
// Hilfsfunktion um zu prüfen ob Benutzer Admin ist
const isAdmin = () => {
if (!user) return false
// Prüfe ob Benutzer das "admin" Label hat
return user.labels?.includes('admin') || false
}
const isAdmin = Boolean(user?.labels?.includes('admin'))
const value = {
user,
loading,
login,
logout,
isAdmin: isAdmin()
isAdmin
}
return (

View File

@@ -1,6 +1,5 @@
import { Client, Account, Databases, Storage, ID, Query } from 'appwrite'
// Debug: Zeige geladene Umgebungsvariablen (nur in Development)
if (import.meta.env.DEV) {
console.log('🔧 Appwrite Konfiguration:')
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')
}
const endpoint = import.meta.env.VITE_APPWRITE_ENDPOINT || 'https://appwrite.webklar.com/v1'
const projectId = import.meta.env.VITE_APPWRITE_PROJECT_ID || ''
const endpoint = import.meta.env.VITE_APPWRITE_ENDPOINT || 'https://ticket.webklar.com/v1'
const projectId = (import.meta.env.VITE_APPWRITE_PROJECT_ID || '').trim()
if (!projectId) {
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'
// Collection IDs - Verwende die tatsächlichen Collection IDs aus Appwrite!
export const COLLECTIONS = {
WORKORDERS: '6943bf7d001901baa60c', // Collection ID für workorders
CONFIG: 'config', // Collection ID für Admin-Konfiguration (wird erstellt)
CUSTOMERS: '694bd1fb002b2e583d13', // Collection ID für customers
EMPLOYEES: '695280510031c6c6153b', // Collection ID für employees
WORKSHEETS: '6952dbcf0032a92e1168', // Collection ID für worksheets
WORKORDERS: 'workorders',
CONFIG: 'config',
CUSTOMERS: 'customers',
EMPLOYEES: 'employees',
WORKSHEETS: 'worksheets',
USERS: 'users',
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 }