mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-22 01:46:43 +02:00
Phase 1 des managarten me-images → mana-me Cutovers
(siehe mana/docs/USER_CONTEXT_STRATEGY.md). Spiegelt Primary-Slot-
Wechsel (face-ref, body-ref) in die Plattform-Service mana-me, damit
cross-app-Konsumenten (Werdrobe, Memoro) den aktuellen Face/Body
des Users ohne managarten-DB-Abhängigkeit bekommen.
- apps/api/src/lib/mana-me.ts: Service-to-Service-Client (X-Service-Key)
mit slot→kind-Mapping (face-ref→face, body-ref→fullbody).
- apps/api/.../profile/routes.ts: POST /me-images/sync-primary (JWT)
ruft den Client; best-effort, blockt setPrimary nicht.
- web stores/me-images.svelte.ts: setPrimary('face-ref'|'body-ref')
triggert die Brücke via api/me-images.ts.
cloudflared: me.mana.how → localhost:3078 in der Plattform-Sektion
(neben share/mcp).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
68 lines
2.3 KiB
TypeScript
68 lines
2.3 KiB
TypeScript
/**
|
|
* Service-to-service client for mana-me.
|
|
*
|
|
* managarten's Dexie-local-first me-images module stays the user's
|
|
* curated, encrypted, per-space collection (with `aiReference`-opt-in
|
|
* etc.) — but when the user sets the primary slot for face-ref /
|
|
* body-ref, that decision is the *platform-truth* every other app
|
|
* needs ("which photo is currently this user's face"). This client
|
|
* mirrors that single bit into mana-me via its internal write-through
|
|
* endpoint, so consumers like Werdrobe and Memoro don't depend on the
|
|
* managarten DB.
|
|
*
|
|
* Best-effort: mana-me being unreachable does not fail the user-facing
|
|
* write. The managarten row stays authoritative locally; the next
|
|
* primary-change reconciles. We log + return false instead of throwing.
|
|
*/
|
|
|
|
const MANA_ME_URL = process.env.MANA_ME_URL || 'http://localhost:3078';
|
|
const MANA_SERVICE_KEY = process.env.MANA_SERVICE_KEY || 'dev-service-key';
|
|
|
|
export type ManaMeKind = 'face' | 'fullbody' | 'halfbody' | 'hands' | 'reference';
|
|
|
|
/**
|
|
* Map managarten's `primaryFor` slot to mana-me's `kind`. We only
|
|
* mirror the two slots that have cross-app meaning — `avatar` is
|
|
* already handled by the existing auth.users.image sync hook in
|
|
* `me-images.svelte.ts` and doesn't need a mana-me entry.
|
|
*/
|
|
export function slotToManaMeKind(slot: 'avatar' | 'face-ref' | 'body-ref'): ManaMeKind | null {
|
|
if (slot === 'face-ref') return 'face';
|
|
if (slot === 'body-ref') return 'fullbody';
|
|
return null;
|
|
}
|
|
|
|
export interface MeImageWriteThrough {
|
|
userId: string;
|
|
kind: ManaMeKind;
|
|
mediaId: string;
|
|
makePrimary: boolean;
|
|
}
|
|
|
|
export async function mirrorMeImageToManaMe(input: MeImageWriteThrough): Promise<boolean> {
|
|
const url = `${MANA_ME_URL}/api/v1/internal/me/${encodeURIComponent(input.userId)}/images`;
|
|
try {
|
|
const res = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Service-Key': MANA_SERVICE_KEY,
|
|
},
|
|
body: JSON.stringify({
|
|
kind: input.kind,
|
|
mediaId: input.mediaId,
|
|
makePrimary: input.makePrimary,
|
|
}),
|
|
});
|
|
if (!res.ok) {
|
|
console.warn(
|
|
`[mana-me] mirror failed for user=${input.userId} kind=${input.kind}: ${res.status}`
|
|
);
|
|
return false;
|
|
}
|
|
return true;
|
|
} catch (err) {
|
|
console.warn(`[mana-me] mirror network error for user=${input.userId}:`, err);
|
|
return false;
|
|
}
|
|
}
|