Files
temetro/backend/drizzle/0028_military_cyclops.sql
Khalid Abdi 2d47abcc42 feat: patient wallet — real Signing, encrypted share relay, import-from-app
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>
2026-06-21 18:19:57 +03:00

33 lines
1.9 KiB
SQL

CREATE TABLE "clinic_signing_keys" (
"organization_id" text PRIMARY KEY NOT NULL,
"algorithm" text DEFAULT 'ed25519' NOT NULL,
"public_key" text NOT NULL,
"fingerprint" text NOT NULL,
"private_key_enc" text NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"rotated_at" timestamp
);
--> statement-breakpoint
CREATE TABLE "wallet_share_requests" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"organization_id" text NOT NULL,
"requested_by" text NOT NULL,
"wallet_number" text NOT NULL,
"ephemeral_pub_key" text NOT NULL,
"ephemeral_priv_enc" text NOT NULL,
"status" text DEFAULT 'pending' NOT NULL,
"share_mode" text DEFAULT 'permanent' NOT NULL,
"share_expires_at" timestamp,
"draft" jsonb,
"committed_file_number" text,
"created_at" timestamp DEFAULT now() NOT NULL,
"resolved_at" timestamp
);
--> statement-breakpoint
ALTER TABLE "patients" ADD COLUMN "share_origin" text;--> statement-breakpoint
ALTER TABLE "patients" ADD COLUMN "share_expires_at" timestamp;--> statement-breakpoint
ALTER TABLE "clinic_signing_keys" ADD CONSTRAINT "clinic_signing_keys_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "wallet_share_requests" ADD CONSTRAINT "wallet_share_requests_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "wallet_share_requests" ADD CONSTRAINT "wallet_share_requests_requested_by_user_id_fk" FOREIGN KEY ("requested_by") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "wallet_share_org_idx" ON "wallet_share_requests" USING btree ("organization_id");--> statement-breakpoint
CREATE INDEX "wallet_share_wallet_idx" ON "wallet_share_requests" USING btree ("wallet_number");