refactor(apicore-sync): API key and shop feed only, no password login

The sync now talks to ApiCore exclusively through the secured shop
interface: GET /shop-sync with an API key as bearer token. Removed:

- the email/password service-account login (and its MFA handling),
- the fallback to ApiCore's general read endpoints (/harvests,
  /products, /extractions, /bottling-batches, /apiaries).

A missing interface (404) or a rejected key (401/403) now stops the run
with a specific message instead of falling back. APICORE_SYNC_EMAIL and
APICORE_SYNC_PASSWORD are gone from .env.example, README and the k8s
secret template; the system status needs APICORE_API_KEY.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-09-28 06:05:11 +02:00
1 parent 55be1fe881
commit 3bc0693e4d
10 files changed
+248 -548

No files matched your search

+4 -4
View File
@@ -5,13 +5,13 @@ import { revalidatePath } from "next/cache";
import { prisma } from "@/lib/prisma";
import { logAdminAction } from "@/lib/audit-log";
import {
openApiCoreSession,
getApiCoreConnection,
fetchApiCoreShopData,
isSellableStatus,
ApiCoreConfigError,
ApiCoreRequestError,
type ApiCoreShopData,
type ApiCoreHarvestWithApiaryName,
type ApiCoreHarvest,
type ApiCoreProduct,
type ApiCoreBottlingBatchWithHarvestId,
} from "@/lib/apicore-client";
@@ -128,7 +128,7 @@ function describeError(error: unknown): string {
// Ernten
// ---------------------------------------------------------------------------
async function syncHarvests(sources: ApiCoreHarvestWithApiaryName[]): Promise<HarvestSyncCounts> {
async function syncHarvests(sources: ApiCoreHarvest[]): Promise<HarvestSyncCounts> {
const counts: HarvestSyncCounts = { created: 0, updated: 0, skipped: 0, failed: 0, protectedDrafts: 0 };
for (const source of sources) {
@@ -480,7 +480,7 @@ export async function syncApiCoreAction(formData: FormData): Promise<void> {
let source: ApiCoreShopData;
try {
source = await fetchApiCoreShopData(await openApiCoreSession());
source = await fetchApiCoreShopData(getApiCoreConnection());
} catch (error: unknown) {
redirect(`${returnTo}?syncError=${encodeURIComponent(describeError(error))}`);
}