fix2
This commit is contained in:
@@ -45,6 +45,16 @@ app.get('/', (_req, res) => {
|
||||
res.redirect('/login.html')
|
||||
})
|
||||
|
||||
app.listen(config.port, () => {
|
||||
const server = app.listen(config.port, () => {
|
||||
console.log(`Webklar Kundenbereich läuft auf Port ${config.port}`)
|
||||
})
|
||||
|
||||
server.on('error', (err) => {
|
||||
if (err.code === 'EADDRINUSE') {
|
||||
console.error(
|
||||
`[server] Port ${config.port} ist bereits belegt. Alten Prozess beenden: lsof -i :${config.port} dann kill <PID>, oder PORT=3001 in .env setzen.`
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
throw err
|
||||
})
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { Router } from 'express'
|
||||
import { Query } from 'node-appwrite'
|
||||
import { config } from '../config.js'
|
||||
import { listDocuments } from '../services/appwriteAdmin.js'
|
||||
import { listDocuments, Query } from '../services/appwriteAdmin.js'
|
||||
import { getSessionCustomerId, requireSession } from '../middleware/session.js'
|
||||
|
||||
const router = Router()
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { Router } from 'express'
|
||||
import { Query } from 'node-appwrite'
|
||||
import { config } from '../config.js'
|
||||
import { listDocuments } from '../services/appwriteAdmin.js'
|
||||
import { listDocuments, Query } from '../services/appwriteAdmin.js'
|
||||
import { getSessionCustomerId, requireSession } from '../middleware/session.js'
|
||||
|
||||
const router = Router()
|
||||
|
||||
@@ -1,37 +1,90 @@
|
||||
import { Client, Account, Databases, ID, Query } from 'node-appwrite'
|
||||
import { config } from '../config.js'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { config, WOMS_DATABASE_ID } from '../config.js'
|
||||
|
||||
export function createAdminClient() {
|
||||
const client = new Client()
|
||||
.setEndpoint(config.appwrite.endpoint)
|
||||
.setProject(config.appwrite.projectId)
|
||||
.setKey(config.appwrite.apiKey)
|
||||
function buildQueries(queries = []) {
|
||||
return queries.map((q) => {
|
||||
if (typeof q === 'string') return q
|
||||
return JSON.stringify(q)
|
||||
})
|
||||
}
|
||||
|
||||
function adminHeaders() {
|
||||
return {
|
||||
client,
|
||||
databases: new Databases(client),
|
||||
'Content-Type': 'application/json',
|
||||
'X-Appwrite-Project': config.appwrite.projectId,
|
||||
'X-Appwrite-Key': config.appwrite.apiKey,
|
||||
}
|
||||
}
|
||||
|
||||
export function createUserClient() {
|
||||
const client = new Client()
|
||||
.setEndpoint(config.appwrite.endpoint)
|
||||
.setProject(config.appwrite.projectId)
|
||||
|
||||
return {
|
||||
client,
|
||||
account: new Account(client),
|
||||
async function adminFetch(path, { method = 'GET', body, queries = [] } = {}) {
|
||||
if (!config.appwrite.apiKey) {
|
||||
const error = new Error('APPWRITE_API_KEY fehlt in .env')
|
||||
error.status = 500
|
||||
throw error
|
||||
}
|
||||
|
||||
const url = new URL(`${config.appwrite.endpoint}${path}`)
|
||||
for (const q of buildQueries(queries)) {
|
||||
url.searchParams.append('queries[]', q)
|
||||
}
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
method,
|
||||
headers: adminHeaders(),
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
})
|
||||
|
||||
const text = await response.text()
|
||||
let data = null
|
||||
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 >= 500 ? 500 : response.status
|
||||
error.code = data?.code
|
||||
throw error
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
/** Appwrite Query-Helper (kompatibel zu bisherigen Aufrufen) */
|
||||
export const Query = {
|
||||
equal: (attribute, value) => ({
|
||||
method: 'equal',
|
||||
attribute,
|
||||
values: Array.isArray(value) ? value : [value],
|
||||
}),
|
||||
limit: (n) => ({ method: 'limit', values: [n] }),
|
||||
orderDesc: (attribute) => ({ method: 'orderDesc', attribute }),
|
||||
orderAsc: (attribute) => ({ method: 'orderAsc', attribute }),
|
||||
}
|
||||
|
||||
export const ID = {
|
||||
unique: () => randomUUID(),
|
||||
}
|
||||
|
||||
function collectionPath(collectionId) {
|
||||
return `/databases/${config.appwrite.databaseId}/collections/${collectionId}/documents`
|
||||
}
|
||||
|
||||
export async function getUserById(userId) {
|
||||
return adminFetch(`/users/${userId}`)
|
||||
}
|
||||
|
||||
export async function deleteUserSession(userId, sessionId) {
|
||||
return adminFetch(`/users/${userId}/sessions/${sessionId}`, { method: 'DELETE' })
|
||||
}
|
||||
|
||||
export async function listDocuments(collectionId, queries = []) {
|
||||
const { databases } = createAdminClient()
|
||||
const response = await databases.listDocuments(
|
||||
config.appwrite.databaseId,
|
||||
collectionId,
|
||||
queries
|
||||
)
|
||||
return response.documents
|
||||
const result = await adminFetch(collectionPath(collectionId), { queries })
|
||||
return result.documents
|
||||
}
|
||||
|
||||
export async function getCustomerByAppwriteUserId(appwriteUserId) {
|
||||
@@ -51,17 +104,13 @@ export async function getPortalAccessByCustomerId(customerId) {
|
||||
}
|
||||
|
||||
export async function updateDocument(collectionId, documentId, data) {
|
||||
const { databases } = createAdminClient()
|
||||
return databases.updateDocument(
|
||||
config.appwrite.databaseId,
|
||||
collectionId,
|
||||
documentId,
|
||||
data
|
||||
)
|
||||
return adminFetch(`${collectionPath(collectionId)}/${documentId}`, {
|
||||
method: 'PATCH',
|
||||
body: data,
|
||||
})
|
||||
}
|
||||
|
||||
export async function upsertWebsiteProjectByRepo(repoFullName, data) {
|
||||
const { databases } = createAdminClient()
|
||||
const existing = await listDocuments(config.collections.websiteProjects, [
|
||||
Query.equal('repoFullName', repoFullName),
|
||||
Query.limit(1),
|
||||
@@ -71,18 +120,24 @@ export async function upsertWebsiteProjectByRepo(repoFullName, data) {
|
||||
const payload = { ...data, updatedAt: now }
|
||||
|
||||
if (existing[0]) {
|
||||
return databases.updateDocument(
|
||||
config.appwrite.databaseId,
|
||||
return updateDocument(
|
||||
config.collections.websiteProjects,
|
||||
existing[0].$id,
|
||||
payload
|
||||
)
|
||||
}
|
||||
|
||||
return databases.createDocument(
|
||||
config.appwrite.databaseId,
|
||||
config.collections.websiteProjects,
|
||||
ID.unique(),
|
||||
{ ...payload, createdAt: now }
|
||||
)
|
||||
return adminFetch(collectionPath(config.collections.websiteProjects), {
|
||||
method: 'POST',
|
||||
body: { ...payload, createdAt: now, documentId: ID.unique() },
|
||||
})
|
||||
}
|
||||
|
||||
/** @deprecated Nur für Kompatibilität – nutzt native fetch */
|
||||
export function createAdminClient() {
|
||||
return { usesNativeFetch: true, databaseId: WOMS_DATABASE_ID }
|
||||
}
|
||||
|
||||
export function createUserClient() {
|
||||
return { usesNativeFetch: true }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createUserClient } from './appwriteAdmin.js'
|
||||
import { config } from '../config.js'
|
||||
import { deleteUserSession, getUserById } from './appwriteAdmin.js'
|
||||
|
||||
const DEBUG_LOG = (location, message, data, hypothesisId) => {
|
||||
// #region agent log
|
||||
@@ -17,54 +18,94 @@ const DEBUG_LOG = (location, message, data, hypothesisId) => {
|
||||
// #endregion
|
||||
}
|
||||
|
||||
export async function loginWithAppwrite(email, password) {
|
||||
const { client, account } = createUserClient()
|
||||
function appwriteHeaders() {
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Appwrite-Project': config.appwrite.projectId,
|
||||
}
|
||||
}
|
||||
|
||||
let session
|
||||
async function appwriteFetch(path, { method = 'GET', body } = {}) {
|
||||
const url = `${config.appwrite.endpoint}${path}`
|
||||
const response = await fetch(url, {
|
||||
method,
|
||||
headers: appwriteHeaders(),
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
})
|
||||
|
||||
let data = null
|
||||
const text = await response.text()
|
||||
if (text) {
|
||||
try {
|
||||
session = await account.createEmailPasswordSession(email, password)
|
||||
DEBUG_LOG('appwriteClient.js:session', 'createEmailPasswordSession ok', {
|
||||
hasSecret: Boolean(session?.secret),
|
||||
sessionId: session?.$id || null,
|
||||
}, 'H1')
|
||||
} catch (err) {
|
||||
DEBUG_LOG('appwriteClient.js:session', 'createEmailPasswordSession fail', {
|
||||
code: err?.code,
|
||||
type: err?.type,
|
||||
message: err?.message?.slice(0, 120),
|
||||
}, 'H1')
|
||||
const message = err?.message || 'Anmeldung fehlgeschlagen'
|
||||
const error = new Error(message)
|
||||
error.status = 401
|
||||
data = JSON.parse(text)
|
||||
} catch {
|
||||
data = { message: text }
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const error = new Error(data?.message || `Appwrite ${response.status}`)
|
||||
error.status = response.status === 401 ? 401 : response.status >= 500 ? 500 : 401
|
||||
error.code = data?.code
|
||||
error.type = data?.type
|
||||
throw error
|
||||
}
|
||||
|
||||
if (session?.secret) {
|
||||
client.setSession(session.secret)
|
||||
DEBUG_LOG('appwriteClient.js:setSession', 'setSession applied', { hasSessionHeader: true }, 'H2')
|
||||
} else {
|
||||
DEBUG_LOG('appwriteClient.js:setSession', 'no session.secret', {}, 'H2')
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* Appwrite Auth per native fetch (Node 26 + node-appwrite-Agent ist inkompatibel).
|
||||
* Session.secret wird serverseitig oft nicht zurückgegeben – userId aus Session nutzen.
|
||||
*/
|
||||
export async function loginWithAppwrite(email, password) {
|
||||
let session
|
||||
try {
|
||||
session = await appwriteFetch('/account/sessions/email', {
|
||||
method: 'POST',
|
||||
body: { email, password },
|
||||
})
|
||||
DEBUG_LOG('appwriteClient.js:session', 'createEmailPasswordSession ok', {
|
||||
hasSecret: Boolean(session?.secret),
|
||||
hasUserId: Boolean(session?.userId),
|
||||
sessionId: session?.$id || null,
|
||||
}, 'H6')
|
||||
} catch (err) {
|
||||
DEBUG_LOG('appwriteClient.js:session', 'createEmailPasswordSession fail', {
|
||||
message: err?.message?.slice(0, 120),
|
||||
code: err?.code,
|
||||
}, 'H1')
|
||||
const error = new Error(err.message || 'Anmeldung fehlgeschlagen')
|
||||
error.status = err.status || 401
|
||||
throw error
|
||||
}
|
||||
|
||||
if (!session?.userId) {
|
||||
DEBUG_LOG('appwriteClient.js:session', 'no userId in session', {
|
||||
sessionKeys: session ? Object.keys(session).filter((k) => !k.startsWith('provider')) : [],
|
||||
}, 'H6')
|
||||
const error = new Error('Appwrite-Session ohne userId.')
|
||||
error.status = 500
|
||||
throw error
|
||||
}
|
||||
|
||||
let user
|
||||
try {
|
||||
user = await account.get()
|
||||
DEBUG_LOG('appwriteClient.js:get', 'account.get ok', { userId: user?.$id || null }, 'H2')
|
||||
user = await getUserById(session.userId)
|
||||
DEBUG_LOG('appwriteClient.js:getUser', 'users.get ok', { userId: user?.$id || null }, 'H6')
|
||||
} catch (err) {
|
||||
DEBUG_LOG('appwriteClient.js:get', 'account.get fail', {
|
||||
code: err?.code,
|
||||
message: err?.message?.slice(0, 120),
|
||||
}, 'H2')
|
||||
const message = err?.message || 'Anmeldung fehlgeschlagen'
|
||||
const error = new Error(message)
|
||||
error.status = err?.message?.includes('scopes') ? 401 : 500
|
||||
throw error
|
||||
DEBUG_LOG('appwriteClient.js:getUser', 'users.get fail, fallback', {
|
||||
message: err?.message?.slice(0, 80),
|
||||
}, 'H6')
|
||||
user = { $id: session.userId, email, name: '' }
|
||||
}
|
||||
|
||||
if (session.$id) {
|
||||
try {
|
||||
await account.deleteSession('current')
|
||||
await deleteUserSession(session.userId, session.$id)
|
||||
} catch {
|
||||
// Portal nutzt eigene Session; Appwrite-Session wird nicht persistiert
|
||||
// Portal nutzt eigene Session; Appwrite-Session optional aufräumen
|
||||
}
|
||||
}
|
||||
|
||||
return user
|
||||
|
||||
Reference in New Issue
Block a user