@arcblock/did-connect-service 4.1.7 → 4.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/_generated/asset-bytes.js +1 -1
  2. package/dist/_generated/asset-manifest.js +2 -2
  3. package/dist/assets/admin-core.e83fd379.js +1397 -0
  4. package/dist/assets/admin-extra.841b2bbe.js +2530 -0
  5. package/dist/blocklet-service-rpc.js +11 -11
  6. package/dist/blocklet-service-rpc.js.map +1 -1
  7. package/dist/handlers/access-key-handler.js +1 -1
  8. package/dist/handlers/access-key-handler.js.map +1 -1
  9. package/dist/handlers/admin-instance-handler.d.ts.map +1 -1
  10. package/dist/handlers/admin-instance-handler.js +20 -19
  11. package/dist/handlers/admin-instance-handler.js.map +1 -1
  12. package/dist/handlers/avatar-handler.d.ts +1 -1
  13. package/dist/handlers/avatar-handler.d.ts.map +1 -1
  14. package/dist/handlers/avatar-handler.js +1 -1
  15. package/dist/handlers/avatar-handler.js.map +1 -1
  16. package/dist/handlers/branding-handler.js +7 -7
  17. package/dist/handlers/branding-handler.js.map +1 -1
  18. package/dist/handlers/did-connect-handler.d.ts.map +1 -1
  19. package/dist/handlers/did-connect-handler.js +5 -2
  20. package/dist/handlers/did-connect-handler.js.map +1 -1
  21. package/dist/handlers/email-login-handler.d.ts.map +1 -1
  22. package/dist/handlers/email-login-handler.js +4 -2
  23. package/dist/handlers/email-login-handler.js.map +1 -1
  24. package/dist/handlers/federation-admin-handler.d.ts.map +1 -1
  25. package/dist/handlers/federation-admin-handler.js +23 -23
  26. package/dist/handlers/federation-admin-handler.js.map +1 -1
  27. package/dist/handlers/membership-handler.js +2 -2
  28. package/dist/handlers/membership-handler.js.map +1 -1
  29. package/dist/handlers/oauth-handler.d.ts.map +1 -1
  30. package/dist/handlers/oauth-handler.js +12 -7
  31. package/dist/handlers/oauth-handler.js.map +1 -1
  32. package/dist/handlers/passkey-handler.d.ts.map +1 -1
  33. package/dist/handlers/passkey-handler.js +7 -4
  34. package/dist/handlers/passkey-handler.js.map +1 -1
  35. package/dist/handlers/team-handler.d.ts.map +1 -1
  36. package/dist/handlers/team-handler.js +22 -22
  37. package/dist/handlers/team-handler.js.map +1 -1
  38. package/dist/identity/auto-membership.js +2 -2
  39. package/dist/identity/auto-membership.js.map +1 -1
  40. package/dist/pages/admin/index.js +1 -1
  41. package/dist/pages/admin/scripts/api-client.d.ts +1 -1
  42. package/dist/pages/admin/scripts/api-client.d.ts.map +1 -1
  43. package/dist/pages/admin/scripts/api-client.js +7 -3
  44. package/dist/pages/admin/scripts/api-client.js.map +1 -1
  45. package/dist/pages/admin/tab-members.d.ts +1 -1
  46. package/dist/pages/admin/tab-members.d.ts.map +1 -1
  47. package/dist/pages/admin/tab-members.js +3 -1
  48. package/dist/pages/admin/tab-members.js.map +1 -1
  49. package/dist/pages/login-page.js +1 -1
  50. package/dist/store/d1-store.d.ts +24 -19
  51. package/dist/store/d1-store.d.ts.map +1 -1
  52. package/dist/store/d1-store.js +156 -83
  53. package/dist/store/d1-store.js.map +1 -1
  54. package/dist/types.d.ts +11 -0
  55. package/dist/types.d.ts.map +1 -1
  56. package/migrations/0008_add_timestamps.sql +10 -0
  57. package/migrations/0009_audit_by_columns.sql +33 -0
  58. package/package.json +3 -3
@@ -35,7 +35,9 @@ CREATE TABLE IF NOT EXISTS users (
35
35
  metadata TEXT DEFAULT '{}',
36
36
  address TEXT DEFAULT '{}',
37
37
  name TEXT,
38
- createdByAppPid TEXT
38
+ createdByAppPid TEXT,
39
+ createdBy TEXT,
40
+ updatedBy TEXT
39
41
  );
40
42
 
41
43
  CREATE TABLE IF NOT EXISTS connected_accounts (
@@ -49,7 +51,11 @@ CREATE TABLE IF NOT EXISTS connected_accounts (
49
51
  lastLoginIp TEXT,
50
52
  userInfo TEXT,
51
53
  extra TEXT,
52
- counter INTEGER DEFAULT 0
54
+ counter INTEGER DEFAULT 0,
55
+ createdAt TEXT,
56
+ updatedAt TEXT,
57
+ createdBy TEXT,
58
+ updatedBy TEXT
53
59
  );
54
60
 
55
61
  CREATE TABLE IF NOT EXISTS challenges (
@@ -70,7 +76,9 @@ CREATE TABLE IF NOT EXISTS invitations (
70
76
  useCount INTEGER DEFAULT 0,
71
77
  status TEXT DEFAULT 'active',
72
78
  createdAt TEXT DEFAULT (datetime('now')),
73
- updatedAt TEXT DEFAULT (datetime('now'))
79
+ updatedAt TEXT DEFAULT (datetime('now')),
80
+ createdBy TEXT,
81
+ updatedBy TEXT
74
82
  );
75
83
 
76
84
  CREATE TABLE IF NOT EXISTS audit_logs (
@@ -104,7 +112,9 @@ CREATE TABLE IF NOT EXISTS access_policies (
104
112
  reverse INTEGER DEFAULT 0,
105
113
  isProtected INTEGER DEFAULT 0,
106
114
  createdAt TEXT DEFAULT (datetime('now')),
107
- updatedAt TEXT DEFAULT (datetime('now'))
115
+ updatedAt TEXT DEFAULT (datetime('now')),
116
+ createdBy TEXT,
117
+ updatedBy TEXT
108
118
  );
109
119
 
110
120
  CREATE TABLE IF NOT EXISTS security_rules (
@@ -115,7 +125,9 @@ CREATE TABLE IF NOT EXISTS security_rules (
115
125
  enabled INTEGER DEFAULT 1,
116
126
  remark TEXT DEFAULT '',
117
127
  createdAt TEXT DEFAULT (datetime('now')),
118
- updatedAt TEXT DEFAULT (datetime('now'))
128
+ updatedAt TEXT DEFAULT (datetime('now')),
129
+ createdBy TEXT,
130
+ updatedBy TEXT
119
131
  );
120
132
 
121
133
  CREATE INDEX IF NOT EXISTS idx_security_rules_priority ON security_rules(priority);
@@ -130,7 +142,8 @@ CREATE TABLE IF NOT EXISTS access_keys (
130
142
  expireAt TEXT,
131
143
  lastUsedAt TEXT,
132
144
  createdAt TEXT DEFAULT (datetime('now')),
133
- updatedAt TEXT DEFAULT (datetime('now'))
145
+ updatedAt TEXT DEFAULT (datetime('now')),
146
+ updatedBy TEXT
134
147
  );
135
148
 
136
149
  CREATE INDEX IF NOT EXISTS idx_access_keys_createdBy ON access_keys(createdBy);
@@ -207,9 +220,9 @@ export class D1Store {
207
220
  * forwarded to the primary (per Cloudflare D1 Sessions API).
208
221
  *
209
222
  * `assumeMigrated` declares the schema is owned by the deploy-time
210
- * migrations (0001…0007 applied via `wrangler d1 migrations apply`) —
223
+ * migrations (0001…0008 applied via `wrangler d1 migrations apply`) —
211
224
  * {@link migrate} then becomes a no-op instead of replaying the full DDL
212
- * (CREATE batches + 8 always-failing ALTERs + seed INSERTs ≈ several
225
+ * (CREATE batches + 14 always-failing ALTERs + seed INSERTs ≈ several
213
226
  * serial D1 round-trips) on every fresh isolate's first handler build.
214
227
  * ONLY safe on migrated databases: with it set, a missing schema fails
215
228
  * loudly at the first real query instead of being silently created.
@@ -249,6 +262,30 @@ export class D1Store {
249
262
  this.db.prepare("ALTER TABLE audit_logs ADD COLUMN instance_did TEXT DEFAULT NULL").run(),
250
263
  this.db.prepare("ALTER TABLE access_policies ADD COLUMN instance_did TEXT DEFAULT NULL").run(),
251
264
  this.db.prepare("ALTER TABLE invitations ADD COLUMN instance_did TEXT DEFAULT NULL").run(),
265
+ this.db.prepare("ALTER TABLE connected_accounts ADD COLUMN createdAt TEXT").run(),
266
+ this.db.prepare("ALTER TABLE connected_accounts ADD COLUMN updatedAt TEXT").run(),
267
+ this.db.prepare("ALTER TABLE memberships ADD COLUMN created_at TEXT").run(),
268
+ this.db.prepare("ALTER TABLE memberships ADD COLUMN updated_at TEXT").run(),
269
+ this.db.prepare("ALTER TABLE settings ADD COLUMN created_at TEXT").run(),
270
+ this.db.prepare("ALTER TABLE instance_hosts ADD COLUMN updated_at TEXT").run(),
271
+ // Phase 3: createdBy/updatedBy audit columns
272
+ this.db.prepare("ALTER TABLE users ADD COLUMN createdBy TEXT").run(),
273
+ this.db.prepare("ALTER TABLE users ADD COLUMN updatedBy TEXT").run(),
274
+ this.db.prepare("ALTER TABLE connected_accounts ADD COLUMN createdBy TEXT").run(),
275
+ this.db.prepare("ALTER TABLE connected_accounts ADD COLUMN updatedBy TEXT").run(),
276
+ this.db.prepare("ALTER TABLE invitations ADD COLUMN createdBy TEXT").run(),
277
+ this.db.prepare("ALTER TABLE invitations ADD COLUMN updatedBy TEXT").run(),
278
+ this.db.prepare("ALTER TABLE access_policies ADD COLUMN createdBy TEXT").run(),
279
+ this.db.prepare("ALTER TABLE access_policies ADD COLUMN updatedBy TEXT").run(),
280
+ this.db.prepare("ALTER TABLE security_rules ADD COLUMN createdBy TEXT").run(),
281
+ this.db.prepare("ALTER TABLE security_rules ADD COLUMN updatedBy TEXT").run(),
282
+ this.db.prepare("ALTER TABLE access_keys ADD COLUMN updatedBy TEXT").run(),
283
+ this.db.prepare("ALTER TABLE memberships ADD COLUMN created_by TEXT").run(),
284
+ this.db.prepare("ALTER TABLE memberships ADD COLUMN updated_by TEXT").run(),
285
+ this.db.prepare("ALTER TABLE settings ADD COLUMN created_by TEXT").run(),
286
+ this.db.prepare("ALTER TABLE settings ADD COLUMN updated_by TEXT").run(),
287
+ this.db.prepare("ALTER TABLE instance_hosts ADD COLUMN created_by TEXT").run(),
288
+ this.db.prepare("ALTER TABLE instance_hosts ADD COLUMN updated_by TEXT").run(),
252
289
  ]);
253
290
  // Batch 2: additional tables + indexes (all CREATE IF NOT EXISTS, safe in one batch)
254
291
  await this.db.batch([
@@ -259,6 +296,10 @@ export class D1Store {
259
296
  role TEXT NOT NULL DEFAULT 'guest',
260
297
  invited_by TEXT,
261
298
  joined_at TEXT NOT NULL DEFAULT (datetime('now')),
299
+ created_at TEXT,
300
+ updated_at TEXT,
301
+ created_by TEXT,
302
+ updated_by TEXT,
262
303
  PRIMARY KEY (user_did, instance_did)
263
304
  )
264
305
  `),
@@ -268,6 +309,9 @@ export class D1Store {
268
309
  key TEXT NOT NULL,
269
310
  value TEXT,
270
311
  updated_at TEXT DEFAULT (datetime('now')),
312
+ created_at TEXT,
313
+ created_by TEXT,
314
+ updated_by TEXT,
271
315
  PRIMARY KEY (instance_did, key)
272
316
  )`),
273
317
  this.db.prepare(`CREATE TABLE IF NOT EXISTS verify_codes (
@@ -302,7 +346,10 @@ export class D1Store {
302
346
  this.db.prepare(`CREATE TABLE IF NOT EXISTS instance_hosts (
303
347
  host TEXT PRIMARY KEY,
304
348
  instance_did TEXT NOT NULL,
305
- created_at TEXT DEFAULT (datetime('now'))
349
+ created_at TEXT DEFAULT (datetime('now')),
350
+ updated_at TEXT,
351
+ created_by TEXT,
352
+ updated_by TEXT
306
353
  )`),
307
354
  this.db.prepare("CREATE INDEX IF NOT EXISTS idx_instance_hosts_instance ON instance_hosts(instance_did)"),
308
355
  ]);
@@ -368,10 +415,13 @@ export class D1Store {
368
415
  // ─── User operations ──────────────────────────────────────────────────
369
416
  async createUser(params) {
370
417
  const now = new Date().toISOString();
418
+ // Self-serve registration: default actor = subject's own DID (plan §二);
419
+ // admin代建 passes createdBy = caller.did explicitly.
420
+ const actor = params.createdBy ?? params.did;
371
421
  await this.db
372
- .prepare(`INSERT INTO users (did, pk, fullName, email, avatar, sourceProvider, emailVerified, firstLoginAt, lastLoginAt, lastLoginIp, sourceDomain, lastLoginDomain, createdAt, updatedAt)
373
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
374
- .bind(params.did, params.pk, params.fullName ?? null, params.email ?? null, params.avatar ?? null, params.sourceProvider, params.emailVerified ?? 0, now, now, params.ip ?? null, params.domain ?? null, params.domain ?? null, now, now)
422
+ .prepare(`INSERT INTO users (did, pk, fullName, email, avatar, sourceProvider, emailVerified, firstLoginAt, lastLoginAt, lastLoginIp, sourceDomain, lastLoginDomain, createdAt, updatedAt, createdBy, updatedBy)
423
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
424
+ .bind(params.did, params.pk, params.fullName ?? null, params.email ?? null, params.avatar ?? null, params.sourceProvider, params.emailVerified ?? 0, now, now, params.ip ?? null, params.domain ?? null, params.domain ?? null, now, now, actor, actor)
375
425
  .run();
376
426
  }
377
427
  async getUserByDid(did) {
@@ -465,19 +515,19 @@ export class D1Store {
465
515
  .first();
466
516
  return row ?? null;
467
517
  }
468
- async updateUserRole(did, role) {
518
+ async updateUserRole(did, role, actor) {
469
519
  await this.db
470
- .prepare("UPDATE users SET role = ?, updatedAt = ? WHERE did = ?")
471
- .bind(role, new Date().toISOString(), did)
520
+ .prepare("UPDATE users SET role = ?, updatedAt = ?, updatedBy = ? WHERE did = ?")
521
+ .bind(role, new Date().toISOString(), actor, did)
472
522
  .run();
473
523
  }
474
- async updateUserApproval(did, approved) {
524
+ async updateUserApproval(did, approved, actor) {
475
525
  await this.db
476
- .prepare("UPDATE users SET approved = ?, updatedAt = ? WHERE did = ?")
477
- .bind(approved ? 1 : 0, new Date().toISOString(), did)
526
+ .prepare("UPDATE users SET approved = ?, updatedAt = ?, updatedBy = ? WHERE did = ?")
527
+ .bind(approved ? 1 : 0, new Date().toISOString(), actor, did)
478
528
  .run();
479
529
  }
480
- async updateUserProfile(did, fields) {
530
+ async updateUserProfile(did, fields, actor) {
481
531
  const sets = [];
482
532
  const params = [];
483
533
  if (fields.fullName !== undefined) {
@@ -500,6 +550,8 @@ export class D1Store {
500
550
  return;
501
551
  sets.push("updatedAt = ?");
502
552
  params.push(new Date().toISOString());
553
+ sets.push("updatedBy = ?");
554
+ params.push(actor);
503
555
  params.push(did);
504
556
  await this.db
505
557
  .prepare(`UPDATE users SET ${sets.join(", ")} WHERE did = ?`)
@@ -513,25 +565,25 @@ export class D1Store {
513
565
  this.db.prepare("DELETE FROM users WHERE did = ?").bind(did),
514
566
  ]);
515
567
  }
516
- async setUserInviter(did, inviterDid) {
568
+ async setUserInviter(did, inviterDid, actor) {
517
569
  await this.db
518
- .prepare("UPDATE users SET inviter = ?, updatedAt = ? WHERE did = ? AND inviter IS NULL")
519
- .bind(inviterDid, new Date().toISOString(), did)
570
+ .prepare("UPDATE users SET inviter = ?, updatedAt = ?, updatedBy = ? WHERE did = ? AND inviter IS NULL")
571
+ .bind(inviterDid, new Date().toISOString(), actor, did)
520
572
  .run();
521
573
  }
522
574
  /**
523
575
  * Atomically transfer ownership: set target to owner, caller to admin.
524
576
  * Uses D1 batch for atomicity.
525
577
  */
526
- async transferOwnership(currentOwnerDid, newOwnerDid) {
578
+ async transferOwnership(currentOwnerDid, newOwnerDid, actor) {
527
579
  const now = new Date().toISOString();
528
580
  await this.db.batch([
529
581
  this.db
530
- .prepare("UPDATE users SET role = 'owner', updatedAt = ? WHERE did = ?")
531
- .bind(now, newOwnerDid),
582
+ .prepare("UPDATE users SET role = 'owner', updatedAt = ?, updatedBy = ? WHERE did = ?")
583
+ .bind(now, actor, newOwnerDid),
532
584
  this.db
533
- .prepare("UPDATE users SET role = 'admin', updatedAt = ? WHERE did = ?")
534
- .bind(now, currentOwnerDid),
585
+ .prepare("UPDATE users SET role = 'admin', updatedAt = ?, updatedBy = ? WHERE did = ?")
586
+ .bind(now, actor, currentOwnerDid),
535
587
  ]);
536
588
  }
537
589
  // ─── Team: Invitations ────────────────────────────────────────────────
@@ -541,10 +593,12 @@ export class D1Store {
541
593
  const expireHours = input.expireHours ?? 168; // 7 days
542
594
  const maxUses = input.maxUses ?? 1;
543
595
  const expireAt = new Date(Date.now() + expireHours * 60 * 60 * 1000).toISOString();
596
+ // Default actor = inviter (the invitation's creator) per plan §五A.
597
+ const actor = input.actor ?? input.inviterDid;
544
598
  await this.db
545
- .prepare(`INSERT INTO invitations (id, teamDid, role, remark, inviterDid, expireAt, maxUses, useCount, status, instance_did, createdAt, updatedAt)
546
- VALUES (?, ?, ?, ?, ?, ?, ?, 0, 'active', ?, ?, ?)`)
547
- .bind(id, input.teamDid, input.role, input.remark ?? "", input.inviterDid, expireAt, maxUses, input.instanceDid ?? null, now, now)
599
+ .prepare(`INSERT INTO invitations (id, teamDid, role, remark, inviterDid, expireAt, maxUses, useCount, status, instance_did, createdAt, updatedAt, createdBy, updatedBy)
600
+ VALUES (?, ?, ?, ?, ?, ?, ?, 0, 'active', ?, ?, ?, ?, ?)`)
601
+ .bind(id, input.teamDid, input.role, input.remark ?? "", input.inviterDid, expireAt, maxUses, input.instanceDid ?? null, now, now, actor, actor)
548
602
  .run();
549
603
  return {
550
604
  id,
@@ -559,6 +613,8 @@ export class D1Store {
559
613
  createdAt: now,
560
614
  updatedAt: now,
561
615
  instance_did: input.instanceDid ?? null,
616
+ createdBy: actor,
617
+ updatedBy: actor,
562
618
  };
563
619
  }
564
620
  async getInvitation(id) {
@@ -606,10 +662,10 @@ export class D1Store {
606
662
  .run();
607
663
  return (result.meta?.changes ?? 0) > 0;
608
664
  }
609
- async updateInvitationStatus(id, status) {
665
+ async updateInvitationStatus(id, status, actor) {
610
666
  await this.db
611
- .prepare("UPDATE invitations SET status = ?, updatedAt = ? WHERE id = ?")
612
- .bind(status, new Date().toISOString(), id)
667
+ .prepare("UPDATE invitations SET status = ?, updatedAt = ?, updatedBy = ? WHERE id = ?")
668
+ .bind(status, new Date().toISOString(), actor, id)
613
669
  .run();
614
670
  }
615
671
  async deleteInvitation(id) {
@@ -703,20 +759,28 @@ export class D1Store {
703
759
  // ─── Connected accounts operations ────────────────────────────────────
704
760
  async createConnectedAccount(params) {
705
761
  const now = new Date().toISOString();
762
+ // Self-serve account link: default actor = the user's own DID (plan §二).
763
+ const actor = params.actor ?? params.userDid;
706
764
  await this.db
707
- .prepare(`INSERT INTO connected_accounts (did, pk, userDid, provider, id, extra, userInfo, firstLoginAt, lastLoginAt, lastLoginIp)
708
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
709
- .bind(params.did, params.pk, params.userDid, params.provider, params.id, params.extra, params.userInfo, now, now, params.ip ?? null)
765
+ .prepare(`INSERT INTO connected_accounts (did, pk, userDid, provider, id, extra, userInfo, firstLoginAt, lastLoginAt, lastLoginIp, createdAt, updatedAt, createdBy, updatedBy)
766
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
767
+ .bind(params.did, params.pk, params.userDid, params.provider, params.id, params.extra, params.userInfo, now, now, params.ip ?? null, now, now, actor, actor)
710
768
  .run();
711
769
  }
712
770
  async upsertConnectedAccount(account) {
771
+ // Self-serve account link: default actor = the user's own DID (plan §二).
772
+ const now = new Date().toISOString();
773
+ const actor = account.actor ?? account.userDid;
713
774
  await this.db
714
- .prepare(`INSERT INTO connected_accounts (did, pk, userDid, provider, id, userInfo, firstLoginAt, lastLoginAt)
715
- VALUES (?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
775
+ .prepare(
776
+ // Bind all timestamps as ISO `?` params (consistent with createConnectedAccount;
777
+ // avoids mixing `datetime('now')` literals with `?` placeholders).
778
+ `INSERT INTO connected_accounts (did, pk, userDid, provider, id, userInfo, firstLoginAt, lastLoginAt, createdAt, updatedAt, createdBy, updatedBy)
779
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
716
780
  ON CONFLICT (did) DO UPDATE SET
717
- pk = excluded.pk, lastLoginAt = datetime('now'),
781
+ pk = excluded.pk, lastLoginAt = excluded.lastLoginAt, updatedAt = excluded.updatedAt,
718
782
  userInfo = COALESCE(excluded.userInfo, connected_accounts.userInfo)`)
719
- .bind(account.did, account.pk, account.userDid, account.provider, account.id, account.userInfo || null)
783
+ .bind(account.did, account.pk, account.userDid, account.provider, account.id, account.userInfo || null, now, now, now, now, actor, actor)
720
784
  .run();
721
785
  }
722
786
  async getConnectedAccountById(credentialId) {
@@ -732,9 +796,10 @@ export class D1Store {
732
796
  .first();
733
797
  }
734
798
  async updateCounter(did, counter) {
799
+ const now = new Date().toISOString();
735
800
  await this.db
736
- .prepare("UPDATE connected_accounts SET counter = ?, lastLoginAt = ? WHERE did = ?")
737
- .bind(counter, new Date().toISOString(), did)
801
+ .prepare("UPDATE connected_accounts SET counter = ?, lastLoginAt = ?, updatedAt = ? WHERE did = ?")
802
+ .bind(counter, now, now, did)
738
803
  .run();
739
804
  }
740
805
  async getConnectedAccountsByUserDid(userDid) {
@@ -774,10 +839,10 @@ export class D1Store {
774
839
  .bind(new Date().toISOString(), did)
775
840
  .run();
776
841
  }
777
- async updateConnectedAccountName(did, name) {
842
+ async updateConnectedAccountName(did, name, actor) {
778
843
  await this.db
779
- .prepare("UPDATE connected_accounts SET userInfo = ? WHERE did = ?")
780
- .bind(JSON.stringify({ name }), did)
844
+ .prepare("UPDATE connected_accounts SET userInfo = ?, updatedAt = ?, updatedBy = ? WHERE did = ?")
845
+ .bind(JSON.stringify({ name }), new Date().toISOString(), actor, did)
781
846
  .run();
782
847
  }
783
848
  async deleteConnectedAccount(did) {
@@ -787,9 +852,9 @@ export class D1Store {
787
852
  async createAccessKey(input) {
788
853
  const now = new Date().toISOString();
789
854
  await this.db
790
- .prepare(`INSERT INTO access_keys (accessKeyId, accessKeyPublic, role, remark, createdBy, expireAt, instance_did, createdAt, updatedAt)
791
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
792
- .bind(input.accessKeyId, input.accessKeyPublic, input.role, input.remark ?? "", input.createdBy, input.expireAt ?? null, input.instanceDid ?? null, now, now)
855
+ .prepare(`INSERT INTO access_keys (accessKeyId, accessKeyPublic, role, remark, createdBy, expireAt, instance_did, createdAt, updatedAt, updatedBy)
856
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
857
+ .bind(input.accessKeyId, input.accessKeyPublic, input.role, input.remark ?? "", input.createdBy, input.expireAt ?? null, input.instanceDid ?? null, now, now, input.createdBy)
793
858
  .run();
794
859
  return {
795
860
  accessKeyId: input.accessKeyId,
@@ -802,6 +867,7 @@ export class D1Store {
802
867
  createdAt: now,
803
868
  updatedAt: now,
804
869
  instance_did: input.instanceDid ?? null,
870
+ updatedBy: input.createdBy,
805
871
  };
806
872
  }
807
873
  async getAccessKeyById(accessKeyId) {
@@ -864,7 +930,7 @@ export class D1Store {
864
930
  total,
865
931
  };
866
932
  }
867
- async updateAccessKey(accessKeyId, input) {
933
+ async updateAccessKey(accessKeyId, input, actor) {
868
934
  const sets = [];
869
935
  const params = [];
870
936
  if (input.remark !== undefined) {
@@ -878,6 +944,8 @@ export class D1Store {
878
944
  if (sets.length > 0) {
879
945
  sets.push("updatedAt = ?");
880
946
  params.push(new Date().toISOString());
947
+ sets.push("updatedBy = ?");
948
+ params.push(actor);
881
949
  params.push(accessKeyId);
882
950
  await this.db
883
951
  .prepare(`UPDATE access_keys SET ${sets.join(", ")} WHERE accessKeyId = ?`)
@@ -929,16 +997,16 @@ export class D1Store {
929
997
  ];
930
998
  const now = new Date().toISOString();
931
999
  await this.db.batch(policies.map((p) => this.db
932
- .prepare(`INSERT OR IGNORE INTO access_policies (id, name, description, roles, reverse, isProtected, createdAt, updatedAt)
933
- VALUES (?, ?, ?, ?, ?, 1, ?, ?)`)
1000
+ .prepare(`INSERT OR IGNORE INTO access_policies (id, name, description, roles, reverse, isProtected, createdAt, updatedAt, createdBy, updatedBy)
1001
+ VALUES (?, ?, ?, ?, ?, 1, ?, ?, 'system', 'system')`)
934
1002
  .bind(p.id, p.name, p.description, p.roles, p.reverse, now, now)));
935
1003
  invalidateRulesCache();
936
1004
  }
937
1005
  async ensureDefaultRule() {
938
1006
  const now = new Date().toISOString();
939
1007
  await this.db
940
- .prepare(`INSERT OR IGNORE INTO security_rules (id, pathPattern, priority, accessPolicyId, enabled, remark, createdAt, updatedAt)
941
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
1008
+ .prepare(`INSERT OR IGNORE INTO security_rules (id, pathPattern, priority, accessPolicyId, enabled, remark, createdAt, updatedAt, createdBy, updatedBy)
1009
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'system', 'system')`)
942
1010
  .bind("default", "*", -1, "public", 1, "Default fallback rule", now, now)
943
1011
  .run();
944
1012
  invalidateRulesCache();
@@ -1011,19 +1079,19 @@ export class D1Store {
1011
1079
  instanceDid: p.instance_did ?? null,
1012
1080
  };
1013
1081
  }
1014
- async createAccessPolicy(input) {
1082
+ async createAccessPolicy(input, actor) {
1015
1083
  const id = crypto.randomUUID();
1016
1084
  const now = new Date().toISOString();
1017
1085
  const { roles: dbRoles, reverse } = await import("../access/access-policy.js").then((m) => m.accessTypeToDb(input.accessType, input.roles));
1018
1086
  await this.db
1019
- .prepare(`INSERT INTO access_policies (id, name, description, roles, reverse, isProtected, instance_did, createdAt, updatedAt)
1020
- VALUES (?, ?, ?, ?, ?, 0, ?, ?, ?)`)
1021
- .bind(id, input.name, input.description ?? "", dbRoles, reverse, input.instanceDid ?? null, now, now)
1087
+ .prepare(`INSERT INTO access_policies (id, name, description, roles, reverse, isProtected, instance_did, createdAt, updatedAt, createdBy, updatedBy)
1088
+ VALUES (?, ?, ?, ?, ?, 0, ?, ?, ?, ?, ?)`)
1089
+ .bind(id, input.name, input.description ?? "", dbRoles, reverse, input.instanceDid ?? null, now, now, actor, actor)
1022
1090
  .run();
1023
1091
  invalidateRulesCache();
1024
1092
  return (await this.getAccessPolicy(id));
1025
1093
  }
1026
- async updateAccessPolicy(id, input) {
1094
+ async updateAccessPolicy(id, input, actor) {
1027
1095
  const now = new Date().toISOString();
1028
1096
  const sets = [];
1029
1097
  const params = [];
@@ -1045,6 +1113,8 @@ export class D1Store {
1045
1113
  if (sets.length > 0) {
1046
1114
  sets.push("updatedAt = ?");
1047
1115
  params.push(now);
1116
+ sets.push("updatedBy = ?");
1117
+ params.push(actor);
1048
1118
  params.push(id);
1049
1119
  await this.db
1050
1120
  .prepare(`UPDATE access_policies SET ${sets.join(", ")} WHERE id = ?`)
@@ -1119,18 +1189,18 @@ export class D1Store {
1119
1189
  instanceDid: r.instance_did ?? null,
1120
1190
  };
1121
1191
  }
1122
- async createSecurityRule(input) {
1192
+ async createSecurityRule(input, actor) {
1123
1193
  const id = crypto.randomUUID();
1124
1194
  const now = new Date().toISOString();
1125
1195
  await this.db
1126
- .prepare(`INSERT INTO security_rules (id, pathPattern, priority, accessPolicyId, enabled, remark, instance_did, createdAt, updatedAt)
1127
- VALUES (?, ?, ?, ?, 1, ?, ?, ?, ?)`)
1128
- .bind(id, input.pathPattern, input.priority ?? 0, input.accessPolicyId, input.remark ?? "", input.instanceDid ?? null, now, now)
1196
+ .prepare(`INSERT INTO security_rules (id, pathPattern, priority, accessPolicyId, enabled, remark, instance_did, createdAt, updatedAt, createdBy, updatedBy)
1197
+ VALUES (?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?)`)
1198
+ .bind(id, input.pathPattern, input.priority ?? 0, input.accessPolicyId, input.remark ?? "", input.instanceDid ?? null, now, now, actor, actor)
1129
1199
  .run();
1130
1200
  invalidateRulesCache();
1131
1201
  return (await this.getSecurityRule(id));
1132
1202
  }
1133
- async updateSecurityRule(id, input) {
1203
+ async updateSecurityRule(id, input, actor) {
1134
1204
  const now = new Date().toISOString();
1135
1205
  const sets = [];
1136
1206
  const params = [];
@@ -1157,6 +1227,8 @@ export class D1Store {
1157
1227
  if (sets.length > 0) {
1158
1228
  sets.push("updatedAt = ?");
1159
1229
  params.push(now);
1230
+ sets.push("updatedBy = ?");
1231
+ params.push(actor);
1160
1232
  params.push(id);
1161
1233
  await this.db
1162
1234
  .prepare(`UPDATE security_rules SET ${sets.join(", ")} WHERE id = ?`)
@@ -1171,10 +1243,11 @@ export class D1Store {
1171
1243
  invalidateRulesCache();
1172
1244
  }
1173
1245
  // ─── Membership operations ──────────────────────────────────────────────
1174
- async createMembership(userDid, instanceDid, role, invitedBy) {
1246
+ async createMembership(userDid, instanceDid, role, invitedBy, actor) {
1247
+ const now = new Date().toISOString();
1175
1248
  await this.db
1176
- .prepare(`INSERT OR IGNORE INTO memberships (user_did, instance_did, role, invited_by) VALUES (?, ?, ?, ?)`)
1177
- .bind(userDid, instanceDid, role, invitedBy ?? null)
1249
+ .prepare(`INSERT OR IGNORE INTO memberships (user_did, instance_did, role, invited_by, created_at, updated_at, created_by, updated_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
1250
+ .bind(userDid, instanceDid, role, invitedBy ?? null, now, now, actor, actor)
1178
1251
  .run();
1179
1252
  invalidateMembership(userDid, instanceDid);
1180
1253
  }
@@ -1249,10 +1322,10 @@ export class D1Store {
1249
1322
  .all();
1250
1323
  return rows.results ?? [];
1251
1324
  }
1252
- async updateMembershipRole(userDid, instanceDid, role) {
1325
+ async updateMembershipRole(userDid, instanceDid, role, actor) {
1253
1326
  await this.db
1254
- .prepare("UPDATE memberships SET role = ? WHERE user_did = ? AND instance_did = ?")
1255
- .bind(role, userDid, instanceDid)
1327
+ .prepare("UPDATE memberships SET role = ?, updated_at = ?, updated_by = ? WHERE user_did = ? AND instance_did = ?")
1328
+ .bind(role, new Date().toISOString(), actor, userDid, instanceDid)
1256
1329
  .run();
1257
1330
  invalidateMembership(userDid, instanceDid);
1258
1331
  }
@@ -1275,13 +1348,13 @@ export class D1Store {
1275
1348
  .first();
1276
1349
  return row?.value ?? null;
1277
1350
  }
1278
- async setSetting(instanceDid, key, value) {
1351
+ async setSetting(instanceDid, key, value, actor) {
1279
1352
  try {
1280
1353
  await this.db
1281
- .prepare(`INSERT INTO settings (instance_did, key, value, updated_at)
1282
- VALUES (?, ?, ?, datetime('now'))
1283
- ON CONFLICT(instance_did, key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`)
1284
- .bind(instanceDid, key, value)
1354
+ .prepare(`INSERT INTO settings (instance_did, key, value, updated_at, created_at, created_by, updated_by)
1355
+ VALUES (?, ?, ?, datetime('now'), datetime('now'), ?, ?)
1356
+ ON CONFLICT(instance_did, key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at, updated_by = excluded.updated_by`)
1357
+ .bind(instanceDid, key, value, actor, actor)
1285
1358
  .run();
1286
1359
  }
1287
1360
  catch (err) {
@@ -1289,8 +1362,8 @@ export class D1Store {
1289
1362
  const msg = err instanceof Error ? err.message : String(err);
1290
1363
  if (msg.includes("UNIQUE constraint")) {
1291
1364
  await this.db
1292
- .prepare(`UPDATE settings SET value = ?, updated_at = datetime('now') WHERE instance_did = ? AND key = ?`)
1293
- .bind(value, instanceDid, key)
1365
+ .prepare(`UPDATE settings SET value = ?, updated_at = datetime('now'), updated_by = ? WHERE instance_did = ? AND key = ?`)
1366
+ .bind(value, actor, instanceDid, key)
1294
1367
  .run();
1295
1368
  }
1296
1369
  else {
@@ -1371,13 +1444,13 @@ export class D1Store {
1371
1444
  /** Bind a host to an instance. Idempotent; returns a conflict result if the
1372
1445
  * host already belongs to a different instance. Uses a dedicated INSERT
1373
1446
  * (not setSetting) so the host PK enforces one-host-one-instance. */
1374
- async bindInstanceHost(instanceDid, host) {
1447
+ async bindInstanceHost(instanceDid, host, actor) {
1375
1448
  const h = this.normalizeHost(host);
1376
1449
  if (!h)
1377
1450
  return { ok: false, reason: "invalid" };
1378
1451
  const res = await this.db
1379
- .prepare("INSERT INTO instance_hosts (host, instance_did) VALUES (?, ?) ON CONFLICT(host) DO NOTHING")
1380
- .bind(h, instanceDid)
1452
+ .prepare("INSERT INTO instance_hosts (host, instance_did, updated_at, created_by, updated_by) VALUES (?, ?, datetime('now'), ?, ?) ON CONFLICT(host) DO NOTHING")
1453
+ .bind(h, instanceDid, actor, actor)
1381
1454
  .run();
1382
1455
  if ((res.meta?.changes ?? 0) > 0) {
1383
1456
  invalidateHostCache();
@@ -1524,15 +1597,15 @@ export class D1Store {
1524
1597
  },
1525
1598
  ];
1526
1599
  await this.db.batch(policies.map((p) => this.db
1527
- .prepare(`INSERT OR IGNORE INTO access_policies (id, name, description, roles, reverse, isProtected, instance_did, createdAt, updatedAt)
1528
- VALUES (?, ?, ?, ?, ?, 1, ?, ?, ?)`)
1600
+ .prepare(`INSERT OR IGNORE INTO access_policies (id, name, description, roles, reverse, isProtected, instance_did, createdAt, updatedAt, createdBy, updatedBy)
1601
+ VALUES (?, ?, ?, ?, ?, 1, ?, ?, ?, 'system', 'system')`)
1529
1602
  .bind(p.id, p.name, p.description, p.roles, p.reverse, instanceDid, now, now)));
1530
1603
  // Seed instance default rule referencing the instance-scoped public policy
1531
1604
  const ruleId = `default-${instanceDid}`;
1532
1605
  await this.db
1533
1606
  .prepare(`INSERT OR IGNORE INTO security_rules
1534
- (id, pathPattern, priority, accessPolicyId, enabled, remark, instance_did, createdAt, updatedAt)
1535
- VALUES (?, '*', 0, ?, 1, 'Instance default: public access', ?, ?, ?)`)
1607
+ (id, pathPattern, priority, accessPolicyId, enabled, remark, instance_did, createdAt, updatedAt, createdBy, updatedBy)
1608
+ VALUES (?, '*', 0, ?, 1, 'Instance default: public access', ?, ?, ?, 'system', 'system')`)
1536
1609
  .bind(ruleId, `public-${instanceDid}`, instanceDid, now, now)
1537
1610
  .run();
1538
1611
  invalidateRulesCache();