Files
Webklar-Kundenbereich/server/services/appwriteClient.js
T

145 lines
4.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { config } from '../config.js'
function appwriteHeaders() {
return {
'Content-Type': 'application/json',
'X-Appwrite-Project': config.appwrite.projectId,
}
}
async function appwriteFetch(path, { method = 'GET', body, headers = {}, onResponse } = {}) {
const url = `${config.appwrite.endpoint}${path}`
const response = await fetch(url, {
method,
headers: { ...appwriteHeaders(), ...headers },
body: body ? JSON.stringify(body) : undefined,
})
onResponse?.(response)
let data = null
const text = await response.text()
if (text) {
try {
data = JSON.parse(text)
} catch {
data = { message: text }
}
}
if (!response.ok) {
const error = new Error(data?.message || `Appwrite ${response.status}`)
error.status = response.status
error.code = data?.code
error.type = data?.type
if (response.status === 429 || data?.type === 'general_rate_limit_exceeded') {
error.message =
'Zu viele Anmeldeversuche. Bitte warte einige Minuten, bevor du es erneut versuchst.'
error.status = 429
}
throw error
}
return data
}
function readSessionCookie(response) {
const name = `a_session_${config.appwrite.projectId}=`
const cookies = response.headers.getSetCookie?.() || []
const cookie = cookies.find((c) => c.startsWith(name))
return cookie ? decodeURIComponent(cookie.slice(name.length).split(';')[0]) : ''
}
/**
* Appwrite-Aufruf im Namen einer Sitzung aus loginWithAppwrite (z. B. MFA-Challenge). Deutsche Sprache,
* damit Appwrite die deutsche Mailvorlage nimmt.
*/
export function appwriteSessionFetch(path, { method = 'GET', body, sessionCookie } = {}) {
return appwriteFetch(path, {
method,
body,
headers: {
Cookie: `a_session_${config.appwrite.projectId}=${encodeURIComponent(sessionCookie || '')}`,
'X-Appwrite-Locale': 'de',
},
})
}
/**
* Login via Appwrite Auth REST. userId kommt aus der Session – kein users.read nötig.
*/
let appwriteLoginBlockedUntil = 0
const APPWRITE_LOGIN_COOLDOWN_MS = 5 * 60 * 1000
const APPWRITE_RATE_LIMIT_COOLDOWN_MS = 15 * 60 * 1000
export function getLoginCooldownRemainingSec() {
const left = appwriteLoginBlockedUntil - Date.now()
return left > 0 ? Math.ceil(left / 1000) : 0
}
export function clearLoginCooldown() {
appwriteLoginBlockedUntil = 0
}
export async function loginWithAppwrite(email, password) {
const now = Date.now()
if (now < appwriteLoginBlockedUntil) {
const waitSec = Math.ceil((appwriteLoginBlockedUntil - now) / 1000)
const error = new Error(
`Zu viele Anmeldeversuche. Bitte warte noch ${waitSec} Sekunden.`
)
error.status = 429
throw error
}
let session
let sessionCookie = ''
try {
session = await appwriteFetch('/account/sessions/email', {
method: 'POST',
body: { email, password },
onResponse: (response) => {
sessionCookie = readSessionCookie(response)
},
})
} catch (err) {
if (err.status === 429) {
appwriteLoginBlockedUntil = Date.now() + APPWRITE_RATE_LIMIT_COOLDOWN_MS
}
const error = new Error(err.message || 'Anmeldung fehlgeschlagen')
error.status = err.status || 401
throw error
}
if (!session?.userId) {
const error = new Error('Appwrite-Session ohne userId.')
error.status = 500
throw error
}
// sessionCookie: fuer den zweiten Faktor (Mitarbeiter-2FA) arbeitet der Server im Namen dieser Sitzung weiter
const user = { $id: session.userId, email, name: '', sessionCookie }
clearLoginCooldown()
return user
}
/**
* Prueft ein Passwort ueber eine Appwrite-Session (Passwort aendern im Kundenbereich). Anders als
* loginWithAppwrite ohne die globale Login-Sperre: tippt ein Kunde sein aktuelles Passwort falsch,
* darf das die Anmeldung der anderen nicht blockieren. Die Session loescht der Aufrufer wieder.
* @returns {Promise<{ userId: string, sessionId: string } | null>} null = Passwort falsch
*/
export async function verifyAppwritePassword(email, password) {
try {
const session = await appwriteFetch('/account/sessions/email', {
method: 'POST',
body: { email, password },
})
return { userId: session?.userId || '', sessionId: session?.$id || '' }
} catch (err) {
// 400: kuerzer als 8 Zeichen (kann nie stimmen), 401: falsche Zugangsdaten; gesperrt u. a. weiterreichen
if (err.status === 400 || (err.status === 401 && err.type === 'user_invalid_credentials')) return null
throw err
}
}