mirror of
https://github.com/temetro/temetro.git
synced 2026-08-21 15:36:47 +00:00
2d47abcc42
Backend - clinic Ed25519 signing key (services/signing.ts, routes/signing.ts, clinic_signing_keys table) — Settings → Signing is now real - @noble wallet-crypto (lib/wallet-crypto.ts): ed25519 identity, base58check wallet numbers, sealed-box (x25519 + xchacha20poly1305) - /wallet Socket.io relay namespace (challenge-signed device auth) forwarding only ciphertext; emitToWallet helper - import-from-app flow (routes/patients-wallet.ts, services/wallet-share.ts): request-share → patient approval → decrypt + verify → review draft → commit - temporary shares: patients.share_expires_at + 5-min auto-delete sweep; revoke Frontend - SigningPanel wired to live key/fingerprint/rotate + shared-records list - "Import from a patient app" dialog (lib/signing.ts, import-from-wallet-dialog) reusing the draft-review path; temporary badge on the patient list Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
41 lines
1.4 KiB
TypeScript
41 lines
1.4 KiB
TypeScript
// Client for the clinic signing key (Settings → Signing) and the "import from a
|
|
// patient app" wallet-share flow. Both call the backend over the shared fetch
|
|
// wrapper (session cookie sent automatically).
|
|
|
|
import { apiFetch } from "@/lib/api-client";
|
|
|
|
export type SigningKey = {
|
|
algorithm: string;
|
|
publicKey: string;
|
|
fingerprint: string;
|
|
createdAt: string; // ISO
|
|
rotatedAt: string | null;
|
|
};
|
|
|
|
export type WalletShareMode = "permanent" | "temporary";
|
|
|
|
export type SharedRecord = {
|
|
id: string;
|
|
walletNumber: string;
|
|
status: "pending" | "approved" | "denied" | "expired";
|
|
shareMode: WalletShareMode;
|
|
shareExpiresAt: string | null;
|
|
// The draft is only returned by the request-share poll, not the list.
|
|
};
|
|
|
|
// The clinic's Ed25519 signing key. The backend creates one lazily on first
|
|
// read, so this always resolves to a real key + fingerprint.
|
|
export async function getSigningKey(): Promise<SigningKey> {
|
|
return apiFetch<SigningKey>("/api/signing/key");
|
|
}
|
|
|
|
// Rotate the signing key (owner/admin only). Returns the new key.
|
|
export async function rotateSigningKey(): Promise<SigningKey> {
|
|
return apiFetch<SigningKey>("/api/signing/key/rotate", { method: "POST" });
|
|
}
|
|
|
|
// Recent records shared from patient wallets — feeds the panel's list.
|
|
export async function listSignedRecords(): Promise<SharedRecord[]> {
|
|
return apiFetch<SharedRecord[]>("/api/signing/records");
|
|
}
|