@pkcprotocol/pkc-js 0.0.11
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.
- package/LICENSE +339 -0
- package/README.md +1663 -0
- package/dist/browser/challenges.d.ts +1 -0
- package/dist/browser/challenges.js +2 -0
- package/dist/browser/challenges.js.map +1 -0
- package/dist/browser/clients/base-client-manager.d.ts +126 -0
- package/dist/browser/clients/base-client-manager.js +673 -0
- package/dist/browser/clients/base-client-manager.js.map +1 -0
- package/dist/browser/clients/name-resolver-client.d.ts +8 -0
- package/dist/browser/clients/name-resolver-client.js +10 -0
- package/dist/browser/clients/name-resolver-client.js.map +1 -0
- package/dist/browser/clients/pkc-typed-emitter.d.ts +9 -0
- package/dist/browser/clients/pkc-typed-emitter.js +52 -0
- package/dist/browser/clients/pkc-typed-emitter.js.map +1 -0
- package/dist/browser/clients/rpc-client/decode-rpc-response-util.d.ts +8 -0
- package/dist/browser/clients/rpc-client/decode-rpc-response-util.js +53 -0
- package/dist/browser/clients/rpc-client/decode-rpc-response-util.js.map +1 -0
- package/dist/browser/clients/rpc-client/pkc-rpc-client.d.ts +68 -0
- package/dist/browser/clients/rpc-client/pkc-rpc-client.js +404 -0
- package/dist/browser/clients/rpc-client/pkc-rpc-client.js.map +1 -0
- package/dist/browser/clients/rpc-client/rpc-schema-util.d.ts +147 -0
- package/dist/browser/clients/rpc-client/rpc-schema-util.js +11 -0
- package/dist/browser/clients/rpc-client/rpc-schema-util.js.map +1 -0
- package/dist/browser/clients/rpc-client/schema.d.ts +433 -0
- package/dist/browser/clients/rpc-client/schema.js +49 -0
- package/dist/browser/clients/rpc-client/schema.js.map +1 -0
- package/dist/browser/clients/rpc-client/types.d.ts +8 -0
- package/dist/browser/clients/rpc-client/types.js +2 -0
- package/dist/browser/clients/rpc-client/types.js.map +1 -0
- package/dist/browser/community/community-client-manager.d.ts +60 -0
- package/dist/browser/community/community-client-manager.js +717 -0
- package/dist/browser/community/community-client-manager.js.map +1 -0
- package/dist/browser/community/community-clients.d.ts +18 -0
- package/dist/browser/community/community-clients.js +12 -0
- package/dist/browser/community/community-clients.js.map +1 -0
- package/dist/browser/community/community-wire.d.ts +20 -0
- package/dist/browser/community/community-wire.js +38 -0
- package/dist/browser/community/community-wire.js.map +1 -0
- package/dist/browser/community/remote-community.d.ts +110 -0
- package/dist/browser/community/remote-community.js +555 -0
- package/dist/browser/community/remote-community.js.map +1 -0
- package/dist/browser/community/rpc-local-community.d.ts +41 -0
- package/dist/browser/community/rpc-local-community.js +289 -0
- package/dist/browser/community/rpc-local-community.js.map +1 -0
- package/dist/browser/community/rpc-remote-community.d.ts +18 -0
- package/dist/browser/community/rpc-remote-community.js +286 -0
- package/dist/browser/community/rpc-remote-community.js.map +1 -0
- package/dist/browser/community/schema.d.ts +4217 -0
- package/dist/browser/community/schema.js +289 -0
- package/dist/browser/community/schema.js.map +1 -0
- package/dist/browser/community/types.d.ts +135 -0
- package/dist/browser/community/types.js +2 -0
- package/dist/browser/community/types.js.map +1 -0
- package/dist/browser/constants.d.ts +6 -0
- package/dist/browser/constants.js +9 -0
- package/dist/browser/constants.js.map +1 -0
- package/dist/browser/decorator-util.d.ts +1 -0
- package/dist/browser/decorator-util.js +35 -0
- package/dist/browser/decorator-util.js.map +1 -0
- package/dist/browser/errors.d.ts +343 -0
- package/dist/browser/errors.js +358 -0
- package/dist/browser/errors.js.map +1 -0
- package/dist/browser/general-util/limited-set.d.ts +15 -0
- package/dist/browser/general-util/limited-set.js +66 -0
- package/dist/browser/general-util/limited-set.js.map +1 -0
- package/dist/browser/generated-version.d.ts +1 -0
- package/dist/browser/generated-version.js +3 -0
- package/dist/browser/generated-version.js.map +1 -0
- package/dist/browser/generic-state-client.d.ts +6 -0
- package/dist/browser/generic-state-client.js +11 -0
- package/dist/browser/generic-state-client.js.map +1 -0
- package/dist/browser/helia/helia-for-pkc.d.ts +3 -0
- package/dist/browser/helia/helia-for-pkc.js +255 -0
- package/dist/browser/helia/helia-for-pkc.js.map +1 -0
- package/dist/browser/helia/ipns-over-pubsub-with-fetch.d.ts +36 -0
- package/dist/browser/helia/ipns-over-pubsub-with-fetch.js +229 -0
- package/dist/browser/helia/ipns-over-pubsub-with-fetch.js.map +1 -0
- package/dist/browser/helia/libp2pjsClient.d.ts +27 -0
- package/dist/browser/helia/libp2pjsClient.js +15 -0
- package/dist/browser/helia/libp2pjsClient.js.map +1 -0
- package/dist/browser/helia/types.d.ts +19 -0
- package/dist/browser/helia/types.js +2 -0
- package/dist/browser/helia/types.js.map +1 -0
- package/dist/browser/helia/util.d.ts +13 -0
- package/dist/browser/helia/util.js +98 -0
- package/dist/browser/helia/util.js.map +1 -0
- package/dist/browser/index.d.ts +244 -0
- package/dist/browser/index.js +36 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/logger.d.ts +12 -0
- package/dist/browser/logger.js +11 -0
- package/dist/browser/logger.js.map +1 -0
- package/dist/browser/pages/pages-client-manager.d.ts +159 -0
- package/dist/browser/pages/pages-client-manager.js +334 -0
- package/dist/browser/pages/pages-client-manager.js.map +1 -0
- package/dist/browser/pages/pages-clients.d.ts +11 -0
- package/dist/browser/pages/pages-clients.js +10 -0
- package/dist/browser/pages/pages-clients.js.map +1 -0
- package/dist/browser/pages/pages.d.ts +107 -0
- package/dist/browser/pages/pages.js +262 -0
- package/dist/browser/pages/pages.js.map +1 -0
- package/dist/browser/pages/schema-util.d.ts +3 -0
- package/dist/browser/pages/schema-util.js +3 -0
- package/dist/browser/pages/schema-util.js.map +1 -0
- package/dist/browser/pages/schema.d.ts +719 -0
- package/dist/browser/pages/schema.js +32 -0
- package/dist/browser/pages/schema.js.map +1 -0
- package/dist/browser/pages/types.d.ts +44 -0
- package/dist/browser/pages/types.js +2 -0
- package/dist/browser/pages/types.js.map +1 -0
- package/dist/browser/pages/util.d.ts +56 -0
- package/dist/browser/pages/util.js +446 -0
- package/dist/browser/pages/util.js.map +1 -0
- package/dist/browser/pkc/pkc-client-manager.d.ts +44 -0
- package/dist/browser/pkc/pkc-client-manager.js +156 -0
- package/dist/browser/pkc/pkc-client-manager.js.map +1 -0
- package/dist/browser/pkc/pkc-clients.d.ts +11 -0
- package/dist/browser/pkc/pkc-clients.js +8 -0
- package/dist/browser/pkc/pkc-clients.js.map +1 -0
- package/dist/browser/pkc/pkc-with-rpc-client.d.ts +19 -0
- package/dist/browser/pkc/pkc-with-rpc-client.js +128 -0
- package/dist/browser/pkc/pkc-with-rpc-client.js.map +1 -0
- package/dist/browser/pkc/pkc.d.ts +137 -0
- package/dist/browser/pkc/pkc.js +888 -0
- package/dist/browser/pkc/pkc.js.map +1 -0
- package/dist/browser/pkc/tracked-instance-registry-util.d.ts +44 -0
- package/dist/browser/pkc/tracked-instance-registry-util.js +106 -0
- package/dist/browser/pkc/tracked-instance-registry-util.js.map +1 -0
- package/dist/browser/pkc/tracked-instance-registry.d.ts +18 -0
- package/dist/browser/pkc/tracked-instance-registry.js +134 -0
- package/dist/browser/pkc/tracked-instance-registry.js.map +1 -0
- package/dist/browser/pkc-error.d.ts +65 -0
- package/dist/browser/pkc-error.js +137 -0
- package/dist/browser/pkc-error.js.map +1 -0
- package/dist/browser/publications/comment/comment-client-manager.d.ts +86 -0
- package/dist/browser/publications/comment/comment-client-manager.js +908 -0
- package/dist/browser/publications/comment/comment-client-manager.js.map +1 -0
- package/dist/browser/publications/comment/comment-clients.d.ts +19 -0
- package/dist/browser/publications/comment/comment-clients.js +12 -0
- package/dist/browser/publications/comment/comment-clients.js.map +1 -0
- package/dist/browser/publications/comment/comment-util.d.ts +10 -0
- package/dist/browser/publications/comment/comment-util.js +202 -0
- package/dist/browser/publications/comment/comment-util.js.map +1 -0
- package/dist/browser/publications/comment/comment.d.ts +147 -0
- package/dist/browser/publications/comment/comment.js +1044 -0
- package/dist/browser/publications/comment/comment.js.map +1 -0
- package/dist/browser/publications/comment/schema.d.ts +1237 -0
- package/dist/browser/publications/comment/schema.js +184 -0
- package/dist/browser/publications/comment/schema.js.map +1 -0
- package/dist/browser/publications/comment/types.d.ts +100 -0
- package/dist/browser/publications/comment/types.js +2 -0
- package/dist/browser/publications/comment/types.js.map +1 -0
- package/dist/browser/publications/comment-edit/comment-edit.d.ts +41 -0
- package/dist/browser/publications/comment-edit/comment-edit.js +63 -0
- package/dist/browser/publications/comment-edit/comment-edit.js.map +1 -0
- package/dist/browser/publications/comment-edit/schema.d.ts +295 -0
- package/dist/browser/publications/comment-edit/schema.js +55 -0
- package/dist/browser/publications/comment-edit/schema.js.map +1 -0
- package/dist/browser/publications/comment-edit/types.d.ts +25 -0
- package/dist/browser/publications/comment-edit/types.js +2 -0
- package/dist/browser/publications/comment-edit/types.js.map +1 -0
- package/dist/browser/publications/comment-moderation/comment-moderation.d.ts +36 -0
- package/dist/browser/publications/comment-moderation/comment-moderation.js +53 -0
- package/dist/browser/publications/comment-moderation/comment-moderation.js.map +1 -0
- package/dist/browser/publications/comment-moderation/schema.d.ts +315 -0
- package/dist/browser/publications/comment-moderation/schema.js +62 -0
- package/dist/browser/publications/comment-moderation/schema.js.map +1 -0
- package/dist/browser/publications/comment-moderation/types.d.ts +22 -0
- package/dist/browser/publications/comment-moderation/types.js +2 -0
- package/dist/browser/publications/comment-moderation/types.js.map +1 -0
- package/dist/browser/publications/community-edit/community-edit.d.ts +35 -0
- package/dist/browser/publications/community-edit/community-edit.js +50 -0
- package/dist/browser/publications/community-edit/community-edit.js.map +1 -0
- package/dist/browser/publications/community-edit/schema.d.ts +467 -0
- package/dist/browser/publications/community-edit/schema.js +36 -0
- package/dist/browser/publications/community-edit/schema.js.map +1 -0
- package/dist/browser/publications/community-edit/types.d.ts +19 -0
- package/dist/browser/publications/community-edit/types.js +2 -0
- package/dist/browser/publications/community-edit/types.js.map +1 -0
- package/dist/browser/publications/publication-author.d.ts +22 -0
- package/dist/browser/publications/publication-author.js +66 -0
- package/dist/browser/publications/publication-author.js.map +1 -0
- package/dist/browser/publications/publication-client-manager.d.ts +62 -0
- package/dist/browser/publications/publication-client-manager.js +257 -0
- package/dist/browser/publications/publication-client-manager.js.map +1 -0
- package/dist/browser/publications/publication-clients.d.ts +19 -0
- package/dist/browser/publications/publication-clients.js +12 -0
- package/dist/browser/publications/publication-clients.js.map +1 -0
- package/dist/browser/publications/publication-community.d.ts +55 -0
- package/dist/browser/publications/publication-community.js +87 -0
- package/dist/browser/publications/publication-community.js.map +1 -0
- package/dist/browser/publications/publication.d.ts +120 -0
- package/dist/browser/publications/publication.js +950 -0
- package/dist/browser/publications/publication.js.map +1 -0
- package/dist/browser/publications/types.d.ts +26 -0
- package/dist/browser/publications/types.js +2 -0
- package/dist/browser/publications/types.js.map +1 -0
- package/dist/browser/publications/vote/schema.d.ts +150 -0
- package/dist/browser/publications/vote/schema.js +44 -0
- package/dist/browser/publications/vote/schema.js.map +1 -0
- package/dist/browser/publications/vote/types.d.ts +21 -0
- package/dist/browser/publications/vote/types.js +2 -0
- package/dist/browser/publications/vote/types.js.map +1 -0
- package/dist/browser/publications/vote/vote.d.ts +36 -0
- package/dist/browser/publications/vote/vote.js +49 -0
- package/dist/browser/publications/vote/vote.js.map +1 -0
- package/dist/browser/pubsub-messages/schema.d.ts +964 -0
- package/dist/browser/pubsub-messages/schema.js +98 -0
- package/dist/browser/pubsub-messages/schema.js.map +1 -0
- package/dist/browser/pubsub-messages/types.d.ts +81 -0
- package/dist/browser/pubsub-messages/types.js +2 -0
- package/dist/browser/pubsub-messages/types.js.map +1 -0
- package/dist/browser/rpc/src/index.d.ts +483 -0
- package/dist/browser/rpc/src/index.js +1267 -0
- package/dist/browser/rpc/src/index.js.map +1 -0
- package/dist/browser/rpc/src/json-rpc-util.d.ts +1 -0
- package/dist/browser/rpc/src/json-rpc-util.js +19 -0
- package/dist/browser/rpc/src/json-rpc-util.js.map +1 -0
- package/dist/browser/rpc/src/lib/pkc-js/index.d.ts +132 -0
- package/dist/browser/rpc/src/lib/pkc-js/index.js +29 -0
- package/dist/browser/rpc/src/lib/pkc-js/index.js.map +1 -0
- package/dist/browser/rpc/src/lib/pkc-js/pkc-js-mock.d.ts +1 -0
- package/dist/browser/rpc/src/lib/pkc-js/pkc-js-mock.js +472 -0
- package/dist/browser/rpc/src/lib/pkc-js/pkc-js-mock.js.map +1 -0
- package/dist/browser/rpc/src/schema.d.ts +843 -0
- package/dist/browser/rpc/src/schema.js +28 -0
- package/dist/browser/rpc/src/schema.js.map +1 -0
- package/dist/browser/rpc/src/types.d.ts +24 -0
- package/dist/browser/rpc/src/types.js +2 -0
- package/dist/browser/rpc/src/types.js.map +1 -0
- package/dist/browser/rpc/src/utils.d.ts +7 -0
- package/dist/browser/rpc/src/utils.js +58 -0
- package/dist/browser/rpc/src/utils.js.map +1 -0
- package/dist/browser/runtime/browser/community/challenges/index.d.ts +6 -0
- package/dist/browser/runtime/browser/community/challenges/index.js +7 -0
- package/dist/browser/runtime/browser/community/challenges/index.js.map +1 -0
- package/dist/browser/runtime/browser/community/local-community.d.ts +3 -0
- package/dist/browser/runtime/browser/community/local-community.js +6 -0
- package/dist/browser/runtime/browser/community/local-community.js.map +1 -0
- package/dist/browser/runtime/browser/db-handler.d.ts +4 -0
- package/dist/browser/runtime/browser/db-handler.js +8 -0
- package/dist/browser/runtime/browser/db-handler.js.map +1 -0
- package/dist/browser/runtime/browser/localforage-lru.d.ts +15 -0
- package/dist/browser/runtime/browser/localforage-lru.js +140 -0
- package/dist/browser/runtime/browser/localforage-lru.js.map +1 -0
- package/dist/browser/runtime/browser/lru-storage.d.ts +14 -0
- package/dist/browser/runtime/browser/lru-storage.js +34 -0
- package/dist/browser/runtime/browser/lru-storage.js.map +1 -0
- package/dist/browser/runtime/browser/native-functions.d.ts +3 -0
- package/dist/browser/runtime/browser/native-functions.js +6 -0
- package/dist/browser/runtime/browser/native-functions.js.map +1 -0
- package/dist/browser/runtime/browser/polyfill.d.ts +3 -0
- package/dist/browser/runtime/browser/polyfill.js +37 -0
- package/dist/browser/runtime/browser/polyfill.js.map +1 -0
- package/dist/browser/runtime/browser/setup-kubo-address-rewriter-and-http-router.d.ts +1 -0
- package/dist/browser/runtime/browser/setup-kubo-address-rewriter-and-http-router.js +4 -0
- package/dist/browser/runtime/browser/setup-kubo-address-rewriter-and-http-router.js.map +1 -0
- package/dist/browser/runtime/browser/storage.d.ts +13 -0
- package/dist/browser/runtime/browser/storage.js +37 -0
- package/dist/browser/runtime/browser/storage.js.map +1 -0
- package/dist/browser/runtime/browser/util.d.ts +14 -0
- package/dist/browser/runtime/browser/util.js +61 -0
- package/dist/browser/runtime/browser/util.js.map +1 -0
- package/dist/browser/runtime/node/address-rewriter-db.d.ts +31 -0
- package/dist/browser/runtime/node/address-rewriter-db.js +156 -0
- package/dist/browser/runtime/node/address-rewriter-db.js.map +1 -0
- package/dist/browser/runtime/node/addresses-rewriter-proxy-server.d.ts +45 -0
- package/dist/browser/runtime/node/addresses-rewriter-proxy-server.js +493 -0
- package/dist/browser/runtime/node/addresses-rewriter-proxy-server.js.map +1 -0
- package/dist/browser/runtime/node/community/challenges/exclude/exclude.d.ts +8 -0
- package/dist/browser/runtime/node/community/challenges/exclude/exclude.js +280 -0
- package/dist/browser/runtime/node/community/challenges/exclude/exclude.js.map +1 -0
- package/dist/browser/runtime/node/community/challenges/exclude/index.d.ts +3 -0
- package/dist/browser/runtime/node/community/challenges/exclude/index.js +4 -0
- package/dist/browser/runtime/node/community/challenges/exclude/index.js.map +1 -0
- package/dist/browser/runtime/node/community/challenges/exclude/rate-limiter.d.ts +5 -0
- package/dist/browser/runtime/node/community/challenges/exclude/rate-limiter.js +127 -0
- package/dist/browser/runtime/node/community/challenges/exclude/rate-limiter.js.map +1 -0
- package/dist/browser/runtime/node/community/challenges/exclude/utils.d.ts +13 -0
- package/dist/browser/runtime/node/community/challenges/exclude/utils.js +52 -0
- package/dist/browser/runtime/node/community/challenges/exclude/utils.js.map +1 -0
- package/dist/browser/runtime/node/community/challenges/index.d.ts +32 -0
- package/dist/browser/runtime/node/community/challenges/index.js +307 -0
- package/dist/browser/runtime/node/community/challenges/index.js.map +1 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/blacklist.d.ts +5 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/blacklist.js +118 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/blacklist.js.map +1 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/fail.d.ts +5 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/fail.js +26 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/fail.js.map +1 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/publication-match.d.ts +5 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/publication-match.js +135 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/publication-match.js.map +1 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/question.d.ts +5 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/question.js +66 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/question.js.map +1 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/text-math.d.ts +5 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/text-math.js +61 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/text-math.js.map +1 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/whitelist.d.ts +5 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/whitelist.js +118 -0
- package/dist/browser/runtime/node/community/challenges/pkc-js-challenges/whitelist.js.map +1 -0
- package/dist/browser/runtime/node/community/db-handler-types.d.ts +19 -0
- package/dist/browser/runtime/node/community/db-handler-types.js +2 -0
- package/dist/browser/runtime/node/community/db-handler-types.js.map +1 -0
- package/dist/browser/runtime/node/community/db-handler.d.ts +226 -0
- package/dist/browser/runtime/node/community/db-handler.js +2462 -0
- package/dist/browser/runtime/node/community/db-handler.js.map +1 -0
- package/dist/browser/runtime/node/community/db-row-parser.d.ts +19 -0
- package/dist/browser/runtime/node/community/db-row-parser.js +40 -0
- package/dist/browser/runtime/node/community/db-row-parser.js.map +1 -0
- package/dist/browser/runtime/node/community/keyv-better-sqlite3.d.ts +68 -0
- package/dist/browser/runtime/node/community/keyv-better-sqlite3.js +251 -0
- package/dist/browser/runtime/node/community/keyv-better-sqlite3.js.map +1 -0
- package/dist/browser/runtime/node/community/local-community.d.ts +129 -0
- package/dist/browser/runtime/node/community/local-community.js +2978 -0
- package/dist/browser/runtime/node/community/local-community.js.map +1 -0
- package/dist/browser/runtime/node/community/page-generator.d.ts +433 -0
- package/dist/browser/runtime/node/community/page-generator.js +441 -0
- package/dist/browser/runtime/node/community/page-generator.js.map +1 -0
- package/dist/browser/runtime/node/lru-storage.d.ts +14 -0
- package/dist/browser/runtime/node/lru-storage.js +40 -0
- package/dist/browser/runtime/node/lru-storage.js.map +1 -0
- package/dist/browser/runtime/node/native-functions.d.ts +3 -0
- package/dist/browser/runtime/node/native-functions.js +7 -0
- package/dist/browser/runtime/node/native-functions.js.map +1 -0
- package/dist/browser/runtime/node/polyfill.d.ts +3 -0
- package/dist/browser/runtime/node/polyfill.js +20 -0
- package/dist/browser/runtime/node/polyfill.js.map +1 -0
- package/dist/browser/runtime/node/setup-kubo-address-rewriter-and-http-router.d.ts +4 -0
- package/dist/browser/runtime/node/setup-kubo-address-rewriter-and-http-router.js +240 -0
- package/dist/browser/runtime/node/setup-kubo-address-rewriter-and-http-router.js.map +1 -0
- package/dist/browser/runtime/node/sqlite-lru-cache.d.ts +52 -0
- package/dist/browser/runtime/node/sqlite-lru-cache.js +127 -0
- package/dist/browser/runtime/node/sqlite-lru-cache.js.map +1 -0
- package/dist/browser/runtime/node/storage.d.ts +14 -0
- package/dist/browser/runtime/node/storage.js +52 -0
- package/dist/browser/runtime/node/storage.js.map +1 -0
- package/dist/browser/runtime/node/test/helpers/hanging-runner.d.ts +1 -0
- package/dist/browser/runtime/node/test/helpers/hanging-runner.js +157 -0
- package/dist/browser/runtime/node/test/helpers/hanging-runner.js.map +1 -0
- package/dist/browser/runtime/node/test/helpers/run-hanging-node.d.ts +7 -0
- package/dist/browser/runtime/node/test/helpers/run-hanging-node.js +68 -0
- package/dist/browser/runtime/node/test/helpers/run-hanging-node.js.map +1 -0
- package/dist/browser/runtime/node/test/mock-http-router.d.ts +54 -0
- package/dist/browser/runtime/node/test/mock-http-router.js +397 -0
- package/dist/browser/runtime/node/test/mock-http-router.js.map +1 -0
- package/dist/browser/runtime/node/util.d.ts +43 -0
- package/dist/browser/runtime/node/util.js +384 -0
- package/dist/browser/runtime/node/util.js.map +1 -0
- package/dist/browser/schema/schema-util.d.ts +2751 -0
- package/dist/browser/schema/schema-util.js +562 -0
- package/dist/browser/schema/schema-util.js.map +1 -0
- package/dist/browser/schema/schema.d.ts +237 -0
- package/dist/browser/schema/schema.js +128 -0
- package/dist/browser/schema/schema.js.map +1 -0
- package/dist/browser/schema.d.ts +1142 -0
- package/dist/browser/schema.js +104 -0
- package/dist/browser/schema.js.map +1 -0
- package/dist/browser/signer/constants.d.ts +2 -0
- package/dist/browser/signer/constants.js +3 -0
- package/dist/browser/signer/constants.js.map +1 -0
- package/dist/browser/signer/encryption.d.ts +21 -0
- package/dist/browser/signer/encryption.js +122 -0
- package/dist/browser/signer/encryption.js.map +1 -0
- package/dist/browser/signer/index.d.ts +21 -0
- package/dist/browser/signer/index.js +49 -0
- package/dist/browser/signer/index.js.map +1 -0
- package/dist/browser/signer/signatures.d.ts +200 -0
- package/dist/browser/signer/signatures.js +594 -0
- package/dist/browser/signer/signatures.js.map +1 -0
- package/dist/browser/signer/types.d.ts +20 -0
- package/dist/browser/signer/types.js +2 -0
- package/dist/browser/signer/types.js.map +1 -0
- package/dist/browser/signer/util.d.ts +14 -0
- package/dist/browser/signer/util.js +156 -0
- package/dist/browser/signer/util.js.map +1 -0
- package/dist/browser/stats.d.ts +15 -0
- package/dist/browser/stats.js +64 -0
- package/dist/browser/stats.js.map +1 -0
- package/dist/browser/test/mock-ipfs-client.d.ts +34 -0
- package/dist/browser/test/mock-ipfs-client.js +208 -0
- package/dist/browser/test/mock-ipfs-client.js.map +1 -0
- package/dist/browser/test/node/hanging-test/scenarios/comment-publish-pending.scenario.d.ts +8 -0
- package/dist/browser/test/node/hanging-test/scenarios/comment-publish-pending.scenario.js +21 -0
- package/dist/browser/test/node/hanging-test/scenarios/comment-publish-pending.scenario.js.map +1 -0
- package/dist/browser/test/node/hanging-test/scenarios/comment-publish.scenario.d.ts +8 -0
- package/dist/browser/test/node/hanging-test/scenarios/comment-publish.scenario.js +19 -0
- package/dist/browser/test/node/hanging-test/scenarios/comment-publish.scenario.js.map +1 -0
- package/dist/browser/test/node/hanging-test/scenarios/comment-update.scenario.d.ts +8 -0
- package/dist/browser/test/node/hanging-test/scenarios/comment-update.scenario.js +22 -0
- package/dist/browser/test/node/hanging-test/scenarios/comment-update.scenario.js.map +1 -0
- package/dist/browser/test/node/hanging-test/scenarios/community-start.scenario.d.ts +8 -0
- package/dist/browser/test/node/hanging-test/scenarios/community-start.scenario.js +23 -0
- package/dist/browser/test/node/hanging-test/scenarios/community-start.scenario.js.map +1 -0
- package/dist/browser/test/node/hanging-test/scenarios/community-update.scenario.d.ts +8 -0
- package/dist/browser/test/node/hanging-test/scenarios/community-update.scenario.js +21 -0
- package/dist/browser/test/node/hanging-test/scenarios/community-update.scenario.js.map +1 -0
- package/dist/browser/test/node/hanging-test/scenarios/destroy-only.scenario.d.ts +7 -0
- package/dist/browser/test/node/hanging-test/scenarios/destroy-only.scenario.js +15 -0
- package/dist/browser/test/node/hanging-test/scenarios/destroy-only.scenario.js.map +1 -0
- package/dist/browser/test/node/hanging-test/scenarios/hanging-test-util.d.ts +30 -0
- package/dist/browser/test/node/hanging-test/scenarios/hanging-test-util.js +46 -0
- package/dist/browser/test/node/hanging-test/scenarios/hanging-test-util.js.map +1 -0
- package/dist/browser/test/test-util.d.ts +1019 -0
- package/dist/browser/test/test-util.js +1886 -0
- package/dist/browser/test/test-util.js.map +1 -0
- package/dist/browser/types.d.ts +165 -0
- package/dist/browser/types.js +2 -0
- package/dist/browser/types.js.map +1 -0
- package/dist/browser/util/inflight-fetch-manager.d.ts +11 -0
- package/dist/browser/util/inflight-fetch-manager.js +41 -0
- package/dist/browser/util/inflight-fetch-manager.js.map +1 -0
- package/dist/browser/util.d.ts +120 -0
- package/dist/browser/util.js +816 -0
- package/dist/browser/util.js.map +1 -0
- package/dist/browser/version.d.ts +7 -0
- package/dist/browser/version.js +12 -0
- package/dist/browser/version.js.map +1 -0
- package/dist/browser/zod-error-map.d.ts +1 -0
- package/dist/browser/zod-error-map.js +10 -0
- package/dist/browser/zod-error-map.js.map +1 -0
- package/dist/node/challenges.d.ts +1 -0
- package/dist/node/challenges.js +2 -0
- package/dist/node/challenges.js.map +1 -0
- package/dist/node/clients/base-client-manager.d.ts +126 -0
- package/dist/node/clients/base-client-manager.js +673 -0
- package/dist/node/clients/base-client-manager.js.map +1 -0
- package/dist/node/clients/name-resolver-client.d.ts +8 -0
- package/dist/node/clients/name-resolver-client.js +10 -0
- package/dist/node/clients/name-resolver-client.js.map +1 -0
- package/dist/node/clients/pkc-typed-emitter.d.ts +9 -0
- package/dist/node/clients/pkc-typed-emitter.js +52 -0
- package/dist/node/clients/pkc-typed-emitter.js.map +1 -0
- package/dist/node/clients/rpc-client/decode-rpc-response-util.d.ts +8 -0
- package/dist/node/clients/rpc-client/decode-rpc-response-util.js +53 -0
- package/dist/node/clients/rpc-client/decode-rpc-response-util.js.map +1 -0
- package/dist/node/clients/rpc-client/pkc-rpc-client.d.ts +68 -0
- package/dist/node/clients/rpc-client/pkc-rpc-client.js +404 -0
- package/dist/node/clients/rpc-client/pkc-rpc-client.js.map +1 -0
- package/dist/node/clients/rpc-client/rpc-schema-util.d.ts +147 -0
- package/dist/node/clients/rpc-client/rpc-schema-util.js +11 -0
- package/dist/node/clients/rpc-client/rpc-schema-util.js.map +1 -0
- package/dist/node/clients/rpc-client/schema.d.ts +433 -0
- package/dist/node/clients/rpc-client/schema.js +49 -0
- package/dist/node/clients/rpc-client/schema.js.map +1 -0
- package/dist/node/clients/rpc-client/types.d.ts +8 -0
- package/dist/node/clients/rpc-client/types.js +2 -0
- package/dist/node/clients/rpc-client/types.js.map +1 -0
- package/dist/node/community/community-client-manager.d.ts +60 -0
- package/dist/node/community/community-client-manager.js +717 -0
- package/dist/node/community/community-client-manager.js.map +1 -0
- package/dist/node/community/community-clients.d.ts +18 -0
- package/dist/node/community/community-clients.js +12 -0
- package/dist/node/community/community-clients.js.map +1 -0
- package/dist/node/community/community-wire.d.ts +20 -0
- package/dist/node/community/community-wire.js +38 -0
- package/dist/node/community/community-wire.js.map +1 -0
- package/dist/node/community/remote-community.d.ts +110 -0
- package/dist/node/community/remote-community.js +555 -0
- package/dist/node/community/remote-community.js.map +1 -0
- package/dist/node/community/rpc-local-community.d.ts +41 -0
- package/dist/node/community/rpc-local-community.js +289 -0
- package/dist/node/community/rpc-local-community.js.map +1 -0
- package/dist/node/community/rpc-remote-community.d.ts +18 -0
- package/dist/node/community/rpc-remote-community.js +286 -0
- package/dist/node/community/rpc-remote-community.js.map +1 -0
- package/dist/node/community/schema.d.ts +4217 -0
- package/dist/node/community/schema.js +289 -0
- package/dist/node/community/schema.js.map +1 -0
- package/dist/node/community/types.d.ts +135 -0
- package/dist/node/community/types.js +2 -0
- package/dist/node/community/types.js.map +1 -0
- package/dist/node/constants.d.ts +6 -0
- package/dist/node/constants.js +9 -0
- package/dist/node/constants.js.map +1 -0
- package/dist/node/decorator-util.d.ts +1 -0
- package/dist/node/decorator-util.js +35 -0
- package/dist/node/decorator-util.js.map +1 -0
- package/dist/node/errors.d.ts +343 -0
- package/dist/node/errors.js +358 -0
- package/dist/node/errors.js.map +1 -0
- package/dist/node/general-util/limited-set.d.ts +15 -0
- package/dist/node/general-util/limited-set.js +66 -0
- package/dist/node/general-util/limited-set.js.map +1 -0
- package/dist/node/generated-version.d.ts +1 -0
- package/dist/node/generated-version.js +3 -0
- package/dist/node/generated-version.js.map +1 -0
- package/dist/node/generic-state-client.d.ts +6 -0
- package/dist/node/generic-state-client.js +11 -0
- package/dist/node/generic-state-client.js.map +1 -0
- package/dist/node/helia/helia-for-pkc.d.ts +3 -0
- package/dist/node/helia/helia-for-pkc.js +255 -0
- package/dist/node/helia/helia-for-pkc.js.map +1 -0
- package/dist/node/helia/ipns-over-pubsub-with-fetch.d.ts +36 -0
- package/dist/node/helia/ipns-over-pubsub-with-fetch.js +229 -0
- package/dist/node/helia/ipns-over-pubsub-with-fetch.js.map +1 -0
- package/dist/node/helia/libp2pjsClient.d.ts +27 -0
- package/dist/node/helia/libp2pjsClient.js +15 -0
- package/dist/node/helia/libp2pjsClient.js.map +1 -0
- package/dist/node/helia/types.d.ts +19 -0
- package/dist/node/helia/types.js +2 -0
- package/dist/node/helia/types.js.map +1 -0
- package/dist/node/helia/util.d.ts +13 -0
- package/dist/node/helia/util.js +98 -0
- package/dist/node/helia/util.js.map +1 -0
- package/dist/node/index.d.ts +244 -0
- package/dist/node/index.js +36 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/logger.d.ts +12 -0
- package/dist/node/logger.js +11 -0
- package/dist/node/logger.js.map +1 -0
- package/dist/node/pages/pages-client-manager.d.ts +159 -0
- package/dist/node/pages/pages-client-manager.js +334 -0
- package/dist/node/pages/pages-client-manager.js.map +1 -0
- package/dist/node/pages/pages-clients.d.ts +11 -0
- package/dist/node/pages/pages-clients.js +10 -0
- package/dist/node/pages/pages-clients.js.map +1 -0
- package/dist/node/pages/pages.d.ts +107 -0
- package/dist/node/pages/pages.js +262 -0
- package/dist/node/pages/pages.js.map +1 -0
- package/dist/node/pages/schema-util.d.ts +3 -0
- package/dist/node/pages/schema-util.js +3 -0
- package/dist/node/pages/schema-util.js.map +1 -0
- package/dist/node/pages/schema.d.ts +719 -0
- package/dist/node/pages/schema.js +32 -0
- package/dist/node/pages/schema.js.map +1 -0
- package/dist/node/pages/types.d.ts +44 -0
- package/dist/node/pages/types.js +2 -0
- package/dist/node/pages/types.js.map +1 -0
- package/dist/node/pages/util.d.ts +56 -0
- package/dist/node/pages/util.js +446 -0
- package/dist/node/pages/util.js.map +1 -0
- package/dist/node/pkc/pkc-client-manager.d.ts +44 -0
- package/dist/node/pkc/pkc-client-manager.js +156 -0
- package/dist/node/pkc/pkc-client-manager.js.map +1 -0
- package/dist/node/pkc/pkc-clients.d.ts +11 -0
- package/dist/node/pkc/pkc-clients.js +8 -0
- package/dist/node/pkc/pkc-clients.js.map +1 -0
- package/dist/node/pkc/pkc-with-rpc-client.d.ts +19 -0
- package/dist/node/pkc/pkc-with-rpc-client.js +128 -0
- package/dist/node/pkc/pkc-with-rpc-client.js.map +1 -0
- package/dist/node/pkc/pkc.d.ts +137 -0
- package/dist/node/pkc/pkc.js +888 -0
- package/dist/node/pkc/pkc.js.map +1 -0
- package/dist/node/pkc/tracked-instance-registry-util.d.ts +44 -0
- package/dist/node/pkc/tracked-instance-registry-util.js +106 -0
- package/dist/node/pkc/tracked-instance-registry-util.js.map +1 -0
- package/dist/node/pkc/tracked-instance-registry.d.ts +18 -0
- package/dist/node/pkc/tracked-instance-registry.js +134 -0
- package/dist/node/pkc/tracked-instance-registry.js.map +1 -0
- package/dist/node/pkc-error.d.ts +65 -0
- package/dist/node/pkc-error.js +137 -0
- package/dist/node/pkc-error.js.map +1 -0
- package/dist/node/publications/comment/comment-client-manager.d.ts +86 -0
- package/dist/node/publications/comment/comment-client-manager.js +908 -0
- package/dist/node/publications/comment/comment-client-manager.js.map +1 -0
- package/dist/node/publications/comment/comment-clients.d.ts +19 -0
- package/dist/node/publications/comment/comment-clients.js +12 -0
- package/dist/node/publications/comment/comment-clients.js.map +1 -0
- package/dist/node/publications/comment/comment-util.d.ts +10 -0
- package/dist/node/publications/comment/comment-util.js +202 -0
- package/dist/node/publications/comment/comment-util.js.map +1 -0
- package/dist/node/publications/comment/comment.d.ts +147 -0
- package/dist/node/publications/comment/comment.js +1044 -0
- package/dist/node/publications/comment/comment.js.map +1 -0
- package/dist/node/publications/comment/schema.d.ts +1237 -0
- package/dist/node/publications/comment/schema.js +184 -0
- package/dist/node/publications/comment/schema.js.map +1 -0
- package/dist/node/publications/comment/types.d.ts +100 -0
- package/dist/node/publications/comment/types.js +2 -0
- package/dist/node/publications/comment/types.js.map +1 -0
- package/dist/node/publications/comment-edit/comment-edit.d.ts +41 -0
- package/dist/node/publications/comment-edit/comment-edit.js +63 -0
- package/dist/node/publications/comment-edit/comment-edit.js.map +1 -0
- package/dist/node/publications/comment-edit/schema.d.ts +295 -0
- package/dist/node/publications/comment-edit/schema.js +55 -0
- package/dist/node/publications/comment-edit/schema.js.map +1 -0
- package/dist/node/publications/comment-edit/types.d.ts +25 -0
- package/dist/node/publications/comment-edit/types.js +2 -0
- package/dist/node/publications/comment-edit/types.js.map +1 -0
- package/dist/node/publications/comment-moderation/comment-moderation.d.ts +36 -0
- package/dist/node/publications/comment-moderation/comment-moderation.js +53 -0
- package/dist/node/publications/comment-moderation/comment-moderation.js.map +1 -0
- package/dist/node/publications/comment-moderation/schema.d.ts +315 -0
- package/dist/node/publications/comment-moderation/schema.js +62 -0
- package/dist/node/publications/comment-moderation/schema.js.map +1 -0
- package/dist/node/publications/comment-moderation/types.d.ts +22 -0
- package/dist/node/publications/comment-moderation/types.js +2 -0
- package/dist/node/publications/comment-moderation/types.js.map +1 -0
- package/dist/node/publications/community-edit/community-edit.d.ts +35 -0
- package/dist/node/publications/community-edit/community-edit.js +50 -0
- package/dist/node/publications/community-edit/community-edit.js.map +1 -0
- package/dist/node/publications/community-edit/schema.d.ts +467 -0
- package/dist/node/publications/community-edit/schema.js +36 -0
- package/dist/node/publications/community-edit/schema.js.map +1 -0
- package/dist/node/publications/community-edit/types.d.ts +19 -0
- package/dist/node/publications/community-edit/types.js +2 -0
- package/dist/node/publications/community-edit/types.js.map +1 -0
- package/dist/node/publications/publication-author.d.ts +22 -0
- package/dist/node/publications/publication-author.js +66 -0
- package/dist/node/publications/publication-author.js.map +1 -0
- package/dist/node/publications/publication-client-manager.d.ts +62 -0
- package/dist/node/publications/publication-client-manager.js +257 -0
- package/dist/node/publications/publication-client-manager.js.map +1 -0
- package/dist/node/publications/publication-clients.d.ts +19 -0
- package/dist/node/publications/publication-clients.js +12 -0
- package/dist/node/publications/publication-clients.js.map +1 -0
- package/dist/node/publications/publication-community.d.ts +55 -0
- package/dist/node/publications/publication-community.js +87 -0
- package/dist/node/publications/publication-community.js.map +1 -0
- package/dist/node/publications/publication.d.ts +120 -0
- package/dist/node/publications/publication.js +950 -0
- package/dist/node/publications/publication.js.map +1 -0
- package/dist/node/publications/types.d.ts +26 -0
- package/dist/node/publications/types.js +2 -0
- package/dist/node/publications/types.js.map +1 -0
- package/dist/node/publications/vote/schema.d.ts +150 -0
- package/dist/node/publications/vote/schema.js +44 -0
- package/dist/node/publications/vote/schema.js.map +1 -0
- package/dist/node/publications/vote/types.d.ts +21 -0
- package/dist/node/publications/vote/types.js +2 -0
- package/dist/node/publications/vote/types.js.map +1 -0
- package/dist/node/publications/vote/vote.d.ts +36 -0
- package/dist/node/publications/vote/vote.js +49 -0
- package/dist/node/publications/vote/vote.js.map +1 -0
- package/dist/node/pubsub-messages/schema.d.ts +964 -0
- package/dist/node/pubsub-messages/schema.js +98 -0
- package/dist/node/pubsub-messages/schema.js.map +1 -0
- package/dist/node/pubsub-messages/types.d.ts +81 -0
- package/dist/node/pubsub-messages/types.js +2 -0
- package/dist/node/pubsub-messages/types.js.map +1 -0
- package/dist/node/rpc/src/index.d.ts +483 -0
- package/dist/node/rpc/src/index.js +1267 -0
- package/dist/node/rpc/src/index.js.map +1 -0
- package/dist/node/rpc/src/json-rpc-util.d.ts +1 -0
- package/dist/node/rpc/src/json-rpc-util.js +19 -0
- package/dist/node/rpc/src/json-rpc-util.js.map +1 -0
- package/dist/node/rpc/src/lib/pkc-js/index.d.ts +132 -0
- package/dist/node/rpc/src/lib/pkc-js/index.js +29 -0
- package/dist/node/rpc/src/lib/pkc-js/index.js.map +1 -0
- package/dist/node/rpc/src/lib/pkc-js/pkc-js-mock.d.ts +1 -0
- package/dist/node/rpc/src/lib/pkc-js/pkc-js-mock.js +472 -0
- package/dist/node/rpc/src/lib/pkc-js/pkc-js-mock.js.map +1 -0
- package/dist/node/rpc/src/schema.d.ts +843 -0
- package/dist/node/rpc/src/schema.js +28 -0
- package/dist/node/rpc/src/schema.js.map +1 -0
- package/dist/node/rpc/src/types.d.ts +24 -0
- package/dist/node/rpc/src/types.js +2 -0
- package/dist/node/rpc/src/types.js.map +1 -0
- package/dist/node/rpc/src/utils.d.ts +7 -0
- package/dist/node/rpc/src/utils.js +58 -0
- package/dist/node/rpc/src/utils.js.map +1 -0
- package/dist/node/runtime/browser/community/challenges/index.d.ts +6 -0
- package/dist/node/runtime/browser/community/challenges/index.js +7 -0
- package/dist/node/runtime/browser/community/challenges/index.js.map +1 -0
- package/dist/node/runtime/browser/community/local-community.d.ts +3 -0
- package/dist/node/runtime/browser/community/local-community.js +6 -0
- package/dist/node/runtime/browser/community/local-community.js.map +1 -0
- package/dist/node/runtime/browser/db-handler.d.ts +4 -0
- package/dist/node/runtime/browser/db-handler.js +8 -0
- package/dist/node/runtime/browser/db-handler.js.map +1 -0
- package/dist/node/runtime/browser/localforage-lru.d.ts +15 -0
- package/dist/node/runtime/browser/localforage-lru.js +140 -0
- package/dist/node/runtime/browser/localforage-lru.js.map +1 -0
- package/dist/node/runtime/browser/lru-storage.d.ts +14 -0
- package/dist/node/runtime/browser/lru-storage.js +34 -0
- package/dist/node/runtime/browser/lru-storage.js.map +1 -0
- package/dist/node/runtime/browser/native-functions.d.ts +3 -0
- package/dist/node/runtime/browser/native-functions.js +6 -0
- package/dist/node/runtime/browser/native-functions.js.map +1 -0
- package/dist/node/runtime/browser/polyfill.d.ts +3 -0
- package/dist/node/runtime/browser/polyfill.js +37 -0
- package/dist/node/runtime/browser/polyfill.js.map +1 -0
- package/dist/node/runtime/browser/setup-kubo-address-rewriter-and-http-router.d.ts +1 -0
- package/dist/node/runtime/browser/setup-kubo-address-rewriter-and-http-router.js +4 -0
- package/dist/node/runtime/browser/setup-kubo-address-rewriter-and-http-router.js.map +1 -0
- package/dist/node/runtime/browser/storage.d.ts +13 -0
- package/dist/node/runtime/browser/storage.js +37 -0
- package/dist/node/runtime/browser/storage.js.map +1 -0
- package/dist/node/runtime/browser/util.d.ts +14 -0
- package/dist/node/runtime/browser/util.js +61 -0
- package/dist/node/runtime/browser/util.js.map +1 -0
- package/dist/node/runtime/node/address-rewriter-db.d.ts +31 -0
- package/dist/node/runtime/node/address-rewriter-db.js +156 -0
- package/dist/node/runtime/node/address-rewriter-db.js.map +1 -0
- package/dist/node/runtime/node/addresses-rewriter-proxy-server.d.ts +45 -0
- package/dist/node/runtime/node/addresses-rewriter-proxy-server.js +493 -0
- package/dist/node/runtime/node/addresses-rewriter-proxy-server.js.map +1 -0
- package/dist/node/runtime/node/community/challenges/exclude/exclude.d.ts +8 -0
- package/dist/node/runtime/node/community/challenges/exclude/exclude.js +280 -0
- package/dist/node/runtime/node/community/challenges/exclude/exclude.js.map +1 -0
- package/dist/node/runtime/node/community/challenges/exclude/index.d.ts +3 -0
- package/dist/node/runtime/node/community/challenges/exclude/index.js +4 -0
- package/dist/node/runtime/node/community/challenges/exclude/index.js.map +1 -0
- package/dist/node/runtime/node/community/challenges/exclude/rate-limiter.d.ts +5 -0
- package/dist/node/runtime/node/community/challenges/exclude/rate-limiter.js +127 -0
- package/dist/node/runtime/node/community/challenges/exclude/rate-limiter.js.map +1 -0
- package/dist/node/runtime/node/community/challenges/exclude/utils.d.ts +13 -0
- package/dist/node/runtime/node/community/challenges/exclude/utils.js +52 -0
- package/dist/node/runtime/node/community/challenges/exclude/utils.js.map +1 -0
- package/dist/node/runtime/node/community/challenges/index.d.ts +32 -0
- package/dist/node/runtime/node/community/challenges/index.js +307 -0
- package/dist/node/runtime/node/community/challenges/index.js.map +1 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/blacklist.d.ts +5 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/blacklist.js +118 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/blacklist.js.map +1 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/fail.d.ts +5 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/fail.js +26 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/fail.js.map +1 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/publication-match.d.ts +5 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/publication-match.js +135 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/publication-match.js.map +1 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/question.d.ts +5 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/question.js +66 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/question.js.map +1 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/text-math.d.ts +5 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/text-math.js +61 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/text-math.js.map +1 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/whitelist.d.ts +5 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/whitelist.js +118 -0
- package/dist/node/runtime/node/community/challenges/pkc-js-challenges/whitelist.js.map +1 -0
- package/dist/node/runtime/node/community/db-handler-types.d.ts +19 -0
- package/dist/node/runtime/node/community/db-handler-types.js +2 -0
- package/dist/node/runtime/node/community/db-handler-types.js.map +1 -0
- package/dist/node/runtime/node/community/db-handler.d.ts +226 -0
- package/dist/node/runtime/node/community/db-handler.js +2462 -0
- package/dist/node/runtime/node/community/db-handler.js.map +1 -0
- package/dist/node/runtime/node/community/db-row-parser.d.ts +19 -0
- package/dist/node/runtime/node/community/db-row-parser.js +40 -0
- package/dist/node/runtime/node/community/db-row-parser.js.map +1 -0
- package/dist/node/runtime/node/community/keyv-better-sqlite3.d.ts +68 -0
- package/dist/node/runtime/node/community/keyv-better-sqlite3.js +251 -0
- package/dist/node/runtime/node/community/keyv-better-sqlite3.js.map +1 -0
- package/dist/node/runtime/node/community/local-community.d.ts +129 -0
- package/dist/node/runtime/node/community/local-community.js +2978 -0
- package/dist/node/runtime/node/community/local-community.js.map +1 -0
- package/dist/node/runtime/node/community/page-generator.d.ts +433 -0
- package/dist/node/runtime/node/community/page-generator.js +441 -0
- package/dist/node/runtime/node/community/page-generator.js.map +1 -0
- package/dist/node/runtime/node/lru-storage.d.ts +14 -0
- package/dist/node/runtime/node/lru-storage.js +40 -0
- package/dist/node/runtime/node/lru-storage.js.map +1 -0
- package/dist/node/runtime/node/native-functions.d.ts +3 -0
- package/dist/node/runtime/node/native-functions.js +7 -0
- package/dist/node/runtime/node/native-functions.js.map +1 -0
- package/dist/node/runtime/node/polyfill.d.ts +3 -0
- package/dist/node/runtime/node/polyfill.js +20 -0
- package/dist/node/runtime/node/polyfill.js.map +1 -0
- package/dist/node/runtime/node/setup-kubo-address-rewriter-and-http-router.d.ts +4 -0
- package/dist/node/runtime/node/setup-kubo-address-rewriter-and-http-router.js +240 -0
- package/dist/node/runtime/node/setup-kubo-address-rewriter-and-http-router.js.map +1 -0
- package/dist/node/runtime/node/sqlite-lru-cache.d.ts +52 -0
- package/dist/node/runtime/node/sqlite-lru-cache.js +127 -0
- package/dist/node/runtime/node/sqlite-lru-cache.js.map +1 -0
- package/dist/node/runtime/node/storage.d.ts +14 -0
- package/dist/node/runtime/node/storage.js +52 -0
- package/dist/node/runtime/node/storage.js.map +1 -0
- package/dist/node/runtime/node/test/helpers/hanging-runner.d.ts +1 -0
- package/dist/node/runtime/node/test/helpers/hanging-runner.js +157 -0
- package/dist/node/runtime/node/test/helpers/hanging-runner.js.map +1 -0
- package/dist/node/runtime/node/test/helpers/run-hanging-node.d.ts +7 -0
- package/dist/node/runtime/node/test/helpers/run-hanging-node.js +68 -0
- package/dist/node/runtime/node/test/helpers/run-hanging-node.js.map +1 -0
- package/dist/node/runtime/node/test/mock-http-router.d.ts +54 -0
- package/dist/node/runtime/node/test/mock-http-router.js +397 -0
- package/dist/node/runtime/node/test/mock-http-router.js.map +1 -0
- package/dist/node/runtime/node/util.d.ts +43 -0
- package/dist/node/runtime/node/util.js +384 -0
- package/dist/node/runtime/node/util.js.map +1 -0
- package/dist/node/schema/schema-util.d.ts +2751 -0
- package/dist/node/schema/schema-util.js +562 -0
- package/dist/node/schema/schema-util.js.map +1 -0
- package/dist/node/schema/schema.d.ts +237 -0
- package/dist/node/schema/schema.js +128 -0
- package/dist/node/schema/schema.js.map +1 -0
- package/dist/node/schema.d.ts +1142 -0
- package/dist/node/schema.js +104 -0
- package/dist/node/schema.js.map +1 -0
- package/dist/node/signer/constants.d.ts +2 -0
- package/dist/node/signer/constants.js +3 -0
- package/dist/node/signer/constants.js.map +1 -0
- package/dist/node/signer/encryption.d.ts +21 -0
- package/dist/node/signer/encryption.js +122 -0
- package/dist/node/signer/encryption.js.map +1 -0
- package/dist/node/signer/index.d.ts +21 -0
- package/dist/node/signer/index.js +49 -0
- package/dist/node/signer/index.js.map +1 -0
- package/dist/node/signer/signatures.d.ts +200 -0
- package/dist/node/signer/signatures.js +594 -0
- package/dist/node/signer/signatures.js.map +1 -0
- package/dist/node/signer/types.d.ts +20 -0
- package/dist/node/signer/types.js +2 -0
- package/dist/node/signer/types.js.map +1 -0
- package/dist/node/signer/util.d.ts +14 -0
- package/dist/node/signer/util.js +156 -0
- package/dist/node/signer/util.js.map +1 -0
- package/dist/node/stats.d.ts +15 -0
- package/dist/node/stats.js +64 -0
- package/dist/node/stats.js.map +1 -0
- package/dist/node/test/mock-ipfs-client.d.ts +34 -0
- package/dist/node/test/mock-ipfs-client.js +208 -0
- package/dist/node/test/mock-ipfs-client.js.map +1 -0
- package/dist/node/test/node/hanging-test/scenarios/comment-publish-pending.scenario.d.ts +8 -0
- package/dist/node/test/node/hanging-test/scenarios/comment-publish-pending.scenario.js +21 -0
- package/dist/node/test/node/hanging-test/scenarios/comment-publish-pending.scenario.js.map +1 -0
- package/dist/node/test/node/hanging-test/scenarios/comment-publish.scenario.d.ts +8 -0
- package/dist/node/test/node/hanging-test/scenarios/comment-publish.scenario.js +19 -0
- package/dist/node/test/node/hanging-test/scenarios/comment-publish.scenario.js.map +1 -0
- package/dist/node/test/node/hanging-test/scenarios/comment-update.scenario.d.ts +8 -0
- package/dist/node/test/node/hanging-test/scenarios/comment-update.scenario.js +22 -0
- package/dist/node/test/node/hanging-test/scenarios/comment-update.scenario.js.map +1 -0
- package/dist/node/test/node/hanging-test/scenarios/community-start.scenario.d.ts +8 -0
- package/dist/node/test/node/hanging-test/scenarios/community-start.scenario.js +23 -0
- package/dist/node/test/node/hanging-test/scenarios/community-start.scenario.js.map +1 -0
- package/dist/node/test/node/hanging-test/scenarios/community-update.scenario.d.ts +8 -0
- package/dist/node/test/node/hanging-test/scenarios/community-update.scenario.js +21 -0
- package/dist/node/test/node/hanging-test/scenarios/community-update.scenario.js.map +1 -0
- package/dist/node/test/node/hanging-test/scenarios/destroy-only.scenario.d.ts +7 -0
- package/dist/node/test/node/hanging-test/scenarios/destroy-only.scenario.js +15 -0
- package/dist/node/test/node/hanging-test/scenarios/destroy-only.scenario.js.map +1 -0
- package/dist/node/test/node/hanging-test/scenarios/hanging-test-util.d.ts +30 -0
- package/dist/node/test/node/hanging-test/scenarios/hanging-test-util.js +46 -0
- package/dist/node/test/node/hanging-test/scenarios/hanging-test-util.js.map +1 -0
- package/dist/node/test/test-util.d.ts +1019 -0
- package/dist/node/test/test-util.js +1886 -0
- package/dist/node/test/test-util.js.map +1 -0
- package/dist/node/types.d.ts +165 -0
- package/dist/node/types.js +2 -0
- package/dist/node/types.js.map +1 -0
- package/dist/node/util/inflight-fetch-manager.d.ts +11 -0
- package/dist/node/util/inflight-fetch-manager.js +41 -0
- package/dist/node/util/inflight-fetch-manager.js.map +1 -0
- package/dist/node/util.d.ts +120 -0
- package/dist/node/util.js +816 -0
- package/dist/node/util.js.map +1 -0
- package/dist/node/version.d.ts +7 -0
- package/dist/node/version.js +12 -0
- package/dist/node/version.js.map +1 -0
- package/dist/node/zod-error-map.d.ts +1 -0
- package/dist/node/zod-error-map.js +10 -0
- package/dist/node/zod-error-map.js.map +1 -0
- package/package.json +212 -0
|
@@ -0,0 +1,2462 @@
|
|
|
1
|
+
import { getEquivalentCommunityAddresses, hideClassPrivateProps, isStringDomain, removeNullUndefinedValues, timestamp } from "../../../util.js";
|
|
2
|
+
import { PKCError } from "../../../pkc-error.js";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import assert from "assert";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import os from "os";
|
|
7
|
+
import Logger from "../../../logger.js";
|
|
8
|
+
import { deleteOldCommunityInWindows, getDefaultCommunityDbConfig } from "../util.js";
|
|
9
|
+
import env from "../../../version.js";
|
|
10
|
+
import Database from "better-sqlite3";
|
|
11
|
+
import { sha256 } from "js-sha256";
|
|
12
|
+
import lockfile from "@pkc/proper-lock-file";
|
|
13
|
+
import { getPKCAddressFromPublicKey, getPKCAddressFromPublicKeySync } from "../../../signer/util.js";
|
|
14
|
+
import * as remeda from "remeda";
|
|
15
|
+
import { CommentIpfsSchema, CommentUpdateSchema } from "../../../publications/comment/schema.js";
|
|
16
|
+
import { verifyCommentEdit, verifyCommentIpfs } from "../../../signer/signatures.js";
|
|
17
|
+
import { getCommunityChallengeFromCommunityChallengeSettings, pkcJsChallenges } from "./challenges/index.js";
|
|
18
|
+
import KeyvBetterSqlite3 from "./keyv-better-sqlite3.js";
|
|
19
|
+
import { STORAGE_KEYS } from "../../../constants.js";
|
|
20
|
+
import { CommentEditPubsubMessagePublicationSchema } from "../../../publications/comment-edit/schema.js";
|
|
21
|
+
import { TIMEFRAMES_TO_SECONDS } from "../../../pages/util.js";
|
|
22
|
+
import { parseCommentEditsRow, parseCommentUpdateRow, parseCommentsTableRow, parsePrefixedComment, parseVoteRow } from "./db-row-parser.js";
|
|
23
|
+
import { ZodError } from "zod";
|
|
24
|
+
import { messages } from "../../../errors.js";
|
|
25
|
+
import { getAuthorDomainFromWire } from "../../../publications/publication-author.js";
|
|
26
|
+
const TABLES = Object.freeze({
|
|
27
|
+
COMMENTS: "comments",
|
|
28
|
+
COMMENT_UPDATES: "commentUpdates",
|
|
29
|
+
VOTES: "votes",
|
|
30
|
+
COMMENT_MODERATIONS: "commentModerations",
|
|
31
|
+
COMMENT_EDITS: "commentEdits",
|
|
32
|
+
PSEUDONYMITY_ALIASES: "pseudonymityAliases"
|
|
33
|
+
});
|
|
34
|
+
export class DbHandler {
|
|
35
|
+
constructor(community) {
|
|
36
|
+
this._community = community;
|
|
37
|
+
this._transactionDepth = 0;
|
|
38
|
+
this._createdTables = false;
|
|
39
|
+
hideClassPrivateProps(this);
|
|
40
|
+
}
|
|
41
|
+
_parsePrefixedComment(row) {
|
|
42
|
+
const parsed = parsePrefixedComment(row);
|
|
43
|
+
const comment = removeNullUndefinedValues(this._spreadExtraProps(parsed.comment));
|
|
44
|
+
const commentUpdate = removeNullUndefinedValues(this._spreadExtraProps(parsed.commentUpdate));
|
|
45
|
+
return {
|
|
46
|
+
comment,
|
|
47
|
+
commentUpdate,
|
|
48
|
+
extras: parsed.extras
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
_parseCommentsTableRow(row) {
|
|
52
|
+
const parsed = parseCommentsTableRow(row);
|
|
53
|
+
return removeNullUndefinedValues(parsed);
|
|
54
|
+
}
|
|
55
|
+
_parseCommentUpdatesRow(row) {
|
|
56
|
+
const parsed = parseCommentUpdateRow(row);
|
|
57
|
+
return removeNullUndefinedValues(parsed);
|
|
58
|
+
}
|
|
59
|
+
_parseCommentEditsRow(row) {
|
|
60
|
+
const parsedRow = parseCommentEditsRow(row);
|
|
61
|
+
const parsed = removeNullUndefinedValues(parsedRow);
|
|
62
|
+
if (typeof parsed.id === "string") {
|
|
63
|
+
const numericId = Number(parsed.id);
|
|
64
|
+
if (!Number.isNaN(numericId))
|
|
65
|
+
parsed.id = numericId;
|
|
66
|
+
}
|
|
67
|
+
return parsed;
|
|
68
|
+
}
|
|
69
|
+
_parseVoteRow(row) {
|
|
70
|
+
const parsed = parseVoteRow(row);
|
|
71
|
+
return removeNullUndefinedValues(parsed);
|
|
72
|
+
}
|
|
73
|
+
async initDbConfigIfNeeded() {
|
|
74
|
+
if (!this._dbConfig)
|
|
75
|
+
this._dbConfig = await getDefaultCommunityDbConfig(this._community.address, this._community._pkc);
|
|
76
|
+
}
|
|
77
|
+
toJSON() {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
async initDbIfNeeded(dbConfigOptions) {
|
|
81
|
+
const log = Logger("pkc-js:local-community:db-handler:initDbIfNeeded");
|
|
82
|
+
assert(typeof this._community.address === "string" && this._community.address.length > 0, `DbHandler needs to be an instantiated with a Community that has a valid address, (${this._community.address}) was provided`);
|
|
83
|
+
await this.initDbConfigIfNeeded();
|
|
84
|
+
const dbFilePath = this._dbConfig.filename;
|
|
85
|
+
if (!this._db || !this._db.open) {
|
|
86
|
+
this._db = new Database(dbFilePath, { ...this._dbConfig, ...dbConfigOptions });
|
|
87
|
+
try {
|
|
88
|
+
this._db.pragma("journal_mode = WAL");
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
log(`Could not set WAL journal mode for ${dbFilePath}`, e);
|
|
92
|
+
throw e;
|
|
93
|
+
}
|
|
94
|
+
log("initialized a new connection to db", dbFilePath);
|
|
95
|
+
}
|
|
96
|
+
if (!this._keyv)
|
|
97
|
+
this._keyv = new KeyvBetterSqlite3(this._db);
|
|
98
|
+
}
|
|
99
|
+
async createOrMigrateTablesIfNeeded() {
|
|
100
|
+
const log = Logger("pkc-js:local-community:db-handler:createOrMigrateTablesIfNeeded");
|
|
101
|
+
if (this._createdTables)
|
|
102
|
+
return;
|
|
103
|
+
try {
|
|
104
|
+
await this._createOrMigrateTablesIfNeeded();
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
await this.initDbIfNeeded();
|
|
108
|
+
log.error(`Community (${this._community.address}) failed to create/migrate tables. Current db version (${this.getDbVersion()}), latest db version (${env.DB_VERSION}). Error`, e);
|
|
109
|
+
await this.destoryConnection();
|
|
110
|
+
throw e;
|
|
111
|
+
}
|
|
112
|
+
hideClassPrivateProps(this);
|
|
113
|
+
}
|
|
114
|
+
getDbConfig() {
|
|
115
|
+
return this._dbConfig;
|
|
116
|
+
}
|
|
117
|
+
keyvGet(key) {
|
|
118
|
+
try {
|
|
119
|
+
const res = this._keyv.get(key);
|
|
120
|
+
return res;
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
e.details = { ...e.details, key };
|
|
124
|
+
throw e;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
keyvSet(key, value, ttl) {
|
|
128
|
+
return this._keyv.set(key, value, ttl);
|
|
129
|
+
}
|
|
130
|
+
keyvDelete(key) {
|
|
131
|
+
return this._keyv.delete(key);
|
|
132
|
+
}
|
|
133
|
+
keyvHas(key) {
|
|
134
|
+
return this._keyv.has(key);
|
|
135
|
+
}
|
|
136
|
+
destoryConnection() {
|
|
137
|
+
const log = Logger("pkc-js:local-community:dbHandler:destroyConnection");
|
|
138
|
+
if (this._db && this._db.open) {
|
|
139
|
+
this._db.exec("PRAGMA checkpoint"); // write all wal to disk
|
|
140
|
+
this._db.close();
|
|
141
|
+
}
|
|
142
|
+
if (this._keyv)
|
|
143
|
+
this._keyv.disconnect();
|
|
144
|
+
//@ts-expect-error
|
|
145
|
+
this._db = this._keyv = undefined;
|
|
146
|
+
this._transactionDepth = 0;
|
|
147
|
+
log("Destroyed DB connection to community", this._community.address, "successfully");
|
|
148
|
+
}
|
|
149
|
+
createTransaction() {
|
|
150
|
+
if (this._transactionDepth === 0) {
|
|
151
|
+
this._db.exec("BEGIN");
|
|
152
|
+
}
|
|
153
|
+
this._transactionDepth++;
|
|
154
|
+
}
|
|
155
|
+
commitTransaction() {
|
|
156
|
+
if (this._transactionDepth > 0) {
|
|
157
|
+
this._transactionDepth--;
|
|
158
|
+
if (this._transactionDepth === 0) {
|
|
159
|
+
this._db.exec("COMMIT");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
rollbackTransaction() {
|
|
164
|
+
const log = Logger("pkc-js:local-community:db-handler:rollbackTransaction");
|
|
165
|
+
if (this._transactionDepth > 0) {
|
|
166
|
+
if (this._transactionDepth === 1) {
|
|
167
|
+
try {
|
|
168
|
+
this._db.exec("ROLLBACK");
|
|
169
|
+
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
log.error(`Failed to rollback transaction due to error`, e);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
this._transactionDepth--;
|
|
175
|
+
}
|
|
176
|
+
else if (this._db && this._db.open && this._db.inTransaction) {
|
|
177
|
+
log(`Transaction depth was 0, but DB was in transaction. Attempting rollback.`);
|
|
178
|
+
try {
|
|
179
|
+
this._db.exec("ROLLBACK");
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
log.error(`Failed to rollback transaction (fallback) due to error`, e);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (this._transactionDepth < 0)
|
|
186
|
+
this._transactionDepth = 0;
|
|
187
|
+
log.trace(`Rolledback transaction, this._transactionDepth = ${this._transactionDepth}`);
|
|
188
|
+
}
|
|
189
|
+
async rollbackAllTransactions() {
|
|
190
|
+
const log = Logger("pkc-js:local-community:db-handler:rollbackAllTransactions");
|
|
191
|
+
let initialDepth = this._transactionDepth;
|
|
192
|
+
while (this._transactionDepth > 0) {
|
|
193
|
+
this.rollbackTransaction();
|
|
194
|
+
}
|
|
195
|
+
if (initialDepth > 0) {
|
|
196
|
+
log.trace(`Rolled back all transactions. Initial depth was ${initialDepth}, now ${this._transactionDepth}.`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
_createCommentsTable(tableName) {
|
|
200
|
+
this._db.exec(`
|
|
201
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
202
|
+
cid TEXT NOT NULL PRIMARY KEY UNIQUE,
|
|
203
|
+
authorSignerAddress TEXT NOT NULL,
|
|
204
|
+
author TEXT NULLABLE, -- JSON
|
|
205
|
+
link TEXT NULLABLE,
|
|
206
|
+
linkWidth INTEGER NULLABLE,
|
|
207
|
+
linkHeight INTEGER NULLABLE,
|
|
208
|
+
thumbnailUrl TEXT NULLABLE,
|
|
209
|
+
thumbnailUrlWidth INTEGER NULLABLE,
|
|
210
|
+
thumbnailUrlHeight INTEGER NULLABLE,
|
|
211
|
+
parentCid TEXT NULLABLE REFERENCES ${TABLES.COMMENTS}(cid),
|
|
212
|
+
postCid TEXT NOT NULL REFERENCES ${TABLES.COMMENTS}(cid),
|
|
213
|
+
previousCid TEXT NULLABLE,
|
|
214
|
+
communityPublicKey TEXT,
|
|
215
|
+
communityName TEXT,
|
|
216
|
+
content TEXT NULLABLE,
|
|
217
|
+
timestamp INTEGER NOT NULL,
|
|
218
|
+
signature TEXT NOT NULL, -- JSON
|
|
219
|
+
originalCommentSignatureEncoded TEXT NULLABLE, -- original publication signature before local anonymization
|
|
220
|
+
title TEXT NULLABLE,
|
|
221
|
+
depth INTEGER NOT NULL,
|
|
222
|
+
linkHtmlTagName TEXT NULLABLE,
|
|
223
|
+
flairs TEXT NULLABLE, -- JSON
|
|
224
|
+
spoiler INTEGER NULLABLE, -- BOOLEAN (0/1)
|
|
225
|
+
pendingApproval INTEGER NULLABLE, -- BOOLEAN (0/1)
|
|
226
|
+
number INTEGER NULLABLE,
|
|
227
|
+
postNumber INTEGER NULLABLE,
|
|
228
|
+
nsfw INTEGER NULLABLE, -- BOOLEAN (0/1)
|
|
229
|
+
pseudonymityMode TEXT NULLABLE,
|
|
230
|
+
quotedCids TEXT NULLABLE, -- JSON array
|
|
231
|
+
extraProps TEXT NULLABLE, -- JSON
|
|
232
|
+
protocolVersion TEXT NOT NULL,
|
|
233
|
+
insertedAt INTEGER NOT NULL
|
|
234
|
+
)
|
|
235
|
+
`);
|
|
236
|
+
}
|
|
237
|
+
_createCommentUpdatesTable(tableName) {
|
|
238
|
+
this._db.exec(`
|
|
239
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
240
|
+
cid TEXT NOT NULL PRIMARY KEY UNIQUE REFERENCES ${TABLES.COMMENTS}(cid),
|
|
241
|
+
edit TEXT NULLABLE, -- JSON
|
|
242
|
+
upvoteCount INTEGER NOT NULL,
|
|
243
|
+
downvoteCount INTEGER NOT NULL,
|
|
244
|
+
replyCount INTEGER NOT NULL,
|
|
245
|
+
childCount INTEGER NOT NULL,
|
|
246
|
+
number INTEGER NULLABLE,
|
|
247
|
+
postNumber INTEGER NULLABLE,
|
|
248
|
+
flairs TEXT NULLABLE, -- JSON
|
|
249
|
+
spoiler INTEGER NULLABLE, -- BOOLEAN (0/1)
|
|
250
|
+
nsfw INTEGER NULLABLE, -- BOOLEAN (0/1)
|
|
251
|
+
pinned INTEGER NULLABLE, -- BOOLEAN (0/1)
|
|
252
|
+
locked INTEGER NULLABLE, -- BOOLEAN (0/1)
|
|
253
|
+
archived INTEGER NULLABLE, -- BOOLEAN (0/1)
|
|
254
|
+
removed INTEGER NULLABLE, -- BOOLEAN (0/1)
|
|
255
|
+
approved INTEGER NULLABLE, -- BOOLEAN (0/1)
|
|
256
|
+
reason TEXT NULLABLE,
|
|
257
|
+
updatedAt INTEGER NOT NULL CHECK(updatedAt > 0),
|
|
258
|
+
protocolVersion TEXT NOT NULL,
|
|
259
|
+
signature TEXT NOT NULL, -- JSON
|
|
260
|
+
author TEXT NULLABLE, -- JSON
|
|
261
|
+
replies TEXT NULLABLE, -- JSON
|
|
262
|
+
lastChildCid TEXT NULLABLE,
|
|
263
|
+
lastReplyTimestamp INTEGER NULLABLE,
|
|
264
|
+
postUpdatesBucket INTEGER NULLABLE,
|
|
265
|
+
publishedToPostUpdatesMFS INTEGER NOT NULL, -- BOOLEAN (0/1)
|
|
266
|
+
insertedAt INTEGER NOT NULL
|
|
267
|
+
)
|
|
268
|
+
`);
|
|
269
|
+
}
|
|
270
|
+
_createVotesTable(tableName) {
|
|
271
|
+
this._db.exec(`
|
|
272
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
273
|
+
commentCid TEXT NOT NULL REFERENCES ${TABLES.COMMENTS}(cid),
|
|
274
|
+
authorSignerAddress TEXT NOT NULL,
|
|
275
|
+
timestamp INTEGER CHECK(timestamp > 0) NOT NULL,
|
|
276
|
+
vote INTEGER CHECK(vote BETWEEN -1 AND 1) NOT NULL,
|
|
277
|
+
protocolVersion TEXT NOT NULL,
|
|
278
|
+
insertedAt INTEGER NOT NULL,
|
|
279
|
+
extraProps TEXT NULLABLE, -- JSON
|
|
280
|
+
PRIMARY KEY (commentCid, authorSignerAddress)
|
|
281
|
+
)
|
|
282
|
+
`);
|
|
283
|
+
}
|
|
284
|
+
_createCommentEditsTable(tableName) {
|
|
285
|
+
this._db.exec(`
|
|
286
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
287
|
+
commentCid TEXT NOT NULL REFERENCES ${TABLES.COMMENTS}(cid),
|
|
288
|
+
authorSignerAddress TEXT NOT NULL,
|
|
289
|
+
author TEXT NULLABLE, -- JSON
|
|
290
|
+
signature TEXT NOT NULL, -- JSON
|
|
291
|
+
protocolVersion TEXT NOT NULL,
|
|
292
|
+
communityPublicKey TEXT,
|
|
293
|
+
communityName TEXT,
|
|
294
|
+
timestamp INTEGER CHECK(timestamp > 0) NOT NULL,
|
|
295
|
+
content TEXT NULLABLE,
|
|
296
|
+
reason TEXT NULLABLE,
|
|
297
|
+
deleted INTEGER NULLABLE, -- BOOLEAN (0/1)
|
|
298
|
+
flairs TEXT NULLABLE, -- JSON
|
|
299
|
+
spoiler INTEGER NULLABLE, -- BOOLEAN (0/1)
|
|
300
|
+
nsfw INTEGER NULLABLE, -- BOOLEAN (0/1)
|
|
301
|
+
isAuthorEdit INTEGER NOT NULL, -- BOOLEAN (0/1)
|
|
302
|
+
insertedAt INTEGER NOT NULL,
|
|
303
|
+
extraProps TEXT NULLABLE -- JSON
|
|
304
|
+
)
|
|
305
|
+
`);
|
|
306
|
+
}
|
|
307
|
+
_createCommentModerationsTable(tableName) {
|
|
308
|
+
this._db.exec(`
|
|
309
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
310
|
+
commentCid TEXT NOT NULL,
|
|
311
|
+
author TEXT NULLABLE, -- JSON
|
|
312
|
+
signature TEXT NOT NULL, -- JSON
|
|
313
|
+
modSignerAddress TEXT NOT NULL,
|
|
314
|
+
protocolVersion TEXT NOT NULL,
|
|
315
|
+
communityPublicKey TEXT,
|
|
316
|
+
communityName TEXT,
|
|
317
|
+
timestamp INTEGER CHECK(timestamp > 0) NOT NULL,
|
|
318
|
+
commentModeration TEXT NOT NULL, -- JSON
|
|
319
|
+
insertedAt INTEGER NOT NULL,
|
|
320
|
+
extraProps TEXT NULLABLE, -- JSON
|
|
321
|
+
targetAuthorSignerAddress TEXT NULLABLE, -- the signer address of the comment author being moderated (for bans/flairs)
|
|
322
|
+
targetAuthorDomain TEXT NULLABLE -- the domain address (e.g., spammer.bso) of the comment author being moderated
|
|
323
|
+
)
|
|
324
|
+
`);
|
|
325
|
+
}
|
|
326
|
+
_createPseudonymityAliasesTable(tableName) {
|
|
327
|
+
this._db.exec(`
|
|
328
|
+
CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
329
|
+
commentCid TEXT NOT NULL PRIMARY KEY UNIQUE REFERENCES ${TABLES.COMMENTS}(cid) ON DELETE CASCADE,
|
|
330
|
+
aliasPrivateKey TEXT NOT NULL,
|
|
331
|
+
originalAuthorSignerPublicKey TEXT NOT NULL,
|
|
332
|
+
originalAuthorDomain TEXT NULLABLE, -- the original author's domain address (e.g., user.eth) if they used one
|
|
333
|
+
mode TEXT NOT NULL CHECK(mode IN ('per-post', 'per-reply', 'per-author')),
|
|
334
|
+
insertedAt INTEGER NOT NULL
|
|
335
|
+
)
|
|
336
|
+
`);
|
|
337
|
+
}
|
|
338
|
+
getDbVersion() {
|
|
339
|
+
const result = this._db.pragma("user_version", { simple: true });
|
|
340
|
+
return Number(result);
|
|
341
|
+
}
|
|
342
|
+
_migrateOldSettings(oldSettings) {
|
|
343
|
+
const fieldsToRemove = ["post", "reply", "vote"];
|
|
344
|
+
const newSettings = remeda.clone(oldSettings);
|
|
345
|
+
if (Array.isArray(newSettings.challenges)) {
|
|
346
|
+
// Filter out challenges that reference removed built-in names
|
|
347
|
+
newSettings.challenges = newSettings.challenges.filter((cs) => !cs.name || cs.path || cs.name in pkcJsChallenges);
|
|
348
|
+
for (const oldChallengeSetting of newSettings.challenges)
|
|
349
|
+
if (oldChallengeSetting.exclude)
|
|
350
|
+
for (const oldExcludeSetting of oldChallengeSetting.exclude)
|
|
351
|
+
for (const fieldToMove of fieldsToRemove)
|
|
352
|
+
delete oldExcludeSetting[fieldToMove];
|
|
353
|
+
}
|
|
354
|
+
return newSettings;
|
|
355
|
+
}
|
|
356
|
+
async _createOrMigrateTablesIfNeeded() {
|
|
357
|
+
const log = Logger("pkc-js:local-community:db-handler:createOrMigrateTablesIfNeeded");
|
|
358
|
+
const currentDbVersion = this.getDbVersion();
|
|
359
|
+
log.trace(`current db version: ${currentDbVersion}`);
|
|
360
|
+
if (currentDbVersion > env.DB_VERSION)
|
|
361
|
+
throw new Error(`DB version ${currentDbVersion} is greater than the latest version ${env.DB_VERSION}. You need to upgrade your client to accommodate the new DB version`);
|
|
362
|
+
const needToMigrate = currentDbVersion < env.DB_VERSION;
|
|
363
|
+
const dbPath = this._dbConfig.filename;
|
|
364
|
+
let backupDbPath;
|
|
365
|
+
const dbExistsAlready = fs.existsSync(dbPath);
|
|
366
|
+
if (needToMigrate) {
|
|
367
|
+
if (dbExistsAlready && currentDbVersion > 0) {
|
|
368
|
+
this.destoryConnection();
|
|
369
|
+
backupDbPath = path.join(path.dirname(dbPath), ".backup_before_migration", `${path.basename(dbPath)}.${currentDbVersion}.${timestamp()}`);
|
|
370
|
+
log(`Copying db ${path.basename(dbPath)} to ${backupDbPath} before migration`);
|
|
371
|
+
if (!fs.existsSync(path.dirname(backupDbPath)))
|
|
372
|
+
await fs.promises.mkdir(path.dirname(backupDbPath), { recursive: true });
|
|
373
|
+
const sourceDb = new Database(dbPath, { fileMustExist: true });
|
|
374
|
+
await sourceDb.backup(backupDbPath); // Use better-sqlite3's native backup method
|
|
375
|
+
sourceDb.close();
|
|
376
|
+
this._db = new Database(dbPath);
|
|
377
|
+
this._db.pragma("journal_mode = WAL");
|
|
378
|
+
}
|
|
379
|
+
this._db.exec("PRAGMA foreign_keys = OFF");
|
|
380
|
+
const tablesToDrop = ["challengeRequests", "challenges", "challengeAnswers", "challengeVerifications", "signers"];
|
|
381
|
+
for (const tableName of tablesToDrop)
|
|
382
|
+
this._db.exec(`DROP TABLE IF EXISTS ${tableName}`);
|
|
383
|
+
this._db.exec(`DROP TABLE IF EXISTS ${TABLES.COMMENT_UPDATES}`);
|
|
384
|
+
}
|
|
385
|
+
const createTableFunctions = [
|
|
386
|
+
this._createCommentsTable.bind(this),
|
|
387
|
+
this._createCommentUpdatesTable.bind(this),
|
|
388
|
+
this._createVotesTable.bind(this),
|
|
389
|
+
this._createCommentModerationsTable.bind(this),
|
|
390
|
+
this._createCommentEditsTable.bind(this),
|
|
391
|
+
this._createPseudonymityAliasesTable.bind(this)
|
|
392
|
+
];
|
|
393
|
+
const tables = Object.values(TABLES);
|
|
394
|
+
for (let i = 0; i < tables.length; i++) {
|
|
395
|
+
const tableName = tables[i];
|
|
396
|
+
const tableExists = this._tableExists(tableName);
|
|
397
|
+
if (!tableExists) {
|
|
398
|
+
log(`Table ${tableName} does not exist. Will create schema`);
|
|
399
|
+
createTableFunctions[i](tableName);
|
|
400
|
+
}
|
|
401
|
+
else if (tableExists && needToMigrate) {
|
|
402
|
+
log(`Migrating table ${tableName} to new schema`);
|
|
403
|
+
const tempTableName = `${tableName}_${env.DB_VERSION}_new`;
|
|
404
|
+
this._db.exec(`DROP TABLE IF EXISTS ${tempTableName}`);
|
|
405
|
+
createTableFunctions[i](tempTableName);
|
|
406
|
+
await this._copyTable(tableName, tempTableName, currentDbVersion);
|
|
407
|
+
this._db.exec(`DROP TABLE ${tableName}`);
|
|
408
|
+
this._db.exec(`ALTER TABLE ${tempTableName} RENAME TO ${tableName}`);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (needToMigrate) {
|
|
412
|
+
await this._purgeCommentsWithInvalidSchemaOrSignature();
|
|
413
|
+
await this._purgeCommentEditsWithInvalidSchemaOrSignature();
|
|
414
|
+
await this._purgePublicationTablesWithDuplicateSignatures();
|
|
415
|
+
if (currentDbVersion < 29)
|
|
416
|
+
this._backfillApprovedCommentNumbers();
|
|
417
|
+
if (currentDbVersion < 31)
|
|
418
|
+
this._backfillTargetAuthorSignerAddress();
|
|
419
|
+
if (currentDbVersion < 32)
|
|
420
|
+
this._backfillTargetAuthorDomain();
|
|
421
|
+
this._db.exec("PRAGMA foreign_keys = ON");
|
|
422
|
+
this._db.pragma(`user_version = ${env.DB_VERSION}`);
|
|
423
|
+
await this.initDbIfNeeded(); // to init keyv
|
|
424
|
+
const internalState = this.keyvHas(STORAGE_KEYS[STORAGE_KEYS.INTERNAL_COMMUNITY])
|
|
425
|
+
? (await this.keyvGet(STORAGE_KEYS[STORAGE_KEYS.INTERNAL_COMMUNITY]))
|
|
426
|
+
: undefined;
|
|
427
|
+
if (internalState) {
|
|
428
|
+
const protocolVersion = internalState.protocolVersion || env.PROTOCOL_VERSION;
|
|
429
|
+
const _usingDefaultChallenge = "_usingDefaultChallenge" in internalState
|
|
430
|
+
? internalState._usingDefaultChallenge
|
|
431
|
+
: //@ts-expect-error
|
|
432
|
+
remeda.isDeepEqual(this._community._defaultCommunityChallenges, internalState?.settings?.challenges);
|
|
433
|
+
const updateCid = "updateCid" in internalState && typeof internalState.updateCid === "string"
|
|
434
|
+
? internalState.updateCid
|
|
435
|
+
: "QmYHzA8euDgUpNy3fh7JRwpPwt6jCgF35YTutYkyGGyr8f";
|
|
436
|
+
const newSettings = this._migrateOldSettings(internalState.settings);
|
|
437
|
+
const newChallenges = newSettings.challenges
|
|
438
|
+
? await Promise.all(newSettings.challenges?.map((cs) => getCommunityChallengeFromCommunityChallengeSettings(cs, this._community._pkc)))
|
|
439
|
+
: newSettings.challenges;
|
|
440
|
+
await this._community._updateDbInternalState({
|
|
441
|
+
posts: undefined,
|
|
442
|
+
challenges: newChallenges,
|
|
443
|
+
settings: newSettings,
|
|
444
|
+
updateCid,
|
|
445
|
+
protocolVersion,
|
|
446
|
+
_usingDefaultChallenge
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
this._db.exec(`VACUUM;`); // Run vacuum outside transaction or after commit
|
|
451
|
+
const newDbVersion = this.getDbVersion();
|
|
452
|
+
assert.equal(newDbVersion, env.DB_VERSION);
|
|
453
|
+
this._createdTables = true;
|
|
454
|
+
if (needToMigrate)
|
|
455
|
+
log(`Created/migrated the tables to the latest (${newDbVersion}) version and saved to path`, this._dbConfig.filename);
|
|
456
|
+
if (backupDbPath)
|
|
457
|
+
await fs.promises.rm(backupDbPath);
|
|
458
|
+
}
|
|
459
|
+
_tableExists(tableName) {
|
|
460
|
+
const stmt = this._db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?");
|
|
461
|
+
return !!stmt.get(tableName);
|
|
462
|
+
}
|
|
463
|
+
_backfillApprovedCommentNumbers() {
|
|
464
|
+
const log = Logger("pkc-js:local-community:db-handler:_backfillApprovedCommentNumbers");
|
|
465
|
+
const comments = this._db
|
|
466
|
+
.prepare(`SELECT cid, depth FROM ${TABLES.COMMENTS} WHERE pendingApproval IS NULL OR pendingApproval != 1 ORDER BY rowid ASC`)
|
|
467
|
+
.all();
|
|
468
|
+
if (comments.length === 0)
|
|
469
|
+
return;
|
|
470
|
+
let nextNumber = 1;
|
|
471
|
+
let nextPostNumber = 1;
|
|
472
|
+
const updateStmt = this._db.prepare(`UPDATE ${TABLES.COMMENTS} SET number = ?, postNumber = ? WHERE cid = ?`);
|
|
473
|
+
const updateMany = this._db.transaction((items) => {
|
|
474
|
+
for (const comment of items) {
|
|
475
|
+
const postNumber = comment.depth === 0 ? nextPostNumber++ : null;
|
|
476
|
+
updateStmt.run(nextNumber++, postNumber, comment.cid);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
updateMany(comments);
|
|
480
|
+
log(`Backfilled number/postNumber for ${comments.length} non-pending comments`);
|
|
481
|
+
}
|
|
482
|
+
_backfillTargetAuthorSignerAddress() {
|
|
483
|
+
const log = Logger("pkc-js:local-community:db-handler:_backfillTargetAuthorSignerAddress");
|
|
484
|
+
// Find comment moderations that have author-related edits (bans/flairs) but no targetAuthorSignerAddress
|
|
485
|
+
const moderationsToUpdate = this._db
|
|
486
|
+
.prepare(`
|
|
487
|
+
SELECT cm.rowid, cm.commentCid, c.authorSignerAddress,
|
|
488
|
+
pa.originalAuthorSignerPublicKey
|
|
489
|
+
FROM ${TABLES.COMMENT_MODERATIONS} cm
|
|
490
|
+
LEFT JOIN ${TABLES.COMMENTS} c ON cm.commentCid = c.cid
|
|
491
|
+
LEFT JOIN ${TABLES.PSEUDONYMITY_ALIASES} pa ON cm.commentCid = pa.commentCid
|
|
492
|
+
WHERE cm.targetAuthorSignerAddress IS NULL
|
|
493
|
+
AND json_extract(cm.commentModeration, '$.author') IS NOT NULL
|
|
494
|
+
`)
|
|
495
|
+
.all();
|
|
496
|
+
if (moderationsToUpdate.length === 0)
|
|
497
|
+
return;
|
|
498
|
+
const updateStmt = this._db.prepare(`UPDATE ${TABLES.COMMENT_MODERATIONS} SET targetAuthorSignerAddress = ? WHERE rowid = ?`);
|
|
499
|
+
const updateMany = this._db.transaction((items) => {
|
|
500
|
+
for (const mod of items) {
|
|
501
|
+
let targetAddress = null;
|
|
502
|
+
// If the comment was published with pseudonymity, use the original author's address
|
|
503
|
+
if (mod.originalAuthorSignerPublicKey) {
|
|
504
|
+
try {
|
|
505
|
+
targetAddress = getPKCAddressFromPublicKeySync(mod.originalAuthorSignerPublicKey);
|
|
506
|
+
}
|
|
507
|
+
catch {
|
|
508
|
+
// If we can't derive the address from the public key, fall back to authorSignerAddress
|
|
509
|
+
targetAddress = mod.authorSignerAddress;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
targetAddress = mod.authorSignerAddress;
|
|
514
|
+
}
|
|
515
|
+
if (targetAddress) {
|
|
516
|
+
updateStmt.run(targetAddress, mod.rowid);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
updateMany(moderationsToUpdate);
|
|
521
|
+
log(`Backfilled targetAuthorSignerAddress for ${moderationsToUpdate.length} comment moderations`);
|
|
522
|
+
}
|
|
523
|
+
_backfillTargetAuthorDomain() {
|
|
524
|
+
const log = Logger("pkc-js:local-community:db-handler:_backfillTargetAuthorDomain");
|
|
525
|
+
// Find comment moderations that have author-related edits (bans/flairs) but no targetAuthorDomain
|
|
526
|
+
// and the comment author used a domain address
|
|
527
|
+
const moderationsToUpdate = this._db
|
|
528
|
+
.prepare(`
|
|
529
|
+
SELECT cm.rowid, c.author as commentAuthor,
|
|
530
|
+
pa.originalAuthorDomain
|
|
531
|
+
FROM ${TABLES.COMMENT_MODERATIONS} cm
|
|
532
|
+
LEFT JOIN ${TABLES.COMMENTS} c ON cm.commentCid = c.cid
|
|
533
|
+
LEFT JOIN ${TABLES.PSEUDONYMITY_ALIASES} pa ON cm.commentCid = pa.commentCid
|
|
534
|
+
WHERE cm.targetAuthorDomain IS NULL
|
|
535
|
+
AND json_extract(cm.commentModeration, '$.author') IS NOT NULL
|
|
536
|
+
`)
|
|
537
|
+
.all();
|
|
538
|
+
if (moderationsToUpdate.length === 0)
|
|
539
|
+
return;
|
|
540
|
+
const updateStmt = this._db.prepare(`UPDATE ${TABLES.COMMENT_MODERATIONS} SET targetAuthorDomain = ? WHERE rowid = ?`);
|
|
541
|
+
let updatedCount = 0;
|
|
542
|
+
const updateMany = this._db.transaction((items) => {
|
|
543
|
+
for (const mod of items) {
|
|
544
|
+
let targetDomain = null;
|
|
545
|
+
// If the comment was published with pseudonymity, use the original author's domain
|
|
546
|
+
if (mod.originalAuthorDomain) {
|
|
547
|
+
targetDomain = mod.originalAuthorDomain;
|
|
548
|
+
}
|
|
549
|
+
else if (mod.commentAuthor) {
|
|
550
|
+
try {
|
|
551
|
+
const author = JSON.parse(mod.commentAuthor);
|
|
552
|
+
targetDomain = getAuthorDomainFromWire(author) || null;
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
// Ignore parse errors
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (targetDomain) {
|
|
559
|
+
updateStmt.run(targetDomain, mod.rowid);
|
|
560
|
+
updatedCount++;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
updateMany(moderationsToUpdate);
|
|
565
|
+
log(`Backfilled targetAuthorDomain for ${updatedCount} comment moderations`);
|
|
566
|
+
}
|
|
567
|
+
_getColumnNames(tableName) {
|
|
568
|
+
const results = this._db.pragma(`table_info(${tableName})`);
|
|
569
|
+
return results.map((col) => col.name);
|
|
570
|
+
}
|
|
571
|
+
async _copyTable(srcTable, dstTable, currentDbVersion) {
|
|
572
|
+
const log = Logger("pkc-js:local-community:db-handler:createTablesIfNeeded:copyTable");
|
|
573
|
+
const dstTableColumns = this._getColumnNames(dstTable);
|
|
574
|
+
// Include rowid in the SELECT to preserve it
|
|
575
|
+
const srcRecordsRaw = this._db.prepare(`SELECT rowid, * FROM ${srcTable} ORDER BY rowid ASC`).all();
|
|
576
|
+
if (srcRecordsRaw.length > 0) {
|
|
577
|
+
log(`Attempting to copy ${srcRecordsRaw.length} records from ${srcTable} to ${dstTable}`);
|
|
578
|
+
// Add rowid to the column list for insertion
|
|
579
|
+
const columnsWithRowid = ["rowid", ...dstTableColumns];
|
|
580
|
+
const insertStmt = this._db.prepare(`INSERT INTO ${dstTable} (${columnsWithRowid.join(", ")}) VALUES (${columnsWithRowid.map(() => "?").join(", ")})`);
|
|
581
|
+
const recordsToInsert = [];
|
|
582
|
+
for (let srcRecord of srcRecordsRaw) {
|
|
583
|
+
srcRecord = { ...srcRecord }; // Ensure mutable
|
|
584
|
+
// Pre-process specific migrations
|
|
585
|
+
if (currentDbVersion <= 11 && srcTable === TABLES.COMMENT_EDITS) {
|
|
586
|
+
const parsedSig = typeof srcRecord.signature === "string" ? JSON.parse(srcRecord.signature) : srcRecord.signature;
|
|
587
|
+
const commentToBeEdited = this.queryComment(srcRecord.commentCid);
|
|
588
|
+
if (!commentToBeEdited)
|
|
589
|
+
throw Error(`Failed to compute isAuthorEdit for ${srcRecord.commentCid}`);
|
|
590
|
+
srcRecord["isAuthorEdit"] = parsedSig.publicKey === commentToBeEdited.signature.publicKey;
|
|
591
|
+
const commentEditFieldsNotIncludedAnymore = ["removed"];
|
|
592
|
+
const extraProps = removeNullUndefinedValues(remeda.pick(srcRecord, commentEditFieldsNotIncludedAnymore));
|
|
593
|
+
if (Object.keys(extraProps).length > 0)
|
|
594
|
+
srcRecord.extraProps = { ...srcRecord.extraProps, ...extraProps };
|
|
595
|
+
}
|
|
596
|
+
if (currentDbVersion <= 12 && srcRecord["authorAddress"] && srcRecord["signature"]) {
|
|
597
|
+
const sig = typeof srcRecord.signature === "string" ? JSON.parse(srcRecord.signature) : srcRecord.signature;
|
|
598
|
+
srcRecord["authorSignerAddress"] = await getPKCAddressFromPublicKey(sig["publicKey"]);
|
|
599
|
+
}
|
|
600
|
+
if (srcTable === TABLES.COMMENTS) {
|
|
601
|
+
const commentIpfsFieldsNotIncludedAnymore = ["ipnsName"];
|
|
602
|
+
const extraProps = removeNullUndefinedValues(remeda.pick(srcRecord, commentIpfsFieldsNotIncludedAnymore));
|
|
603
|
+
if (Object.keys(extraProps).length > 0)
|
|
604
|
+
srcRecord.extraProps = { ...srcRecord.extraProps, ...extraProps };
|
|
605
|
+
}
|
|
606
|
+
// Migrate subplebbitAddress to communityPublicKey/communityName (v36 → v37)
|
|
607
|
+
if (currentDbVersion < 37 && srcRecord["subplebbitAddress"]) {
|
|
608
|
+
const addr = srcRecord["subplebbitAddress"];
|
|
609
|
+
if (isStringDomain(addr)) {
|
|
610
|
+
srcRecord["communityName"] = addr;
|
|
611
|
+
// Leave communityPublicKey as NULL for domain-based old rows
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
srcRecord["communityPublicKey"] = addr;
|
|
615
|
+
}
|
|
616
|
+
// Preserve subplebbitAddress in extraProps for CID reconstruction
|
|
617
|
+
const existingExtra = typeof srcRecord.extraProps === "string" ? JSON.parse(srcRecord.extraProps) : srcRecord.extraProps || {};
|
|
618
|
+
srcRecord.extraProps = { ...existingExtra, subplebbitAddress: addr };
|
|
619
|
+
delete srcRecord["subplebbitAddress"];
|
|
620
|
+
}
|
|
621
|
+
// Prepare record for insertion (stringify JSONs, convert booleans)
|
|
622
|
+
const processedRecord = this._processRecordsForDbBeforeInsert([srcRecord])[0];
|
|
623
|
+
// Map values including rowid (preserve the original rowid value)
|
|
624
|
+
const finalRecordValues = columnsWithRowid.map((col) => {
|
|
625
|
+
if (col === "rowid") {
|
|
626
|
+
return srcRecord.rowid; // Use original rowid value
|
|
627
|
+
}
|
|
628
|
+
return processedRecord[col];
|
|
629
|
+
});
|
|
630
|
+
recordsToInsert.push(finalRecordValues);
|
|
631
|
+
}
|
|
632
|
+
if (recordsToInsert.length > 0) {
|
|
633
|
+
const insertMany = this._db.transaction((items) => {
|
|
634
|
+
for (const itemArgs of items) {
|
|
635
|
+
insertStmt.run(...itemArgs);
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
insertMany(recordsToInsert);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
log(`copied table ${srcTable} to table ${dstTable}`);
|
|
642
|
+
}
|
|
643
|
+
async _purgePublicationTablesWithDuplicateSignatures() {
|
|
644
|
+
const log = Logger("pkc-js:local-community:db-handler:_purgePublicationTablesWithDuplicateSignatures");
|
|
645
|
+
const publicationTables = [TABLES.COMMENTS, TABLES.COMMENT_EDITS, TABLES.COMMENT_MODERATIONS, TABLES.COMMENT_UPDATES];
|
|
646
|
+
for (const tableName of publicationTables) {
|
|
647
|
+
const columnNames = this._getColumnNames(tableName);
|
|
648
|
+
if (!columnNames.includes("signature")) {
|
|
649
|
+
log.trace(`Skipping duplicate signature purge for ${tableName} because column signature is missing.`);
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
const jsonValidExpr = (alias) => `json_valid(${alias}.signature) = 1`;
|
|
653
|
+
const signatureExtractExpr = (alias) => `json_extract(${alias}.signature, '$.signature')`;
|
|
654
|
+
const duplicateRows = this._db
|
|
655
|
+
.prepare(`
|
|
656
|
+
SELECT newer.rowid AS rowid
|
|
657
|
+
FROM ${tableName} AS newer
|
|
658
|
+
WHERE ${jsonValidExpr("newer")}
|
|
659
|
+
AND ${signatureExtractExpr("newer")} IS NOT NULL
|
|
660
|
+
AND EXISTS (
|
|
661
|
+
SELECT 1
|
|
662
|
+
FROM ${tableName} AS older
|
|
663
|
+
WHERE ${jsonValidExpr("older")}
|
|
664
|
+
AND ${signatureExtractExpr("older")} = ${signatureExtractExpr("newer")}
|
|
665
|
+
AND older.rowid < newer.rowid
|
|
666
|
+
)
|
|
667
|
+
`)
|
|
668
|
+
.all();
|
|
669
|
+
if (duplicateRows.length === 0)
|
|
670
|
+
continue;
|
|
671
|
+
if (tableName === TABLES.COMMENTS) {
|
|
672
|
+
const duplicateCids = this._db
|
|
673
|
+
.prepare(`
|
|
674
|
+
SELECT cid
|
|
675
|
+
FROM ${TABLES.COMMENTS} AS newer
|
|
676
|
+
WHERE ${jsonValidExpr("newer")}
|
|
677
|
+
AND ${signatureExtractExpr("newer")} IS NOT NULL
|
|
678
|
+
AND EXISTS (
|
|
679
|
+
SELECT 1
|
|
680
|
+
FROM ${TABLES.COMMENTS} AS older
|
|
681
|
+
WHERE ${jsonValidExpr("older")}
|
|
682
|
+
AND ${signatureExtractExpr("older")} = ${signatureExtractExpr("newer")}
|
|
683
|
+
AND older.rowid < newer.rowid
|
|
684
|
+
)
|
|
685
|
+
`)
|
|
686
|
+
.all();
|
|
687
|
+
for (const { cid } of duplicateCids) {
|
|
688
|
+
const purgedRows = this.purgeComment(cid);
|
|
689
|
+
for (const row of purgedRows)
|
|
690
|
+
await this._community._addAllCidsUnderPurgedCommentToBeRemoved(row);
|
|
691
|
+
}
|
|
692
|
+
log(`Purged ${duplicateCids.length} duplicate comment row(s) based on signature.signature with higher rowid values.`);
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
const deleteStmt = this._db.prepare(`DELETE FROM ${tableName} WHERE rowid = ?`);
|
|
696
|
+
const deleteMany = this._db.transaction((rows) => {
|
|
697
|
+
for (const row of rows)
|
|
698
|
+
deleteStmt.run(row.rowid);
|
|
699
|
+
});
|
|
700
|
+
deleteMany(duplicateRows);
|
|
701
|
+
log(`Purged ${duplicateRows.length} duplicate row(s) from ${tableName} based on signature.signature with higher rowid values.`);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
async _purgeCommentEditsWithInvalidSchemaOrSignature() {
|
|
705
|
+
const log = Logger("pkc-js:local-community:db-handler:_purgeCommentEditsWithInvalidSchemaOrSignature");
|
|
706
|
+
const commentEditsOrderedByASC = this._db
|
|
707
|
+
.prepare(`SELECT rowid as rowid, * FROM ${TABLES.COMMENT_EDITS} ORDER BY rowid ASC`)
|
|
708
|
+
.all();
|
|
709
|
+
for (const rawCommentEditRecord of commentEditsOrderedByASC) {
|
|
710
|
+
let commentEditRecord;
|
|
711
|
+
try {
|
|
712
|
+
commentEditRecord = this._parseCommentEditsRow(rawCommentEditRecord);
|
|
713
|
+
}
|
|
714
|
+
catch (error) {
|
|
715
|
+
if (error instanceof ZodError) {
|
|
716
|
+
log.error(`Comment edit (${rawCommentEditRecord.commentCid}) row ${rawCommentEditRecord.rowid} in DB failed to parse and will be purged from comment edits table.`, error);
|
|
717
|
+
this._deleteCommentEditRow(rawCommentEditRecord.rowid);
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
throw error;
|
|
721
|
+
}
|
|
722
|
+
try {
|
|
723
|
+
CommentEditPubsubMessagePublicationSchema.strip().parse(commentEditRecord);
|
|
724
|
+
}
|
|
725
|
+
catch (e) {
|
|
726
|
+
log.error(`Comment edit (${commentEditRecord.commentCid}) row ${rawCommentEditRecord.rowid} in DB has an invalid schema and will be purged from comment edits table.`, e);
|
|
727
|
+
this._deleteCommentEditRow(rawCommentEditRecord.rowid);
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
const commentEditPubsub = remeda.pick(commentEditRecord, [
|
|
731
|
+
...commentEditRecord.signature.signedPropertyNames,
|
|
732
|
+
"signature"
|
|
733
|
+
]);
|
|
734
|
+
const validRes = await verifyCommentEdit({
|
|
735
|
+
edit: commentEditPubsub,
|
|
736
|
+
resolveAuthorNames: false,
|
|
737
|
+
clientsManager: this._community._clientsManager
|
|
738
|
+
});
|
|
739
|
+
if (!validRes.valid && validRes.reason === messages.ERR_SIGNATURE_IS_INVALID) {
|
|
740
|
+
log.error(`Comment edit (${commentEditRecord.commentCid}) row ${rawCommentEditRecord.rowid} in DB has invalid signature due to ${validRes.reason}. Removing comment edit entry.`);
|
|
741
|
+
this._deleteCommentEditRow(rawCommentEditRecord.rowid);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
async _purgeCommentsWithInvalidSchemaOrSignature() {
|
|
746
|
+
const log = Logger("pkc-js:local-community:db-handler:_purgeCommentsWithInvalidSchema");
|
|
747
|
+
const commentsOrderedByASC = this._db.prepare(`SELECT * FROM ${TABLES.COMMENTS} ORDER BY rowid ASC`).all();
|
|
748
|
+
const alreadyPurgedCids = new Set();
|
|
749
|
+
for (const rawCommentRecord of commentsOrderedByASC) {
|
|
750
|
+
if (alreadyPurgedCids.has(rawCommentRecord.cid))
|
|
751
|
+
continue;
|
|
752
|
+
let commentRecord;
|
|
753
|
+
try {
|
|
754
|
+
commentRecord = this._parseCommentsTableRow(rawCommentRecord);
|
|
755
|
+
}
|
|
756
|
+
catch (error) {
|
|
757
|
+
if (error instanceof ZodError) {
|
|
758
|
+
const purged = this.purgeComment(rawCommentRecord.cid);
|
|
759
|
+
for (const p of purged)
|
|
760
|
+
alreadyPurgedCids.add(p.commentTableRow.cid);
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
throw error;
|
|
764
|
+
}
|
|
765
|
+
try {
|
|
766
|
+
CommentIpfsSchema.strip().parse(commentRecord);
|
|
767
|
+
}
|
|
768
|
+
catch (e) {
|
|
769
|
+
log.error(`Comment (${commentRecord.cid}) in DB has an invalid schema, will be purged.`, e);
|
|
770
|
+
const purged = this.purgeComment(commentRecord.cid);
|
|
771
|
+
for (const p of purged)
|
|
772
|
+
alreadyPurgedCids.add(p.commentTableRow.cid);
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
const validRes = await verifyCommentIpfs({
|
|
776
|
+
comment: { ...commentRecord, ...commentRecord.extraProps },
|
|
777
|
+
resolveAuthorNames: false,
|
|
778
|
+
calculatedCommentCid: commentRecord.cid,
|
|
779
|
+
clientsManager: this._community._clientsManager
|
|
780
|
+
});
|
|
781
|
+
if (!validRes.valid) {
|
|
782
|
+
log.error(`Comment ${commentRecord.cid} in DB has invalid signature due to ${validRes.reason}. Will be purged.`);
|
|
783
|
+
const purged = this.purgeComment(commentRecord.cid);
|
|
784
|
+
for (const p of purged)
|
|
785
|
+
alreadyPurgedCids.add(p.commentTableRow.cid);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
deleteVote(authorSignerAddress, commentCid) {
|
|
790
|
+
this._db
|
|
791
|
+
.prepare(`DELETE FROM ${TABLES.VOTES} WHERE commentCid = ? AND authorSignerAddress = ?`)
|
|
792
|
+
.run(commentCid, authorSignerAddress);
|
|
793
|
+
}
|
|
794
|
+
_deleteCommentEditRow(rowid) {
|
|
795
|
+
const deleteResult = this._db.prepare(`DELETE FROM ${TABLES.COMMENT_EDITS} WHERE rowid = ?`).run(rowid);
|
|
796
|
+
return deleteResult.changes > 0;
|
|
797
|
+
}
|
|
798
|
+
insertVotes(votes) {
|
|
799
|
+
if (votes.length === 0)
|
|
800
|
+
return;
|
|
801
|
+
const processedVotes = this._processRecordsForDbBeforeInsert(votes);
|
|
802
|
+
// Get all column names from the votes table to create defaults
|
|
803
|
+
const columnNames = this._getColumnNames(TABLES.VOTES);
|
|
804
|
+
const stmt = this._db.prepare(`
|
|
805
|
+
INSERT INTO ${TABLES.VOTES}
|
|
806
|
+
(commentCid, authorSignerAddress, timestamp, vote, protocolVersion, insertedAt, extraProps)
|
|
807
|
+
VALUES (@commentCid, @authorSignerAddress, @timestamp, @vote, @protocolVersion, @insertedAt, @extraProps)
|
|
808
|
+
`);
|
|
809
|
+
const insertMany = this._db.transaction((items) => {
|
|
810
|
+
for (const vote of items) {
|
|
811
|
+
// Create default object with null values for all columns
|
|
812
|
+
const defaults = {};
|
|
813
|
+
columnNames.forEach((column) => {
|
|
814
|
+
if (!(column in vote)) {
|
|
815
|
+
defaults[column] = null;
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
// Merge defaults with actual vote data
|
|
819
|
+
const completeVote = { ...defaults, ...vote };
|
|
820
|
+
stmt.run(completeVote);
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
insertMany(processedVotes);
|
|
824
|
+
}
|
|
825
|
+
insertComments(comments) {
|
|
826
|
+
if (comments.length === 0)
|
|
827
|
+
return;
|
|
828
|
+
const processedComments = this._processRecordsForDbBeforeInsert(comments);
|
|
829
|
+
// Get all column names from the comments table to create defaults
|
|
830
|
+
const columnNames = this._getColumnNames(TABLES.COMMENTS);
|
|
831
|
+
// TODO: refactor to derive column list from CommentsTableRowSchema instead of hardcoding.
|
|
832
|
+
// Adding a new column to the comments table requires updating this list manually, which is error-prone.
|
|
833
|
+
const stmt = this._db.prepare(`
|
|
834
|
+
INSERT INTO ${TABLES.COMMENTS}
|
|
835
|
+
(cid, authorSignerAddress, author, link, linkWidth, linkHeight, thumbnailUrl, thumbnailUrlWidth, thumbnailUrlHeight, parentCid, postCid, previousCid, communityPublicKey, communityName, content, timestamp, signature, originalCommentSignatureEncoded, title, depth, linkHtmlTagName, flairs, spoiler, pendingApproval, number, postNumber, nsfw, pseudonymityMode, quotedCids, extraProps, protocolVersion, insertedAt)
|
|
836
|
+
VALUES (@cid, @authorSignerAddress, @author, @link, @linkWidth, @linkHeight, @thumbnailUrl, @thumbnailUrlWidth, @thumbnailUrlHeight, @parentCid, @postCid, @previousCid, @communityPublicKey, @communityName, @content, @timestamp, @signature, @originalCommentSignatureEncoded, @title, @depth, @linkHtmlTagName, @flairs, @spoiler, @pendingApproval, @number, @postNumber, @nsfw, @pseudonymityMode, @quotedCids, @extraProps, @protocolVersion, @insertedAt)
|
|
837
|
+
`);
|
|
838
|
+
// Create default object with null values for all columns
|
|
839
|
+
const defaults = remeda.mapToObj(columnNames, (column) => [column, null]);
|
|
840
|
+
const insertMany = this._db.transaction((items) => {
|
|
841
|
+
for (const comment of items) {
|
|
842
|
+
// Merge defaults with actual comment data
|
|
843
|
+
const completeComment = { ...defaults, ...comment };
|
|
844
|
+
stmt.run(completeComment);
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
insertMany(processedComments);
|
|
848
|
+
}
|
|
849
|
+
insertPseudonymityAliases(aliases) {
|
|
850
|
+
if (aliases.length === 0)
|
|
851
|
+
return;
|
|
852
|
+
const processedAliases = this._processRecordsForDbBeforeInsert(aliases);
|
|
853
|
+
const stmt = this._db.prepare(`
|
|
854
|
+
INSERT OR REPLACE INTO ${TABLES.PSEUDONYMITY_ALIASES}
|
|
855
|
+
(commentCid, aliasPrivateKey, originalAuthorSignerPublicKey, originalAuthorDomain, mode, insertedAt)
|
|
856
|
+
VALUES (@commentCid, @aliasPrivateKey, @originalAuthorSignerPublicKey, @originalAuthorDomain, @mode, @insertedAt)
|
|
857
|
+
`);
|
|
858
|
+
const insertMany = this._db.transaction((items) => {
|
|
859
|
+
for (const alias of items)
|
|
860
|
+
stmt.run(alias);
|
|
861
|
+
});
|
|
862
|
+
insertMany(processedAliases);
|
|
863
|
+
}
|
|
864
|
+
upsertCommentUpdates(updates) {
|
|
865
|
+
const processedUpdates = this._processRecordsForDbBeforeInsert(updates);
|
|
866
|
+
// Get all column names from the comment_updates table to create defaults
|
|
867
|
+
const columnNames = this._getColumnNames(TABLES.COMMENT_UPDATES);
|
|
868
|
+
const stmt = this._db.prepare(`
|
|
869
|
+
INSERT INTO ${TABLES.COMMENT_UPDATES}
|
|
870
|
+
(cid, edit, upvoteCount, downvoteCount, replyCount, childCount, number, postNumber, flairs, spoiler, nsfw, pinned, locked, archived, removed, approved, reason, updatedAt, protocolVersion, signature, author, replies, lastChildCid, lastReplyTimestamp, postUpdatesBucket, publishedToPostUpdatesMFS, insertedAt)
|
|
871
|
+
VALUES (@cid, @edit, @upvoteCount, @downvoteCount, @replyCount, @childCount, @number, @postNumber, @flairs, @spoiler, @nsfw, @pinned, @locked, @archived, @removed, @approved, @reason, @updatedAt, @protocolVersion, @signature, @author, @replies, @lastChildCid, @lastReplyTimestamp, @postUpdatesBucket, @publishedToPostUpdatesMFS, @insertedAt)
|
|
872
|
+
ON CONFLICT(cid) DO UPDATE SET
|
|
873
|
+
edit = excluded.edit, upvoteCount = excluded.upvoteCount, downvoteCount = excluded.downvoteCount, replyCount = excluded.replyCount, childCount = excluded.childCount,
|
|
874
|
+
number = COALESCE(excluded.number, ${TABLES.COMMENT_UPDATES}.number),
|
|
875
|
+
postNumber = COALESCE(excluded.postNumber, ${TABLES.COMMENT_UPDATES}.postNumber),
|
|
876
|
+
flairs = excluded.flairs, spoiler = excluded.spoiler, nsfw = excluded.nsfw, pinned = excluded.pinned, locked = excluded.locked, archived = excluded.archived,
|
|
877
|
+
removed = excluded.removed, approved = excluded.approved, reason = excluded.reason, updatedAt = excluded.updatedAt, protocolVersion = excluded.protocolVersion,
|
|
878
|
+
signature = excluded.signature, author = excluded.author, replies = excluded.replies, lastChildCid = excluded.lastChildCid,
|
|
879
|
+
lastReplyTimestamp = excluded.lastReplyTimestamp, postUpdatesBucket = excluded.postUpdatesBucket,
|
|
880
|
+
publishedToPostUpdatesMFS = excluded.publishedToPostUpdatesMFS,
|
|
881
|
+
insertedAt = excluded.insertedAt
|
|
882
|
+
`);
|
|
883
|
+
const defaults = remeda.mapToObj(columnNames, (column) => [column, null]);
|
|
884
|
+
const upsertMany = this._db.transaction((items) => {
|
|
885
|
+
for (const update of items) {
|
|
886
|
+
// Create default object with null values for all columns
|
|
887
|
+
// Merge defaults with actual update data
|
|
888
|
+
const completeUpdate = { ...defaults, ...update };
|
|
889
|
+
stmt.run(completeUpdate);
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
upsertMany(processedUpdates);
|
|
893
|
+
}
|
|
894
|
+
insertCommentModerations(moderations) {
|
|
895
|
+
if (moderations.length === 0)
|
|
896
|
+
return;
|
|
897
|
+
const processedModerations = this._processRecordsForDbBeforeInsert(moderations);
|
|
898
|
+
// Get all column names from the comment_moderations table to create defaults
|
|
899
|
+
const columnNames = this._getColumnNames(TABLES.COMMENT_MODERATIONS);
|
|
900
|
+
const stmt = this._db.prepare(`
|
|
901
|
+
INSERT INTO ${TABLES.COMMENT_MODERATIONS}
|
|
902
|
+
(commentCid, author, signature, modSignerAddress, protocolVersion, communityPublicKey, communityName, timestamp, commentModeration, insertedAt, extraProps, targetAuthorSignerAddress, targetAuthorDomain)
|
|
903
|
+
VALUES (@commentCid, @author, @signature, @modSignerAddress, @protocolVersion, @communityPublicKey, @communityName, @timestamp, @commentModeration, @insertedAt, @extraProps, @targetAuthorSignerAddress, @targetAuthorDomain)
|
|
904
|
+
`);
|
|
905
|
+
const defaults = remeda.mapToObj(columnNames, (column) => [column, null]);
|
|
906
|
+
const insertMany = this._db.transaction((items) => {
|
|
907
|
+
for (const mod of items) {
|
|
908
|
+
// Create default object with null values for all columns
|
|
909
|
+
// Merge defaults with actual moderation data
|
|
910
|
+
const completeMod = { ...defaults, ...mod };
|
|
911
|
+
stmt.run(completeMod);
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
insertMany(processedModerations);
|
|
915
|
+
}
|
|
916
|
+
insertCommentEdits(edits) {
|
|
917
|
+
if (edits.length === 0)
|
|
918
|
+
return;
|
|
919
|
+
const processedEdits = this._processRecordsForDbBeforeInsert(edits);
|
|
920
|
+
// Get all column names from the comment_edits table to create defaults
|
|
921
|
+
const columnNames = this._getColumnNames(TABLES.COMMENT_EDITS);
|
|
922
|
+
const stmt = this._db.prepare(`
|
|
923
|
+
INSERT INTO ${TABLES.COMMENT_EDITS}
|
|
924
|
+
(commentCid, authorSignerAddress, author, signature, protocolVersion, communityPublicKey, communityName, timestamp, content, reason, deleted, flairs, spoiler, nsfw, isAuthorEdit, insertedAt, extraProps)
|
|
925
|
+
VALUES (@commentCid, @authorSignerAddress, @author, @signature, @protocolVersion, @communityPublicKey, @communityName, @timestamp, @content, @reason, @deleted, @flairs, @spoiler, @nsfw, @isAuthorEdit, @insertedAt, @extraProps)
|
|
926
|
+
`);
|
|
927
|
+
const defaults = remeda.mapToObj(columnNames, (column) => [column, null]);
|
|
928
|
+
const insertMany = this._db.transaction((items) => {
|
|
929
|
+
for (const edit of items) {
|
|
930
|
+
// Create default object with null values for all columns
|
|
931
|
+
// Merge defaults with actual edit data
|
|
932
|
+
const completeEdit = { ...defaults, ...edit };
|
|
933
|
+
stmt.run(completeEdit);
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
insertMany(processedEdits);
|
|
937
|
+
}
|
|
938
|
+
queryVote(commentCid, authorSignerAddress) {
|
|
939
|
+
const row = this._db
|
|
940
|
+
.prepare(`SELECT * FROM ${TABLES.VOTES} WHERE commentCid = ? AND authorSignerAddress = ?`)
|
|
941
|
+
.get(commentCid, authorSignerAddress);
|
|
942
|
+
if (!row)
|
|
943
|
+
return undefined;
|
|
944
|
+
return this._parseVoteRow(row);
|
|
945
|
+
}
|
|
946
|
+
_approvedClause(alias) {
|
|
947
|
+
return `(${alias}.approved IS NULL OR ${alias}.approved = 1 OR ${alias}.approved IS TRUE)`;
|
|
948
|
+
}
|
|
949
|
+
_removedClause(alias) {
|
|
950
|
+
return `(${alias}.removed IS NOT 1 AND ${alias}.removed IS NOT TRUE)`;
|
|
951
|
+
}
|
|
952
|
+
_deletedFromUpdatesClause(alias) {
|
|
953
|
+
return `(json_extract(${alias}.edit, '$.deleted') IS NULL OR json_extract(${alias}.edit, '$.deleted') != 1)`;
|
|
954
|
+
}
|
|
955
|
+
_deletedFromLookupClause(alias) {
|
|
956
|
+
return `(${alias}.deleted_flag IS NULL OR ${alias}.deleted_flag != 1)`;
|
|
957
|
+
}
|
|
958
|
+
_pendingApprovalClause(alias) {
|
|
959
|
+
return `(${alias}.pendingApproval IS NULL OR ${alias}.pendingApproval != 1)`;
|
|
960
|
+
}
|
|
961
|
+
_communityAddressClause(alias) {
|
|
962
|
+
const address = this._community.address;
|
|
963
|
+
const addresses = getEquivalentCommunityAddresses(address);
|
|
964
|
+
if (isStringDomain(address)) {
|
|
965
|
+
// Domain-based: match communityName OR communityPublicKey (domain strings and IPNS keys never overlap)
|
|
966
|
+
const domainPlaceholders = addresses.map(() => "?").join(", ");
|
|
967
|
+
return {
|
|
968
|
+
clause: `(${alias}.communityName IN (${domainPlaceholders}) OR ${alias}.communityPublicKey IN (${domainPlaceholders}))`,
|
|
969
|
+
params: [...addresses, ...addresses]
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
else {
|
|
973
|
+
// IPNS key: match communityPublicKey directly
|
|
974
|
+
return { clause: `${alias}.communityPublicKey = ?`, params: [address] };
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
_communityAddressClauseNamed(alias, paramPrefix) {
|
|
978
|
+
const address = this._community.address;
|
|
979
|
+
const addresses = getEquivalentCommunityAddresses(address);
|
|
980
|
+
if (isStringDomain(address)) {
|
|
981
|
+
// Domain-based: match communityName OR communityPublicKey (domain strings and IPNS keys never overlap)
|
|
982
|
+
const params = {};
|
|
983
|
+
const namePlaceholders = [];
|
|
984
|
+
const keyPlaceholders = [];
|
|
985
|
+
addresses.forEach((addr, i) => {
|
|
986
|
+
const nameParam = `${paramPrefix}CommunityName${i}`;
|
|
987
|
+
const keyParam = `${paramPrefix}CommunityKey${i}`;
|
|
988
|
+
params[nameParam] = addr;
|
|
989
|
+
params[keyParam] = addr;
|
|
990
|
+
namePlaceholders.push(`:${nameParam}`);
|
|
991
|
+
keyPlaceholders.push(`:${keyParam}`);
|
|
992
|
+
});
|
|
993
|
+
return {
|
|
994
|
+
clause: `(${alias}.communityName IN (${namePlaceholders.join(", ")}) OR ${alias}.communityPublicKey IN (${keyPlaceholders.join(", ")}))`,
|
|
995
|
+
params
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
else {
|
|
999
|
+
// IPNS key: match communityPublicKey directly
|
|
1000
|
+
const paramName = `${paramPrefix}CommunityKey`;
|
|
1001
|
+
return { clause: `${alias}.communityPublicKey = :${paramName}`, params: { [paramName]: address } };
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
_buildPageQueryParts(options) {
|
|
1005
|
+
const commentsTable = TABLES.COMMENTS;
|
|
1006
|
+
const commentUpdatesTable = TABLES.COMMENT_UPDATES;
|
|
1007
|
+
const whereClauses = [`${commentsTable}.parentCid = ?`];
|
|
1008
|
+
const params = [options.parentCid];
|
|
1009
|
+
if (options.excludeCommentsWithDifferentCommunityAddress) {
|
|
1010
|
+
const { clause, params: addrParams } = this._communityAddressClause(commentsTable);
|
|
1011
|
+
whereClauses.push(clause);
|
|
1012
|
+
params.push(...addrParams);
|
|
1013
|
+
}
|
|
1014
|
+
if (options.excludeCommentPendingApproval)
|
|
1015
|
+
whereClauses.push(this._pendingApprovalClause(commentsTable));
|
|
1016
|
+
if (options.excludeRemovedComments)
|
|
1017
|
+
whereClauses.push(this._removedClause(commentUpdatesTable));
|
|
1018
|
+
if (options.excludeDeletedComments)
|
|
1019
|
+
whereClauses.push(this._deletedFromUpdatesClause(commentUpdatesTable));
|
|
1020
|
+
if (options.excludeCommentWithApprovedFalse)
|
|
1021
|
+
whereClauses.push(this._approvedClause(commentUpdatesTable));
|
|
1022
|
+
return { whereClauses, params };
|
|
1023
|
+
}
|
|
1024
|
+
queryMaximumTimestampUnderComment(comment) {
|
|
1025
|
+
const { clause: addrClause, params: addrParams } = this._communityAddressClause("c");
|
|
1026
|
+
const query = `
|
|
1027
|
+
WITH RECURSIVE descendants AS (
|
|
1028
|
+
SELECT c.cid, c.timestamp FROM ${TABLES.COMMENTS} c
|
|
1029
|
+
LEFT JOIN ${TABLES.COMMENT_UPDATES} cu ON cu.cid = c.cid
|
|
1030
|
+
WHERE c.parentCid = ?
|
|
1031
|
+
AND COALESCE(cu.approved, 1) != 0
|
|
1032
|
+
AND (c.pendingApproval IS NULL OR c.pendingApproval != 1)
|
|
1033
|
+
UNION ALL
|
|
1034
|
+
SELECT c.cid, c.timestamp FROM ${TABLES.COMMENTS} c
|
|
1035
|
+
INNER JOIN ${TABLES.COMMENT_UPDATES} cu ON c.cid = cu.cid
|
|
1036
|
+
LEFT JOIN (SELECT cid, json_extract(edit, '$.deleted') AS deleted_flag FROM ${TABLES.COMMENT_UPDATES}) AS d ON c.cid = d.cid
|
|
1037
|
+
JOIN descendants desc_nodes ON c.parentCid = desc_nodes.cid
|
|
1038
|
+
WHERE ${addrClause} AND (cu.removed IS NOT 1 AND cu.removed IS NOT TRUE) AND (d.deleted_flag IS NULL OR d.deleted_flag != 1)
|
|
1039
|
+
AND COALESCE(cu.approved, 1) != 0
|
|
1040
|
+
AND (c.pendingApproval IS NULL OR c.pendingApproval != 1)
|
|
1041
|
+
)
|
|
1042
|
+
SELECT MAX(timestamp) AS max_timestamp FROM descendants
|
|
1043
|
+
`;
|
|
1044
|
+
const result = this._db.prepare(query).get(comment.cid, ...addrParams);
|
|
1045
|
+
if (result.max_timestamp === null)
|
|
1046
|
+
return undefined;
|
|
1047
|
+
return result.max_timestamp;
|
|
1048
|
+
}
|
|
1049
|
+
queryPageComments(options) {
|
|
1050
|
+
const commentUpdateCols = remeda.keys.strict(options.commentUpdateFieldsToExclude
|
|
1051
|
+
? remeda.omit(CommentUpdateSchema.shape, options.commentUpdateFieldsToExclude)
|
|
1052
|
+
: CommentUpdateSchema.shape);
|
|
1053
|
+
const commentUpdateSelects = commentUpdateCols.map((col) => `${TABLES.COMMENT_UPDATES}.${col} AS commentUpdate_${col}`);
|
|
1054
|
+
const commentIpfsCols = [...remeda.keys.strict(CommentIpfsSchema.shape), "extraProps"];
|
|
1055
|
+
const commentIpfsSelects = commentIpfsCols.map((col) => `${TABLES.COMMENTS}.${col} AS commentIpfs_${col}`);
|
|
1056
|
+
const { whereClauses, params } = this._buildPageQueryParts(options);
|
|
1057
|
+
const queryStr = `
|
|
1058
|
+
SELECT ${commentIpfsSelects.join(", ")}, ${commentUpdateSelects.join(", ")}
|
|
1059
|
+
FROM ${TABLES.COMMENTS} INNER JOIN ${TABLES.COMMENT_UPDATES} ON ${TABLES.COMMENTS}.cid = ${TABLES.COMMENT_UPDATES}.cid
|
|
1060
|
+
WHERE ${whereClauses.join(" AND ")}
|
|
1061
|
+
`;
|
|
1062
|
+
const commentsRaw = this._db.prepare(queryStr).all(...params);
|
|
1063
|
+
return commentsRaw.map((commentRaw) => {
|
|
1064
|
+
const { comment, commentUpdate } = this._parsePrefixedComment(commentRaw);
|
|
1065
|
+
return { comment, commentUpdate };
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
queryFlattenedPageReplies(options) {
|
|
1069
|
+
const commentUpdateCols = remeda.keys.strict(options.commentUpdateFieldsToExclude
|
|
1070
|
+
? remeda.omit(CommentUpdateSchema.shape, options.commentUpdateFieldsToExclude)
|
|
1071
|
+
: CommentUpdateSchema.shape);
|
|
1072
|
+
// TODO, is it omitting replies?
|
|
1073
|
+
const commentUpdateSelects = commentUpdateCols.map((col) => `c_updates.${col} AS commentUpdate_${col}`);
|
|
1074
|
+
const commentIpfsCols = [...remeda.keys.strict(CommentIpfsSchema.shape), "extraProps"];
|
|
1075
|
+
const commentIpfsSelects = commentIpfsCols.map((col) => `comments_alias.${col} AS commentIpfs_${col}`);
|
|
1076
|
+
let baseWhereClausesStr = "";
|
|
1077
|
+
let recursiveWhereClausesStr = "";
|
|
1078
|
+
const params = [options.parentCid];
|
|
1079
|
+
const baseFilterClauses = [];
|
|
1080
|
+
const recursiveFilterClauses = [];
|
|
1081
|
+
const commentsAlias = "comments";
|
|
1082
|
+
const commentUpdatesAlias = "c_updates";
|
|
1083
|
+
const deletedLookupAlias = "d";
|
|
1084
|
+
if (options.excludeCommentsWithDifferentCommunityAddress) {
|
|
1085
|
+
const { clause: baseClause, params: baseAddrParams } = this._communityAddressClause(commentsAlias);
|
|
1086
|
+
baseFilterClauses.push(baseClause);
|
|
1087
|
+
params.push(...baseAddrParams);
|
|
1088
|
+
const { clause: recClause, params: recAddrParams } = this._communityAddressClause(commentsAlias);
|
|
1089
|
+
recursiveFilterClauses.push(recClause);
|
|
1090
|
+
params.push(...recAddrParams);
|
|
1091
|
+
}
|
|
1092
|
+
if (options.excludeCommentPendingApproval) {
|
|
1093
|
+
const clause = this._pendingApprovalClause(commentsAlias);
|
|
1094
|
+
baseFilterClauses.push(clause);
|
|
1095
|
+
recursiveFilterClauses.push(clause);
|
|
1096
|
+
}
|
|
1097
|
+
if (options.excludeRemovedComments) {
|
|
1098
|
+
const clause = this._removedClause(commentUpdatesAlias);
|
|
1099
|
+
baseFilterClauses.push(clause);
|
|
1100
|
+
recursiveFilterClauses.push(clause);
|
|
1101
|
+
}
|
|
1102
|
+
if (options.excludeDeletedComments) {
|
|
1103
|
+
const clause = this._deletedFromLookupClause(deletedLookupAlias);
|
|
1104
|
+
baseFilterClauses.push(clause);
|
|
1105
|
+
recursiveFilterClauses.push(clause);
|
|
1106
|
+
}
|
|
1107
|
+
if (options.excludeCommentWithApprovedFalse) {
|
|
1108
|
+
const clause = this._approvedClause(commentUpdatesAlias);
|
|
1109
|
+
baseFilterClauses.push(clause);
|
|
1110
|
+
recursiveFilterClauses.push(clause);
|
|
1111
|
+
}
|
|
1112
|
+
baseWhereClausesStr = baseFilterClauses.length > 0 ? `AND ${baseFilterClauses.join(" AND ")}` : "";
|
|
1113
|
+
recursiveWhereClausesStr = recursiveFilterClauses.length > 0 ? `AND ${recursiveFilterClauses.join(" AND ")}` : "";
|
|
1114
|
+
const query = `
|
|
1115
|
+
WITH RECURSIVE comment_tree AS (
|
|
1116
|
+
SELECT comments.*, ${commentUpdateCols.map((c) => `c_updates.${c} AS c_updates_${c}`).join(", ")}, 0 AS tree_level
|
|
1117
|
+
FROM ${TABLES.COMMENTS} comments
|
|
1118
|
+
INNER JOIN ${TABLES.COMMENT_UPDATES} c_updates ON comments.cid = c_updates.cid
|
|
1119
|
+
LEFT JOIN (SELECT cid, json_extract(edit, '$.deleted') AS deleted_flag FROM ${TABLES.COMMENT_UPDATES}) AS d ON comments.cid = d.cid
|
|
1120
|
+
WHERE comments.parentCid = ? ${baseWhereClausesStr}
|
|
1121
|
+
UNION ALL
|
|
1122
|
+
SELECT comments.*, ${commentUpdateCols.map((c) => `c_updates.${c} AS c_updates_${c}`).join(", ")}, tree.tree_level + 1
|
|
1123
|
+
FROM ${TABLES.COMMENTS} comments
|
|
1124
|
+
INNER JOIN ${TABLES.COMMENT_UPDATES} c_updates ON comments.cid = c_updates.cid
|
|
1125
|
+
LEFT JOIN (SELECT cid, json_extract(edit, '$.deleted') AS deleted_flag FROM ${TABLES.COMMENT_UPDATES}) AS d ON comments.cid = d.cid
|
|
1126
|
+
INNER JOIN comment_tree tree ON comments.parentCid = tree.cid
|
|
1127
|
+
WHERE 1=1 ${recursiveWhereClausesStr}
|
|
1128
|
+
)
|
|
1129
|
+
SELECT ${commentIpfsSelects.join(", ")}, ${commentUpdateCols.map((col) => `comments_alias.c_updates_${col} AS commentUpdate_${col}`).join(", ")}
|
|
1130
|
+
FROM comment_tree comments_alias
|
|
1131
|
+
`;
|
|
1132
|
+
const commentsRaw = this._db.prepare(query).all(...params);
|
|
1133
|
+
return commentsRaw.map((commentRaw) => {
|
|
1134
|
+
const { comment, commentUpdate } = this._parsePrefixedComment(commentRaw);
|
|
1135
|
+
return { comment, commentUpdate };
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
queryStoredCommentUpdate(comment) {
|
|
1139
|
+
const row = this._db.prepare(`SELECT * FROM ${TABLES.COMMENT_UPDATES} WHERE cid = ?`).get(comment.cid);
|
|
1140
|
+
if (!row)
|
|
1141
|
+
return undefined;
|
|
1142
|
+
return this._parseCommentUpdatesRow(row);
|
|
1143
|
+
}
|
|
1144
|
+
hasCommentWithSignatureEncoded(signatureEncoded) {
|
|
1145
|
+
const row = this._db
|
|
1146
|
+
.prepare(`SELECT 1 FROM ${TABLES.COMMENTS}
|
|
1147
|
+
WHERE json_extract(signature, '$.signature') = ?
|
|
1148
|
+
OR originalCommentSignatureEncoded = ?
|
|
1149
|
+
LIMIT 1`)
|
|
1150
|
+
.get(signatureEncoded, signatureEncoded);
|
|
1151
|
+
return row !== undefined;
|
|
1152
|
+
}
|
|
1153
|
+
queryCommentBySignatureEncoded(signatureEncoded) {
|
|
1154
|
+
const row = this._db
|
|
1155
|
+
.prepare(`SELECT * FROM ${TABLES.COMMENTS}
|
|
1156
|
+
WHERE json_extract(signature, '$.signature') = ?
|
|
1157
|
+
OR originalCommentSignatureEncoded = ?
|
|
1158
|
+
LIMIT 1`)
|
|
1159
|
+
.get(signatureEncoded, signatureEncoded);
|
|
1160
|
+
if (!row)
|
|
1161
|
+
return undefined;
|
|
1162
|
+
return this._parseCommentsTableRow(row);
|
|
1163
|
+
}
|
|
1164
|
+
hasCommentModerationWithSignatureEncoded(signatureEncoded) {
|
|
1165
|
+
const row = this._db
|
|
1166
|
+
.prepare(`SELECT 1 FROM ${TABLES.COMMENT_MODERATIONS} WHERE json_extract(signature, '$.signature') = ? LIMIT 1`)
|
|
1167
|
+
.get(signatureEncoded);
|
|
1168
|
+
return row !== undefined;
|
|
1169
|
+
}
|
|
1170
|
+
hasCommentEditWithSignatureEncoded(signatureEncoded) {
|
|
1171
|
+
const row = this._db
|
|
1172
|
+
.prepare(`SELECT 1 FROM ${TABLES.COMMENT_EDITS} WHERE json_extract(signature, '$.signature') = ? LIMIT 1`)
|
|
1173
|
+
.get(signatureEncoded);
|
|
1174
|
+
return row !== undefined;
|
|
1175
|
+
}
|
|
1176
|
+
queryParentsCids(rootComment) {
|
|
1177
|
+
if (!rootComment.parentCid)
|
|
1178
|
+
throw Error("Root comment has no parent cid");
|
|
1179
|
+
const query = `
|
|
1180
|
+
WITH RECURSIVE parent_chain AS (
|
|
1181
|
+
SELECT cid, parentCid, 0 AS level FROM ${TABLES.COMMENTS} WHERE cid = ?
|
|
1182
|
+
UNION ALL
|
|
1183
|
+
SELECT c.cid, c.parentCid, pc.level + 1 FROM ${TABLES.COMMENTS} c JOIN parent_chain pc ON c.cid = pc.parentCid
|
|
1184
|
+
) SELECT cid FROM parent_chain ORDER BY level
|
|
1185
|
+
`;
|
|
1186
|
+
return this._db.prepare(query).all(rootComment.parentCid);
|
|
1187
|
+
}
|
|
1188
|
+
queryCommentsPendingApproval() {
|
|
1189
|
+
const results = this._db
|
|
1190
|
+
.prepare(`SELECT * FROM ${TABLES.COMMENTS} WHERE pendingApproval = 1 ORDER BY rowid DESC`)
|
|
1191
|
+
.all();
|
|
1192
|
+
return results.map((r) => this._parseCommentsTableRow(r));
|
|
1193
|
+
}
|
|
1194
|
+
queryCommentsToBeUpdated() {
|
|
1195
|
+
// TODO optimize this query in the future
|
|
1196
|
+
// Make sure tests in commentsToUpdate.db.community.test.js are passing
|
|
1197
|
+
const query = `
|
|
1198
|
+
WITH RECURSIVE
|
|
1199
|
+
direct_updates AS (
|
|
1200
|
+
SELECT c.* FROM ${TABLES.COMMENTS} c LEFT JOIN ${TABLES.COMMENT_UPDATES} cu ON c.cid = cu.cid
|
|
1201
|
+
WHERE (c.pendingApproval IS NULL OR c.pendingApproval != 1)
|
|
1202
|
+
AND (cu.cid IS NULL OR (cu.publishedToPostUpdatesMFS = 0 OR cu.publishedToPostUpdatesMFS IS FALSE))
|
|
1203
|
+
UNION
|
|
1204
|
+
SELECT c.* FROM ${TABLES.COMMENTS} c JOIN ${TABLES.COMMENT_UPDATES} cu ON c.cid = cu.cid
|
|
1205
|
+
WHERE (c.pendingApproval IS NULL OR c.pendingApproval != 1)
|
|
1206
|
+
AND (
|
|
1207
|
+
EXISTS (SELECT 1 FROM ${TABLES.VOTES} v WHERE v.commentCid = c.cid AND v.insertedAt >= cu.insertedAt)
|
|
1208
|
+
OR EXISTS (SELECT 1 FROM ${TABLES.COMMENT_EDITS} ce WHERE ce.commentCid = c.cid AND ce.insertedAt >= cu.insertedAt)
|
|
1209
|
+
OR EXISTS (SELECT 1 FROM ${TABLES.COMMENT_MODERATIONS} cm WHERE cm.commentCid = c.cid AND cm.insertedAt >= cu.insertedAt)
|
|
1210
|
+
OR EXISTS (SELECT 1 FROM ${TABLES.COMMENTS} cc WHERE cc.parentCid = c.cid AND cc.insertedAt >= cu.insertedAt)
|
|
1211
|
+
)
|
|
1212
|
+
),
|
|
1213
|
+
child_counts AS (
|
|
1214
|
+
SELECT
|
|
1215
|
+
c.parentCid AS cid,
|
|
1216
|
+
COUNT(*) AS actual_child_count
|
|
1217
|
+
FROM ${TABLES.COMMENTS} c
|
|
1218
|
+
JOIN ${TABLES.COMMENT_UPDATES} cu_child ON c.cid = cu_child.cid
|
|
1219
|
+
LEFT JOIN (
|
|
1220
|
+
SELECT cid, json_extract(edit, '$.deleted') AS deleted_flag FROM ${TABLES.COMMENT_UPDATES}
|
|
1221
|
+
) deleted_lookup ON deleted_lookup.cid = c.cid
|
|
1222
|
+
WHERE c.parentCid IS NOT NULL
|
|
1223
|
+
AND (c.pendingApproval IS NULL OR c.pendingApproval != 1)
|
|
1224
|
+
AND (cu_child.removed IS NOT 1 AND cu_child.removed IS NOT TRUE)
|
|
1225
|
+
AND (deleted_lookup.deleted_flag IS NULL OR deleted_lookup.deleted_flag != 1)
|
|
1226
|
+
GROUP BY c.parentCid
|
|
1227
|
+
),
|
|
1228
|
+
filtered_children AS (
|
|
1229
|
+
SELECT
|
|
1230
|
+
c.parentCid AS cid,
|
|
1231
|
+
c.cid AS child_cid,
|
|
1232
|
+
ROW_NUMBER() OVER (PARTITION BY c.parentCid ORDER BY c.rowid DESC) AS child_rank
|
|
1233
|
+
FROM ${TABLES.COMMENTS} c
|
|
1234
|
+
JOIN ${TABLES.COMMENT_UPDATES} cu_child ON c.cid = cu_child.cid
|
|
1235
|
+
LEFT JOIN (
|
|
1236
|
+
SELECT cid, json_extract(edit, '$.deleted') AS deleted_flag FROM ${TABLES.COMMENT_UPDATES}
|
|
1237
|
+
) deleted_lookup ON deleted_lookup.cid = c.cid
|
|
1238
|
+
WHERE c.parentCid IS NOT NULL
|
|
1239
|
+
AND (c.pendingApproval IS NULL OR c.pendingApproval != 1)
|
|
1240
|
+
AND (cu_child.removed IS NOT 1 AND cu_child.removed IS NOT TRUE)
|
|
1241
|
+
AND (deleted_lookup.deleted_flag IS NULL OR deleted_lookup.deleted_flag != 1)
|
|
1242
|
+
AND COALESCE(cu_child.approved, 1) != 0
|
|
1243
|
+
),
|
|
1244
|
+
last_child_cids AS (
|
|
1245
|
+
SELECT cid, child_cid AS actual_last_child_cid
|
|
1246
|
+
FROM filtered_children
|
|
1247
|
+
WHERE child_rank = 1
|
|
1248
|
+
),
|
|
1249
|
+
stale_child_counts AS (
|
|
1250
|
+
SELECT parent.cid
|
|
1251
|
+
FROM ${TABLES.COMMENTS} parent
|
|
1252
|
+
JOIN ${TABLES.COMMENT_UPDATES} cu_parent ON parent.cid = cu_parent.cid
|
|
1253
|
+
LEFT JOIN child_counts cc ON cc.cid = parent.cid
|
|
1254
|
+
WHERE (parent.pendingApproval IS NULL OR parent.pendingApproval != 1)
|
|
1255
|
+
AND COALESCE(cc.actual_child_count, 0) != COALESCE(cu_parent.childCount, 0)
|
|
1256
|
+
),
|
|
1257
|
+
stale_last_child_cids AS (
|
|
1258
|
+
SELECT parent.cid
|
|
1259
|
+
FROM ${TABLES.COMMENTS} parent
|
|
1260
|
+
JOIN ${TABLES.COMMENT_UPDATES} cu_parent ON parent.cid = cu_parent.cid
|
|
1261
|
+
LEFT JOIN last_child_cids lc ON lc.cid = parent.cid
|
|
1262
|
+
WHERE (parent.pendingApproval IS NULL OR parent.pendingApproval != 1)
|
|
1263
|
+
AND COALESCE(lc.actual_last_child_cid, '') != COALESCE(cu_parent.lastChildCid, '')
|
|
1264
|
+
),
|
|
1265
|
+
replies_json AS (
|
|
1266
|
+
SELECT
|
|
1267
|
+
cu_parent.cid AS parentCid,
|
|
1268
|
+
json_extract(comment_entry.value, '$.comment.cid') AS comment_child_cid,
|
|
1269
|
+
json_extract(comment_entry.value, '$.commentUpdate.cid') AS update_child_cid,
|
|
1270
|
+
json_extract(comment_entry.value, '$.commentUpdate.updatedAt') AS json_child_updated_at
|
|
1271
|
+
FROM ${TABLES.COMMENT_UPDATES} cu_parent
|
|
1272
|
+
INNER JOIN ${TABLES.COMMENTS} parent ON parent.cid = cu_parent.cid
|
|
1273
|
+
JOIN json_each(cu_parent.replies, '$.pages') pages
|
|
1274
|
+
JOIN json_each(pages.value, '$.comments') comment_entry
|
|
1275
|
+
WHERE cu_parent.replies IS NOT NULL
|
|
1276
|
+
AND json_type(cu_parent.replies, '$.pages') = 'object'
|
|
1277
|
+
AND (parent.pendingApproval IS NULL OR parent.pendingApproval != 1)
|
|
1278
|
+
),
|
|
1279
|
+
stale_replies_json AS (
|
|
1280
|
+
SELECT r.parentCid AS cid
|
|
1281
|
+
FROM replies_json r
|
|
1282
|
+
LEFT JOIN ${TABLES.COMMENTS} existing_child ON existing_child.cid = COALESCE(r.comment_child_cid, r.update_child_cid)
|
|
1283
|
+
LEFT JOIN ${TABLES.COMMENT_UPDATES} actual_child_update ON actual_child_update.cid = COALESCE(r.comment_child_cid, r.update_child_cid)
|
|
1284
|
+
WHERE COALESCE(r.comment_child_cid, r.update_child_cid) IS NOT NULL
|
|
1285
|
+
AND (
|
|
1286
|
+
existing_child.cid IS NULL
|
|
1287
|
+
OR actual_child_update.cid IS NULL
|
|
1288
|
+
OR (
|
|
1289
|
+
r.json_child_updated_at IS NOT NULL
|
|
1290
|
+
AND actual_child_update.cid IS NOT NULL
|
|
1291
|
+
AND CAST(r.json_child_updated_at AS INTEGER) < actual_child_update.updatedAt
|
|
1292
|
+
)
|
|
1293
|
+
)
|
|
1294
|
+
GROUP BY r.parentCid
|
|
1295
|
+
),
|
|
1296
|
+
base_updates AS (
|
|
1297
|
+
SELECT * FROM direct_updates
|
|
1298
|
+
UNION SELECT c.* FROM ${TABLES.COMMENTS} c JOIN stale_child_counts scc ON c.cid = scc.cid
|
|
1299
|
+
UNION SELECT c.* FROM ${TABLES.COMMENTS} c JOIN stale_last_child_cids slc ON c.cid = slc.cid
|
|
1300
|
+
UNION SELECT c.* FROM ${TABLES.COMMENTS} c JOIN stale_replies_json srj ON c.cid = srj.cid
|
|
1301
|
+
),
|
|
1302
|
+
authors_to_update AS (SELECT DISTINCT authorSignerAddress FROM base_updates),
|
|
1303
|
+
author_comments AS (
|
|
1304
|
+
SELECT c.* FROM ${TABLES.COMMENTS} c JOIN authors_to_update a ON c.authorSignerAddress = a.authorSignerAddress
|
|
1305
|
+
WHERE (c.pendingApproval IS NULL OR c.pendingApproval != 1)
|
|
1306
|
+
),
|
|
1307
|
+
comments_needing_update AS (
|
|
1308
|
+
SELECT * FROM base_updates
|
|
1309
|
+
UNION SELECT * FROM author_comments
|
|
1310
|
+
),
|
|
1311
|
+
parent_chain AS (
|
|
1312
|
+
SELECT DISTINCT p.* FROM ${TABLES.COMMENTS} p JOIN comments_needing_update cnu ON p.cid = cnu.parentCid
|
|
1313
|
+
WHERE p.cid IS NOT NULL AND (p.pendingApproval IS NULL OR p.pendingApproval != 1)
|
|
1314
|
+
UNION
|
|
1315
|
+
SELECT DISTINCT p.* FROM ${TABLES.COMMENTS} p JOIN parent_chain pc ON p.cid = pc.parentCid
|
|
1316
|
+
WHERE p.cid IS NOT NULL AND (p.pendingApproval IS NULL OR p.pendingApproval != 1)
|
|
1317
|
+
),
|
|
1318
|
+
all_updates AS (
|
|
1319
|
+
SELECT cid FROM comments_needing_update UNION SELECT cid FROM parent_chain
|
|
1320
|
+
)
|
|
1321
|
+
SELECT c.* FROM ${TABLES.COMMENTS} c JOIN all_updates au ON c.cid = au.cid
|
|
1322
|
+
WHERE (c.pendingApproval IS NULL OR c.pendingApproval != 1)
|
|
1323
|
+
ORDER BY c.rowid
|
|
1324
|
+
`;
|
|
1325
|
+
const results = this._db.prepare(query).all();
|
|
1326
|
+
return results.map((r) => this._parseCommentsTableRow(r));
|
|
1327
|
+
}
|
|
1328
|
+
queryCommunityStats() {
|
|
1329
|
+
// if you change this logic, make sure to run stats.community.test.js
|
|
1330
|
+
const now = timestamp(); // All timestamps are in seconds
|
|
1331
|
+
const { clause: commentAddrClause, params: commentAddrParams } = this._communityAddressClauseNamed("comments", "statsComments");
|
|
1332
|
+
const { clause: votesAddrClause, params: votesAddrParams } = this._communityAddressClauseNamed("comments_for_votes", "statsVotes");
|
|
1333
|
+
const removedCommentsClause = this._removedClause("cu_comments");
|
|
1334
|
+
const deletedCommentsClause = this._deletedFromUpdatesClause("cu_comments");
|
|
1335
|
+
const removedVotesClause = this._removedClause("cu_votes");
|
|
1336
|
+
const deletedVotesClause = this._deletedFromUpdatesClause("cu_votes");
|
|
1337
|
+
const pendingCommentsClause = this._pendingApprovalClause("comments");
|
|
1338
|
+
const postAndReplyCountsQuery = `
|
|
1339
|
+
SELECT
|
|
1340
|
+
COALESCE(SUM(CASE WHEN comments.depth = 0 AND comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.HOUR} THEN 1 ELSE 0 END), 0) AS hourPostCount,
|
|
1341
|
+
COALESCE(SUM(CASE WHEN comments.depth = 0 AND comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.DAY} THEN 1 ELSE 0 END), 0) AS dayPostCount,
|
|
1342
|
+
COALESCE(SUM(CASE WHEN comments.depth = 0 AND comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.WEEK} THEN 1 ELSE 0 END), 0) AS weekPostCount,
|
|
1343
|
+
COALESCE(SUM(CASE WHEN comments.depth = 0 AND comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.MONTH} THEN 1 ELSE 0 END), 0) AS monthPostCount,
|
|
1344
|
+
COALESCE(SUM(CASE WHEN comments.depth = 0 AND comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.YEAR} THEN 1 ELSE 0 END), 0) AS yearPostCount,
|
|
1345
|
+
COALESCE(SUM(CASE WHEN comments.depth = 0 THEN 1 ELSE 0 END), 0) AS allPostCount,
|
|
1346
|
+
COALESCE(SUM(CASE WHEN comments.depth > 0 AND comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.HOUR} THEN 1 ELSE 0 END), 0) AS hourReplyCount,
|
|
1347
|
+
COALESCE(SUM(CASE WHEN comments.depth > 0 AND comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.DAY} THEN 1 ELSE 0 END), 0) AS dayReplyCount,
|
|
1348
|
+
COALESCE(SUM(CASE WHEN comments.depth > 0 AND comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.WEEK} THEN 1 ELSE 0 END), 0) AS weekReplyCount,
|
|
1349
|
+
COALESCE(SUM(CASE WHEN comments.depth > 0 AND comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.MONTH} THEN 1 ELSE 0 END), 0) AS monthReplyCount,
|
|
1350
|
+
COALESCE(SUM(CASE WHEN comments.depth > 0 AND comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.YEAR} THEN 1 ELSE 0 END), 0) AS yearReplyCount,
|
|
1351
|
+
COALESCE(SUM(CASE WHEN comments.depth > 0 THEN 1 ELSE 0 END), 0) AS allReplyCount
|
|
1352
|
+
FROM ${TABLES.COMMENTS} AS comments
|
|
1353
|
+
LEFT JOIN ${TABLES.COMMENT_UPDATES} AS cu_comments ON cu_comments.cid = comments.cid
|
|
1354
|
+
WHERE ${commentAddrClause}
|
|
1355
|
+
AND ${removedCommentsClause}
|
|
1356
|
+
AND ${deletedCommentsClause}
|
|
1357
|
+
AND ${pendingCommentsClause}
|
|
1358
|
+
`;
|
|
1359
|
+
const postAndReplyCounts = this._db.prepare(postAndReplyCountsQuery).get(commentAddrParams);
|
|
1360
|
+
const activeIdentityRowsQuery = `
|
|
1361
|
+
SELECT
|
|
1362
|
+
activity.authorSignerAddress AS authorSignerAddress,
|
|
1363
|
+
MAX(activity.hour_active) AS hourActive,
|
|
1364
|
+
MAX(activity.day_active) AS dayActive,
|
|
1365
|
+
MAX(activity.week_active) AS weekActive,
|
|
1366
|
+
MAX(activity.month_active) AS monthActive,
|
|
1367
|
+
MAX(activity.year_active) AS yearActive
|
|
1368
|
+
FROM (
|
|
1369
|
+
SELECT
|
|
1370
|
+
comments.authorSignerAddress,
|
|
1371
|
+
CASE WHEN comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.HOUR} THEN 1 ELSE 0 END AS hour_active,
|
|
1372
|
+
CASE WHEN comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.DAY} THEN 1 ELSE 0 END AS day_active,
|
|
1373
|
+
CASE WHEN comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.WEEK} THEN 1 ELSE 0 END AS week_active,
|
|
1374
|
+
CASE WHEN comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.MONTH} THEN 1 ELSE 0 END AS month_active,
|
|
1375
|
+
CASE WHEN comments.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.YEAR} THEN 1 ELSE 0 END AS year_active
|
|
1376
|
+
FROM ${TABLES.COMMENTS} AS comments
|
|
1377
|
+
LEFT JOIN ${TABLES.COMMENT_UPDATES} AS cu_comments ON cu_comments.cid = comments.cid
|
|
1378
|
+
WHERE ${commentAddrClause}
|
|
1379
|
+
AND ${removedCommentsClause}
|
|
1380
|
+
AND ${deletedCommentsClause}
|
|
1381
|
+
AND ${pendingCommentsClause}
|
|
1382
|
+
UNION ALL
|
|
1383
|
+
SELECT
|
|
1384
|
+
votes.authorSignerAddress,
|
|
1385
|
+
CASE WHEN votes.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.HOUR} THEN 1 ELSE 0 END AS hour_active,
|
|
1386
|
+
CASE WHEN votes.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.DAY} THEN 1 ELSE 0 END AS day_active,
|
|
1387
|
+
CASE WHEN votes.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.WEEK} THEN 1 ELSE 0 END AS week_active,
|
|
1388
|
+
CASE WHEN votes.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.MONTH} THEN 1 ELSE 0 END AS month_active,
|
|
1389
|
+
CASE WHEN votes.timestamp >= ${now - TIMEFRAMES_TO_SECONDS.YEAR} THEN 1 ELSE 0 END AS year_active
|
|
1390
|
+
FROM ${TABLES.VOTES} AS votes
|
|
1391
|
+
INNER JOIN ${TABLES.COMMENTS} AS comments_for_votes ON comments_for_votes.cid = votes.commentCid
|
|
1392
|
+
LEFT JOIN ${TABLES.COMMENT_UPDATES} AS cu_votes ON cu_votes.cid = comments_for_votes.cid
|
|
1393
|
+
WHERE ${votesAddrClause}
|
|
1394
|
+
AND ${removedVotesClause}
|
|
1395
|
+
AND ${deletedVotesClause}
|
|
1396
|
+
) AS activity
|
|
1397
|
+
GROUP BY activity.authorSignerAddress
|
|
1398
|
+
`;
|
|
1399
|
+
const activeIdentityRows = this._db
|
|
1400
|
+
.prepare(activeIdentityRowsQuery)
|
|
1401
|
+
.all({ ...commentAddrParams, ...votesAddrParams });
|
|
1402
|
+
const canonicalAddressesByAlias = new Map();
|
|
1403
|
+
if (activeIdentityRows.length > 0) {
|
|
1404
|
+
const uniqueActiveAddresses = [...new Set(activeIdentityRows.map((row) => row.authorSignerAddress))];
|
|
1405
|
+
const aliasPlaceholders = uniqueActiveAddresses.map(() => "?").join(", ");
|
|
1406
|
+
const aliasesQuery = `
|
|
1407
|
+
SELECT DISTINCT
|
|
1408
|
+
comments.authorSignerAddress AS aliasSignerAddress,
|
|
1409
|
+
alias.originalAuthorSignerPublicKey AS originalAuthorSignerPublicKey
|
|
1410
|
+
FROM ${TABLES.PSEUDONYMITY_ALIASES} AS alias
|
|
1411
|
+
INNER JOIN ${TABLES.COMMENTS} AS comments ON comments.cid = alias.commentCid
|
|
1412
|
+
WHERE comments.authorSignerAddress IN (${aliasPlaceholders})
|
|
1413
|
+
`;
|
|
1414
|
+
const aliasRows = this._db.prepare(aliasesQuery).all(...uniqueActiveAddresses);
|
|
1415
|
+
for (const aliasRow of aliasRows) {
|
|
1416
|
+
let originalAuthorAddress;
|
|
1417
|
+
try {
|
|
1418
|
+
originalAuthorAddress = getPKCAddressFromPublicKeySync(aliasRow.originalAuthorSignerPublicKey);
|
|
1419
|
+
}
|
|
1420
|
+
catch {
|
|
1421
|
+
throw new Error(`Failed to resolve original author address for alias signer address ${aliasRow.aliasSignerAddress}`);
|
|
1422
|
+
}
|
|
1423
|
+
const existingCanonicalAddress = canonicalAddressesByAlias.get(aliasRow.aliasSignerAddress);
|
|
1424
|
+
if (existingCanonicalAddress && existingCanonicalAddress !== originalAuthorAddress) {
|
|
1425
|
+
throw new Error(`Inconsistent pseudonymity alias mappings for signer address ${aliasRow.aliasSignerAddress}`);
|
|
1426
|
+
}
|
|
1427
|
+
canonicalAddressesByAlias.set(aliasRow.aliasSignerAddress, originalAuthorAddress);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
const canonicalActivityByAddress = new Map();
|
|
1431
|
+
for (const activeIdentityRow of activeIdentityRows) {
|
|
1432
|
+
const canonicalAddress = canonicalAddressesByAlias.get(activeIdentityRow.authorSignerAddress) || activeIdentityRow.authorSignerAddress;
|
|
1433
|
+
const existing = canonicalActivityByAddress.get(canonicalAddress);
|
|
1434
|
+
if (!existing) {
|
|
1435
|
+
canonicalActivityByAddress.set(canonicalAddress, {
|
|
1436
|
+
hourActive: activeIdentityRow.hourActive,
|
|
1437
|
+
dayActive: activeIdentityRow.dayActive,
|
|
1438
|
+
weekActive: activeIdentityRow.weekActive,
|
|
1439
|
+
monthActive: activeIdentityRow.monthActive,
|
|
1440
|
+
yearActive: activeIdentityRow.yearActive
|
|
1441
|
+
});
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
existing.hourActive = Math.max(existing.hourActive, activeIdentityRow.hourActive);
|
|
1445
|
+
existing.dayActive = Math.max(existing.dayActive, activeIdentityRow.dayActive);
|
|
1446
|
+
existing.weekActive = Math.max(existing.weekActive, activeIdentityRow.weekActive);
|
|
1447
|
+
existing.monthActive = Math.max(existing.monthActive, activeIdentityRow.monthActive);
|
|
1448
|
+
existing.yearActive = Math.max(existing.yearActive, activeIdentityRow.yearActive);
|
|
1449
|
+
}
|
|
1450
|
+
const activeUserCounts = {
|
|
1451
|
+
hourActiveUserCount: 0,
|
|
1452
|
+
dayActiveUserCount: 0,
|
|
1453
|
+
weekActiveUserCount: 0,
|
|
1454
|
+
monthActiveUserCount: 0,
|
|
1455
|
+
yearActiveUserCount: 0,
|
|
1456
|
+
allActiveUserCount: canonicalActivityByAddress.size
|
|
1457
|
+
};
|
|
1458
|
+
for (const canonicalActivity of canonicalActivityByAddress.values()) {
|
|
1459
|
+
if (canonicalActivity.hourActive > 0)
|
|
1460
|
+
activeUserCounts.hourActiveUserCount++;
|
|
1461
|
+
if (canonicalActivity.dayActive > 0)
|
|
1462
|
+
activeUserCounts.dayActiveUserCount++;
|
|
1463
|
+
if (canonicalActivity.weekActive > 0)
|
|
1464
|
+
activeUserCounts.weekActiveUserCount++;
|
|
1465
|
+
if (canonicalActivity.monthActive > 0)
|
|
1466
|
+
activeUserCounts.monthActiveUserCount++;
|
|
1467
|
+
if (canonicalActivity.yearActive > 0)
|
|
1468
|
+
activeUserCounts.yearActiveUserCount++;
|
|
1469
|
+
}
|
|
1470
|
+
return {
|
|
1471
|
+
...activeUserCounts,
|
|
1472
|
+
...postAndReplyCounts
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
queryCommentsUnderComment(parentCid) {
|
|
1476
|
+
const results = this._db.prepare(`SELECT * FROM ${TABLES.COMMENTS} WHERE parentCid = ?`).all(parentCid);
|
|
1477
|
+
return results.map((r) => this._parseCommentsTableRow(r));
|
|
1478
|
+
}
|
|
1479
|
+
queryFirstCommentWithDepth(commentDepth) {
|
|
1480
|
+
if (!Number.isInteger(commentDepth) || commentDepth < 0)
|
|
1481
|
+
throw new Error("commentDepth must be a non-negative integer");
|
|
1482
|
+
const { clause: addrClause, params: addrParams } = this._communityAddressClauseNamed("c", "firstComment");
|
|
1483
|
+
const exactDepthRow = this._db
|
|
1484
|
+
.prepare(`SELECT c.* FROM ${TABLES.COMMENTS} c
|
|
1485
|
+
LEFT JOIN ${TABLES.COMMENT_UPDATES} cu ON cu.cid = c.cid
|
|
1486
|
+
WHERE ${addrClause}
|
|
1487
|
+
AND c.depth = @commentDepth
|
|
1488
|
+
ORDER BY COALESCE(cu.replyCount, 0) DESC
|
|
1489
|
+
LIMIT 1`)
|
|
1490
|
+
.get({ ...addrParams, commentDepth });
|
|
1491
|
+
if (exactDepthRow)
|
|
1492
|
+
return this._parseCommentsTableRow(exactDepthRow);
|
|
1493
|
+
const lowerDepthRow = this._db
|
|
1494
|
+
.prepare(`SELECT c.* FROM ${TABLES.COMMENTS} c
|
|
1495
|
+
LEFT JOIN ${TABLES.COMMENT_UPDATES} cu ON cu.cid = c.cid
|
|
1496
|
+
WHERE ${addrClause}
|
|
1497
|
+
AND c.depth < @commentDepth
|
|
1498
|
+
ORDER BY c.depth DESC, COALESCE(cu.replyCount, 0) DESC
|
|
1499
|
+
LIMIT 1`)
|
|
1500
|
+
.get({ ...addrParams, commentDepth });
|
|
1501
|
+
if (!lowerDepthRow)
|
|
1502
|
+
return undefined;
|
|
1503
|
+
return this._parseCommentsTableRow(lowerDepthRow);
|
|
1504
|
+
}
|
|
1505
|
+
queryCombinedHashOfPendingComments() {
|
|
1506
|
+
const rows = this._db.prepare(`SELECT cid FROM ${TABLES.COMMENTS} WHERE pendingApproval = 1 ORDER BY rowid ASC`).all();
|
|
1507
|
+
const concatenated = rows.map((r) => r.cid).join("");
|
|
1508
|
+
const hash = sha256(concatenated);
|
|
1509
|
+
return hash;
|
|
1510
|
+
}
|
|
1511
|
+
queryComment(cid) {
|
|
1512
|
+
const row = this._db.prepare(`SELECT * FROM ${TABLES.COMMENTS} WHERE cid = ?`).get(cid);
|
|
1513
|
+
if (!row)
|
|
1514
|
+
return undefined;
|
|
1515
|
+
return this._parseCommentsTableRow(row);
|
|
1516
|
+
}
|
|
1517
|
+
queryPseudonymityAliasByCommentCid(commentCid) {
|
|
1518
|
+
const row = this._db
|
|
1519
|
+
.prepare(`SELECT commentCid, aliasPrivateKey, originalAuthorSignerPublicKey, originalAuthorDomain, mode, insertedAt FROM ${TABLES.PSEUDONYMITY_ALIASES} WHERE commentCid = ?`)
|
|
1520
|
+
.get(commentCid);
|
|
1521
|
+
return row;
|
|
1522
|
+
}
|
|
1523
|
+
queryPseudonymityAliasForPost(originalAuthorSignerPublicKey, postCid) {
|
|
1524
|
+
const row = this._db
|
|
1525
|
+
.prepare(`
|
|
1526
|
+
SELECT alias.commentCid, alias.aliasPrivateKey, alias.originalAuthorSignerPublicKey, alias.originalAuthorDomain, alias.mode, alias.insertedAt
|
|
1527
|
+
FROM ${TABLES.PSEUDONYMITY_ALIASES} AS alias
|
|
1528
|
+
INNER JOIN ${TABLES.COMMENTS} AS comments ON comments.cid = alias.commentCid
|
|
1529
|
+
WHERE alias.mode = 'per-post' AND alias.originalAuthorSignerPublicKey = ? AND comments.postCid = ?
|
|
1530
|
+
ORDER BY alias.insertedAt ASC
|
|
1531
|
+
LIMIT 1
|
|
1532
|
+
`)
|
|
1533
|
+
.get(originalAuthorSignerPublicKey, postCid);
|
|
1534
|
+
return row;
|
|
1535
|
+
}
|
|
1536
|
+
queryPseudonymityAliasForAuthor(originalAuthorSignerPublicKey) {
|
|
1537
|
+
const row = this._db
|
|
1538
|
+
.prepare(`
|
|
1539
|
+
SELECT commentCid, aliasPrivateKey, originalAuthorSignerPublicKey, originalAuthorDomain, mode, insertedAt
|
|
1540
|
+
FROM ${TABLES.PSEUDONYMITY_ALIASES}
|
|
1541
|
+
WHERE mode = 'per-author' AND originalAuthorSignerPublicKey = ?
|
|
1542
|
+
ORDER BY insertedAt ASC
|
|
1543
|
+
LIMIT 1
|
|
1544
|
+
`)
|
|
1545
|
+
.get(originalAuthorSignerPublicKey);
|
|
1546
|
+
return row;
|
|
1547
|
+
}
|
|
1548
|
+
_queryCommentAuthorAndParentWithoutParsing(cid) {
|
|
1549
|
+
const row = this._db.prepare(`SELECT authorSignerAddress, parentCid FROM ${TABLES.COMMENTS} WHERE cid = ?`).get(cid);
|
|
1550
|
+
if (!row)
|
|
1551
|
+
return undefined;
|
|
1552
|
+
const authorSignerAddress = typeof row.authorSignerAddress === "string" ? row.authorSignerAddress : undefined;
|
|
1553
|
+
const parentCid = typeof row.parentCid === "string" ? row.parentCid : row.parentCid === null ? null : undefined;
|
|
1554
|
+
return { authorSignerAddress, parentCid };
|
|
1555
|
+
}
|
|
1556
|
+
_queryCommentCounts(cid) {
|
|
1557
|
+
const { clause: addrClause, params: addrParams } = this._communityAddressClauseNamed("c", "cc");
|
|
1558
|
+
const query = `
|
|
1559
|
+
SELECT
|
|
1560
|
+
(SELECT COUNT(*) FROM ${TABLES.VOTES} WHERE commentCid = :cid AND vote = 1) AS upvoteCount,
|
|
1561
|
+
(SELECT COUNT(*) FROM ${TABLES.VOTES} WHERE commentCid = :cid AND vote = -1) AS downvoteCount,
|
|
1562
|
+
(
|
|
1563
|
+
WITH RECURSIVE descendants AS (
|
|
1564
|
+
SELECT c.cid FROM ${TABLES.COMMENTS} c
|
|
1565
|
+
INNER JOIN ${TABLES.COMMENT_UPDATES} cu ON c.cid = cu.cid
|
|
1566
|
+
LEFT JOIN (SELECT cid, json_extract(edit, '$.deleted') AS deleted_flag FROM ${TABLES.COMMENT_UPDATES}) AS d ON c.cid = d.cid
|
|
1567
|
+
WHERE c.parentCid = :cid AND ${addrClause} AND (cu.removed IS NOT 1 AND cu.removed IS NOT TRUE) AND (d.deleted_flag IS NULL OR d.deleted_flag != 1)
|
|
1568
|
+
UNION ALL
|
|
1569
|
+
SELECT c.cid FROM ${TABLES.COMMENTS} c
|
|
1570
|
+
INNER JOIN ${TABLES.COMMENT_UPDATES} cu ON c.cid = cu.cid
|
|
1571
|
+
LEFT JOIN (SELECT cid, json_extract(edit, '$.deleted') AS deleted_flag FROM ${TABLES.COMMENT_UPDATES}) AS d ON c.cid = d.cid
|
|
1572
|
+
JOIN descendants desc_nodes ON c.parentCid = desc_nodes.cid
|
|
1573
|
+
WHERE ${addrClause} AND (cu.removed IS NOT 1 AND cu.removed IS NOT TRUE) AND (d.deleted_flag IS NULL OR d.deleted_flag != 1)
|
|
1574
|
+
) SELECT COUNT(*) FROM descendants
|
|
1575
|
+
) AS replyCount,
|
|
1576
|
+
(
|
|
1577
|
+
SELECT COUNT(*) FROM ${TABLES.COMMENTS} c
|
|
1578
|
+
INNER JOIN ${TABLES.COMMENT_UPDATES} cu ON c.cid = cu.cid
|
|
1579
|
+
LEFT JOIN (SELECT cid, json_extract(edit, '$.deleted') AS deleted_flag FROM ${TABLES.COMMENT_UPDATES}) AS d ON c.cid = d.cid
|
|
1580
|
+
WHERE c.parentCid = :cid AND ${addrClause} AND (cu.removed IS NOT 1 AND cu.removed IS NOT TRUE) AND (d.deleted_flag IS NULL OR d.deleted_flag != 1)
|
|
1581
|
+
) AS childCount
|
|
1582
|
+
`;
|
|
1583
|
+
return this._db.prepare(query).get({ cid, ...addrParams });
|
|
1584
|
+
}
|
|
1585
|
+
queryPostsWithOutdatedBuckets(buckets) {
|
|
1586
|
+
const currentTimestampSeconds = timestamp(); // timestamp is in seconds
|
|
1587
|
+
const maxBucket = Math.max(...buckets);
|
|
1588
|
+
const caseClauses = buckets
|
|
1589
|
+
.sort((a, b) => a - b)
|
|
1590
|
+
.map((bucket) => `WHEN (${currentTimestampSeconds} - c.timestamp) <= ${bucket} THEN ${bucket}`)
|
|
1591
|
+
.join(" ");
|
|
1592
|
+
const { clause: addrClause, params: addrParams } = this._communityAddressClause("c");
|
|
1593
|
+
const query = `
|
|
1594
|
+
WITH post_data AS (
|
|
1595
|
+
SELECT c.cid, c.timestamp, cu.postUpdatesBucket AS current_bucket,
|
|
1596
|
+
CASE ${caseClauses} ELSE ${maxBucket} END AS new_bucket
|
|
1597
|
+
FROM ${TABLES.COMMENTS} as c INNER JOIN ${TABLES.COMMENT_UPDATES} as cu ON c.cid = cu.cid
|
|
1598
|
+
WHERE ${addrClause}
|
|
1599
|
+
AND (c.pendingApproval IS NULL OR c.pendingApproval != 1)
|
|
1600
|
+
AND cu.postUpdatesBucket IS NOT NULL AND cu.postUpdatesBucket != ?
|
|
1601
|
+
) SELECT cid, timestamp, current_bucket AS currentBucket, new_bucket AS newBucket
|
|
1602
|
+
FROM post_data WHERE current_bucket != new_bucket
|
|
1603
|
+
`;
|
|
1604
|
+
return this._db.prepare(query).all(...addrParams, maxBucket);
|
|
1605
|
+
}
|
|
1606
|
+
_queryLatestAuthorEdit(cid, authorSignerAddress) {
|
|
1607
|
+
const row = this._db
|
|
1608
|
+
.prepare(`
|
|
1609
|
+
SELECT * FROM ${TABLES.COMMENT_EDITS}
|
|
1610
|
+
WHERE commentCid = ? AND authorSignerAddress = ? AND (isAuthorEdit = 1)
|
|
1611
|
+
ORDER BY rowid DESC LIMIT 1
|
|
1612
|
+
`)
|
|
1613
|
+
.get(cid, authorSignerAddress);
|
|
1614
|
+
if (!row)
|
|
1615
|
+
return undefined;
|
|
1616
|
+
const parsed = this._spreadExtraProps(this._parseCommentEditsRow(row));
|
|
1617
|
+
const signedKeys = parsed.signature.signedPropertyNames;
|
|
1618
|
+
const commentEditFields = remeda.keys.strict(CommentEditPubsubMessagePublicationSchema.shape);
|
|
1619
|
+
return remeda.pick(parsed, ["signature", ...signedKeys, ...commentEditFields]);
|
|
1620
|
+
}
|
|
1621
|
+
removeCommentFromPendingApproval(comment) {
|
|
1622
|
+
const log = Logger("pkc-js:local-community:db-handler:removeCommentFromPendingApproval");
|
|
1623
|
+
const stmt = this._db.prepare(`UPDATE ${TABLES.COMMENTS} SET pendingApproval = 0 WHERE cid = ?`);
|
|
1624
|
+
const res = stmt.run(comment.cid);
|
|
1625
|
+
log.trace(`Removed pendingApproval for cid=${comment.cid}, changes=${res.changes}`);
|
|
1626
|
+
}
|
|
1627
|
+
approvePendingComment(comment) {
|
|
1628
|
+
const log = Logger("pkc-js:local-community:db-handler:approvePendingComment");
|
|
1629
|
+
const assignNumbers = this._db.transaction((commentCid) => {
|
|
1630
|
+
this._db.prepare(`UPDATE ${TABLES.COMMENTS} SET pendingApproval = 0 WHERE cid = ?`).run(commentCid);
|
|
1631
|
+
return this._assignNumbersForComment(commentCid);
|
|
1632
|
+
});
|
|
1633
|
+
const numbers = assignNumbers(comment.cid);
|
|
1634
|
+
log.trace(`Approved pending comment cid=${comment.cid}`, numbers);
|
|
1635
|
+
return numbers;
|
|
1636
|
+
}
|
|
1637
|
+
getNextCommentNumbers(depth) {
|
|
1638
|
+
const pendingClause = this._pendingApprovalClause("c");
|
|
1639
|
+
const maxNumberRow = this._db
|
|
1640
|
+
.prepare(`SELECT COALESCE(MAX(number), 0) AS maxNumber FROM ${TABLES.COMMENTS} c WHERE number IS NOT NULL AND ${pendingClause}`)
|
|
1641
|
+
.get();
|
|
1642
|
+
const number = (maxNumberRow?.maxNumber || 0) + 1;
|
|
1643
|
+
if (depth !== 0)
|
|
1644
|
+
return { number };
|
|
1645
|
+
const maxPostNumberRow = this._db
|
|
1646
|
+
.prepare(`SELECT COALESCE(MAX(postNumber), 0) AS maxPostNumber FROM ${TABLES.COMMENTS} c WHERE postNumber IS NOT NULL AND depth = 0 AND ${pendingClause}`)
|
|
1647
|
+
.get();
|
|
1648
|
+
const postNumber = (maxPostNumberRow?.maxPostNumber || 0) + 1;
|
|
1649
|
+
return { number, postNumber };
|
|
1650
|
+
}
|
|
1651
|
+
_assignNumbersForComment(commentCid) {
|
|
1652
|
+
const commentRow = this._db
|
|
1653
|
+
.prepare(`SELECT depth, pendingApproval, number, postNumber FROM ${TABLES.COMMENTS} WHERE cid = ? LIMIT 1`)
|
|
1654
|
+
.get(commentCid);
|
|
1655
|
+
if (!commentRow)
|
|
1656
|
+
throw Error(`Failed to query comment row for ${commentCid}`);
|
|
1657
|
+
if (commentRow.pendingApproval === 1)
|
|
1658
|
+
return {};
|
|
1659
|
+
if (typeof commentRow.number === "number" && commentRow.number > 0) {
|
|
1660
|
+
return {
|
|
1661
|
+
number: commentRow.number,
|
|
1662
|
+
...(typeof commentRow.postNumber === "number" && commentRow.postNumber > 0 ? { postNumber: commentRow.postNumber } : {})
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
const pendingClause = this._pendingApprovalClause("c");
|
|
1666
|
+
const maxNumberRow = this._db
|
|
1667
|
+
.prepare(`SELECT COALESCE(MAX(number), 0) AS maxNumber FROM ${TABLES.COMMENTS} c WHERE number IS NOT NULL AND ${pendingClause}`)
|
|
1668
|
+
.get();
|
|
1669
|
+
const number = (maxNumberRow?.maxNumber || 0) + 1;
|
|
1670
|
+
let postNumber;
|
|
1671
|
+
if (commentRow.depth === 0) {
|
|
1672
|
+
const maxPostNumberRow = this._db
|
|
1673
|
+
.prepare(`SELECT COALESCE(MAX(postNumber), 0) AS maxPostNumber FROM ${TABLES.COMMENTS} c WHERE postNumber IS NOT NULL AND depth = 0 AND ${pendingClause}`)
|
|
1674
|
+
.get();
|
|
1675
|
+
postNumber = (maxPostNumberRow?.maxPostNumber || 0) + 1;
|
|
1676
|
+
}
|
|
1677
|
+
this._db
|
|
1678
|
+
.prepare(`UPDATE ${TABLES.COMMENTS} SET number = ?, postNumber = ? WHERE cid = ?`)
|
|
1679
|
+
.run(number, postNumber ?? null, commentCid);
|
|
1680
|
+
return { number, ...(postNumber !== undefined ? { postNumber } : {}) };
|
|
1681
|
+
}
|
|
1682
|
+
// Remove oldest comments pending approval when exceeding the configured limit
|
|
1683
|
+
removeOldestPendingCommentIfWeHitMaxPendingCount(maxPendingApprovalCount) {
|
|
1684
|
+
const log = Logger("pkc-js:local-community:db-handler:removeOldestPendingCommentIfWeHitMaxPendingCount");
|
|
1685
|
+
// Assume maxPendingApprovalCount is a valid integer > 0
|
|
1686
|
+
try {
|
|
1687
|
+
const { cnt } = this._db.prepare(`SELECT COUNT(1) as cnt FROM ${TABLES.COMMENTS} WHERE pendingApproval = 1`).get();
|
|
1688
|
+
if (cnt <= maxPendingApprovalCount)
|
|
1689
|
+
return;
|
|
1690
|
+
const toRemove = cnt - maxPendingApprovalCount;
|
|
1691
|
+
const oldest = this._db
|
|
1692
|
+
.prepare(`SELECT cid FROM ${TABLES.COMMENTS} WHERE pendingApproval = 1 ORDER BY rowid ASC LIMIT ?`)
|
|
1693
|
+
.all(toRemove);
|
|
1694
|
+
if (oldest.length === 0)
|
|
1695
|
+
return;
|
|
1696
|
+
log(`Evicting ${oldest.length} oldest pending comments (count=${cnt}, limit=${maxPendingApprovalCount})`);
|
|
1697
|
+
this.createTransaction();
|
|
1698
|
+
try {
|
|
1699
|
+
for (const { cid } of oldest)
|
|
1700
|
+
this.purgeComment(cid);
|
|
1701
|
+
this.commitTransaction();
|
|
1702
|
+
}
|
|
1703
|
+
catch (e) {
|
|
1704
|
+
this.rollbackTransaction();
|
|
1705
|
+
throw e;
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
catch (e) {
|
|
1709
|
+
log.error("Failed to enforce maxPendingApprovalCount", e);
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
purgeDisapprovedCommentsOlderThan(retentionSeconds) {
|
|
1713
|
+
const log = Logger("pkc-js:local-community:db-handler:purgeDisapprovedCommentsOlderThan");
|
|
1714
|
+
if (!Number.isFinite(retentionSeconds) || retentionSeconds <= 0)
|
|
1715
|
+
return;
|
|
1716
|
+
const now = timestamp();
|
|
1717
|
+
const cutoffTimestamp = now - retentionSeconds;
|
|
1718
|
+
const rows = this._db
|
|
1719
|
+
.prepare(`
|
|
1720
|
+
WITH first_disapproved AS (
|
|
1721
|
+
SELECT commentCid AS cid,
|
|
1722
|
+
MIN(timestamp) AS first_disapproved_at
|
|
1723
|
+
FROM ${TABLES.COMMENT_MODERATIONS}
|
|
1724
|
+
WHERE json_type(commentModeration, '$.approved') = 'false'
|
|
1725
|
+
GROUP BY commentCid
|
|
1726
|
+
)
|
|
1727
|
+
SELECT c.cid AS cid,
|
|
1728
|
+
c.parentCid AS parentCid,
|
|
1729
|
+
COALESCE(fd.first_disapproved_at, cu.updatedAt) AS firstDisapprovedAt,
|
|
1730
|
+
cu.postUpdatesBucket AS postUpdatesBucket
|
|
1731
|
+
FROM ${TABLES.COMMENT_UPDATES} cu
|
|
1732
|
+
INNER JOIN ${TABLES.COMMENTS} c ON c.cid = cu.cid
|
|
1733
|
+
LEFT JOIN first_disapproved fd ON fd.cid = cu.cid
|
|
1734
|
+
WHERE (COALESCE(cu.approved, 1) = 0 OR cu.approved = 'false')
|
|
1735
|
+
AND COALESCE(fd.first_disapproved_at, cu.updatedAt) <= ?
|
|
1736
|
+
`)
|
|
1737
|
+
.all(cutoffTimestamp);
|
|
1738
|
+
if (rows.length === 0)
|
|
1739
|
+
return;
|
|
1740
|
+
log(`Purging ${rows.length} disapproved comments older than ${retentionSeconds} seconds (cutoff ${cutoffTimestamp}).`);
|
|
1741
|
+
const purgedDetails = [];
|
|
1742
|
+
for (const row of rows) {
|
|
1743
|
+
const purgedTableRows = this.purgeComment(row.cid);
|
|
1744
|
+
purgedDetails.push({
|
|
1745
|
+
cid: row.cid,
|
|
1746
|
+
parentCid: row.parentCid,
|
|
1747
|
+
postUpdatesBucket: row.postUpdatesBucket || undefined,
|
|
1748
|
+
purgedTableRows
|
|
1749
|
+
});
|
|
1750
|
+
}
|
|
1751
|
+
return purgedDetails;
|
|
1752
|
+
}
|
|
1753
|
+
_queryLatestModeratorReason(comment) {
|
|
1754
|
+
const result = this._db
|
|
1755
|
+
.prepare(`
|
|
1756
|
+
SELECT json_extract(commentModeration, '$.reason') AS reason FROM ${TABLES.COMMENT_MODERATIONS}
|
|
1757
|
+
WHERE commentCid = ? AND json_extract(commentModeration, '$.reason') IS NOT NULL ORDER BY rowid DESC LIMIT 1
|
|
1758
|
+
`)
|
|
1759
|
+
.get(comment.cid);
|
|
1760
|
+
if (!result)
|
|
1761
|
+
return undefined;
|
|
1762
|
+
return result;
|
|
1763
|
+
}
|
|
1764
|
+
queryCommentFlagsSetByMod(cid) {
|
|
1765
|
+
const query = `
|
|
1766
|
+
WITH flags_with_rank AS (
|
|
1767
|
+
SELECT commentCid,
|
|
1768
|
+
json_extract(commentModeration, '$.spoiler') AS spoiler, json_extract(commentModeration, '$.pinned') AS pinned,
|
|
1769
|
+
json_extract(commentModeration, '$.locked') AS locked, json_extract(commentModeration, '$.archived') AS archived,
|
|
1770
|
+
json_extract(commentModeration, '$.removed') AS removed,
|
|
1771
|
+
json_extract(commentModeration, '$.nsfw') AS nsfw,
|
|
1772
|
+
ROW_NUMBER() OVER (PARTITION BY commentCid, CASE WHEN json_extract(commentModeration, '$.spoiler') IS NOT NULL THEN 'spoiler' ELSE NULL END ORDER BY rowid DESC) AS spoiler_rank,
|
|
1773
|
+
ROW_NUMBER() OVER (PARTITION BY commentCid, CASE WHEN json_extract(commentModeration, '$.pinned') IS NOT NULL THEN 'pinned' ELSE NULL END ORDER BY rowid DESC) AS pinned_rank,
|
|
1774
|
+
ROW_NUMBER() OVER (PARTITION BY commentCid, CASE WHEN json_extract(commentModeration, '$.locked') IS NOT NULL THEN 'locked' ELSE NULL END ORDER BY rowid DESC) AS locked_rank,
|
|
1775
|
+
ROW_NUMBER() OVER (PARTITION BY commentCid, CASE WHEN json_extract(commentModeration, '$.archived') IS NOT NULL THEN 'archived' ELSE NULL END ORDER BY rowid DESC) AS archived_rank,
|
|
1776
|
+
ROW_NUMBER() OVER (PARTITION BY commentCid, CASE WHEN json_extract(commentModeration, '$.removed') IS NOT NULL THEN 'removed' ELSE NULL END ORDER BY rowid DESC) AS removed_rank,
|
|
1777
|
+
ROW_NUMBER() OVER (PARTITION BY commentCid, CASE WHEN json_extract(commentModeration, '$.nsfw') IS NOT NULL THEN 'nsfw' ELSE NULL END ORDER BY rowid DESC) AS nsfw_rank
|
|
1778
|
+
FROM ${TABLES.COMMENT_MODERATIONS} WHERE commentCid = ?
|
|
1779
|
+
)
|
|
1780
|
+
SELECT
|
|
1781
|
+
MAX(CASE WHEN spoiler IS NOT NULL AND spoiler_rank = 1 THEN spoiler ELSE NULL END) AS spoiler,
|
|
1782
|
+
MAX(CASE WHEN pinned IS NOT NULL AND pinned_rank = 1 THEN pinned ELSE NULL END) AS pinned,
|
|
1783
|
+
MAX(CASE WHEN locked IS NOT NULL AND locked_rank = 1 THEN locked ELSE NULL END) AS locked,
|
|
1784
|
+
MAX(CASE WHEN archived IS NOT NULL AND archived_rank = 1 THEN archived ELSE NULL END) AS archived,
|
|
1785
|
+
MAX(CASE WHEN removed IS NOT NULL AND removed_rank = 1 THEN removed ELSE NULL END) AS removed,
|
|
1786
|
+
MAX(CASE WHEN nsfw IS NOT NULL AND nsfw_rank = 1 THEN nsfw ELSE NULL END) AS nsfw
|
|
1787
|
+
FROM flags_with_rank
|
|
1788
|
+
`;
|
|
1789
|
+
const flags = this._db.prepare(query).get(cid);
|
|
1790
|
+
if (!flags)
|
|
1791
|
+
return {};
|
|
1792
|
+
return remeda.mapValues(removeNullUndefinedValues(flags), Boolean);
|
|
1793
|
+
}
|
|
1794
|
+
queryAuthorEditDeleted(cid) {
|
|
1795
|
+
const result = this._db
|
|
1796
|
+
.prepare(`
|
|
1797
|
+
SELECT deleted FROM ${TABLES.COMMENT_EDITS}
|
|
1798
|
+
WHERE commentCid = ? AND (isAuthorEdit = 1 OR isAuthorEdit = TRUE) AND deleted IS NOT NULL ORDER BY rowid DESC LIMIT 1
|
|
1799
|
+
`)
|
|
1800
|
+
.get(cid);
|
|
1801
|
+
return result && result.deleted !== null ? { deleted: Boolean(result.deleted) } : undefined;
|
|
1802
|
+
}
|
|
1803
|
+
_queryModCommentFlairs(comment) {
|
|
1804
|
+
const result = this._db
|
|
1805
|
+
.prepare(`
|
|
1806
|
+
SELECT json_extract(commentModeration, '$.flairs') AS flairs FROM ${TABLES.COMMENT_MODERATIONS}
|
|
1807
|
+
WHERE commentCid = ? AND json_extract(commentModeration, '$.flairs') IS NOT NULL ORDER BY rowid DESC LIMIT 1
|
|
1808
|
+
`)
|
|
1809
|
+
.get(comment.cid);
|
|
1810
|
+
if (!result)
|
|
1811
|
+
return undefined;
|
|
1812
|
+
return { flairs: JSON.parse(result.flairs) };
|
|
1813
|
+
}
|
|
1814
|
+
_queryLastChildCidAndLastReplyTimestamp(comment) {
|
|
1815
|
+
const lastChildCid = this._db
|
|
1816
|
+
.prepare(`SELECT c.cid FROM ${TABLES.COMMENTS} c
|
|
1817
|
+
INNER JOIN ${TABLES.COMMENT_UPDATES} cu ON cu.cid = c.cid
|
|
1818
|
+
LEFT JOIN (
|
|
1819
|
+
SELECT cid, json_extract(edit, '$.deleted') AS deleted_flag FROM ${TABLES.COMMENT_UPDATES}
|
|
1820
|
+
) deleted_lookup ON deleted_lookup.cid = c.cid
|
|
1821
|
+
WHERE c.parentCid = ?
|
|
1822
|
+
AND (c.pendingApproval IS NULL OR c.pendingApproval != 1)
|
|
1823
|
+
AND COALESCE(cu.approved, 1) != 0
|
|
1824
|
+
AND (cu.removed IS NOT 1 AND cu.removed IS NOT TRUE)
|
|
1825
|
+
AND (deleted_lookup.deleted_flag IS NULL OR deleted_lookup.deleted_flag != 1)
|
|
1826
|
+
ORDER BY c.rowid DESC
|
|
1827
|
+
LIMIT 1`)
|
|
1828
|
+
.get(comment.cid);
|
|
1829
|
+
const lastReplyTimestamp = this.queryMaximumTimestampUnderComment(comment);
|
|
1830
|
+
return { lastChildCid: lastChildCid?.cid, lastReplyTimestamp };
|
|
1831
|
+
}
|
|
1832
|
+
_queryIsCommentApproved(comment) {
|
|
1833
|
+
const result = this._db
|
|
1834
|
+
.prepare(`
|
|
1835
|
+
SELECT json_extract(commentModeration, '$.approved') AS approved FROM ${TABLES.COMMENT_MODERATIONS}
|
|
1836
|
+
WHERE commentCid = ? AND json_extract(commentModeration, '$.approved') IS NOT NULL ORDER BY rowid DESC LIMIT 1
|
|
1837
|
+
`)
|
|
1838
|
+
.get(comment.cid);
|
|
1839
|
+
if (!result || result.approved === null)
|
|
1840
|
+
return undefined;
|
|
1841
|
+
return { approved: Boolean(result.approved) };
|
|
1842
|
+
}
|
|
1843
|
+
_calculateCommentNumbers(cid) {
|
|
1844
|
+
const commentRowMeta = this._db
|
|
1845
|
+
.prepare(`SELECT rowid as rowid, depth, pendingApproval, number, postNumber FROM ${TABLES.COMMENTS} WHERE cid = ?`)
|
|
1846
|
+
.get(cid);
|
|
1847
|
+
if (!commentRowMeta)
|
|
1848
|
+
throw Error(`Failed to query row metadata for comment ${cid}`);
|
|
1849
|
+
if (commentRowMeta.pendingApproval === 1)
|
|
1850
|
+
return {};
|
|
1851
|
+
let commentNumber = typeof commentRowMeta.number === "number" && commentRowMeta.number > 0 ? commentRowMeta.number : undefined;
|
|
1852
|
+
let postNumber = typeof commentRowMeta.postNumber === "number" && commentRowMeta.postNumber > 0 ? commentRowMeta.postNumber : undefined;
|
|
1853
|
+
if (commentNumber === undefined || (commentRowMeta.depth === 0 && postNumber === undefined)) {
|
|
1854
|
+
const existingNumbers = this._db
|
|
1855
|
+
.prepare(`SELECT number, postNumber FROM ${TABLES.COMMENT_UPDATES} WHERE cid = ? LIMIT 1`)
|
|
1856
|
+
.get(cid);
|
|
1857
|
+
if (commentNumber === undefined && typeof existingNumbers?.number === "number" && existingNumbers.number > 0)
|
|
1858
|
+
commentNumber = existingNumbers.number;
|
|
1859
|
+
if (commentRowMeta.depth === 0 && postNumber === undefined) {
|
|
1860
|
+
if (typeof existingNumbers?.postNumber === "number" && existingNumbers.postNumber > 0)
|
|
1861
|
+
postNumber = existingNumbers.postNumber;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
return {
|
|
1865
|
+
...(commentNumber !== undefined ? { number: commentNumber } : undefined),
|
|
1866
|
+
...(postNumber !== undefined ? { postNumber } : undefined)
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
queryCalculatedCommentUpdate(opts) {
|
|
1870
|
+
const { comment, authorDomain } = opts;
|
|
1871
|
+
const authorCommunity = this.queryCommunityAuthorForCommentUpdate({
|
|
1872
|
+
authorSignerAddress: comment.authorSignerAddress,
|
|
1873
|
+
commentCid: comment.cid,
|
|
1874
|
+
authorDomain
|
|
1875
|
+
});
|
|
1876
|
+
const authorEdit = this._queryLatestAuthorEdit(comment.cid, comment.authorSignerAddress);
|
|
1877
|
+
const commentUpdateCounts = this._queryCommentCounts(comment.cid);
|
|
1878
|
+
const moderatorReason = this._queryLatestModeratorReason(comment);
|
|
1879
|
+
const commentFlags = this.queryCommentFlagsSetByMod(comment.cid);
|
|
1880
|
+
const commentModFlairs = this._queryModCommentFlairs(comment);
|
|
1881
|
+
const lastChildAndLastReplyTimestamp = this._queryLastChildCidAndLastReplyTimestamp(comment);
|
|
1882
|
+
const isThisCommentApproved = this._queryIsCommentApproved(comment);
|
|
1883
|
+
const removedFromApproved = isThisCommentApproved?.approved === false ? { removed: true } : undefined; // automatically add removed:true if approved=false. Will be overridden if there's commentFlags.removed
|
|
1884
|
+
const { number: commentNumber, postNumber } = this._calculateCommentNumbers(comment.cid);
|
|
1885
|
+
if (!authorCommunity)
|
|
1886
|
+
throw Error("Failed to query author.community in queryCalculatedCommentUpdate");
|
|
1887
|
+
return {
|
|
1888
|
+
...(removedFromApproved ? removedFromApproved : undefined),
|
|
1889
|
+
cid: comment.cid,
|
|
1890
|
+
...(commentNumber !== undefined ? { number: commentNumber } : undefined),
|
|
1891
|
+
...(postNumber !== undefined ? { postNumber } : undefined),
|
|
1892
|
+
...commentUpdateCounts,
|
|
1893
|
+
flairs: commentModFlairs?.flairs || authorEdit?.flairs,
|
|
1894
|
+
...commentFlags,
|
|
1895
|
+
reason: moderatorReason?.reason,
|
|
1896
|
+
author: { community: authorCommunity },
|
|
1897
|
+
...lastChildAndLastReplyTimestamp,
|
|
1898
|
+
...(authorEdit ? { edit: authorEdit } : undefined),
|
|
1899
|
+
...(isThisCommentApproved ? { approved: isThisCommentApproved.approved } : undefined)
|
|
1900
|
+
};
|
|
1901
|
+
}
|
|
1902
|
+
queryLatestPostCid() {
|
|
1903
|
+
return this._db
|
|
1904
|
+
.prepare(`SELECT c.cid FROM ${TABLES.COMMENTS} c
|
|
1905
|
+
LEFT JOIN ${TABLES.COMMENT_UPDATES} cu ON cu.cid = c.cid
|
|
1906
|
+
WHERE c.depth = 0
|
|
1907
|
+
AND c.pendingApproval IS NOT 1
|
|
1908
|
+
AND COALESCE(cu.approved, 1) != 0
|
|
1909
|
+
ORDER BY c.rowid DESC
|
|
1910
|
+
LIMIT 1`)
|
|
1911
|
+
.get();
|
|
1912
|
+
}
|
|
1913
|
+
queryLatestCommentCid() {
|
|
1914
|
+
return this._db
|
|
1915
|
+
.prepare(`SELECT c.cid FROM ${TABLES.COMMENTS} c
|
|
1916
|
+
LEFT JOIN ${TABLES.COMMENT_UPDATES} cu ON cu.cid = c.cid
|
|
1917
|
+
WHERE c.pendingApproval IS NOT 1
|
|
1918
|
+
AND COALESCE(cu.approved, 1) != 0
|
|
1919
|
+
ORDER BY c.rowid DESC
|
|
1920
|
+
LIMIT 1`)
|
|
1921
|
+
.get();
|
|
1922
|
+
}
|
|
1923
|
+
queryAllCommentsOrderedByIdAsc() {
|
|
1924
|
+
const results = this._db.prepare(`SELECT * FROM ${TABLES.COMMENTS} ORDER BY rowid ASC`).all();
|
|
1925
|
+
return results.map((r) => this._parseCommentsTableRow(r));
|
|
1926
|
+
}
|
|
1927
|
+
queryAuthorModEdits(opts) {
|
|
1928
|
+
const { authorSignerAddresses, authorDomain } = opts;
|
|
1929
|
+
if (authorSignerAddresses.length === 0 && !authorDomain)
|
|
1930
|
+
return {};
|
|
1931
|
+
const conditions = [];
|
|
1932
|
+
const params = [];
|
|
1933
|
+
if (authorSignerAddresses.length > 0) {
|
|
1934
|
+
const placeholders = authorSignerAddresses.map(() => "?").join(",");
|
|
1935
|
+
conditions.push(`targetAuthorSignerAddress IN (${placeholders})`);
|
|
1936
|
+
params.push(...authorSignerAddresses);
|
|
1937
|
+
}
|
|
1938
|
+
if (authorDomain) {
|
|
1939
|
+
conditions.push(`targetAuthorDomain = ?`);
|
|
1940
|
+
params.push(authorDomain);
|
|
1941
|
+
}
|
|
1942
|
+
// Query directly by targetAuthorSignerAddress or targetAuthorDomain to find bans/flairs even for purged comments
|
|
1943
|
+
const modAuthorEditsRaw = this._db
|
|
1944
|
+
.prepare(`
|
|
1945
|
+
SELECT json_extract(commentModeration, '$.author') AS commentAuthorJson FROM ${TABLES.COMMENT_MODERATIONS}
|
|
1946
|
+
WHERE (${conditions.join(" OR ")}) AND json_extract(commentModeration, '$.author') IS NOT NULL ORDER BY rowid DESC
|
|
1947
|
+
`)
|
|
1948
|
+
.all(...params);
|
|
1949
|
+
const modAuthorEdits = modAuthorEditsRaw.map((r) => JSON.parse(r.commentAuthorJson));
|
|
1950
|
+
const banAuthor = modAuthorEdits.find((modEdit) => typeof modEdit?.banExpiresAt === "number");
|
|
1951
|
+
const authorFlairsByMod = modAuthorEdits.find((modEdit) => modEdit?.flairs);
|
|
1952
|
+
const aggregateAuthor = {};
|
|
1953
|
+
if (banAuthor?.banExpiresAt)
|
|
1954
|
+
aggregateAuthor.banExpiresAt = banAuthor.banExpiresAt;
|
|
1955
|
+
if (authorFlairsByMod?.flairs)
|
|
1956
|
+
aggregateAuthor.flairs = authorFlairsByMod.flairs;
|
|
1957
|
+
return aggregateAuthor;
|
|
1958
|
+
}
|
|
1959
|
+
queryAuthorPublicationCounts(authorSignerAddress) {
|
|
1960
|
+
const pendingClause = this._pendingApprovalClause(TABLES.COMMENTS);
|
|
1961
|
+
const result = this._db
|
|
1962
|
+
.prepare(`
|
|
1963
|
+
SELECT
|
|
1964
|
+
COALESCE(SUM(CASE WHEN depth = 0 THEN 1 ELSE 0 END), 0) as postCount,
|
|
1965
|
+
COALESCE(SUM(CASE WHEN depth > 0 THEN 1 ELSE 0 END), 0) as replyCount
|
|
1966
|
+
FROM ${TABLES.COMMENTS}
|
|
1967
|
+
WHERE authorSignerAddress = ? AND ${pendingClause}
|
|
1968
|
+
`)
|
|
1969
|
+
.get(authorSignerAddress);
|
|
1970
|
+
return result;
|
|
1971
|
+
}
|
|
1972
|
+
queryCommunityAuthor(authorSignerAddress, authorDomain) {
|
|
1973
|
+
const authorSignerAddresses = new Set([authorSignerAddress]);
|
|
1974
|
+
// If the provided address is the original signer, include all alias signer addresses for that author.
|
|
1975
|
+
const aliasRowsForOriginal = this._db
|
|
1976
|
+
.prepare(`
|
|
1977
|
+
SELECT alias.commentCid, alias.originalAuthorSignerPublicKey
|
|
1978
|
+
FROM ${TABLES.PSEUDONYMITY_ALIASES} AS alias
|
|
1979
|
+
`)
|
|
1980
|
+
.all();
|
|
1981
|
+
for (const aliasRow of aliasRowsForOriginal) {
|
|
1982
|
+
try {
|
|
1983
|
+
const originalAddress = getPKCAddressFromPublicKeySync(aliasRow.originalAuthorSignerPublicKey);
|
|
1984
|
+
if (originalAddress === authorSignerAddress) {
|
|
1985
|
+
const commentRow = this._db
|
|
1986
|
+
.prepare(`SELECT authorSignerAddress FROM ${TABLES.COMMENTS} WHERE cid = ?`)
|
|
1987
|
+
.get(aliasRow.commentCid);
|
|
1988
|
+
if (commentRow?.authorSignerAddress)
|
|
1989
|
+
authorSignerAddresses.add(commentRow.authorSignerAddress);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
catch {
|
|
1993
|
+
// ignore malformed keys
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
// If the provided address is an alias, include the original signer address for that alias.
|
|
1997
|
+
const aliasRowsForAliasAddress = this._db
|
|
1998
|
+
.prepare(`
|
|
1999
|
+
SELECT alias.originalAuthorSignerPublicKey
|
|
2000
|
+
FROM ${TABLES.PSEUDONYMITY_ALIASES} AS alias
|
|
2001
|
+
INNER JOIN ${TABLES.COMMENTS} AS comments ON comments.cid = alias.commentCid
|
|
2002
|
+
WHERE comments.authorSignerAddress = ?
|
|
2003
|
+
`)
|
|
2004
|
+
.all(authorSignerAddress);
|
|
2005
|
+
for (const aliasRow of aliasRowsForAliasAddress) {
|
|
2006
|
+
try {
|
|
2007
|
+
const originalAddress = getPKCAddressFromPublicKeySync(aliasRow.originalAuthorSignerPublicKey);
|
|
2008
|
+
authorSignerAddresses.add(originalAddress);
|
|
2009
|
+
}
|
|
2010
|
+
catch {
|
|
2011
|
+
// ignore malformed keys
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
return this._queryCommunityAuthorByAddresses([...authorSignerAddresses], undefined, authorDomain);
|
|
2015
|
+
}
|
|
2016
|
+
/** Shared helper: query karma for a set of addresses, with optional separate addresses for mod edits */
|
|
2017
|
+
_queryCommunityAuthorByAddresses(karmaAddresses, modEditAddresses = karmaAddresses, authorDomain) {
|
|
2018
|
+
if (karmaAddresses.length === 0)
|
|
2019
|
+
return undefined;
|
|
2020
|
+
const placeholders = karmaAddresses.map(() => "?").join(", ");
|
|
2021
|
+
const modAuthorEdits = this.queryAuthorModEdits({ authorSignerAddresses: modEditAddresses, authorDomain });
|
|
2022
|
+
const authorCommentsData = this._db
|
|
2023
|
+
.prepare(`
|
|
2024
|
+
SELECT c.depth, c.rowid, c.timestamp, c.cid,
|
|
2025
|
+
COALESCE(SUM(CASE WHEN v.vote = 1 THEN 1 ELSE 0 END), 0) as upvoteCount,
|
|
2026
|
+
COALESCE(SUM(CASE WHEN v.vote = -1 THEN 1 ELSE 0 END), 0) as downvoteCount
|
|
2027
|
+
FROM ${TABLES.COMMENTS} c LEFT JOIN ${TABLES.VOTES} v ON c.cid = v.commentCid
|
|
2028
|
+
WHERE c.authorSignerAddress IN (${placeholders}) GROUP BY c.cid
|
|
2029
|
+
`)
|
|
2030
|
+
.all(...karmaAddresses);
|
|
2031
|
+
if (authorCommentsData.length === 0) {
|
|
2032
|
+
if (Object.keys(modAuthorEdits).length > 0) {
|
|
2033
|
+
return modAuthorEdits;
|
|
2034
|
+
}
|
|
2035
|
+
return undefined;
|
|
2036
|
+
}
|
|
2037
|
+
const authorPosts = authorCommentsData.filter((c) => c.depth === 0);
|
|
2038
|
+
const authorReplies = authorCommentsData.filter((c) => c.depth > 0);
|
|
2039
|
+
const postScore = remeda.sumBy(authorPosts, (p) => p.upvoteCount) - remeda.sumBy(authorPosts, (p) => p.downvoteCount);
|
|
2040
|
+
const replyScore = remeda.sumBy(authorReplies, (r) => r.upvoteCount) - remeda.sumBy(authorReplies, (r) => r.downvoteCount);
|
|
2041
|
+
const lastCommentCid = remeda.maxBy(authorCommentsData, (c) => c.rowid)?.cid;
|
|
2042
|
+
if (!lastCommentCid)
|
|
2043
|
+
throw Error("Failed to query communityAuthor.lastCommentCid");
|
|
2044
|
+
const firstCommentTimestamp = remeda.minBy(authorCommentsData, (c) => c.rowid)?.timestamp;
|
|
2045
|
+
if (typeof firstCommentTimestamp !== "number")
|
|
2046
|
+
throw Error("Failed to query communityAuthor.firstCommentTimestamp");
|
|
2047
|
+
return { postScore, replyScore, lastCommentCid, ...modAuthorEdits, firstCommentTimestamp };
|
|
2048
|
+
}
|
|
2049
|
+
/**
|
|
2050
|
+
* Returns author.community for CommentUpdates, respecting pseudonymity mode boundaries.
|
|
2051
|
+
*
|
|
2052
|
+
* The alias address already encodes the isolation boundary:
|
|
2053
|
+
* - per-reply: Each reply has a unique alias, so querying by alias = that one comment's karma
|
|
2054
|
+
* - per-post: All comments in a thread share an alias, so querying by alias = thread karma
|
|
2055
|
+
* - per-author: One alias for all comments, so querying by alias = total karma
|
|
2056
|
+
*
|
|
2057
|
+
* We query karma for ONLY the alias address (no lookup to other aliases like queryCommunityAuthor does),
|
|
2058
|
+
* but include mod edits from both alias and original author.
|
|
2059
|
+
*/
|
|
2060
|
+
queryCommunityAuthorForCommentUpdate(opts) {
|
|
2061
|
+
const { authorSignerAddress, commentCid, authorDomain } = opts;
|
|
2062
|
+
// Check if this comment has a pseudonymity alias
|
|
2063
|
+
const aliasRow = this.queryPseudonymityAliasByCommentCid(commentCid);
|
|
2064
|
+
if (!aliasRow) {
|
|
2065
|
+
// No pseudonymity mode - use standard aggregated karma
|
|
2066
|
+
return this.queryCommunityAuthor(authorSignerAddress, authorDomain);
|
|
2067
|
+
}
|
|
2068
|
+
// Get original author's address for mod edits (bans/flairs are applied to original author)
|
|
2069
|
+
const modEditAddresses = [authorSignerAddress];
|
|
2070
|
+
try {
|
|
2071
|
+
const originalAddress = getPKCAddressFromPublicKeySync(aliasRow.originalAuthorSignerPublicKey);
|
|
2072
|
+
if (originalAddress !== authorSignerAddress) {
|
|
2073
|
+
modEditAddresses.push(originalAddress);
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
catch {
|
|
2077
|
+
// ignore malformed keys
|
|
2078
|
+
}
|
|
2079
|
+
// For mod edits (bans/flairs), use the original author's domain if available
|
|
2080
|
+
const modEditDomain = aliasRow.originalAuthorDomain || authorDomain;
|
|
2081
|
+
// Query karma for just this alias, but mod edits from both alias and original
|
|
2082
|
+
return this._queryCommunityAuthorByAddresses([authorSignerAddress], modEditAddresses, modEditDomain);
|
|
2083
|
+
}
|
|
2084
|
+
_getAllDescendantCids(cid) {
|
|
2085
|
+
const allCids = [cid];
|
|
2086
|
+
const directChildren = this._db.prepare(`SELECT cid FROM ${TABLES.COMMENTS} WHERE parentCid = ?`).all(cid);
|
|
2087
|
+
for (const child of directChildren) {
|
|
2088
|
+
allCids.push(...this._getAllDescendantCids(child.cid));
|
|
2089
|
+
}
|
|
2090
|
+
return allCids;
|
|
2091
|
+
}
|
|
2092
|
+
purgeComment(cid, isNestedCall = false) {
|
|
2093
|
+
const log = Logger("pkc-js:local-community:db-handler:purgeComment");
|
|
2094
|
+
const purgedRecords = [];
|
|
2095
|
+
const detachedPageCids = [];
|
|
2096
|
+
if (!isNestedCall)
|
|
2097
|
+
this.createTransaction();
|
|
2098
|
+
try {
|
|
2099
|
+
// Get all CIDs that will be purged (including descendants) and their authors
|
|
2100
|
+
const allCidsToBeDeleted = this._getAllDescendantCids(cid);
|
|
2101
|
+
const allAffectedAuthors = new Set();
|
|
2102
|
+
const commentsToForceUpdate = new Set();
|
|
2103
|
+
// Collect all unique authorSignerAddresses from comments that will be purged
|
|
2104
|
+
if (!isNestedCall) {
|
|
2105
|
+
for (const cidToDelete of allCidsToBeDeleted) {
|
|
2106
|
+
const commentToDelete = this._queryCommentAuthorAndParentWithoutParsing(cidToDelete);
|
|
2107
|
+
if (!commentToDelete) {
|
|
2108
|
+
throw new Error(`Comment with cid ${cidToDelete} not found when attempting to purge`);
|
|
2109
|
+
}
|
|
2110
|
+
if (!commentToDelete.authorSignerAddress) {
|
|
2111
|
+
throw new Error(`Comment with cid ${cidToDelete} has no authorSignerAddress`);
|
|
2112
|
+
}
|
|
2113
|
+
allAffectedAuthors.add(commentToDelete.authorSignerAddress);
|
|
2114
|
+
// Collect comments that received votes FROM this purged comment
|
|
2115
|
+
const votesFromPurgedComment = this._db
|
|
2116
|
+
.prepare(`SELECT commentCid FROM ${TABLES.VOTES} WHERE authorSignerAddress = ?`)
|
|
2117
|
+
.all(commentToDelete.authorSignerAddress);
|
|
2118
|
+
votesFromPurgedComment.forEach((vote) => commentsToForceUpdate.add(vote.commentCid));
|
|
2119
|
+
// Collect comments that received edits FROM this purged comment
|
|
2120
|
+
const editsFromPurgedComment = this._db
|
|
2121
|
+
.prepare(`SELECT commentCid FROM ${TABLES.COMMENT_EDITS} WHERE authorSignerAddress = ?`)
|
|
2122
|
+
.all(commentToDelete.authorSignerAddress);
|
|
2123
|
+
editsFromPurgedComment.forEach((edit) => commentsToForceUpdate.add(edit.commentCid));
|
|
2124
|
+
// Collect parent comments of purged comments (for reply count updates)
|
|
2125
|
+
if (commentToDelete.parentCid) {
|
|
2126
|
+
const allAncestors = this.queryParentsCids({ parentCid: commentToDelete.parentCid });
|
|
2127
|
+
allAncestors.forEach((ancestor) => commentsToForceUpdate.add(ancestor.cid));
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
const directChildren = this._db.prepare(`SELECT cid FROM ${TABLES.COMMENTS} WHERE parentCid = ?`).all(cid);
|
|
2132
|
+
for (const child of directChildren)
|
|
2133
|
+
purgedRecords.push(...this.purgeComment(child.cid, true));
|
|
2134
|
+
const commentTableRow = this.queryComment(cid);
|
|
2135
|
+
this._db.prepare(`DELETE FROM ${TABLES.VOTES} WHERE commentCid = ?`).run(cid);
|
|
2136
|
+
this._db.prepare(`DELETE FROM ${TABLES.COMMENT_EDITS} WHERE commentCid = ?`).run(cid);
|
|
2137
|
+
this._db.prepare(`DELETE FROM ${TABLES.PSEUDONYMITY_ALIASES} WHERE commentCid = ?`).run(cid);
|
|
2138
|
+
const commentUpdate = this.queryStoredCommentUpdate({ cid });
|
|
2139
|
+
if (commentUpdate) {
|
|
2140
|
+
this._db.prepare(`DELETE FROM ${TABLES.COMMENT_UPDATES} WHERE cid = ?`).run(cid);
|
|
2141
|
+
}
|
|
2142
|
+
const deleteResult = this._db.prepare(`DELETE FROM ${TABLES.COMMENTS} WHERE cid = ?`).run(cid);
|
|
2143
|
+
if (deleteResult.changes > 0) {
|
|
2144
|
+
if (!commentTableRow)
|
|
2145
|
+
throw new Error(`Comment with cid ${cid} not found when attempting to purge`);
|
|
2146
|
+
purgedRecords.push({
|
|
2147
|
+
commentTableRow,
|
|
2148
|
+
commentUpdateTableRow: commentUpdate
|
|
2149
|
+
});
|
|
2150
|
+
}
|
|
2151
|
+
// Force update on all comments by all affected authors since their statistics have changed
|
|
2152
|
+
if (!isNestedCall && (allAffectedAuthors.size > 0 || commentsToForceUpdate.size > 0)) {
|
|
2153
|
+
const allAffectedAuthorCids = [];
|
|
2154
|
+
for (const authorSignerAddress of allAffectedAuthors) {
|
|
2155
|
+
const authorCommentCids = this._db
|
|
2156
|
+
.prepare(`SELECT cid FROM ${TABLES.COMMENTS} WHERE authorSignerAddress = ?`)
|
|
2157
|
+
.all(authorSignerAddress);
|
|
2158
|
+
allAffectedAuthorCids.push(...authorCommentCids.map((c) => c.cid));
|
|
2159
|
+
}
|
|
2160
|
+
// Combine author comments and comments that received votes/edits/replies from purged comments
|
|
2161
|
+
const allCommentsToUpdate = [...allAffectedAuthorCids, ...Array.from(commentsToForceUpdate)];
|
|
2162
|
+
// Force update on a random comment to ensure IPNS update triggers even if no other comments need updating
|
|
2163
|
+
// Make sure we don't select a comment that's being purged
|
|
2164
|
+
const placeholders = allCidsToBeDeleted.map(() => "?").join(",");
|
|
2165
|
+
const randomComment = this._db
|
|
2166
|
+
.prepare(`SELECT cid FROM ${TABLES.COMMENTS} WHERE cid NOT IN (${placeholders}) ORDER BY RANDOM() LIMIT 1`)
|
|
2167
|
+
.get(...allCidsToBeDeleted);
|
|
2168
|
+
if (randomComment) {
|
|
2169
|
+
allCommentsToUpdate.push(randomComment.cid);
|
|
2170
|
+
log(`Forcing update on random comment ${randomComment.cid} to ensure IPNS update after purge`);
|
|
2171
|
+
}
|
|
2172
|
+
else {
|
|
2173
|
+
log(`No comments left to force update after purge - IPNS will update to show empty community`);
|
|
2174
|
+
}
|
|
2175
|
+
if (allCommentsToUpdate.length > 0) {
|
|
2176
|
+
this.forceUpdateOnAllCommentsWithCid(allCommentsToUpdate);
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
if (!isNestedCall)
|
|
2180
|
+
this.commitTransaction();
|
|
2181
|
+
const uniquePurgedRecords = remeda.uniqueBy(purgedRecords, (record) => record.commentTableRow.cid);
|
|
2182
|
+
return uniquePurgedRecords;
|
|
2183
|
+
}
|
|
2184
|
+
catch (error) {
|
|
2185
|
+
log.error(`Error during comment purge for ${cid}: ${error}`);
|
|
2186
|
+
if (!isNestedCall)
|
|
2187
|
+
this.rollbackTransaction();
|
|
2188
|
+
throw error;
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
async changeDbFilename(oldDbName, newDbName) {
|
|
2192
|
+
const log = Logger("pkc-js:local-community:db-handler:changeDbFilename");
|
|
2193
|
+
if (this._db || this._keyv)
|
|
2194
|
+
await this.destoryConnection();
|
|
2195
|
+
this._transactionDepth = 0;
|
|
2196
|
+
const dataPath = this._community._pkc.dataPath;
|
|
2197
|
+
const oldPathString = path.join(dataPath, "communities", oldDbName);
|
|
2198
|
+
const newPathString = path.join(dataPath, "communities", newDbName);
|
|
2199
|
+
await fs.promises.mkdir(path.dirname(oldPathString), { recursive: true });
|
|
2200
|
+
await fs.promises.mkdir(path.dirname(newPathString), { recursive: true });
|
|
2201
|
+
try {
|
|
2202
|
+
// Check if oldDb exists before attempting to open for backup
|
|
2203
|
+
if (!fs.existsSync(oldPathString)) {
|
|
2204
|
+
log(`Old DB file ${oldPathString} does not exist. Cannot backup/rename.`);
|
|
2205
|
+
// If old doesn't exist, maybe we just want to set up the new path?
|
|
2206
|
+
// For now, this will mean the operation can't proceed as intended.
|
|
2207
|
+
}
|
|
2208
|
+
else {
|
|
2209
|
+
const sourceDb = new Database(oldPathString, { fileMustExist: true });
|
|
2210
|
+
await sourceDb.backup(newPathString); // backup is synchronous in better-sqlite3 v8+
|
|
2211
|
+
sourceDb.close();
|
|
2212
|
+
if (os.type() === "Windows_NT")
|
|
2213
|
+
await deleteOldCommunityInWindows(oldPathString, this._community._pkc);
|
|
2214
|
+
else
|
|
2215
|
+
await fs.promises.rm(oldPathString, { force: true });
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
catch (error) {
|
|
2219
|
+
log.error(`Failed to backup/rename database from ${oldPathString} to ${newPathString}: `, error);
|
|
2220
|
+
throw error;
|
|
2221
|
+
}
|
|
2222
|
+
this._dbConfig = { ...this._dbConfig, filename: newPathString };
|
|
2223
|
+
log(`Changed db path from (${oldPathString}) to (${newPathString})`);
|
|
2224
|
+
}
|
|
2225
|
+
async lockCommunityStart(communityAddress = this._community.address) {
|
|
2226
|
+
const log = Logger("pkc-js:local-community:db-handler:lock:start");
|
|
2227
|
+
const lockfilePath = path.join(this._community._pkc.dataPath, "communities", `${communityAddress}.start.lock`);
|
|
2228
|
+
const communityDbPath = path.join(this._community._pkc.dataPath, "communities", communityAddress);
|
|
2229
|
+
try {
|
|
2230
|
+
await lockfile.lock(communityDbPath, {
|
|
2231
|
+
lockfilePath,
|
|
2232
|
+
onCompromised: () => { } // Temporary bandaid for the moment. Should be deleted later
|
|
2233
|
+
});
|
|
2234
|
+
log(`Locked the start of community (${communityAddress}) successfully`);
|
|
2235
|
+
}
|
|
2236
|
+
catch (e) {
|
|
2237
|
+
if (e instanceof Error && e.message === "Lock file is already being held")
|
|
2238
|
+
throw new PKCError("ERR_COMMUNITY_ALREADY_STARTED", { communityAddress: communityAddress, error: e });
|
|
2239
|
+
else {
|
|
2240
|
+
log(`Error while trying to lock start of community (${communityAddress}): ${e}`);
|
|
2241
|
+
throw e;
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
async unlockCommunityStart(communityAddress = this._community.address) {
|
|
2246
|
+
const log = Logger("pkc-js:local-community:db-handler:unlock:start");
|
|
2247
|
+
log.trace(`Attempting to unlock the start of community (${communityAddress})`);
|
|
2248
|
+
const lockfilePath = path.join(this._community._pkc.dataPath, "communities", `${communityAddress}.start.lock`);
|
|
2249
|
+
const communityDbPath = path.join(this._community._pkc.dataPath, "communities", communityAddress);
|
|
2250
|
+
if (!fs.existsSync(lockfilePath) || !fs.existsSync(communityDbPath))
|
|
2251
|
+
return;
|
|
2252
|
+
try {
|
|
2253
|
+
await lockfile.unlock(communityDbPath, { lockfilePath });
|
|
2254
|
+
log(`Unlocked start of community (${communityAddress})`);
|
|
2255
|
+
}
|
|
2256
|
+
catch (e) {
|
|
2257
|
+
log(`Error while trying to unlock start of community (${communityAddress}): ${e}`);
|
|
2258
|
+
throw e;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
async isCommunityStartLocked(communityAddress = this._community.address) {
|
|
2262
|
+
const lockfilePath = path.join(this._community._pkc.dataPath, "communities", `${communityAddress}.start.lock`);
|
|
2263
|
+
const communityDbPath = path.join(this._community._pkc.dataPath, "communities", communityAddress);
|
|
2264
|
+
const isLocked = await lockfile.check(communityDbPath, { lockfilePath, realpath: false, stale: 10000 });
|
|
2265
|
+
return isLocked;
|
|
2266
|
+
}
|
|
2267
|
+
// Community state lock
|
|
2268
|
+
async lockCommunityState() {
|
|
2269
|
+
const log = Logger("pkc-js:local-community:db-handler:lock:lockCommunityState");
|
|
2270
|
+
const lockfilePath = path.join(this._community._pkc.dataPath, "communities", `${this._community.address}.state.lock`);
|
|
2271
|
+
const communityDbPath = path.join(this._community._pkc.dataPath, "communities", this._community.address);
|
|
2272
|
+
try {
|
|
2273
|
+
await lockfile.lock(communityDbPath, {
|
|
2274
|
+
lockfilePath,
|
|
2275
|
+
retries: 5,
|
|
2276
|
+
onCompromised: () => { }
|
|
2277
|
+
});
|
|
2278
|
+
}
|
|
2279
|
+
catch (e) {
|
|
2280
|
+
log.error(`Error when attempting to lock community state`, this._community.address, e);
|
|
2281
|
+
if (e instanceof Error && e.message === "Lock file is already being held")
|
|
2282
|
+
throw new PKCError("ERR_COMMUNITY_STATE_LOCKED", { communityAddress: this._community.address, error: e });
|
|
2283
|
+
// Not sure, do we need to throw error here
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
async unlockCommunityState() {
|
|
2287
|
+
const log = Logger("pkc-js:local-community:db-handler:lock:unlockCommunityState");
|
|
2288
|
+
const lockfilePath = path.join(this._community._pkc.dataPath, "communities", `${this._community.address}.state.lock`);
|
|
2289
|
+
const communityDbPath = path.join(this._community._pkc.dataPath, "communities", this._community.address);
|
|
2290
|
+
if (!fs.existsSync(lockfilePath))
|
|
2291
|
+
return;
|
|
2292
|
+
try {
|
|
2293
|
+
await lockfile.unlock(communityDbPath, { lockfilePath });
|
|
2294
|
+
}
|
|
2295
|
+
catch (e) {
|
|
2296
|
+
log.error(`Error when attempting to unlock community state`, this._community.address, e);
|
|
2297
|
+
if (e instanceof Error && "code" in e && e.code !== "ENOTACQUIRED")
|
|
2298
|
+
throw e;
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
communityDbExists() {
|
|
2302
|
+
const communityDbPath = path.join(this._community._pkc.dataPath, "communities", this._community.address);
|
|
2303
|
+
return fs.existsSync(communityDbPath);
|
|
2304
|
+
}
|
|
2305
|
+
markCommentsAsPublishedToPostUpdates(commentCids) {
|
|
2306
|
+
if (commentCids.length === 0)
|
|
2307
|
+
return;
|
|
2308
|
+
const stmt = this._db.prepare(`UPDATE ${TABLES.COMMENT_UPDATES} SET publishedToPostUpdatesMFS = 1 WHERE cid IN (${commentCids.map(() => "?").join(",")})`);
|
|
2309
|
+
stmt.run(...commentCids);
|
|
2310
|
+
}
|
|
2311
|
+
forceUpdateOnAllComments() {
|
|
2312
|
+
this._db.prepare(`UPDATE ${TABLES.COMMENT_UPDATES} SET publishedToPostUpdatesMFS = 0`).run();
|
|
2313
|
+
}
|
|
2314
|
+
forceUpdateOnAllCommentsWithCid(commentCids) {
|
|
2315
|
+
if (commentCids.length === 0)
|
|
2316
|
+
return;
|
|
2317
|
+
this._db
|
|
2318
|
+
.prepare(`UPDATE ${TABLES.COMMENT_UPDATES} SET publishedToPostUpdatesMFS = 0 WHERE cid IN (${commentCids.map(() => "?").join(",")})`)
|
|
2319
|
+
.run(...commentCids);
|
|
2320
|
+
}
|
|
2321
|
+
queryAllCommentCidsAndTheirReplies() {
|
|
2322
|
+
const log = Logger("pkc-js:local-community:db-handler:queryAllCidsUnderThisCommunity");
|
|
2323
|
+
const rows = this._db
|
|
2324
|
+
.prepare(`SELECT
|
|
2325
|
+
c.cid AS cid,
|
|
2326
|
+
CASE
|
|
2327
|
+
WHEN cu.replies IS NULL THEN NULL
|
|
2328
|
+
ELSE json_set(
|
|
2329
|
+
cu.replies,
|
|
2330
|
+
'$.pages',
|
|
2331
|
+
(
|
|
2332
|
+
SELECT json_group_object(
|
|
2333
|
+
pages.key,
|
|
2334
|
+
json_set(pages.value, '$.comments', json('[]'))
|
|
2335
|
+
)
|
|
2336
|
+
FROM json_each(cu.replies, '$.pages') AS pages
|
|
2337
|
+
)
|
|
2338
|
+
)
|
|
2339
|
+
END AS replies
|
|
2340
|
+
FROM ${TABLES.COMMENTS} c
|
|
2341
|
+
LEFT JOIN ${TABLES.COMMENT_UPDATES} cu ON c.cid = cu.cid`)
|
|
2342
|
+
.all();
|
|
2343
|
+
return rows.map((row) => {
|
|
2344
|
+
let parsedReplies;
|
|
2345
|
+
if (typeof row.replies === "string" && row.replies.length > 0) {
|
|
2346
|
+
try {
|
|
2347
|
+
parsedReplies = JSON.parse(row.replies);
|
|
2348
|
+
}
|
|
2349
|
+
catch (e) {
|
|
2350
|
+
log.error(`Failed to parse replies JSON for comment ${row.cid} when collecting cids`, e);
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
return { cid: row.cid, replies: parsedReplies };
|
|
2354
|
+
});
|
|
2355
|
+
}
|
|
2356
|
+
queryPostsWithActiveScore(pageOptions) {
|
|
2357
|
+
const { clause: rootAddrClause, params: rootAddrParams } = this._communityAddressClauseNamed("p", "asRoot");
|
|
2358
|
+
const { clause: descAddrClause, params: descAddrParams } = this._communityAddressClauseNamed("c", "asDesc");
|
|
2359
|
+
const { clause: postsAddrClause, params: postsAddrParams } = this._communityAddressClauseNamed("c", "asPosts");
|
|
2360
|
+
const activeScoreRootConditions = ["p.depth = 0"];
|
|
2361
|
+
if (pageOptions.excludeCommentsWithDifferentCommunityAddress)
|
|
2362
|
+
activeScoreRootConditions.push(rootAddrClause);
|
|
2363
|
+
if (pageOptions.excludeCommentPendingApproval)
|
|
2364
|
+
activeScoreRootConditions.push(this._pendingApprovalClause("p"));
|
|
2365
|
+
if (pageOptions.excludeRemovedComments)
|
|
2366
|
+
activeScoreRootConditions.push(this._removedClause("cu_root"));
|
|
2367
|
+
if (pageOptions.excludeDeletedComments)
|
|
2368
|
+
activeScoreRootConditions.push(this._deletedFromUpdatesClause("cu_root"));
|
|
2369
|
+
if (pageOptions.excludeCommentWithApprovedFalse)
|
|
2370
|
+
activeScoreRootConditions.push(this._approvedClause("cu_root"));
|
|
2371
|
+
const activeScoreRootWhere = `WHERE ${activeScoreRootConditions.join(" AND ")}`;
|
|
2372
|
+
const activeScoreDescendantConditions = [];
|
|
2373
|
+
if (pageOptions.excludeCommentsWithDifferentCommunityAddress)
|
|
2374
|
+
activeScoreDescendantConditions.push(descAddrClause);
|
|
2375
|
+
if (pageOptions.excludeCommentPendingApproval)
|
|
2376
|
+
activeScoreDescendantConditions.push(this._pendingApprovalClause("c"));
|
|
2377
|
+
if (pageOptions.excludeRemovedComments)
|
|
2378
|
+
activeScoreDescendantConditions.push(this._removedClause("cu"));
|
|
2379
|
+
if (pageOptions.excludeDeletedComments)
|
|
2380
|
+
activeScoreDescendantConditions.push(this._deletedFromLookupClause("deleted_lookup"));
|
|
2381
|
+
if (pageOptions.excludeCommentWithApprovedFalse)
|
|
2382
|
+
activeScoreDescendantConditions.push(this._approvedClause("cu"));
|
|
2383
|
+
const activeScoreDescendantWhere = activeScoreDescendantConditions.length > 0 ? `WHERE ${activeScoreDescendantConditions.join(" AND ")}` : "";
|
|
2384
|
+
const activeScoresCte = `
|
|
2385
|
+
WITH RECURSIVE descendants AS (
|
|
2386
|
+
SELECT p.cid AS post_cid, p.cid AS current_cid, p.timestamp
|
|
2387
|
+
FROM ${TABLES.COMMENTS} p
|
|
2388
|
+
INNER JOIN ${TABLES.COMMENT_UPDATES} cu_root ON p.cid = cu_root.cid
|
|
2389
|
+
${activeScoreRootWhere}
|
|
2390
|
+
UNION ALL
|
|
2391
|
+
SELECT desc_tree.post_cid, c.cid AS current_cid, c.timestamp FROM ${TABLES.COMMENTS} c
|
|
2392
|
+
INNER JOIN ${TABLES.COMMENT_UPDATES} cu ON c.cid = cu.cid
|
|
2393
|
+
LEFT JOIN (SELECT cid, json_extract(edit, '$.deleted') AS deleted_flag FROM ${TABLES.COMMENT_UPDATES}) AS deleted_lookup ON c.cid = deleted_lookup.cid
|
|
2394
|
+
JOIN descendants desc_tree ON c.parentCid = desc_tree.current_cid
|
|
2395
|
+
${activeScoreDescendantWhere}
|
|
2396
|
+
) SELECT post_cid, MAX(timestamp) as active_score FROM descendants GROUP BY post_cid
|
|
2397
|
+
`;
|
|
2398
|
+
const commentUpdateCols = remeda.keys.strict(pageOptions.commentUpdateFieldsToExclude
|
|
2399
|
+
? remeda.omit(CommentUpdateSchema.shape, pageOptions.commentUpdateFieldsToExclude)
|
|
2400
|
+
: CommentUpdateSchema.shape);
|
|
2401
|
+
const commentUpdateSelects = commentUpdateCols.map((col) => `cu.${col} AS commentUpdate_${col}`);
|
|
2402
|
+
const commentIpfsCols = [...remeda.keys.strict(CommentIpfsSchema.shape), "extraProps"];
|
|
2403
|
+
const commentIpfsSelects = commentIpfsCols.map((col) => `c.${col} AS commentIpfs_${col}`);
|
|
2404
|
+
let postsQueryStr = `
|
|
2405
|
+
SELECT ${commentIpfsSelects.join(", ")}, ${commentUpdateSelects.join(", ")}, asc_scores.active_score
|
|
2406
|
+
FROM ${TABLES.COMMENTS} c INNER JOIN ${TABLES.COMMENT_UPDATES} cu ON c.cid = cu.cid
|
|
2407
|
+
INNER JOIN (${activeScoresCte}) AS asc_scores ON c.cid = asc_scores.post_cid
|
|
2408
|
+
`;
|
|
2409
|
+
const params = { ...rootAddrParams, ...descAddrParams };
|
|
2410
|
+
const postsWhereClauses = ["c.depth = 0"];
|
|
2411
|
+
if (pageOptions.excludeCommentsWithDifferentCommunityAddress) {
|
|
2412
|
+
postsWhereClauses.push(postsAddrClause);
|
|
2413
|
+
Object.assign(params, postsAddrParams);
|
|
2414
|
+
}
|
|
2415
|
+
if (pageOptions.excludeRemovedComments)
|
|
2416
|
+
postsWhereClauses.push(this._removedClause("cu"));
|
|
2417
|
+
if (pageOptions.excludeDeletedComments)
|
|
2418
|
+
postsWhereClauses.push(this._deletedFromUpdatesClause("cu"));
|
|
2419
|
+
if (pageOptions.excludeCommentPendingApproval)
|
|
2420
|
+
postsWhereClauses.push(this._pendingApprovalClause("c"));
|
|
2421
|
+
if (pageOptions.excludeCommentWithApprovedFalse)
|
|
2422
|
+
postsWhereClauses.push(this._approvedClause("cu"));
|
|
2423
|
+
postsQueryStr += ` WHERE ${postsWhereClauses.join(" AND ")}`;
|
|
2424
|
+
const postsRaw = this._db.prepare(postsQueryStr).all(params);
|
|
2425
|
+
return postsRaw.map((postRaw) => {
|
|
2426
|
+
const { comment, commentUpdate } = this._parsePrefixedComment(postRaw);
|
|
2427
|
+
return {
|
|
2428
|
+
comment,
|
|
2429
|
+
commentUpdate,
|
|
2430
|
+
activeScore: postRaw.active_score
|
|
2431
|
+
};
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
_processRecordsForDbBeforeInsert(records) {
|
|
2435
|
+
return records.map((record) => {
|
|
2436
|
+
const processed = { ...record };
|
|
2437
|
+
for (const [key, value] of remeda.entries(processed)) {
|
|
2438
|
+
if (remeda.isPlainObject(value) || Array.isArray(value))
|
|
2439
|
+
processed[key] = JSON.stringify(value);
|
|
2440
|
+
else if (typeof value === "boolean")
|
|
2441
|
+
processed[key] = value ? 1 : 0;
|
|
2442
|
+
}
|
|
2443
|
+
return processed;
|
|
2444
|
+
});
|
|
2445
|
+
}
|
|
2446
|
+
_spreadExtraProps(record) {
|
|
2447
|
+
const extraPropsNames = ["extraProps", "commentIpfs_extraProps", "commentUpdate_extaProps"];
|
|
2448
|
+
extraPropsNames.forEach((extraPropName) => {
|
|
2449
|
+
record = { ...record, ...record[extraPropName] };
|
|
2450
|
+
delete record[extraPropName];
|
|
2451
|
+
});
|
|
2452
|
+
// For old migrated rows (pre-wire-format-change), extraProps contains subplebbitAddress.
|
|
2453
|
+
// The original CommentIpfs did NOT have communityPublicKey/communityName,
|
|
2454
|
+
// so we must remove them to preserve CID reproducibility.
|
|
2455
|
+
if ("subplebbitAddress" in record) {
|
|
2456
|
+
delete record["communityPublicKey"];
|
|
2457
|
+
delete record["communityName"];
|
|
2458
|
+
}
|
|
2459
|
+
return record;
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
//# sourceMappingURL=db-handler.js.map
|