@atproto/pds 0.5.5 → 0.5.6

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 (147) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/account-manager/account-manager.d.ts +51 -13
  3. package/dist/account-manager/account-manager.d.ts.map +1 -1
  4. package/dist/account-manager/account-manager.js +86 -26
  5. package/dist/account-manager/account-manager.js.map +1 -1
  6. package/dist/account-manager/db/migrations/005-oauth-account-management.d.ts.map +1 -1
  7. package/dist/account-manager/db/migrations/005-oauth-account-management.js +3 -4
  8. package/dist/account-manager/db/migrations/005-oauth-account-management.js.map +1 -1
  9. package/dist/account-manager/db/schema/authorization-request.d.ts +2 -2
  10. package/dist/account-manager/db/schema/authorization-request.d.ts.map +1 -1
  11. package/dist/account-manager/db/schema/authorization-request.js.map +1 -1
  12. package/dist/account-manager/db/schema/authorized-client.d.ts +2 -2
  13. package/dist/account-manager/db/schema/authorized-client.d.ts.map +1 -1
  14. package/dist/account-manager/db/schema/authorized-client.js.map +1 -1
  15. package/dist/account-manager/db/schema/token.d.ts +2 -2
  16. package/dist/account-manager/db/schema/token.d.ts.map +1 -1
  17. package/dist/account-manager/db/schema/token.js.map +1 -1
  18. package/dist/account-manager/helpers/account-device.d.ts +23 -196
  19. package/dist/account-manager/helpers/account-device.d.ts.map +1 -1
  20. package/dist/account-manager/helpers/account-device.js +3 -3
  21. package/dist/account-manager/helpers/account-device.js.map +1 -1
  22. package/dist/account-manager/helpers/account.d.ts +14 -4
  23. package/dist/account-manager/helpers/account.d.ts.map +1 -1
  24. package/dist/account-manager/helpers/account.js +17 -11
  25. package/dist/account-manager/helpers/account.js.map +1 -1
  26. package/dist/account-manager/helpers/auth.js +1 -1
  27. package/dist/account-manager/helpers/auth.js.map +1 -1
  28. package/dist/account-manager/helpers/authorization-request.d.ts +83 -5
  29. package/dist/account-manager/helpers/authorization-request.d.ts.map +1 -1
  30. package/dist/account-manager/helpers/authorization-request.js +8 -8
  31. package/dist/account-manager/helpers/authorization-request.js.map +1 -1
  32. package/dist/account-manager/helpers/authorized-client.d.ts +5 -4
  33. package/dist/account-manager/helpers/authorized-client.d.ts.map +1 -1
  34. package/dist/account-manager/helpers/authorized-client.js +3 -0
  35. package/dist/account-manager/helpers/authorized-client.js.map +1 -1
  36. package/dist/account-manager/helpers/device.d.ts +9 -3
  37. package/dist/account-manager/helpers/device.d.ts.map +1 -1
  38. package/dist/account-manager/helpers/device.js +4 -4
  39. package/dist/account-manager/helpers/device.js.map +1 -1
  40. package/dist/account-manager/helpers/invite.d.ts +35 -2
  41. package/dist/account-manager/helpers/invite.d.ts.map +1 -1
  42. package/dist/account-manager/helpers/lexicon.d.ts.map +1 -1
  43. package/dist/account-manager/helpers/lexicon.js +6 -0
  44. package/dist/account-manager/helpers/lexicon.js.map +1 -1
  45. package/dist/account-manager/helpers/password.d.ts +1 -0
  46. package/dist/account-manager/helpers/password.d.ts.map +1 -1
  47. package/dist/account-manager/helpers/password.js +3 -0
  48. package/dist/account-manager/helpers/password.js.map +1 -1
  49. package/dist/account-manager/helpers/token.d.ts +72 -1031
  50. package/dist/account-manager/helpers/token.d.ts.map +1 -1
  51. package/dist/account-manager/helpers/token.js +13 -10
  52. package/dist/account-manager/helpers/token.js.map +1 -1
  53. package/dist/account-manager/helpers/used-refresh-token.d.ts +6 -2
  54. package/dist/account-manager/helpers/used-refresh-token.d.ts.map +1 -1
  55. package/dist/account-manager/oauth-store.d.ts +17 -13
  56. package/dist/account-manager/oauth-store.d.ts.map +1 -1
  57. package/dist/account-manager/oauth-store.js +89 -39
  58. package/dist/account-manager/oauth-store.js.map +1 -1
  59. package/dist/actor-store/blob/reader.d.ts.map +1 -1
  60. package/dist/actor-store/blob/reader.js +2 -3
  61. package/dist/actor-store/blob/reader.js.map +1 -1
  62. package/dist/actor-store/record/reader.js +3 -3
  63. package/dist/actor-store/record/reader.js.map +1 -1
  64. package/dist/actor-store/repo/sql-repo-reader.d.ts +5 -1
  65. package/dist/actor-store/repo/sql-repo-reader.d.ts.map +1 -1
  66. package/dist/actor-store/repo/sql-repo-reader.js.map +1 -1
  67. package/dist/api/com/atproto/admin/updateSubjectStatus.d.ts.map +1 -1
  68. package/dist/api/com/atproto/admin/updateSubjectStatus.js +12 -4
  69. package/dist/api/com/atproto/admin/updateSubjectStatus.js.map +1 -1
  70. package/dist/api/com/atproto/server/activateAccount.d.ts.map +1 -1
  71. package/dist/api/com/atproto/server/activateAccount.js +25 -31
  72. package/dist/api/com/atproto/server/activateAccount.js.map +1 -1
  73. package/dist/api/com/atproto/server/deactivateAccount.d.ts.map +1 -1
  74. package/dist/api/com/atproto/server/deactivateAccount.js +3 -4
  75. package/dist/api/com/atproto/server/deactivateAccount.js.map +1 -1
  76. package/dist/api/com/atproto/server/deleteAccount.d.ts.map +1 -1
  77. package/dist/api/com/atproto/server/deleteAccount.js +51 -37
  78. package/dist/api/com/atproto/server/deleteAccount.js.map +1 -1
  79. package/dist/api/com/atproto/server/util.d.ts +25 -6
  80. package/dist/api/com/atproto/server/util.d.ts.map +1 -1
  81. package/dist/api/com/atproto/server/util.js.map +1 -1
  82. package/dist/config/config.d.ts +2 -1
  83. package/dist/config/config.d.ts.map +1 -1
  84. package/dist/config/config.js +4 -1
  85. package/dist/config/config.js.map +1 -1
  86. package/dist/context.d.ts.map +1 -1
  87. package/dist/context.js +1 -1
  88. package/dist/context.js.map +1 -1
  89. package/dist/db/migrator.d.ts +4 -3
  90. package/dist/db/migrator.d.ts.map +1 -1
  91. package/dist/db/migrator.js +1 -1
  92. package/dist/db/migrator.js.map +1 -1
  93. package/dist/db/pagination.d.ts +2 -1
  94. package/dist/db/pagination.d.ts.map +1 -1
  95. package/dist/db/pagination.js +2 -2
  96. package/dist/db/pagination.js.map +1 -1
  97. package/dist/db/util.d.ts +3 -3
  98. package/dist/db/util.d.ts.map +1 -1
  99. package/dist/db/util.js.map +1 -1
  100. package/dist/mailer/index.d.ts +2 -0
  101. package/dist/mailer/index.d.ts.map +1 -1
  102. package/dist/mailer/index.js +2 -0
  103. package/dist/mailer/index.js.map +1 -1
  104. package/dist/scripts/sequencer-recovery/recovery-db.js.map +1 -1
  105. package/dist/sequencer/events.d.ts.map +1 -1
  106. package/dist/sequencer/events.js +6 -13
  107. package/dist/sequencer/events.js.map +1 -1
  108. package/dist/sequencer/sequencer.d.ts +1 -1
  109. package/dist/sequencer/sequencer.d.ts.map +1 -1
  110. package/dist/sequencer/sequencer.js.map +1 -1
  111. package/package.json +13 -13
  112. package/src/account-manager/account-manager.ts +135 -36
  113. package/src/account-manager/db/migrations/005-oauth-account-management.ts +6 -5
  114. package/src/account-manager/db/schema/authorization-request.ts +2 -1
  115. package/src/account-manager/db/schema/authorized-client.ts +6 -2
  116. package/src/account-manager/db/schema/token.ts +2 -2
  117. package/src/account-manager/helpers/account-device.ts +5 -11
  118. package/src/account-manager/helpers/account.ts +39 -16
  119. package/src/account-manager/helpers/auth.ts +2 -2
  120. package/src/account-manager/helpers/authorization-request.ts +12 -8
  121. package/src/account-manager/helpers/authorized-client.ts +12 -6
  122. package/src/account-manager/helpers/device.ts +4 -4
  123. package/src/account-manager/helpers/lexicon.ts +8 -3
  124. package/src/account-manager/helpers/password.ts +6 -0
  125. package/src/account-manager/helpers/token.ts +17 -11
  126. package/src/account-manager/oauth-store.ts +129 -61
  127. package/src/actor-store/blob/reader.ts +8 -5
  128. package/src/actor-store/record/reader.ts +3 -3
  129. package/src/actor-store/repo/sql-repo-reader.ts +1 -1
  130. package/src/api/com/atproto/admin/updateSubjectStatus.ts +16 -4
  131. package/src/api/com/atproto/server/activateAccount.ts +27 -49
  132. package/src/api/com/atproto/server/deactivateAccount.ts +3 -7
  133. package/src/api/com/atproto/server/deleteAccount.ts +60 -43
  134. package/src/api/com/atproto/server/util.ts +24 -7
  135. package/src/config/config.ts +7 -2
  136. package/src/context.ts +2 -3
  137. package/src/db/migrator.ts +2 -1
  138. package/src/db/pagination.ts +7 -7
  139. package/src/db/util.ts +5 -2
  140. package/src/mailer/index.ts +4 -2
  141. package/src/scripts/sequencer-recovery/recovery-db.ts +1 -1
  142. package/src/sequencer/events.ts +8 -13
  143. package/src/sequencer/sequencer.ts +1 -3
  144. package/tests/account-manager.test.ts +79 -0
  145. package/tests/account-status.test.ts +206 -0
  146. package/tests/db.test.ts +3 -3
  147. package/tests/oauth.test.ts +91 -0
@@ -1 +1 @@
1
- {"version":3,"file":"sequencer.js","sourceRoot":"","sources":["../../src/sequencer/sequencer.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,aAAa,CAAA;AAEtC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAExD,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAA;AAErE,OAAO,EAAE,SAAS,IAAI,GAAG,EAAE,MAAM,cAAc,CAAA;AAE/C,OAAO,EAIL,KAAK,EACL,WAAW,GACZ,MAAM,eAAe,CAAA;AACtB,OAAO,EAML,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,aAAa,CAAA;AAEpB,cAAc,aAAa,CAAA;AAE3B,MAAM,OAAO,SAAU,SAAS,YAA2C;IAMzE,YACS,UAAkB,EAClB,QAAkB,EAClB,QAAQ,GAAG,CAAC,EACnB,wBAAwB,GAAG,KAAK;QAEhC,KAAK,EAAE,CAAA;QALA,eAAU,GAAV,UAAU,CAAQ;QAClB,aAAQ,GAAR,QAAQ,CAAU;QAClB,aAAQ,GAAR,QAAQ,CAAI;QAPrB,cAAS,GAAG,KAAK,CAAA;QACjB,gBAAW,GAAyB,IAAI,CAAA;QACxC,uBAAkB,GAAG,CAAC,CAAA;QASpB,0EAA0E;QAC1E,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAA;QACzB,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAA;IACvD,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAA;QACzB,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACrC,MAAM,QAAQ,CAAC,sBAAsB,EAAE,CAAA;QACvC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAA;QAC9B,IAAI,CAAC,QAAQ,GAAG,IAAI,IAAI,CAAC,CAAA;QACzB,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;YAC9B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,EAAE,CAAA;QAClC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;QACrB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,WAAW,CAAA;QACxB,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACpB,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aACzB,UAAU,CAAC,UAAU,CAAC;aACtB,SAAS,EAAE;aACX,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;aACtB,KAAK,CAAC,CAAC,CAAC;aACR,gBAAgB,EAAE,CAAA;QACrB,OAAO,GAAG,EAAE,GAAG,IAAI,IAAI,CAAA;IACzB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAc;QACvB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aACzB,UAAU,CAAC,UAAU,CAAC;aACtB,SAAS,EAAE;aACX,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC;aACzB,KAAK,CAAC,CAAC,CAAC;aACR,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;aACrB,gBAAgB,EAAE,CAAA;QACrB,OAAO,GAAG,IAAI,IAAI,CAAA;IACpB,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,IAAY;QAClC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aACzB,UAAU,CAAC,UAAU,CAAC;aACtB,SAAS,EAAE;aACX,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC;aAChC,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC;aAC7B,KAAK,CAAC,CAAC,CAAC;aACR,gBAAgB,EAAE,CAAA;QACrB,OAAO,GAAG,IAAI,IAAI,CAAA;IACpB,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,IAKrB;QACC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,IAAI,CAAA;QAE5D,IAAI,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE;aACnB,UAAU,CAAC,UAAU,CAAC;aACtB,SAAS,EAAE;aACX,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;aACrB,KAAK,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;QAC/B,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC9B,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,WAAW,CAAC,CAAA;QAC9C,CAAC;QACD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,SAAS,CAAC,CAAA;QAC7C,CAAC;QACD,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,YAAY,CAAC,CAAA;QACxD,CAAC;QACD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QAC5B,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,CAAA;QAClC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,OAAO,EAAE,CAAA;QACX,CAAC;QAED,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAA;IAC/B,CAAC;IAEO,KAAK,CAAC,MAAM;QAClB,IAAI,IAAI,CAAC,SAAS;YAAE,OAAM;QAC1B,gDAAgD;QAChD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC;gBACtC,WAAW,EAAE,IAAI,CAAC,QAAQ;gBAC1B,KAAK,EAAE,IAAI;aACZ,CAAC,CAAA;YACF,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpB,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAA;gBAC3B,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;gBACzB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAA;YACnD,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAA;YACjC,CAAC;YACD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,EAAE,CAAA;QAClC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,6BAA6B,CAAC,CAAA;YAC1E,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAA;YAC/B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,EAAE,CAAA;QAClC,CAAC;IACH,CAAC;IAED,+EAA+E;IACvE,KAAK,CAAC,kBAAkB;QAC9B,IAAI,CAAC,kBAAkB,EAAE,CAAA;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC,CAAA;QACvE,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAA;IACtB,CAAC;IAES,KAAK,CAAC,YAAY,CAC1B,MAAgC;QAEhC,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,OAAO,EAAE,CAAA;QAC7B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,gBAAgB,CACzC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAClE,CAAA;QACD,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,CAAA;QAC9B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACnC,CAAC;IAEM,KAAK,CAAC,cAAc,CACzB,GAAc,EACd,UAA6B;QAE7B,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,eAAe,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,CAAA;IACnE,CAAC;IAEM,KAAK,CAAC,YAAY,CAAC,GAAc,EAAE,IAAiB;QACzD,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;IAC9D,CAAC;IAEM,KAAK,CAAC,gBAAgB,CAC3B,GAAc,EACd,MAAqB;QAErB,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;IACpE,CAAC;IAEM,KAAK,CAAC,eAAe,CAC1B,GAAc,EACd,MAAqB;QAErB,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;IACnE,CAAC;IAEM,KAAK,CAAC,uBAAuB,CAClC,GAAc,EACd,MAAoB,EACpB,MAAyB;QAEzB,iCAAiC;QACjC,MAAM,IAAI,CAAC,YAAY,CAAC;YACtB,MAAM,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC;YACvC,MAAM,mBAAmB,CAAC,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC;YACpD,MAAM,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC;YAClC,MAAM,gBAAgB,CAAC,GAAG,EAAE,qBAAqB,CAAC,MAAM,CAAC,CAAC;SAC3D,CAAC,CAAA;IACJ,CAAC;IAEM,KAAK,CAAC,yBAAyB,CACpC,GAAc,EACd,MAAoB,EACpB,MAAqB,EACrB,QAAqB;QAErB,iCAAiC;QACjC,MAAM,IAAI,CAAC,YAAY,CAAC;YACtB,MAAM,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC;YACtC,MAAM,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC;YACvC,MAAM,gBAAgB,CAAC,GAAG,EAAE,QAAQ,CAAC;SACtC,CAAC,CAAA;IACJ,CAAC;IAEM,KAAK,CAAC,uBAAuB,CAAC,GAAc;QACjD,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC;YACpC,MAAM,mBAAmB,CAAC,GAAG,EAAE,aAAa,CAAC,OAAO,CAAC;SACtD,CAAC,CAAA;QACF,MAAM,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAC5B,IAAI,CAAC,EAAE,CAAC,EAAE;aACP,UAAU,CAAC,UAAU,CAAC;aACtB,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC;aACtB,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,CAC3B,CAAA;IACH,CAAC;CACF;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAoB,EAAY,EAAE;IACjE,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,gDAAgD;QAChD,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC;YACrB,SAAQ;QACV,CAAC;QACD,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACjC,IAAI,GAAG,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC/B,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,QAAQ;gBACd,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,IAAI,EAAE,GAAG,CAAC,WAA6B;gBACvC,GAAG,EAAE,GAAgB;aACtB,CAAC,CAAA;QACJ,CAAC;aAAM,IAAI,GAAG,CAAC,SAAS,KAAK,MAAM,EAAE,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,MAAM;gBACZ,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,IAAI,EAAE,GAAG,CAAC,WAA6B;gBACvC,GAAG,EAAE,GAAc;aACpB,CAAC,CAAA;QACJ,CAAC;aAAM,IAAI,GAAG,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;YACxC,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,UAAU;gBAChB,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,IAAI,EAAE,GAAG,CAAC,WAA6B;gBACvC,GAAG,EAAE,GAAkB;aACxB,CAAC,CAAA;QACJ,CAAC;aAAM,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACvC,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,SAAS;gBACf,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,IAAI,EAAE,GAAG,CAAC,WAA6B;gBACvC,GAAG,EAAE,GAAiB;aACvB,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAWD,eAAe,SAAS,CAAA","sourcesContent":["import EventEmitter from 'node:events'\nimport type TypedEmitter from 'typed-emitter'\nimport { SECOND, wait } from '@atproto/common'\nimport { decode as cborDecode } from '@atproto/lex-cbor'\nimport { DatetimeString, DidString, HandleString } from '@atproto/syntax'\nimport { AccountStatus } from '../account-manager/helpers/account.js'\nimport { Crawlers } from '../crawlers.js'\nimport { seqLogger as log } from '../logger.js'\nimport { CommitDataWithOps, SyncEvtData } from '../repo/index.js'\nimport {\n RepoSeqEntry,\n RepoSeqInsert,\n SequencerDb,\n getDb,\n getMigrator,\n} from './db/index.js'\nimport {\n AccountEvt,\n CommitEvt,\n IdentityEvt,\n SeqEvt,\n SyncEvt,\n formatSeqAccountEvt,\n formatSeqCommit,\n formatSeqIdentityEvt,\n formatSeqSyncEvt,\n syncEvtDataFromCommit,\n} from './events.js'\n\nexport * from './events.js'\n\nexport class Sequencer extends (EventEmitter as new () => SequencerEmitter) {\n db: SequencerDb\n destroyed = false\n pollPromise: Promise<void> | null = null\n triesWithNoResults = 0\n\n constructor(\n public dbLocation: string,\n public crawlers: Crawlers,\n public lastSeen = 0,\n disableWalAutoCheckpoint = false,\n ) {\n super()\n // note: this does not err when surpassed, just prints a warning to stderr\n this.setMaxListeners(100)\n this.db = getDb(dbLocation, disableWalAutoCheckpoint)\n }\n\n async start() {\n await this.db.ensureWal()\n const migrator = getMigrator(this.db)\n await migrator.migrateToLatestOrThrow()\n const curr = await this.curr()\n this.lastSeen = curr ?? 0\n if (this.pollPromise === null) {\n this.pollPromise = this.pollDb()\n }\n }\n\n async destroy() {\n this.destroyed = true\n if (this.pollPromise) {\n await this.pollPromise\n }\n this.emit('close')\n }\n\n async curr(): Promise<number | null> {\n const got = await this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .orderBy('seq', 'desc')\n .limit(1)\n .executeTakeFirst()\n return got?.seq ?? null\n }\n\n async next(cursor: number): Promise<SeqRow | null> {\n const got = await this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .where('seq', '>', cursor)\n .limit(1)\n .orderBy('seq', 'asc')\n .executeTakeFirst()\n return got || null\n }\n\n async earliestAfterTime(time: string): Promise<SeqRow | null> {\n const got = await this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .where('sequencedAt', '>=', time)\n .orderBy('sequencedAt', 'asc')\n .limit(1)\n .executeTakeFirst()\n return got || null\n }\n\n async requestSeqRange(opts: {\n earliestSeq?: number\n latestSeq?: number\n earliestTime?: string\n limit?: number\n }): Promise<SeqEvt[]> {\n const { earliestSeq, latestSeq, earliestTime, limit } = opts\n\n let seqQb = this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .orderBy('seq', 'asc')\n .where('invalidated', '=', 0)\n if (earliestSeq !== undefined) {\n seqQb = seqQb.where('seq', '>', earliestSeq)\n }\n if (latestSeq !== undefined) {\n seqQb = seqQb.where('seq', '<=', latestSeq)\n }\n if (earliestTime !== undefined) {\n seqQb = seqQb.where('sequencedAt', '>=', earliestTime)\n }\n if (limit !== undefined) {\n seqQb = seqQb.limit(limit)\n }\n\n const rows = await seqQb.execute()\n if (rows.length < 1) {\n return []\n }\n\n return parseRepoSeqRows(rows)\n }\n\n private async pollDb(): Promise<void> {\n if (this.destroyed) return\n // if already polling, do not start another poll\n try {\n const evts = await this.requestSeqRange({\n earliestSeq: this.lastSeen,\n limit: 1000,\n })\n if (evts.length > 0) {\n this.triesWithNoResults = 0\n this.emit('events', evts)\n this.lastSeen = evts.at(-1)?.seq ?? this.lastSeen\n } else {\n await this.exponentialBackoff()\n }\n this.pollPromise = this.pollDb()\n } catch (err) {\n log.error({ err, lastSeen: this.lastSeen }, 'sequencer failed to poll db')\n await this.exponentialBackoff()\n this.pollPromise = this.pollDb()\n }\n }\n\n // when no results, exponential backoff on pulling, with a max of a second wait\n private async exponentialBackoff(): Promise<void> {\n this.triesWithNoResults++\n const waitTime = Math.min(Math.pow(2, this.triesWithNoResults), SECOND)\n await wait(waitTime)\n }\n\n protected async sequenceEvts(\n events: readonly RepoSeqInsert[],\n ): Promise<number[]> {\n if (!events.length) return []\n const rows = await this.db.executeWithRetry(\n this.db.db.insertInto('repo_seq').values(events).returning('seq'),\n )\n this.crawlers.notifyOfUpdate()\n return rows.map((row) => row.seq)\n }\n\n public async sequenceCommit(\n did: DidString,\n commitData: CommitDataWithOps,\n ): Promise<void> {\n await this.sequenceEvts([await formatSeqCommit(did, commitData)])\n }\n\n public async sequenceSync(did: DidString, data: SyncEvtData): Promise<void> {\n await this.sequenceEvts([await formatSeqSyncEvt(did, data)])\n }\n\n public async sequenceIdentity(\n did: DidString,\n handle?: HandleString,\n ): Promise<void> {\n await this.sequenceEvts([await formatSeqIdentityEvt(did, handle)])\n }\n\n public async sequenceAccount(\n did: DidString,\n status: AccountStatus,\n ): Promise<void> {\n await this.sequenceEvts([await formatSeqAccountEvt(did, status)])\n }\n\n public async sequenceAccountCreation(\n did: DidString,\n handle: HandleString,\n commit: CommitDataWithOps,\n ): Promise<void> {\n // Atomically sequence all events\n await this.sequenceEvts([\n await formatSeqIdentityEvt(did, handle),\n await formatSeqAccountEvt(did, AccountStatus.Active),\n await formatSeqCommit(did, commit),\n await formatSeqSyncEvt(did, syncEvtDataFromCommit(commit)),\n ])\n }\n\n public async sequenceAccountActivation(\n did: DidString,\n handle: HandleString,\n status: AccountStatus,\n syncData: SyncEvtData,\n ): Promise<void> {\n // Atomically sequence all events\n await this.sequenceEvts([\n await formatSeqAccountEvt(did, status),\n await formatSeqIdentityEvt(did, handle),\n await formatSeqSyncEvt(did, syncData),\n ])\n }\n\n public async sequenceAccountDeletion(did: DidString) {\n const [seq] = await this.sequenceEvts([\n await formatSeqAccountEvt(did, AccountStatus.Deleted),\n ])\n await this.db.executeWithRetry(\n this.db.db\n .deleteFrom('repo_seq')\n .where('did', '=', did)\n .where('seq', '!=', seq),\n )\n }\n}\n\nexport const parseRepoSeqRows = (rows: RepoSeqEntry[]): SeqEvt[] => {\n const seqEvts: SeqEvt[] = []\n for (const row of rows) {\n // should never hit this because of WHERE clause\n if (row.seq === null) {\n continue\n }\n const evt = cborDecode(row.event)\n if (row.eventType === 'append') {\n seqEvts.push({\n type: 'commit',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as CommitEvt,\n })\n } else if (row.eventType === 'sync') {\n seqEvts.push({\n type: 'sync',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as SyncEvt,\n })\n } else if (row.eventType === 'identity') {\n seqEvts.push({\n type: 'identity',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as IdentityEvt,\n })\n } else if (row.eventType === 'account') {\n seqEvts.push({\n type: 'account',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as AccountEvt,\n })\n }\n }\n return seqEvts\n}\n\ntype SeqRow = RepoSeqEntry\n\ntype SequencerEvents = {\n events: (evts: SeqEvt[]) => void\n close: () => void\n}\n\nexport type SequencerEmitter = TypedEmitter.default<SequencerEvents>\n\nexport default Sequencer\n"]}
1
+ {"version":3,"file":"sequencer.js","sourceRoot":"","sources":["../../src/sequencer/sequencer.ts"],"names":[],"mappings":"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","sourcesContent":["import EventEmitter from 'node:events'\nimport type TypedEmitter from 'typed-emitter'\nimport { SECOND, wait } from '@atproto/common'\nimport { decode as cborDecode } from '@atproto/lex-cbor'\nimport { DatetimeString, DidString, HandleString } from '@atproto/syntax'\nimport { AccountStatus } from '../account-manager/helpers/account.js'\nimport { Crawlers } from '../crawlers.js'\nimport { seqLogger as log } from '../logger.js'\nimport { CommitDataWithOps, SyncEvtData } from '../repo/index.js'\nimport {\n RepoSeqEntry,\n RepoSeqInsert,\n SequencerDb,\n getDb,\n getMigrator,\n} from './db/index.js'\nimport {\n AccountEvt,\n CommitEvt,\n IdentityEvt,\n SeqEvt,\n SyncEvt,\n formatSeqAccountEvt,\n formatSeqCommit,\n formatSeqIdentityEvt,\n formatSeqSyncEvt,\n syncEvtDataFromCommit,\n} from './events.js'\n\nexport * from './events.js'\n\nexport class Sequencer extends (EventEmitter as new () => SequencerEmitter) {\n db: SequencerDb\n destroyed = false\n pollPromise: Promise<void> | null = null\n triesWithNoResults = 0\n\n constructor(\n public dbLocation: string,\n public crawlers: Crawlers,\n public lastSeen = 0,\n disableWalAutoCheckpoint = false,\n ) {\n super()\n // note: this does not err when surpassed, just prints a warning to stderr\n this.setMaxListeners(100)\n this.db = getDb(dbLocation, disableWalAutoCheckpoint)\n }\n\n async start() {\n await this.db.ensureWal()\n const migrator = getMigrator(this.db)\n await migrator.migrateToLatestOrThrow()\n const curr = await this.curr()\n this.lastSeen = curr ?? 0\n if (this.pollPromise === null) {\n this.pollPromise = this.pollDb()\n }\n }\n\n async destroy() {\n this.destroyed = true\n if (this.pollPromise) {\n await this.pollPromise\n }\n this.emit('close')\n }\n\n async curr(): Promise<number | null> {\n const got = await this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .orderBy('seq', 'desc')\n .limit(1)\n .executeTakeFirst()\n return got?.seq ?? null\n }\n\n async next(cursor: number): Promise<SeqRow | null> {\n const got = await this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .where('seq', '>', cursor)\n .limit(1)\n .orderBy('seq', 'asc')\n .executeTakeFirst()\n return got || null\n }\n\n async earliestAfterTime(time: string): Promise<SeqRow | null> {\n const got = await this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .where('sequencedAt', '>=', time)\n .orderBy('sequencedAt', 'asc')\n .limit(1)\n .executeTakeFirst()\n return got || null\n }\n\n async requestSeqRange(opts: {\n earliestSeq?: number\n latestSeq?: number\n earliestTime?: string\n limit?: number\n }): Promise<SeqEvt[]> {\n const { earliestSeq, latestSeq, earliestTime, limit } = opts\n\n let seqQb = this.db.db\n .selectFrom('repo_seq')\n .selectAll()\n .orderBy('seq', 'asc')\n .where('invalidated', '=', 0)\n if (earliestSeq !== undefined) {\n seqQb = seqQb.where('seq', '>', earliestSeq)\n }\n if (latestSeq !== undefined) {\n seqQb = seqQb.where('seq', '<=', latestSeq)\n }\n if (earliestTime !== undefined) {\n seqQb = seqQb.where('sequencedAt', '>=', earliestTime)\n }\n if (limit !== undefined) {\n seqQb = seqQb.limit(limit)\n }\n\n const rows = await seqQb.execute()\n if (rows.length < 1) {\n return []\n }\n\n return parseRepoSeqRows(rows)\n }\n\n private async pollDb(): Promise<void> {\n if (this.destroyed) return\n // if already polling, do not start another poll\n try {\n const evts = await this.requestSeqRange({\n earliestSeq: this.lastSeen,\n limit: 1000,\n })\n if (evts.length > 0) {\n this.triesWithNoResults = 0\n this.emit('events', evts)\n this.lastSeen = evts.at(-1)?.seq ?? this.lastSeen\n } else {\n await this.exponentialBackoff()\n }\n this.pollPromise = this.pollDb()\n } catch (err) {\n log.error({ err, lastSeen: this.lastSeen }, 'sequencer failed to poll db')\n await this.exponentialBackoff()\n this.pollPromise = this.pollDb()\n }\n }\n\n // when no results, exponential backoff on pulling, with a max of a second wait\n private async exponentialBackoff(): Promise<void> {\n this.triesWithNoResults++\n const waitTime = Math.min(Math.pow(2, this.triesWithNoResults), SECOND)\n await wait(waitTime)\n }\n\n async sequenceEvts(events: readonly RepoSeqInsert[]): Promise<number[]> {\n if (!events.length) return []\n const rows = await this.db.executeWithRetry(\n this.db.db.insertInto('repo_seq').values(events).returning('seq'),\n )\n this.crawlers.notifyOfUpdate()\n return rows.map((row) => row.seq)\n }\n\n public async sequenceCommit(\n did: DidString,\n commitData: CommitDataWithOps,\n ): Promise<void> {\n await this.sequenceEvts([await formatSeqCommit(did, commitData)])\n }\n\n public async sequenceSync(did: DidString, data: SyncEvtData): Promise<void> {\n await this.sequenceEvts([await formatSeqSyncEvt(did, data)])\n }\n\n public async sequenceIdentity(\n did: DidString,\n handle?: HandleString,\n ): Promise<void> {\n await this.sequenceEvts([await formatSeqIdentityEvt(did, handle)])\n }\n\n public async sequenceAccount(\n did: DidString,\n status: AccountStatus,\n ): Promise<void> {\n await this.sequenceEvts([await formatSeqAccountEvt(did, status)])\n }\n\n public async sequenceAccountCreation(\n did: DidString,\n handle: HandleString,\n commit: CommitDataWithOps,\n ): Promise<void> {\n // Atomically sequence all events\n await this.sequenceEvts([\n await formatSeqIdentityEvt(did, handle),\n await formatSeqAccountEvt(did, AccountStatus.Active),\n await formatSeqCommit(did, commit),\n await formatSeqSyncEvt(did, syncEvtDataFromCommit(commit)),\n ])\n }\n\n public async sequenceAccountActivation(\n did: DidString,\n handle: HandleString,\n status: AccountStatus,\n syncData: SyncEvtData,\n ): Promise<void> {\n // Atomically sequence all events\n await this.sequenceEvts([\n await formatSeqAccountEvt(did, status),\n await formatSeqIdentityEvt(did, handle),\n await formatSeqSyncEvt(did, syncData),\n ])\n }\n\n public async sequenceAccountDeletion(did: DidString) {\n const [seq] = await this.sequenceEvts([\n await formatSeqAccountEvt(did, AccountStatus.Deleted),\n ])\n await this.db.executeWithRetry(\n this.db.db\n .deleteFrom('repo_seq')\n .where('did', '=', did)\n .where('seq', '!=', seq),\n )\n }\n}\n\nexport const parseRepoSeqRows = (rows: RepoSeqEntry[]): SeqEvt[] => {\n const seqEvts: SeqEvt[] = []\n for (const row of rows) {\n // should never hit this because of WHERE clause\n if (row.seq === null) {\n continue\n }\n const evt = cborDecode(row.event)\n if (row.eventType === 'append') {\n seqEvts.push({\n type: 'commit',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as CommitEvt,\n })\n } else if (row.eventType === 'sync') {\n seqEvts.push({\n type: 'sync',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as SyncEvt,\n })\n } else if (row.eventType === 'identity') {\n seqEvts.push({\n type: 'identity',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as IdentityEvt,\n })\n } else if (row.eventType === 'account') {\n seqEvts.push({\n type: 'account',\n seq: row.seq,\n time: row.sequencedAt as DatetimeString,\n evt: evt as AccountEvt,\n })\n }\n }\n return seqEvts\n}\n\ntype SeqRow = RepoSeqEntry\n\ntype SequencerEvents = {\n events: (evts: SeqEvt[]) => void\n close: () => void\n}\n\nexport type SequencerEmitter = TypedEmitter.default<SequencerEvents>\n\nexport default Sequencer\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/pds",
3
- "version": "0.5.5",
3
+ "version": "0.5.6",
4
4
  "license": "MIT",
5
5
  "description": "Reference implementation of atproto Personal Data Server (PDS)",
6
6
  "keywords": [
@@ -32,7 +32,7 @@
32
32
  "ioredis": "^5.3.2",
33
33
  "jose": "^5.0.1",
34
34
  "key-encoder": "^2.0.3",
35
- "kysely": "^0.22.0",
35
+ "kysely": "^0.29.2",
36
36
  "nodemailer": "^6.8.0",
37
37
  "nodemailer-html-to-text": "^3.2.0",
38
38
  "p-queue": "^8.0.0",
@@ -44,24 +44,24 @@
44
44
  "zod": "^3.23.8",
45
45
  "@atproto-labs/fetch-node": "^0.3.1",
46
46
  "@atproto-labs/simple-store": "^0.4.1",
47
- "@atproto-labs/simple-store-redis": "^0.1.1",
47
+ "@atproto-labs/simple-store-memory": "^0.2.1",
48
48
  "@atproto-labs/xrpc-utils": "^0.1.1",
49
+ "@atproto-labs/simple-store-redis": "^0.1.1",
49
50
  "@atproto/common": "^0.6.3",
51
+ "@atproto/aws": "^0.3.1",
50
52
  "@atproto/crypto": "^0.5.1",
51
- "@atproto-labs/simple-store-memory": "^0.2.1",
52
53
  "@atproto/did": "^0.5.1",
53
54
  "@atproto/identity": "^0.5.1",
54
- "@atproto/aws": "^0.3.1",
55
+ "@atproto/lex": "^0.1.4",
55
56
  "@atproto/lex-cbor": "^0.1.1",
56
57
  "@atproto/lex-data": "^0.1.2",
57
- "@atproto/lex": "^0.1.4",
58
- "@atproto/lex-json": "^0.1.1",
59
- "@atproto/oauth-provider": "^0.18.4",
60
58
  "@atproto/oauth-scopes": "^0.5.1",
61
59
  "@atproto/repo": "^0.10.1",
60
+ "@atproto/lex-json": "^0.1.1",
61
+ "@atproto/syntax": "^0.6.2",
62
62
  "@atproto/xrpc": "^0.8.1",
63
- "@atproto/xrpc-server": "^0.11.2",
64
- "@atproto/syntax": "^0.6.2"
63
+ "@atproto/oauth-provider": "^0.19.0",
64
+ "@atproto/xrpc-server": "^0.11.2"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@did-plc/server": "^0.0.1",
@@ -78,10 +78,10 @@
78
78
  "puppeteer": "^23.11.1",
79
79
  "ts-node": "^10.8.2",
80
80
  "ws": "^8.12.0",
81
- "@atproto/api": "^0.20.16",
82
- "@atproto/bsky": "^0.0.242",
81
+ "@atproto/bsky": "^0.0.243",
83
82
  "@atproto/lex-document": "^0.1.1",
84
- "@atproto/oauth-client-browser-example": "^0.1.1"
83
+ "@atproto/oauth-client-browser-example": "^0.1.1",
84
+ "@atproto/api": "^0.20.16"
85
85
  },
86
86
  "type": "module",
87
87
  "exports": {
@@ -1,3 +1,4 @@
1
+ import assert from 'node:assert'
1
2
  import { KeyObject } from 'node:crypto'
2
3
  import { Client as PlcClient } from '@did-plc/lib'
3
4
  import { isEmailValid } from '@hapi/address'
@@ -12,9 +13,16 @@ import {
12
13
  isAtIdentifierString,
13
14
  } from '@atproto/lex'
14
15
  import { Cid } from '@atproto/lex-data'
15
- import { currentDatetimeString, isValidTld } from '@atproto/syntax'
16
+ import {
17
+ INVALID_HANDLE,
18
+ currentDatetimeString,
19
+ isValidTld,
20
+ } from '@atproto/syntax'
16
21
  import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server'
22
+ import { ActorStore } from '../actor-store/actor-store.js'
23
+ import { assertValidDidDocumentForService } from '../api/com/atproto/server/util.js'
17
24
  import { AuthScope } from '../auth-scope.js'
25
+ import { ServerConfig } from '../config/config.js'
18
26
  import { softDeleted } from '../db/index.js'
19
27
  import { hasExplicitSlur } from '../handle/explicit-slurs.js'
20
28
  import {
@@ -27,9 +35,10 @@ import { httpLogger } from '../logger.js'
27
35
  import { ServerMailer } from '../mailer/index.js'
28
36
  import { Sequencer } from '../sequencer/index.js'
29
37
  import { AccountDb, EmailTokenPurpose, getDb, getMigrator } from './db/index.js'
30
- import * as account from './helpers/account.js'
38
+ import * as accountHelpers from './helpers/account.js'
31
39
  import { AccountStatus, ActorAccount } from './helpers/account.js'
32
40
  import * as auth from './helpers/auth.js'
41
+ import * as authorizedClientHelper from './helpers/authorized-client.js'
33
42
  import * as emailToken from './helpers/email-token.js'
34
43
  import * as invite from './helpers/invite.js'
35
44
  import * as password from './helpers/password.js'
@@ -53,7 +62,7 @@ export { AccountStatus, formatAccountStatus } from './helpers/account.js'
53
62
  */
54
63
  export class InvalidPasswordError extends AuthRequiredError {
55
64
  constructor(
56
- public readonly did: string,
65
+ public readonly did: DidString,
57
66
  errorMessage = 'Invalid identifier or password',
58
67
  ) {
59
68
  super(errorMessage)
@@ -69,17 +78,24 @@ export class AccountManager {
69
78
  readonly db: AccountDb
70
79
 
71
80
  constructor(
81
+ readonly cfg: ServerConfig,
82
+ readonly actorStore: ActorStore,
72
83
  readonly idResolver: IdResolver,
73
84
  readonly jwtKey: KeyObject,
74
85
  readonly mailer: ServerMailer,
75
86
  readonly sequencer: Sequencer,
76
87
  readonly plcClient: PlcClient,
77
88
  readonly plcRotationKey: Keypair,
78
- readonly serviceDid: string,
79
- readonly serviceHandleDomains: string[],
80
- db: AccountManagerDbConfig,
81
89
  ) {
82
- this.db = getDb(db.accountDbLoc, db.disableWalAutoCheckpoint)
90
+ this.db = getDb(cfg.db.accountDbLoc, cfg.db.disableWalAutoCheckpoint)
91
+ }
92
+
93
+ get serviceDid(): DidString {
94
+ return this.cfg.service.did
95
+ }
96
+
97
+ get serviceHandleDomains(): string[] {
98
+ return this.cfg.identity.serviceHandleDomains
83
99
  }
84
100
 
85
101
  async migrateOrThrow() {
@@ -96,23 +112,23 @@ export class AccountManager {
96
112
 
97
113
  async getAccount(
98
114
  handleOrDid: AtIdentifierString,
99
- flags?: account.AvailabilityFlags,
115
+ flags?: accountHelpers.AvailabilityFlags,
100
116
  ): Promise<ActorAccount | null> {
101
- return account.getAccount(this.db, handleOrDid, flags)
117
+ return accountHelpers.getAccount(this.db, handleOrDid, flags)
102
118
  }
103
119
 
104
120
  async getAccounts(
105
121
  dids: DidString[],
106
- flags?: account.AvailabilityFlags,
122
+ flags?: accountHelpers.AvailabilityFlags,
107
123
  ): Promise<Map<string, ActorAccount>> {
108
- return account.getAccounts(this.db, dids, flags)
124
+ return accountHelpers.getAccounts(this.db, dids, flags)
109
125
  }
110
126
 
111
127
  async getAccountByEmail(
112
128
  email: string,
113
- flags?: account.AvailabilityFlags,
129
+ flags?: accountHelpers.AvailabilityFlags,
114
130
  ): Promise<ActorAccount | null> {
115
- return account.getAccountByEmail(this.db, email, flags)
131
+ return accountHelpers.getAccountByEmail(this.db, email, flags)
116
132
  }
117
133
 
118
134
  async isAccountActivated(did: DidString): Promise<boolean> {
@@ -123,22 +139,26 @@ export class AccountManager {
123
139
 
124
140
  async getDidForActor(
125
141
  handleOrDid: AtIdentifierString,
126
- flags?: account.AvailabilityFlags,
142
+ flags?: accountHelpers.AvailabilityFlags,
127
143
  ): Promise<DidString | null> {
128
144
  const got = await this.getAccount(handleOrDid, flags)
129
145
  return got?.did ?? null
130
146
  }
131
147
 
132
- async getAccountStatus(
133
- handleOrDid: AtIdentifierString,
134
- ): Promise<AccountStatus> {
148
+ async getAccountStatus(handleOrDid: AtIdentifierString) {
135
149
  const got = await this.getAccount(handleOrDid, {
136
150
  includeDeactivated: true,
137
151
  includeTakenDown: true,
138
152
  })
139
153
 
140
- const res = account.formatAccountStatus(got)
141
- return res.active ? AccountStatus.Active : res.status
154
+ const { active, status = active ? AccountStatus.Active : undefined } =
155
+ accountHelpers.formatAccountStatus(got)
156
+ assert(status != null)
157
+ return { status, account: got } as
158
+ | { status: AccountStatus.Deleted; account: null }
159
+ | { status: AccountStatus.Takendown; account: ActorAccount }
160
+ | { status: AccountStatus.Deactivated; account: ActorAccount }
161
+ | { status: AccountStatus.Active; account: ActorAccount }
142
162
  }
143
163
 
144
164
  async normalizeAndValidateHandle(
@@ -230,10 +250,14 @@ export class AccountManager {
230
250
  await invite.ensureInviteIsAvailable(dbTxn, inviteCode)
231
251
  }
232
252
 
233
- await account.registerActor(dbTxn, { did, handle, deactivated })
253
+ await accountHelpers.registerActor(dbTxn, { did, handle, deactivated })
234
254
 
235
255
  if (email && passwordScrypt) {
236
- await account.registerAccount(dbTxn, { did, email, passwordScrypt })
256
+ await accountHelpers.registerAccount(dbTxn, {
257
+ did,
258
+ email,
259
+ passwordScrypt,
260
+ })
237
261
  }
238
262
 
239
263
  await invite.recordInviteUse(dbTxn, {
@@ -359,7 +383,7 @@ export class AccountManager {
359
383
  did: DidString,
360
384
  handle: HandleString,
361
385
  ): Promise<void> {
362
- await account.updateHandle(this.db, did, handle)
386
+ await accountHelpers.updateHandle(this.db, did, handle)
363
387
 
364
388
  try {
365
389
  await this.sequencer.sequenceIdentity(did, handle)
@@ -369,36 +393,105 @@ export class AccountManager {
369
393
  }
370
394
 
371
395
  async deleteAccount(did: DidString) {
372
- return account.deleteAccount(this.db, did)
396
+ return accountHelpers.deleteAccount(this.db, did)
373
397
  }
374
398
 
375
399
  async takedownAccount(
376
400
  did: DidString,
377
401
  takedown: com.atproto.admin.defs.StatusAttr,
378
402
  ) {
379
- await this.db.transaction(async (dbTxn) =>
380
- Promise.all([
381
- account.updateAccountTakedownStatus(dbTxn, did, takedown),
382
- auth.revokeRefreshTokensByDid(dbTxn, did),
383
- token.removeByDidQB(dbTxn, did).execute(),
384
- ]),
385
- )
403
+ await this.db.transaction(async (dbTxn) => {
404
+ await accountHelpers.updateAccountTakedownStatus(dbTxn, did, takedown)
405
+ await auth.revokeRefreshTokensByDid(dbTxn, did)
406
+ await token.removeByDid(dbTxn, did)
407
+ })
408
+
409
+ await this.sequenceAccountStatus(did)
386
410
  }
387
411
 
388
412
  async getAccountAdminStatus(did: DidString) {
389
- return account.getAccountAdminStatus(this.db, did)
413
+ return accountHelpers.getAccountAdminStatus(this.db, did)
390
414
  }
391
415
 
392
416
  async updateRepoRoot(did: DidString, cid: Cid, rev: string) {
393
417
  return repo.updateRoot(this.db, did, cid, rev)
394
418
  }
395
419
 
396
- async deactivateAccount(did: DidString, deleteAfter: string | null) {
397
- return account.deactivateAccount(this.db, did, deleteAfter)
420
+ async deactivateAccount(
421
+ did: DidString,
422
+ options?: {
423
+ deleteCredentials?: boolean
424
+ deleteAfter?: string | null
425
+ },
426
+ ) {
427
+ const wasUpdated = await this.db.transaction(async (dbTxn) => {
428
+ if (options?.deleteCredentials) {
429
+ await token.removeByDid(dbTxn, did)
430
+ await authorizedClientHelper.deleteAllAuthorizedClients(dbTxn, did)
431
+ await password.deleteAllAppPasswords(dbTxn, did)
432
+ }
433
+
434
+ return accountHelpers.deactivateAccount(
435
+ dbTxn,
436
+ did,
437
+ options?.deleteAfter ?? null,
438
+ )
439
+ })
440
+
441
+ if (!wasUpdated) {
442
+ throw new InvalidRequestError('Account not found')
443
+ }
444
+
445
+ const accountStatus = await this.getAccountStatus(did)
446
+
447
+ // Account is likely soft-deleted (takendown)
448
+ if (accountStatus.status === AccountStatus.Deleted) {
449
+ throw new InvalidRequestError('Account not found')
450
+ }
451
+
452
+ await this.sequencer.sequenceAccount(did, accountStatus.status)
453
+
454
+ return accountStatus
398
455
  }
399
456
 
400
457
  async activateAccount(did: DidString) {
401
- return account.activateAccount(this.db, did)
458
+ await assertValidDidDocumentForService(this, did)
459
+
460
+ const found = await accountHelpers.activateAccount(this.db, did, {
461
+ // We cannot activate a takendown account
462
+ includeTakenDown: false,
463
+ includeDeactivated: true,
464
+ })
465
+ if (!found) {
466
+ throw new InvalidRequestError('user not found', 'AccountNotFound')
467
+ }
468
+
469
+ const accountStatus = await this.getAccountStatus(did)
470
+
471
+ const { account, status } = accountStatus
472
+
473
+ if (status === AccountStatus.Deleted) {
474
+ // A concurrent operation deleted the account
475
+ throw new InvalidRequestError('user not found', 'AccountNotFound')
476
+ }
477
+
478
+ const syncData = await this.actorStore.read(did, (store) => {
479
+ return store.repo.getSyncEventData()
480
+ })
481
+
482
+ await this.sequencer.sequenceAccountActivation(
483
+ did,
484
+ account.handle ?? INVALID_HANDLE,
485
+ status,
486
+ syncData,
487
+ )
488
+
489
+ return accountStatus
490
+ }
491
+
492
+ async sequenceAccountStatus(did: DidString) {
493
+ const { status } = await this.getAccountStatus(did)
494
+ await this.sequencer.sequenceAccount(did, status)
402
495
  }
403
496
 
404
497
  // Auth
@@ -556,6 +649,12 @@ export class AccountManager {
556
649
  did: DidString,
557
650
  passwordStr: string,
558
651
  ): Promise<boolean> {
652
+ if (passwordStr.length > scrypt.OLD_PASSWORD_MAX_LENGTH) {
653
+ // @NOTE Avoid throwing from here to avoid leaking account email validity
654
+ // through error messages.
655
+ return false
656
+ }
657
+
559
658
  return password.verifyAccountPassword(this.db, did, passwordStr)
560
659
  }
561
660
 
@@ -691,7 +790,7 @@ export class AccountManager {
691
790
  const now = currentDatetimeString()
692
791
  await this.db.transaction(async (dbTxn) => {
693
792
  await emailToken.deleteEmailToken(dbTxn, did, 'confirm_email')
694
- await account.setEmailConfirmedAt(dbTxn, did, now)
793
+ await accountHelpers.setEmailConfirmedAt(dbTxn, did, now)
695
794
  })
696
795
 
697
796
  user.emailConfirmedAt = now
@@ -787,7 +886,7 @@ export class AccountManager {
787
886
  async updateAccountEmail(opts: { did: DidString; email: string }) {
788
887
  const { did, email } = opts
789
888
  await this.db.transaction(async (dbTxn) => {
790
- await account.updateEmail(dbTxn, did, email)
889
+ await accountHelpers.updateEmail(dbTxn, did, email)
791
890
  await emailToken.deleteAllEmailTokens(dbTxn, did)
792
891
  })
793
892
  }
@@ -85,13 +85,14 @@ export async function up(
85
85
  .select('authenticatedAt as createdAt') // Best we can do
86
86
  .select('authenticatedAt as updatedAt')
87
87
  .where('remember', '=', 1)
88
- .whereExists((qb) =>
88
+ .where(({ exists, selectFrom }) =>
89
89
  // device_account does not have fkey on account.did,
90
90
  // so we satisfy account_device_did_fk with this condition.
91
- qb
92
- .selectFrom('account')
93
- .selectAll()
94
- .whereRef('account.did', '=', 'device_account.did'),
91
+ exists(
92
+ selectFrom('account')
93
+ .selectAll()
94
+ .whereRef('account.did', '=', 'device_account.did'),
95
+ ),
95
96
  ),
96
97
  )
97
98
  .onConflict((oc) => oc.doNothing())
@@ -4,6 +4,7 @@ import {
4
4
  ClientAuthLegacy,
5
5
  Code,
6
6
  DeviceId,
7
+ Did,
7
8
  OAuthAuthorizationRequestParameters,
8
9
  OAuthClientId,
9
10
  RequestId,
@@ -12,7 +13,7 @@ import { DateISO, JsonEncoded } from '../../../db/index.js'
12
13
 
13
14
  export interface AuthorizationRequest {
14
15
  id: RequestId
15
- did: string | null
16
+ did: Did | null
16
17
  deviceId: DeviceId | null
17
18
 
18
19
  clientId: OAuthClientId
@@ -1,9 +1,13 @@
1
1
  import { Selectable } from 'kysely'
2
- import { AuthorizedClientData, OAuthClientId } from '@atproto/oauth-provider'
2
+ import {
3
+ AuthorizedClientData,
4
+ Did,
5
+ OAuthClientId,
6
+ } from '@atproto/oauth-provider'
3
7
  import { DateISO, JsonEncoded } from '../../../db/index.js'
4
8
 
5
9
  export interface AuthorizedClient {
6
- did: string
10
+ did: Did
7
11
  clientId: OAuthClientId
8
12
 
9
13
  createdAt: DateISO
@@ -4,18 +4,18 @@ import {
4
4
  ClientAuthLegacy,
5
5
  Code,
6
6
  DeviceId,
7
+ Did,
7
8
  OAuthAuthorizationDetails,
8
9
  OAuthAuthorizationRequestParameters,
9
10
  OAuthClientId,
10
11
  RefreshToken,
11
- Sub,
12
12
  TokenId,
13
13
  } from '@atproto/oauth-provider'
14
14
  import { DateISO, JsonEncoded } from '../../../db/cast.js'
15
15
 
16
16
  export interface Token {
17
17
  id: Generated<number>
18
- did: Sub
18
+ did: Did
19
19
 
20
20
  tokenId: TokenId
21
21
  createdAt: DateISO
@@ -1,6 +1,5 @@
1
1
  import assert from 'node:assert'
2
- import { DeviceId } from '@atproto/oauth-provider'
3
- import { DidString } from '@atproto/syntax'
2
+ import { DeviceId, Did } from '@atproto/oauth-provider'
4
3
  import { toDateISO } from '../../db/index.js'
5
4
  import { AccountDb } from '../db/index.js'
6
5
  import { selectAccountQB } from './account.js'
@@ -26,13 +25,10 @@ export function upsertQB(db: AccountDb, deviceId: DeviceId, did: string) {
26
25
 
27
26
  export function selectQB(
28
27
  db: AccountDb,
29
- filter: {
30
- sub?: string
31
- deviceId?: DeviceId
32
- },
28
+ filter: { did?: Did; deviceId?: DeviceId },
33
29
  ) {
34
30
  assert(
35
- filter.sub != null || filter.deviceId != null,
31
+ filter.did != null || filter.deviceId != null,
36
32
  'Either sub or deviceId must be provided',
37
33
  )
38
34
 
@@ -52,10 +48,8 @@ export function selectQB(
52
48
  'device.ipAddress',
53
49
  'device.lastSeenAt',
54
50
  ])
55
- .if(filter.sub != null, (qb) =>
56
- qb.where('actor.did', '=', filter.sub! as DidString),
57
- )
58
- .if(filter.deviceId != null, (qb) =>
51
+ .$if(filter.did != null, (qb) => qb.where('actor.did', '=', filter.did!))
52
+ .$if(filter.deviceId != null, (qb) =>
59
53
  qb.where('account_device.deviceId', '=', filter.deviceId!),
60
54
  )
61
55
  )
@@ -46,8 +46,10 @@ export const selectAccountQB = (db: AccountDb, flags?: AvailabilityFlags) => {
46
46
  return db.db
47
47
  .selectFrom('actor')
48
48
  .leftJoin('account', 'actor.did', 'account.did')
49
- .if(!includeTakenDown, (qb) => qb.where(notSoftDeletedClause(ref('actor'))))
50
- .if(!includeDeactivated, (qb) =>
49
+ .$if(!includeTakenDown, (qb) =>
50
+ qb.where(notSoftDeletedClause(ref('actor'))),
51
+ )
52
+ .$if(!includeDeactivated, (qb) =>
51
53
  qb.where('actor.deactivatedAt', 'is', null),
52
54
  )
53
55
  .select([
@@ -69,11 +71,11 @@ export const getAccount = async (
69
71
  flags?: AvailabilityFlags,
70
72
  ): Promise<ActorAccount | null> => {
71
73
  const found = await selectAccountQB(db, flags)
72
- .where((qb) => {
74
+ .where((eb) => {
73
75
  if (isDidIdentifier(handleOrDid)) {
74
- return qb.where('actor.did', '=', handleOrDid)
76
+ return eb('actor.did', '=', handleOrDid)
75
77
  } else {
76
- return qb.where('actor.handle', '=', handleOrDid)
78
+ return eb('actor.handle', '=', handleOrDid)
77
79
  }
78
80
  })
79
81
  .executeTakeFirst()
@@ -202,12 +204,16 @@ export const updateHandle = async (
202
204
  .updateTable('actor')
203
205
  .set({ handle })
204
206
  .where('did', '=', did)
205
- .whereNotExists(
206
- db.db
207
- .selectFrom('actor')
208
- .where('handle', '=', handle)
209
- .where('did', '!=', did)
210
- .selectAll(),
207
+ .where(({ not, exists }) =>
208
+ not(
209
+ exists(
210
+ db.db
211
+ .selectFrom('actor')
212
+ .where('handle', '=', handle)
213
+ .where('did', '!=', did)
214
+ .selectAll(),
215
+ ),
216
+ ),
211
217
  ),
212
218
  )
213
219
  if (res.numUpdatedRows < 1) {
@@ -290,8 +296,8 @@ export const deactivateAccount = async (
290
296
  db: AccountDb,
291
297
  did: DidString,
292
298
  deleteAfter: string | null,
293
- ) => {
294
- await db.executeWithRetry(
299
+ ): Promise<boolean> => {
300
+ const [res] = await db.executeWithRetry(
295
301
  db.db
296
302
  .updateTable('actor')
297
303
  .set({
@@ -300,18 +306,35 @@ export const deactivateAccount = async (
300
306
  })
301
307
  .where('did', '=', did),
302
308
  )
309
+
310
+ return res.numUpdatedRows > 0
303
311
  }
304
312
 
305
- export const activateAccount = async (db: AccountDb, did: DidString) => {
306
- await db.executeWithRetry(
313
+ export const activateAccount = async (
314
+ db: AccountDb,
315
+ did: DidString,
316
+ flags?: AvailabilityFlags,
317
+ ): Promise<boolean> => {
318
+ const { includeTakenDown = false, includeDeactivated = true } = flags ?? {}
319
+ const { ref } = db.db.dynamic
320
+
321
+ const [res] = await db.executeWithRetry(
307
322
  db.db
308
323
  .updateTable('actor')
309
324
  .set({
310
325
  deactivatedAt: null,
311
326
  deleteAfter: null,
312
327
  })
313
- .where('did', '=', did),
328
+ .where('did', '=', did)
329
+ .$if(!includeTakenDown, (q) =>
330
+ q.where(notSoftDeletedClause(ref('actor'))),
331
+ )
332
+ .$if(!includeDeactivated, (q) =>
333
+ q.where('actor.deactivatedAt', 'is not', null),
334
+ ),
314
335
  )
336
+
337
+ return res.numUpdatedRows > 0
315
338
  }
316
339
 
317
340
  export const formatAccountStatus = (