Convert Server from submodule to normal files

This commit is contained in:
2026-01-18 17:50:49 +01:00
parent 86d2191a25
commit 0012a10249
52 changed files with 11975 additions and 1 deletions

View File

@@ -0,0 +1,339 @@
/**
* Accounts Service
* CRUD-Operationen für Accounts aus Appwrite
* Verwaltet nur "Managed Accounts" (account_owner_user_id == authUserId)
*/
import { databases, databaseId, accountsCollectionId } from "../lib/appwrite";
import { ID, Query } from "appwrite";
/**
* Lädt ein einzelnes Account nach ID
* @param {string} accountId - ID des Accounts
* @returns {Promise<Object|null>} Account-Dokument oder null
*/
export async function getAccountById(accountId) {
if (!accountId) {
console.warn("getAccountById: accountId fehlt");
return null;
}
try {
const document = await databases.getDocument(
databaseId,
accountsCollectionId,
accountId
);
return document;
} catch (e) {
// 404 bedeutet, dass das Dokument nicht existiert
if (e.code === 404 || e.type === 'document_not_found') {
return null;
}
console.error("Fehler beim Laden des Accounts:", e);
throw e;
}
}
/**
* Leitet den Market-Code aus einem Account ab
* @param {Object} account - Account-Dokument
* @returns {string} Market-Code (z.B. "DE", "US", "UNKNOWN")
*/
export function deriveMarketFromAccount(account) {
if (!account) {
return "UNKNOWN";
}
// Priority 1: account_platform_market Feld vorhanden
if (account.account_platform_market && account.account_platform_market.trim()) {
return account.account_platform_market.trim().toUpperCase();
}
// Priority 2: Ableitung aus account_url Hostname
if (account.account_url) {
try {
const url = new URL(account.account_url);
const hostname = url.hostname.toLowerCase();
// Mapping von Hostname zu Market-Code
const hostnameToMarket = {
"ebay.de": "DE",
"www.ebay.de": "DE",
"ebay.com": "US",
"www.ebay.com": "US",
"ebay.co.uk": "UK",
"www.ebay.co.uk": "UK",
"ebay.fr": "FR",
"www.ebay.fr": "FR",
"ebay.it": "IT",
"www.ebay.it": "IT",
"ebay.es": "ES",
"www.ebay.es": "ES",
"ebay.ca": "CA",
"www.ebay.ca": "CA",
"ebay.com.au": "AU",
"www.ebay.com.au": "AU",
};
if (hostnameToMarket[hostname]) {
return hostnameToMarket[hostname];
}
// Fallback: Prüfe ob hostname ein bekanntes Pattern enthält
if (hostname.includes("ebay.de")) return "DE";
if (hostname.includes("ebay.com") && !hostname.includes("ebay.com.au")) return "US";
if (hostname.includes("ebay.co.uk")) return "UK";
if (hostname.includes("ebay.fr")) return "FR";
if (hostname.includes("ebay.it")) return "IT";
if (hostname.includes("ebay.es")) return "ES";
if (hostname.includes("ebay.ca")) return "CA";
if (hostname.includes("ebay.com.au")) return "AU";
} catch (e) {
// URL parsing fehlgeschlagen
console.debug("Fehler beim Parsen der Account-URL:", e);
}
}
return "UNKNOWN";
}
/**
* Leitet die Währung aus einem Market-Code ab
* @param {string} market - Market-Code (z.B. "DE", "US", "UK")
* @returns {string} Währungscode (z.B. "EUR", "USD", "GBP")
*/
export function deriveCurrencyFromMarket(market) {
if (!market || market === "UNKNOWN") {
return "EUR"; // Fallback
}
const marketUpper = market.toUpperCase();
// Mapping Market -> Currency
const marketToCurrency = {
// EUR Länder
"DE": "EUR",
"FR": "EUR",
"IT": "EUR",
"ES": "EUR",
"NL": "EUR",
"AT": "EUR",
"BE": "EUR",
"IE": "EUR",
// Andere Währungen
"US": "USD",
"UK": "GBP",
"CA": "CAD",
"AU": "AUD",
};
return marketToCurrency[marketUpper] || "EUR"; // Fallback zu EUR
}
/**
* Lädt alle Managed Accounts für einen User
* @param {string} authUserId - ID des eingeloggten Users
* @returns {Promise<Array>} Array von Account-Dokumenten
*/
export async function fetchManagedAccounts(authUserId) {
if (!authUserId) {
console.warn("fetchManagedAccounts: authUserId fehlt");
return [];
}
try {
const response = await databases.listDocuments(
databaseId,
accountsCollectionId,
[
Query.equal("account_owner_user_id", authUserId),
Query.orderDesc("$createdAt"),
]
);
return response.documents;
} catch (e) {
console.error("Fehler beim Laden der Accounts:", e);
// Wenn Collection nicht existiert oder Berechtigungen fehlen
if (e.code === 404 || e.code === 401) {
return [];
}
throw e;
}
}
/**
* Erstellt ein neues Managed Account
* @param {string} authUserId - ID des eingeloggten Users
* @param {Object} accountData - Account-Daten
* @param {string} accountData.account_platform_market - Required: Market (z.B. "DE", "US")
* @param {string} accountData.account_platform_account_id - Required: Platform Account ID
* @param {string} [accountData.account_shop_name] - Optional: Shop-Name
* @param {string} [accountData.account_url] - Optional: Account URL
* @param {string} [accountData.account_status] - Optional: Status (z.B. "active", "unknown", "disabled")
* @returns {Promise<Object>} Erstelltes Account-Dokument
*/
export async function createManagedAccount(authUserId, accountData) {
if (!authUserId) {
throw new Error("authUserId ist erforderlich");
}
// Validierung der Required-Felder
const { account_platform_market, account_platform_account_id } = accountData;
// account_platform wird IMMER "ebay" gesetzt (eBay-only Tool)
if (!account_platform_market) {
throw new Error("account_platform_market ist erforderlich");
}
if (!account_platform_account_id) {
throw new Error("account_platform_account_id ist erforderlich");
}
// Payload zusammenstellen
const payload = {
account_owner_user_id: authUserId,
account_platform: "ebay", // IMMER "ebay" für über UI erstellte Accounts
account_platform_market,
account_platform_account_id,
account_shop_name: accountData.account_shop_name || null,
account_url: accountData.account_url || null,
account_sells: accountData.account_sells ?? null,
account_managed: true, // Immer true für über die UI erstellte Accounts
};
// #region agent log
fetch('http://127.0.0.1:7242/ingest/246fe132-ecc5-435f-bd9c-fe5e8e26089d',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'accountsService.js:72',message:'createManagedAccount: payload before Appwrite',data:{account_sells:payload.account_sells,accountData_account_sells:accountData.account_sells},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'E'})}).catch(()=>{});
// #endregion
// account_status ist optional - aufgrund Schema-Konflikt vorerst weglassen
// TODO: Schema in Appwrite prüfen und korrigieren (Enum-Feld sollte String akzeptieren, nicht Array)
// Das Feld wird erst wieder hinzugefügt, wenn das Schema korrekt konfiguriert ist
// Document-Level Permissions für den User setzen
const permissions = [
`read("user:${authUserId}")`,
`update("user:${authUserId}")`,
`delete("user:${authUserId}")`,
];
try {
const document = await databases.createDocument(
databaseId,
accountsCollectionId,
ID.unique(),
payload,
permissions
);
return document;
} catch (e) {
// Duplicate-Conflict behandeln (falls Unique-Index auf platform+market+platform_account_id existiert)
if (e.code === 409 || e.message?.includes("duplicate") || e.message?.includes("unique")) {
throw new Error(
"Dieser Account ist bereits verbunden."
);
}
console.error("Fehler beim Erstellen des Accounts:", e);
throw e;
}
}
/**
* Aktualisiert ein Managed Account (optional für v1)
* @param {string} accountId - ID des Accounts
* @param {Object} accountData - Zu aktualisierende Account-Daten
* @returns {Promise<Object>} Aktualisiertes Account-Dokument
*/
export async function updateManagedAccount(accountId, accountData) {
if (!accountId) {
throw new Error("accountId ist erforderlich");
}
try {
// Entferne undefined/null Werte aus accountData (behalte nur geänderte Felder)
const payload = {};
Object.keys(accountData).forEach((key) => {
if (accountData[key] !== undefined && accountData[key] !== null) {
payload[key] = accountData[key];
}
});
// #region agent log
fetch('http://127.0.0.1:7242/ingest/246fe132-ecc5-435f-bd9c-fe5e8e26089d',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'accountsService.js:133',message:'updateManagedAccount: before updateDocument',data:{accountId,payload,payloadKeys:Object.keys(payload)},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'A'})}).catch(()=>{});
// #endregion
const document = await databases.updateDocument(
databaseId,
accountsCollectionId,
accountId,
payload
);
// #region agent log
fetch('http://127.0.0.1:7242/ingest/246fe132-ecc5-435f-bd9c-fe5e8e26089d',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'accountsService.js:147',message:'updateManagedAccount: success',data:{accountId},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'A'})}).catch(()=>{});
// #endregion
return document;
} catch (e) {
console.error("Fehler beim Aktualisieren des Accounts:", e);
// #region agent log
fetch('http://127.0.0.1:7242/ingest/246fe132-ecc5-435f-bd9c-fe5e8e26089d',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'accountsService.js:149',message:'updateManagedAccount: error',data:{accountId,errorCode:e.code,errorMessage:e.message,errorType:e.type},timestamp:Date.now(),sessionId:'debug-session',runId:'run1',hypothesisId:'C'})}).catch(()=>{});
// #endregion
throw e;
}
}
/**
* Aktualisiert das account_last_scan_at Feld eines Accounts
* @param {string} accountId - ID des Accounts
* @param {string} isoString - ISO 8601 Datum-String (z.B. "2026-01-18T12:34:56.000Z")
* @returns {Promise<Object>} Aktualisiertes Account-Dokument
*/
export async function updateAccountLastScanAt(accountId, isoString) {
if (!accountId) {
throw new Error("accountId ist erforderlich");
}
if (!isoString) {
throw new Error("isoString ist erforderlich");
}
try {
const document = await databases.updateDocument(
databaseId,
accountsCollectionId,
accountId,
{
account_last_scan_at: isoString,
}
);
return document;
} catch (e) {
console.error("Fehler beim Aktualisieren des account_last_scan_at:", e);
throw e;
}
}
/**
* Löscht ein Managed Account (optional für v1)
* @param {string} accountId - ID des Accounts
* @returns {Promise<void>}
*/
export async function deleteManagedAccount(accountId) {
if (!accountId) {
throw new Error("accountId ist erforderlich");
}
try {
await databases.deleteDocument(
databaseId,
accountsCollectionId,
accountId
);
} catch (e) {
console.error("Fehler beim Löschen des Accounts:", e);
throw e;
}
}