chore: aktuellen Live-Stand sichern (Admin-API, Services, Auth-Erweiterungen)

Sichert den bisher uncommitteten Produktionsstand des Kundenbereich-Servers,
damit kuenftige Deploys (git reset --hard) nichts mehr verwerfen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 16:24:53 +00:00
parent cff72eb528
commit cf4b3ce4d1
24 changed files with 1883 additions and 123 deletions

View File

@@ -16,9 +16,9 @@ function adminHeaders() {
}
}
function formatRequestBody(body, method) {
function formatRequestBody(body, method, { raw = false } = {}) {
if (!body || method === 'GET' || method === 'DELETE') return body
if (body.data !== undefined) return body
if (raw || body.data !== undefined) return body
const { documentId, ...fields } = body
const payload = { data: fields }
if (documentId) payload.documentId = documentId
@@ -37,7 +37,8 @@ async function adminFetch(path, { method = 'GET', body, queries = [] } = {}) {
url.searchParams.append('queries[]', q)
}
const requestBody = formatRequestBody(body, method)
const useRawBody = path.startsWith('/users')
const requestBody = formatRequestBody(body, method, { raw: useRawBody })
const response = await fetch(url.toString(), {
method,
@@ -90,6 +91,50 @@ export async function getUserById(userId) {
return adminFetch(`/users/${userId}`)
}
export async function createAppwriteUser({ email, password, name }) {
return adminFetch('/users', {
method: 'POST',
body: {
userId: ID.unique(),
email: email.trim(),
password,
name: name?.trim() || email.trim(),
},
})
}
export async function updateAppwriteUserEmail(userId, email) {
return adminFetch(`/users/${userId}/email`, {
method: 'PATCH',
body: { email: email.trim() },
})
}
export async function updateAppwriteUserPassword(userId, password) {
return adminFetch(`/users/${userId}/password`, {
method: 'PATCH',
body: { password },
})
}
export async function updateAppwriteUserName(userId, name) {
return adminFetch(`/users/${userId}/name`, {
method: 'PATCH',
body: { name: name.trim() },
})
}
export async function updateAppwriteUserLabels(userId, labels) {
return adminFetch(`/users/${userId}/labels`, {
method: 'PUT',
body: { labels },
})
}
export async function deleteAppwriteUser(userId) {
return adminFetch(`/users/${userId}`, { method: 'DELETE' })
}
export async function deleteUserSession(userId, sessionId) {
return adminFetch(`/users/${userId}/sessions/${sessionId}`, { method: 'DELETE' })
}
@@ -131,14 +176,47 @@ export async function updateDocument(collectionId, documentId, data) {
})
}
export async function getDocument(collectionId, documentId) {
return adminFetch(`${collectionPath(collectionId)}/${documentId}`)
}
export async function createDocument(collectionId, data, documentId = ID.unique()) {
return adminFetch(collectionPath(collectionId), {
method: 'POST',
body: { ...data, documentId },
})
}
export async function deleteDocument(collectionId, documentId) {
return adminFetch(`${collectionPath(collectionId)}/${documentId}`, {
method: 'DELETE',
})
}
const PRESERVE_IF_EMPTY = new Set(['customerId', 'ticketId'])
function buildProjectPayload(existing, data) {
const now = new Date().toISOString()
const payload = { updatedAt: now }
for (const [key, value] of Object.entries(data)) {
if (value === undefined) continue
if (PRESERVE_IF_EMPTY.has(key) && (value === '' || value === null)) {
if (existing?.[key]) continue
}
payload[key] = value
}
return payload
}
export async function upsertWebsiteProjectByRepo(repoFullName, data) {
const existing = await listDocuments(config.collections.websiteProjects, [
Query.equal('repoFullName', repoFullName),
Query.limit(1),
])
const now = new Date().toISOString()
const payload = { ...data, updatedAt: now }
const payload = buildProjectPayload(existing[0], data)
if (existing[0]) {
return updateDocument(
@@ -148,9 +226,17 @@ export async function upsertWebsiteProjectByRepo(repoFullName, data) {
)
}
const now = new Date().toISOString()
return adminFetch(collectionPath(config.collections.websiteProjects), {
method: 'POST',
body: { ...payload, createdAt: now, documentId: ID.unique() },
body: {
...payload,
customerId: payload.customerId ?? '',
ticketId: payload.ticketId ?? '',
projectName: payload.projectName || repoFullName.split('/').pop() || repoFullName,
createdAt: now,
documentId: ID.unique(),
},
})
}