spec-3nweb-server 1.8.4 → 1.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitlab-ci-linux.yml +50 -0
- package/.gitlab-ci-mac.yml +29 -0
- package/.gitlab-ci-windows.yml +29 -0
- package/.gitlab-ci.yml +63 -0
- package/build/3nstorage/routes/owner/archive-obj-version.js +2 -2
- package/build/3nstorage/routes/owner/cancel-trans.js +2 -2
- package/build/3nstorage/routes/owner/delete-obj.js +2 -2
- package/build/3nstorage/routes/owner/get-current-obj.js +3 -4
- package/build/3nstorage/routes/owner/param-setter.js +2 -6
- package/build/asmail/routes/config/param-setter.js +2 -2
- package/build/asmail/routes/delivery/finalize-delivery.js +2 -2
- package/build/asmail/routes/delivery/put-obj.js +2 -2
- package/build/asmail/routes/delivery/sender-authorization.js +2 -2
- package/build/asmail/routes/retrieval/remove-message.js +2 -2
- package/build/cli/signup-cmd.js +0 -1
- package/build/lib-server/routes/sessions/close.js +2 -2
- package/build/lib-server/routes/sessions/mid-auth.js +2 -5
- package/build/main.js +12 -15
- package/build/mock/tls-options.js +46 -44
- package/build/signup/routes/add.js +4 -4
- package/build/tests/admin-proto/signup.d.ts +1 -0
- package/build/tests/admin-proto/signup.js +235 -0
- package/build/tests/jasmine.d.ts +1 -0
- package/build/tests/jasmine.js +32 -0
- package/build/tests/libs-for-tests/3nstorage.d.ts +22 -0
- package/build/tests/libs-for-tests/3nstorage.js +123 -0
- package/build/tests/libs-for-tests/asmail.d.ts +22 -0
- package/build/tests/libs-for-tests/asmail.js +179 -0
- package/build/tests/libs-for-tests/assert.d.ts +1 -0
- package/build/tests/libs-for-tests/assert.js +26 -0
- package/build/tests/libs-for-tests/async-jasmine.d.ts +7 -0
- package/build/tests/libs-for-tests/async-jasmine.js +77 -0
- package/build/tests/libs-for-tests/bytes-equal.d.ts +1 -0
- package/build/tests/libs-for-tests/bytes-equal.js +34 -0
- package/build/tests/libs-for-tests/json-copy.d.ts +1 -0
- package/build/tests/libs-for-tests/json-copy.js +45 -0
- package/build/tests/libs-for-tests/json-equal.d.ts +4 -0
- package/build/tests/libs-for-tests/json-equal.js +67 -0
- package/build/tests/libs-for-tests/mailerid.d.ts +13 -0
- package/build/tests/libs-for-tests/mailerid.js +114 -0
- package/build/tests/libs-for-tests/pkl.d.ts +28 -0
- package/build/tests/libs-for-tests/pkl.js +89 -0
- package/build/tests/libs-for-tests/server-components/3nstorage.d.ts +20 -0
- package/build/tests/libs-for-tests/server-components/3nstorage.js +200 -0
- package/build/tests/libs-for-tests/server-components/admin.d.ts +6 -0
- package/build/tests/libs-for-tests/server-components/admin.js +43 -0
- package/build/tests/libs-for-tests/server-components/asmail.d.ts +16 -0
- package/build/tests/libs-for-tests/server-components/asmail.js +143 -0
- package/build/tests/libs-for-tests/server-components/component.d.ts +13 -0
- package/build/tests/libs-for-tests/server-components/component.js +77 -0
- package/build/tests/libs-for-tests/server-components/mailerid.d.ts +10 -0
- package/build/tests/libs-for-tests/server-components/mailerid.js +103 -0
- package/build/tests/libs-for-tests/server-runner.d.ts +15 -0
- package/build/tests/libs-for-tests/server-runner.js +69 -0
- package/build/tests/libs-for-tests/spec-assembly.d.ts +6 -0
- package/build/tests/libs-for-tests/spec-assembly.js +55 -0
- package/build/tests/libs-for-tests/ws-utils.d.ts +3 -0
- package/build/tests/libs-for-tests/ws-utils.js +59 -0
- package/build/tests/libs-for-tests/xhr-utils.d.ts +61 -0
- package/build/tests/libs-for-tests/xhr-utils.js +154 -0
- package/build/tests/protocols/3nstorage/owner/specs/archive-obj-ver.d.ts +2 -0
- package/build/tests/protocols/3nstorage/owner/specs/archive-obj-ver.js +84 -0
- package/build/tests/protocols/3nstorage/owner/specs/archive-root-ver.d.ts +2 -0
- package/build/tests/protocols/3nstorage/owner/specs/archive-root-ver.js +78 -0
- package/build/tests/protocols/3nstorage/owner/specs/delete-archive-obj-ver.d.ts +2 -0
- package/build/tests/protocols/3nstorage/owner/specs/delete-archive-obj-ver.js +83 -0
- package/build/tests/protocols/3nstorage/owner/specs/delete-archive-root-ver.d.ts +2 -0
- package/build/tests/protocols/3nstorage/owner/specs/delete-archive-root-ver.js +77 -0
- package/build/tests/protocols/3nstorage/owner/specs/delete-obj.d.ts +2 -0
- package/build/tests/protocols/3nstorage/owner/specs/delete-obj.js +75 -0
- package/build/tests/protocols/3nstorage/owner/specs/events.d.ts +2 -0
- package/build/tests/protocols/3nstorage/owner/specs/events.js +91 -0
- package/build/tests/protocols/3nstorage/owner/specs/get-current-obj.d.ts +2 -0
- package/build/tests/protocols/3nstorage/owner/specs/get-current-obj.js +269 -0
- package/build/tests/protocols/3nstorage/owner/specs/get-current-root.d.ts +2 -0
- package/build/tests/protocols/3nstorage/owner/specs/get-current-root.js +262 -0
- package/build/tests/protocols/3nstorage/owner/specs/get-obj-status.d.ts +2 -0
- package/build/tests/protocols/3nstorage/owner/specs/get-obj-status.js +79 -0
- package/build/tests/protocols/3nstorage/owner/specs/get-root-status.d.ts +2 -0
- package/build/tests/protocols/3nstorage/owner/specs/get-root-status.js +72 -0
- package/build/tests/protocols/3nstorage/owner/specs/put-current-obj.d.ts +2 -0
- package/build/tests/protocols/3nstorage/owner/specs/put-current-obj.js +287 -0
- package/build/tests/protocols/3nstorage/owner/specs/put-current-root.d.ts +2 -0
- package/build/tests/protocols/3nstorage/owner/specs/put-current-root.js +274 -0
- package/build/tests/protocols/3nstorage/owner/specs/trans-cancel-root.d.ts +2 -0
- package/build/tests/protocols/3nstorage/owner/specs/trans-cancel-root.js +73 -0
- package/build/tests/protocols/3nstorage/owner/specs/trans-cancel.d.ts +2 -0
- package/build/tests/protocols/3nstorage/owner/specs/trans-cancel.js +72 -0
- package/build/tests/protocols/3nstorage/owner/test-utils.d.ts +14 -0
- package/build/tests/protocols/3nstorage/owner/test-utils.js +54 -0
- package/build/tests/protocols/3nstorage/owner.d.ts +1 -0
- package/build/tests/protocols/3nstorage/owner.js +138 -0
- package/build/tests/protocols/asmail/config.d.ts +1 -0
- package/build/tests/protocols/asmail/config.js +117 -0
- package/build/tests/protocols/asmail/delivery/specs/finalize-delivery.d.ts +2 -0
- package/build/tests/protocols/asmail/delivery/specs/finalize-delivery.js +60 -0
- package/build/tests/protocols/asmail/delivery/specs/provide-recipient-pubkey.d.ts +2 -0
- package/build/tests/protocols/asmail/delivery/specs/provide-recipient-pubkey.js +68 -0
- package/build/tests/protocols/asmail/delivery/specs/put-metadata.d.ts +2 -0
- package/build/tests/protocols/asmail/delivery/specs/put-metadata.js +83 -0
- package/build/tests/protocols/asmail/delivery/specs/put-obj.d.ts +2 -0
- package/build/tests/protocols/asmail/delivery/specs/put-obj.js +123 -0
- package/build/tests/protocols/asmail/delivery/specs/restart-session.d.ts +2 -0
- package/build/tests/protocols/asmail/delivery/specs/restart-session.js +79 -0
- package/build/tests/protocols/asmail/delivery/specs/start-session.d.ts +2 -0
- package/build/tests/protocols/asmail/delivery/specs/start-session.js +64 -0
- package/build/tests/protocols/asmail/delivery/test-utils.d.ts +16 -0
- package/build/tests/protocols/asmail/delivery/test-utils.js +46 -0
- package/build/tests/protocols/asmail/delivery.d.ts +1 -0
- package/build/tests/protocols/asmail/delivery.js +46 -0
- package/build/tests/protocols/asmail/retrieval/specs/events.d.ts +2 -0
- package/build/tests/protocols/asmail/retrieval/specs/events.js +62 -0
- package/build/tests/protocols/asmail/retrieval/specs/get-message-meta.d.ts +2 -0
- package/build/tests/protocols/asmail/retrieval/specs/get-message-meta.js +70 -0
- package/build/tests/protocols/asmail/retrieval/specs/get-message-obj.d.ts +2 -0
- package/build/tests/protocols/asmail/retrieval/specs/get-message-obj.js +166 -0
- package/build/tests/protocols/asmail/retrieval/specs/list-messages.d.ts +2 -0
- package/build/tests/protocols/asmail/retrieval/specs/list-messages.js +96 -0
- package/build/tests/protocols/asmail/retrieval/specs/remove-message.d.ts +2 -0
- package/build/tests/protocols/asmail/retrieval/specs/remove-message.js +66 -0
- package/build/tests/protocols/asmail/retrieval/test-utils.d.ts +17 -0
- package/build/tests/protocols/asmail/retrieval/test-utils.js +46 -0
- package/build/tests/protocols/asmail/retrieval.d.ts +1 -0
- package/build/tests/protocols/asmail/retrieval.js +92 -0
- package/build/tests/protocols/mailerid.d.ts +1 -0
- package/build/tests/protocols/mailerid.js +210 -0
- package/build/tests/shared-checks/check-mid-login.d.ts +2 -0
- package/build/tests/shared-checks/check-mid-login.js +131 -0
- package/build/tests/shared-checks/requests.d.ts +20 -0
- package/build/tests/shared-checks/requests.js +123 -0
- package/package.json +1 -1
- package/packing/create-deb.sh +57 -0
- package/packing/deb/lib/systemd/system/3nweb.service +11 -0
- package/packing/deb/postinst +44 -0
- package/packing/deb/usr/share/doc/3nweb-server/conf-for-postinstall.yaml +19 -0
- package/packing/deb/usr/share/doc/3nweb-server/copyright +22 -0
- package/publish-task.sh +10 -0
- package/push-to-github.sh +105 -0
- package/ts-code/3nstorage/3nstorage-app.ts +99 -0
- package/ts-code/3nstorage/owner.ts +178 -0
- package/ts-code/3nstorage/resources/sessions.ts +37 -0
- package/ts-code/3nstorage/resources/store.ts +896 -0
- package/ts-code/3nstorage/resources/users.ts +258 -0
- package/ts-code/3nstorage/routes/owner/archive-obj-version.ts +73 -0
- package/ts-code/3nstorage/routes/owner/cancel-trans.ts +58 -0
- package/ts-code/3nstorage/routes/owner/delete-obj.ts +103 -0
- package/ts-code/3nstorage/routes/owner/get-archived-obj-ver.ts +111 -0
- package/ts-code/3nstorage/routes/owner/get-current-obj.ts +122 -0
- package/ts-code/3nstorage/routes/owner/get-obj-status.ts +55 -0
- package/ts-code/3nstorage/routes/owner/param-getter.ts +51 -0
- package/ts-code/3nstorage/routes/owner/param-setter.ts +50 -0
- package/ts-code/3nstorage/routes/owner/put-current-obj.ts +284 -0
- package/ts-code/3nstorage/routes/owner/session-params.ts +49 -0
- package/ts-code/3nstorage/shared.ts +43 -0
- package/ts-code/asmail/asmail-app.ts +91 -0
- package/ts-code/asmail/config.ts +96 -0
- package/ts-code/asmail/delivery.ts +94 -0
- package/ts-code/asmail/resources/delivery-sessions.ts +118 -0
- package/ts-code/asmail/resources/inbox.ts +696 -0
- package/ts-code/asmail/resources/recipients.ts +495 -0
- package/ts-code/asmail/resources/sessions.ts +37 -0
- package/ts-code/asmail/retrieval.ts +96 -0
- package/ts-code/asmail/routes/config/param-getter.ts +49 -0
- package/ts-code/asmail/routes/config/param-setter.ts +49 -0
- package/ts-code/asmail/routes/delivery/finalize-delivery.ts +50 -0
- package/ts-code/asmail/routes/delivery/pre-flight.ts +132 -0
- package/ts-code/asmail/routes/delivery/provide-recipient-pubkey.ts +58 -0
- package/ts-code/asmail/routes/delivery/put-metadata.ts +94 -0
- package/ts-code/asmail/routes/delivery/put-obj.ts +166 -0
- package/ts-code/asmail/routes/delivery/restart-session.ts +130 -0
- package/ts-code/asmail/routes/delivery/sender-authorization.ts +81 -0
- package/ts-code/asmail/routes/delivery/start-session.ts +151 -0
- package/ts-code/asmail/routes/retrieval/get-message-meta.ts +53 -0
- package/ts-code/asmail/routes/retrieval/get-message-obj.ts +109 -0
- package/ts-code/asmail/routes/retrieval/list-messages.ts +70 -0
- package/ts-code/asmail/routes/retrieval/remove-message.ts +47 -0
- package/ts-code/cli/index.ts +82 -0
- package/ts-code/cli/run-cmd.ts +81 -0
- package/ts-code/cli/show-sample-config.ts +78 -0
- package/ts-code/cli/signup-cmd.ts +229 -0
- package/ts-code/cli/user-cmd.ts +68 -0
- package/ts-code/cli/utils.ts +105 -0
- package/ts-code/config/default-confs.ts +22 -0
- package/ts-code/config/from-yaml.ts +35 -0
- package/ts-code/config/letsencrypt.ts +77 -0
- package/ts-code/config/signup.ts +137 -0
- package/ts-code/lib-common/admin-api/signup.ts +120 -0
- package/ts-code/lib-common/assert.ts +22 -0
- package/ts-code/lib-common/async-fs-node.ts +656 -0
- package/ts-code/lib-common/buffer-utils.ts +124 -0
- package/ts-code/lib-common/byte-streaming/common.ts +166 -0
- package/ts-code/lib-common/bytes-equal.ts +25 -0
- package/ts-code/lib-common/canonical-address.ts +92 -0
- package/ts-code/lib-common/exceptions/error.ts +44 -0
- package/ts-code/lib-common/exceptions/file.ts +84 -0
- package/ts-code/lib-common/exceptions/http.ts +69 -0
- package/ts-code/lib-common/ipc/generic-ipc.ts +750 -0
- package/ts-code/lib-common/ipc/ws-ipc.ts +117 -0
- package/ts-code/lib-common/json-equal.ts +55 -0
- package/ts-code/lib-common/json-utils.ts +130 -0
- package/ts-code/lib-common/jwkeys.ts +246 -0
- package/ts-code/lib-common/map-of-sets.ts +60 -0
- package/ts-code/lib-common/mid-sigs-NaCl-Ed.ts +677 -0
- package/ts-code/lib-common/objs-on-disk/file-layout.ts +480 -0
- package/ts-code/lib-common/objs-on-disk/obj-file.ts +325 -0
- package/ts-code/lib-common/objs-on-disk/utils.ts +201 -0
- package/ts-code/lib-common/objs-on-disk/v1-obj-file-format.ts +329 -0
- package/ts-code/lib-common/processes.ts +501 -0
- package/ts-code/lib-common/random-node.ts +79 -0
- package/ts-code/lib-common/service-api/3nstorage/owner.ts +559 -0
- package/ts-code/lib-common/service-api/3nstorage/root-route.ts +22 -0
- package/ts-code/lib-common/service-api/asmail/config.ts +144 -0
- package/ts-code/lib-common/service-api/asmail/delivery.ts +250 -0
- package/ts-code/lib-common/service-api/asmail/retrieval.ts +238 -0
- package/ts-code/lib-common/service-api/asmail/root-route.ts +22 -0
- package/ts-code/lib-common/service-api/ipc-api.ts +44 -0
- package/ts-code/lib-common/service-api/locker.ts +44 -0
- package/ts-code/lib-common/service-api/mailer-id/login.ts +80 -0
- package/ts-code/lib-common/service-api/mailer-id/provisioning.ts +64 -0
- package/ts-code/lib-common/service-api/mailer-id/root-route.ts +25 -0
- package/ts-code/lib-common/service-api/pub-key-login.ts +75 -0
- package/ts-code/lib-common/service-api/web-socket-base.ts +87 -0
- package/ts-code/lib-common/session-encryptor.ts +54 -0
- package/ts-code/lib-common/time-window-cache.ts +74 -0
- package/ts-code/lib-common/transferable.d.ts +30 -0
- package/ts-code/lib-server/conf-util.ts +35 -0
- package/ts-code/lib-server/mid-access.ts +45 -0
- package/ts-code/lib-server/middleware/body-parsers.ts +248 -0
- package/ts-code/lib-server/middleware/error-handler.ts +79 -0
- package/ts-code/lib-server/pkl-access.ts +46 -0
- package/ts-code/lib-server/resources/dns.ts +151 -0
- package/ts-code/lib-server/resources/mailerid-authorizer.ts +115 -0
- package/ts-code/lib-server/resources/mem-backed-sessions-factory.ts +132 -0
- package/ts-code/lib-server/resources/server-data-folders.ts +160 -0
- package/ts-code/lib-server/resources/server-key-for-pkl-challenge.ts +41 -0
- package/ts-code/lib-server/resources/sessions.ts +232 -0
- package/ts-code/lib-server/resources/user-files.ts +147 -0
- package/ts-code/lib-server/routes/pub-key-login/complete-exchange.ts +63 -0
- package/ts-code/lib-server/routes/pub-key-login/start-exchange.ts +189 -0
- package/ts-code/lib-server/routes/sessions/close.ts +27 -0
- package/ts-code/lib-server/routes/sessions/mid-auth.ts +77 -0
- package/ts-code/lib-server/routes/sessions/start.ts +116 -0
- package/ts-code/lib-server/web-sockets/app.ts +339 -0
- package/ts-code/lib-server/web-sockets/server-events.ts +63 -0
- package/ts-code/lib.ts +29 -0
- package/ts-code/locker/locker-app.ts +60 -0
- package/ts-code/locker/resources/lockers.ts +17 -0
- package/ts-code/locker/resources/sessions.ts +37 -0
- package/ts-code/locker/routes/create-locker.ts +47 -0
- package/ts-code/mailerId/mailerId-app.ts +107 -0
- package/ts-code/mailerId/resources/certifier.ts +181 -0
- package/ts-code/mailerId/resources/sessions.ts +44 -0
- package/ts-code/mailerId/resources/users.ts +88 -0
- package/ts-code/mailerId/routes/certify.ts +68 -0
- package/ts-code/main.ts +304 -0
- package/ts-code/mock/dns.ts +106 -0
- package/ts-code/mock/mock-as-child-proc.ts +130 -0
- package/ts-code/mock/node-child-ipc.ts +69 -0
- package/ts-code/mock/run-in-proc.ts +111 -0
- package/ts-code/mock/tls-options.ts +73 -0
- package/ts-code/services.ts +138 -0
- package/ts-code/signup/resources/users.ts +429 -0
- package/ts-code/signup/routes/add.ts +65 -0
- package/ts-code/signup/routes/get-available-addresses.ts +49 -0
- package/ts-code/signup/routes/get-available-domains.ts +45 -0
- package/ts-code/signup/signup-app.ts +54 -0
- package/ts-code/signup/signup-tokens.ts +148 -0
- package/ts-code/tests/admin-proto/signup.ts +277 -0
- package/ts-code/tests/jasmine.ts +37 -0
- package/ts-code/tests/libs-for-tests/3nstorage.ts +144 -0
- package/ts-code/tests/libs-for-tests/asmail.ts +220 -0
- package/ts-code/tests/libs-for-tests/assert.ts +22 -0
- package/ts-code/tests/libs-for-tests/async-jasmine.ts +89 -0
- package/ts-code/tests/libs-for-tests/bytes-equal.ts +25 -0
- package/ts-code/tests/libs-for-tests/json-copy.ts +38 -0
- package/ts-code/tests/libs-for-tests/json-equal.ts +55 -0
- package/ts-code/tests/libs-for-tests/mailerid.ts +123 -0
- package/ts-code/tests/libs-for-tests/pkl.ts +127 -0
- package/ts-code/tests/libs-for-tests/server-components/3nstorage.ts +206 -0
- package/ts-code/tests/libs-for-tests/server-components/admin.ts +49 -0
- package/ts-code/tests/libs-for-tests/server-components/asmail.ts +152 -0
- package/ts-code/tests/libs-for-tests/server-components/component.ts +90 -0
- package/ts-code/tests/libs-for-tests/server-components/mailerid.ts +114 -0
- package/ts-code/tests/libs-for-tests/server-runner.ts +82 -0
- package/ts-code/tests/libs-for-tests/spec-assembly.ts +60 -0
- package/ts-code/tests/libs-for-tests/ws-utils.ts +61 -0
- package/ts-code/tests/libs-for-tests/xhr-utils.ts +179 -0
- package/ts-code/tests/protocols/3nstorage/owner/specs/archive-obj-ver.ts +100 -0
- package/ts-code/tests/protocols/3nstorage/owner/specs/archive-root-ver.ts +91 -0
- package/ts-code/tests/protocols/3nstorage/owner/specs/delete-archive-obj-ver.ts +99 -0
- package/ts-code/tests/protocols/3nstorage/owner/specs/delete-archive-root-ver.ts +90 -0
- package/ts-code/tests/protocols/3nstorage/owner/specs/delete-obj.ts +84 -0
- package/ts-code/tests/protocols/3nstorage/owner/specs/events.ts +125 -0
- package/ts-code/tests/protocols/3nstorage/owner/specs/get-current-obj.ts +298 -0
- package/ts-code/tests/protocols/3nstorage/owner/specs/get-current-root.ts +290 -0
- package/ts-code/tests/protocols/3nstorage/owner/specs/get-obj-status.ts +88 -0
- package/ts-code/tests/protocols/3nstorage/owner/specs/get-root-status.ts +80 -0
- package/ts-code/tests/protocols/3nstorage/owner/specs/put-current-obj.ts +366 -0
- package/ts-code/tests/protocols/3nstorage/owner/specs/put-current-root.ts +329 -0
- package/ts-code/tests/protocols/3nstorage/owner/specs/trans-cancel-root.ts +86 -0
- package/ts-code/tests/protocols/3nstorage/owner/specs/trans-cancel.ts +86 -0
- package/ts-code/tests/protocols/3nstorage/owner/test-utils.ts +72 -0
- package/ts-code/tests/protocols/3nstorage/owner.ts +198 -0
- package/ts-code/tests/protocols/asmail/config.ts +172 -0
- package/ts-code/tests/protocols/asmail/delivery/specs/finalize-delivery.ts +69 -0
- package/ts-code/tests/protocols/asmail/delivery/specs/provide-recipient-pubkey.ts +88 -0
- package/ts-code/tests/protocols/asmail/delivery/specs/put-metadata.ts +102 -0
- package/ts-code/tests/protocols/asmail/delivery/specs/put-obj.ts +156 -0
- package/ts-code/tests/protocols/asmail/delivery/specs/restart-session.ts +97 -0
- package/ts-code/tests/protocols/asmail/delivery/specs/start-session.ts +79 -0
- package/ts-code/tests/protocols/asmail/delivery/test-utils.ts +64 -0
- package/ts-code/tests/protocols/asmail/delivery.ts +56 -0
- package/ts-code/tests/protocols/asmail/retrieval/specs/events.ts +78 -0
- package/ts-code/tests/protocols/asmail/retrieval/specs/get-message-meta.ts +80 -0
- package/ts-code/tests/protocols/asmail/retrieval/specs/get-message-obj.ts +197 -0
- package/ts-code/tests/protocols/asmail/retrieval/specs/list-messages.ts +110 -0
- package/ts-code/tests/protocols/asmail/retrieval/specs/remove-message.ts +74 -0
- package/ts-code/tests/protocols/asmail/retrieval/test-utils.ts +60 -0
- package/ts-code/tests/protocols/asmail/retrieval.ts +115 -0
- package/ts-code/tests/protocols/mailerid.ts +271 -0
- package/ts-code/tests/shared-checks/check-mid-login.ts +164 -0
- package/ts-code/tests/shared-checks/requests.ts +135 -0
- package/ts-code/tsconfig.json +11 -0
- package/ts-code/web3n.d.ts +1068 -0
|
@@ -0,0 +1,896 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (C) 2015 - 2017, 2019 - 2020, 2022, 2025 3NSoft Inc.
|
|
3
|
+
|
|
4
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
the terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
Foundation, either version 3 of the License, or (at your option) any later
|
|
7
|
+
version.
|
|
8
|
+
|
|
9
|
+
This program is distributed in the hope that it will be useful, but
|
|
10
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
12
|
+
See the GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
You should have received a copy of the GNU General Public License along with
|
|
15
|
+
this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Everything in this module is assumed to be inside of a storage reliance set.
|
|
20
|
+
*
|
|
21
|
+
* Store files are laid out on disk in the following way:
|
|
22
|
+
* (a) store is just a folder with stuff inside;
|
|
23
|
+
* (b) main store folder contains following folders:
|
|
24
|
+
* (b.1) root - is afolder for root object;
|
|
25
|
+
* (b.2) objects - is a folder for non-root object folders;
|
|
26
|
+
* (b.3) transactions - is a folder with non-root object transactions;
|
|
27
|
+
* (b.4) params - is a folder for information files about this storage;
|
|
28
|
+
* (c) object folder's name is object's id;
|
|
29
|
+
* (d) object folder contains:
|
|
30
|
+
* (d.1) status - is a json file with information about an object;
|
|
31
|
+
* (d.2) N. - is a file with an N'th version object's either diff or whole
|
|
32
|
+
* object file;
|
|
33
|
+
* (e) transactions folder (for root and non-root) contains:
|
|
34
|
+
* (e.1) transaction - is a json file with current transaction's info;
|
|
35
|
+
* this file is present only for duration of a transaction,
|
|
36
|
+
* and also acts as a transaction lock;
|
|
37
|
+
* (e.2) new - is a new version, still in transaction, either diff or whole
|
|
38
|
+
* object file;
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import * as fs from '../../lib-common/async-fs-node';
|
|
42
|
+
import { Readable } from 'stream';
|
|
43
|
+
import { PutObjFirstQueryOpts, PutObjSecondQueryOpts } from '../../lib-common/service-api/3nstorage/owner';
|
|
44
|
+
import { stringOfB64UrlSafeChars } from '../../lib-common/random-node';
|
|
45
|
+
import { UserFiles, SC as ufSC, ObjReader } from '../../lib-server/resources/user-files';
|
|
46
|
+
import { DiffInfo, events, ObjStatus } from '../../lib-common/service-api/3nstorage/owner';
|
|
47
|
+
import { streamToObjFile, diffToLayout, chunksInOrderedStream, makeObjPipe, GetObjFile } from '../../lib-common/objs-on-disk/utils';
|
|
48
|
+
import { TimeWindowCache } from '../../lib-common/time-window-cache';
|
|
49
|
+
import { ObjVersionFile } from '../../lib-common/objs-on-disk/obj-file';
|
|
50
|
+
import { errWithCause } from '../../lib-common/exceptions/error';
|
|
51
|
+
import { join } from 'path';
|
|
52
|
+
import { NamedProcs } from '../../lib-common/processes';
|
|
53
|
+
|
|
54
|
+
export { DiffInfo } from '../../lib-common/service-api/3nstorage/owner';
|
|
55
|
+
export { ObjPipe, ObjReader } from '../../lib-server/resources/user-files';
|
|
56
|
+
|
|
57
|
+
export type StorageEventsSink = (
|
|
58
|
+
userId: string, channel: events.EventNameType, event: events.AllTypes
|
|
59
|
+
) => void;
|
|
60
|
+
|
|
61
|
+
export const SC = {
|
|
62
|
+
USER_UNKNOWN: ufSC.USER_UNKNOWN,
|
|
63
|
+
OBJ_EXIST: 'obj-already-exist',
|
|
64
|
+
OBJ_UNKNOWN: 'obj-unknown',
|
|
65
|
+
OBJ_VER_UNKNOWN: 'obj-ver-unknown',
|
|
66
|
+
OBJ_VER_EXIST: 'obj-ver-already-exist',
|
|
67
|
+
CONCURRENT_TRANSACTION: "concurrent-transactions",
|
|
68
|
+
TRANSACTION_UNKNOWN: "transactions-unknown",
|
|
69
|
+
NOT_ENOUGH_SPACE: "not-enough-space",
|
|
70
|
+
OBJ_FILE_INCOMPLETE: 'obj-file-incomplete',
|
|
71
|
+
};
|
|
72
|
+
Object.freeze(SC);
|
|
73
|
+
|
|
74
|
+
export interface MismatchedObjVerException {
|
|
75
|
+
type: 'mismatched-obj-ver';
|
|
76
|
+
current_version: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function makeMismatchedObjVerException(current_version: number):
|
|
80
|
+
MismatchedObjVerException {
|
|
81
|
+
return {
|
|
82
|
+
type: 'mismatched-obj-ver',
|
|
83
|
+
current_version
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface ObjStatusInfo {
|
|
88
|
+
state: 'new' | 'current' | 'archived';
|
|
89
|
+
currentVersion?: number;
|
|
90
|
+
archivedVersions?: number[];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const STATUS_FILE = 'status';
|
|
94
|
+
|
|
95
|
+
export interface TransactionParams {
|
|
96
|
+
isNewObj?: boolean;
|
|
97
|
+
version: number;
|
|
98
|
+
baseVersion?: number;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface TransactionInfo extends TransactionParams {
|
|
102
|
+
transactionType: 'write' | 'remove' | 'archive';
|
|
103
|
+
transactionId: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
interface SpaceInfo {
|
|
107
|
+
free: number;
|
|
108
|
+
used: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface StorageParams {
|
|
112
|
+
"key-deriv": any;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// XXX move this space-tracker into user-files file. One instance for the whole
|
|
116
|
+
// server should do the job.
|
|
117
|
+
class SpaceTracker {
|
|
118
|
+
|
|
119
|
+
private space: {
|
|
120
|
+
[userId: string]: SpaceInfo;
|
|
121
|
+
} = {};
|
|
122
|
+
|
|
123
|
+
constructor() {
|
|
124
|
+
Object.freeze(this);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private async diskUsed(path: string, runNum = 0): Promise<number> {
|
|
128
|
+
|
|
129
|
+
// XXX use calculated value, updating it from time to time with du <folder>. while on windows du might be in linux console!
|
|
130
|
+
|
|
131
|
+
return 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @param store
|
|
136
|
+
* @return a promise, resolvable to space info object.
|
|
137
|
+
*/
|
|
138
|
+
private async updateSpaceInfo(store: Store): Promise<SpaceInfo> {
|
|
139
|
+
const usedSpace = await this.diskUsed(store.path);
|
|
140
|
+
const quota = await store.getSpaceQuota();
|
|
141
|
+
return {
|
|
142
|
+
free: Math.max(0, quota-usedSpace),
|
|
143
|
+
used: usedSpace
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async change(store: Store, delta: number): Promise<void> {
|
|
148
|
+
let s = this.space[store.userId];
|
|
149
|
+
if (!s) {
|
|
150
|
+
s = await this.updateSpaceInfo(store);
|
|
151
|
+
}
|
|
152
|
+
if ((delta > 0) && ((s.free - delta) < 0)) {
|
|
153
|
+
throw SC.NOT_ENOUGH_SPACE;
|
|
154
|
+
}
|
|
155
|
+
s.free -= delta;
|
|
156
|
+
s.used += delta;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
reset(userId: string): void {
|
|
160
|
+
delete this.space[userId];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
}
|
|
164
|
+
Object.freeze(SpaceTracker.prototype);
|
|
165
|
+
Object.freeze(SpaceTracker);
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
const spaceTracker = new SpaceTracker();
|
|
169
|
+
|
|
170
|
+
const SINGLE_BYTE_BUF = Buffer.alloc(1);
|
|
171
|
+
SINGLE_BYTE_BUF[0] = 0;
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
export class Store extends UserFiles<StorageParams> {
|
|
175
|
+
|
|
176
|
+
private objVerFiles = new ObjVerFiles(30*1000);
|
|
177
|
+
|
|
178
|
+
private statuses = new ObjStatuses(30*1000, this.path);
|
|
179
|
+
|
|
180
|
+
private transactions = new ObjTransactions(
|
|
181
|
+
30*1000, this.statuses, this.path, this.objVerFiles.uncache
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
constructor(userId: string, path: string,
|
|
185
|
+
private storageEventsSink: StorageEventsSink,
|
|
186
|
+
writeBufferSize?: string|number, readBufferSize?: string|number
|
|
187
|
+
) {
|
|
188
|
+
super(userId, path, writeBufferSize, readBufferSize);
|
|
189
|
+
Object.freeze(this);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
static async make(
|
|
193
|
+
userFolder: string, userId: string, storageEventsSink: StorageEventsSink,
|
|
194
|
+
writeBufferSize?: string|number, readBufferSize?: string|number
|
|
195
|
+
): Promise<Store> {
|
|
196
|
+
const path = join(userFolder, 'store');
|
|
197
|
+
const store = new Store(userId, path, storageEventsSink,
|
|
198
|
+
writeBufferSize, readBufferSize);
|
|
199
|
+
await store.ensureUserExistsOnDisk();
|
|
200
|
+
return store;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* This method starts object saving transaction, saves bytes from a stream,
|
|
205
|
+
* and, if upload is complete in one request, closes transaction.
|
|
206
|
+
* Returned promise resolves to a transaction id, if transaction is not
|
|
207
|
+
* complete, and to undefined, otherwise.
|
|
208
|
+
* @param objId is a string object id for non-root objects, and null for
|
|
209
|
+
* root object.
|
|
210
|
+
* @param bytes is a stream of bytes that has object header, and some or all
|
|
211
|
+
* segment bytes, following header.
|
|
212
|
+
* @param byteLen is an expected length of a given byte stream.
|
|
213
|
+
* @param opts
|
|
214
|
+
*/
|
|
215
|
+
async startSavingObjNewVersion(
|
|
216
|
+
objId: string|null, diff: DiffInfo|undefined,
|
|
217
|
+
bytes: Readable, byteLen: number, opts: PutObjFirstQueryOpts
|
|
218
|
+
): Promise<string|undefined> {
|
|
219
|
+
if (byteLen > 0) {
|
|
220
|
+
await spaceTracker.change(this, byteLen);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const params: TransactionParams = {
|
|
224
|
+
version: opts.ver,
|
|
225
|
+
baseVersion: (diff ? diff.baseVersion : undefined)
|
|
226
|
+
};
|
|
227
|
+
if (opts.ver === 1) {
|
|
228
|
+
params.isNewObj = true;
|
|
229
|
+
}
|
|
230
|
+
const trans = await this.transactions.startNew(objId, params);
|
|
231
|
+
|
|
232
|
+
let file: ObjVersionFile;
|
|
233
|
+
try {
|
|
234
|
+
const filePath = this.transactions.objFileWritingPath(objId);
|
|
235
|
+
file = await this.objVerFiles.forNewFile(
|
|
236
|
+
objId, trans.version, filePath);
|
|
237
|
+
if (diff) {
|
|
238
|
+
await file.setSegsLayout(diffToLayout(diff), false);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const chunks = chunksInOrderedStream(byteLen, opts.header, 0);
|
|
242
|
+
await streamToObjFile(file, chunks, bytes, this.fileWritingBufferSize);
|
|
243
|
+
|
|
244
|
+
if (opts.last) {
|
|
245
|
+
if (!file.isFileComplete()) { throw SC.OBJ_FILE_INCOMPLETE; }
|
|
246
|
+
await this.transactions.complete(objId, trans, file);
|
|
247
|
+
this.storageEventsSink(
|
|
248
|
+
this.userId,
|
|
249
|
+
events.objChanged.EVENT_NAME,
|
|
250
|
+
{ objId, newVer: opts.ver }
|
|
251
|
+
);
|
|
252
|
+
} else {
|
|
253
|
+
return trans.transactionId;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
} catch (err) {
|
|
257
|
+
await spaceTracker.change(this, -byteLen);
|
|
258
|
+
await this.transactions.cancel(objId, trans.transactionId).catch(noop);
|
|
259
|
+
throw err;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* This method continues object saving transaction. If this request is the
|
|
265
|
+
* last, it closes transaction.
|
|
266
|
+
* Returned promise resolves to a transaction id, if transaction is not
|
|
267
|
+
* complete, and to undefined, if it is closed.
|
|
268
|
+
* @param objId is a string object id for non-root objects, and null for
|
|
269
|
+
* root object.
|
|
270
|
+
* @param bytes is a stream of bytes that has optional diff info, object
|
|
271
|
+
* header, and some or all segment bytes in this order.
|
|
272
|
+
* @param byteLen is an expected length of a given byte stream.
|
|
273
|
+
* @param opts
|
|
274
|
+
*/
|
|
275
|
+
async continueSavingObjNewVersion(
|
|
276
|
+
objId: string|null, bytes: Readable, byteLen: number,
|
|
277
|
+
opts: PutObjSecondQueryOpts
|
|
278
|
+
): Promise<string|undefined> {
|
|
279
|
+
const trans = await this.transactions.get(objId, opts.trans);
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
const filePath = this.transactions.objFileWritingPath(objId);
|
|
283
|
+
const file = await this.objVerFiles.forExistingFile(
|
|
284
|
+
objId, trans.version, filePath);
|
|
285
|
+
|
|
286
|
+
if (byteLen > 0) {
|
|
287
|
+
await spaceTracker.change(this, byteLen);
|
|
288
|
+
const chunks = chunksInOrderedStream(byteLen, undefined, opts.ofs);
|
|
289
|
+
await streamToObjFile(
|
|
290
|
+
file, chunks, bytes, this.fileWritingBufferSize);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (opts.last) {
|
|
294
|
+
if (!file.isFileComplete()) { throw SC.OBJ_FILE_INCOMPLETE; }
|
|
295
|
+
await this.transactions.complete(objId, trans, file);
|
|
296
|
+
this.storageEventsSink(
|
|
297
|
+
this.userId,
|
|
298
|
+
events.objChanged.EVENT_NAME,
|
|
299
|
+
{ objId, newVer: trans.version }
|
|
300
|
+
);
|
|
301
|
+
} else {
|
|
302
|
+
return opts.trans;
|
|
303
|
+
}
|
|
304
|
+
} catch (err) {
|
|
305
|
+
await spaceTracker.change(this, -byteLen);
|
|
306
|
+
await this.transactions.cancel(objId, trans.transactionId).catch(() => {});
|
|
307
|
+
throw err;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async cancelTransaction(
|
|
312
|
+
objId: string|null, transactionId?: string
|
|
313
|
+
): Promise<void> {
|
|
314
|
+
await this.transactions.cancel(objId, transactionId);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* This method promises to return object reader with a corresponding version
|
|
319
|
+
* number.
|
|
320
|
+
* @param objId is a string object id for non-root objects, and null for
|
|
321
|
+
* root object.
|
|
322
|
+
* @param header is a boolean flag, which true value indicates that header
|
|
323
|
+
* bytes should be present.
|
|
324
|
+
* @param segsOffset is a read starting point in segments. If header is
|
|
325
|
+
* present, this offset must be zero.
|
|
326
|
+
* @param segsLimit is a maximum number of segment bytes to read. Undefined
|
|
327
|
+
* indicates that all bytes can be read.
|
|
328
|
+
*/
|
|
329
|
+
async getCurrentObj(
|
|
330
|
+
objId: string|null, header: boolean, segsOffset: number,
|
|
331
|
+
segsLimit: number|undefined
|
|
332
|
+
): Promise<{ reader: ObjReader; version: number; }> {
|
|
333
|
+
const status = await this.statuses.get(objId);
|
|
334
|
+
const version = status.currentVersion;
|
|
335
|
+
if (typeof version !== 'number') { throw SC.OBJ_UNKNOWN; }
|
|
336
|
+
const reader = await this.makeObjReader(
|
|
337
|
+
objId, version, header, segsOffset, segsLimit);
|
|
338
|
+
return { reader, version };
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* This method promises to return object reader.
|
|
343
|
+
* @param objId is a string object id for non-root objects, and null for
|
|
344
|
+
* root object.
|
|
345
|
+
* @param version is object's archived version number
|
|
346
|
+
* @param header is a boolean flag, which true value indicates that header
|
|
347
|
+
* bytes should be present.
|
|
348
|
+
* @param segsOffset is a read starting point in segments. If header is
|
|
349
|
+
* present, this offset must be zero.
|
|
350
|
+
* @param segsLimit is a maximum number of segment bytes to read. Undefined
|
|
351
|
+
* indicates that all bytes can be read.
|
|
352
|
+
*/
|
|
353
|
+
async getArchivedObjVersion(
|
|
354
|
+
objId: string|null, version: number,
|
|
355
|
+
header: boolean, segsOffset: number, segsLimit: number|undefined
|
|
356
|
+
): Promise<ObjReader> {
|
|
357
|
+
const status = await this.statuses.get(objId);
|
|
358
|
+
if (!status.archivedVersions) { throw SC.OBJ_UNKNOWN; }
|
|
359
|
+
if (status.archivedVersions.indexOf(version) < 0) {
|
|
360
|
+
throw SC.OBJ_UNKNOWN; }
|
|
361
|
+
const reader = await this.makeObjReader(
|
|
362
|
+
objId, version, header, segsOffset, segsLimit);
|
|
363
|
+
return reader;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
private async makeObjReader(
|
|
367
|
+
objId: string|null, version: number,
|
|
368
|
+
header: boolean, segsOfs: number, segsLimit: number|undefined
|
|
369
|
+
): Promise<ObjReader> {
|
|
370
|
+
const file = await this.getObjFile(objId, version);
|
|
371
|
+
|
|
372
|
+
// find total segments length
|
|
373
|
+
const segsLen = file.getTotalSegsLen();
|
|
374
|
+
|
|
375
|
+
// contain boundary parameters offsetIntoSegs and len for segment bytes
|
|
376
|
+
if (segsLen < segsOfs) {
|
|
377
|
+
segsOfs = segsLen;
|
|
378
|
+
}
|
|
379
|
+
let segBytesToRead: number;
|
|
380
|
+
if (segsLimit === undefined) {
|
|
381
|
+
segBytesToRead = segsLen - segsOfs;
|
|
382
|
+
} else if ((segsOfs+segsLimit) >= segsLen) {
|
|
383
|
+
segBytesToRead = segsLen - segsOfs;
|
|
384
|
+
} else {
|
|
385
|
+
segBytesToRead = segsLimit;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// construct reader
|
|
389
|
+
let reader: ObjReader;
|
|
390
|
+
if (header) {
|
|
391
|
+
const headerLen = file.getHeaderLen()!;
|
|
392
|
+
reader = {
|
|
393
|
+
len: (segBytesToRead + headerLen),
|
|
394
|
+
segsLen,
|
|
395
|
+
headerLen,
|
|
396
|
+
pipe: makeObjPipe(
|
|
397
|
+
file, true, segsOfs, segBytesToRead, objId, this.getObjFile)
|
|
398
|
+
};
|
|
399
|
+
} else {
|
|
400
|
+
reader = {
|
|
401
|
+
len: segBytesToRead,
|
|
402
|
+
segsLen,
|
|
403
|
+
pipe: makeObjPipe(
|
|
404
|
+
file, false, segsOfs, segBytesToRead, objId, this.getObjFile)
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
Object.freeze(reader);
|
|
408
|
+
|
|
409
|
+
return reader;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
private getObjFile: GetObjFile = async (
|
|
413
|
+
objId: string|null, version: number
|
|
414
|
+
): Promise<ObjVersionFile> => {
|
|
415
|
+
const filePath = this.statuses.objFileReadingPath(objId, version);
|
|
416
|
+
return await this.objVerFiles.forExistingFile(
|
|
417
|
+
objId, version, filePath).catch((exc: fs.FileException) => {
|
|
418
|
+
if (exc.notFound) { throw SC.OBJ_UNKNOWN; }
|
|
419
|
+
throw exc;
|
|
420
|
+
});
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
async deleteCurrentObjVer(
|
|
424
|
+
objId: string, version: number|null = null
|
|
425
|
+
): Promise<void> {
|
|
426
|
+
if (!objId) { throw new Error('Root object is not removable.'); }
|
|
427
|
+
const status = await this.statuses.get(objId);
|
|
428
|
+
if (status.state !== 'current') { throw SC.OBJ_UNKNOWN; }
|
|
429
|
+
if (version && (version !== status.currentVersion)) {
|
|
430
|
+
throw SC.OBJ_VER_UNKNOWN;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// XXX need to put removal transaction, closing it in a finally clause
|
|
434
|
+
|
|
435
|
+
const arch = status.archivedVersions;
|
|
436
|
+
if (!Array.isArray(arch) || (arch.length === 0)) {
|
|
437
|
+
// remove everything related to this object
|
|
438
|
+
await this.statuses.deleteWithObj(objId);
|
|
439
|
+
} else {
|
|
440
|
+
// set status and remove version if it isn't archived
|
|
441
|
+
version = status.currentVersion!;
|
|
442
|
+
delete status.currentVersion;
|
|
443
|
+
status.state = 'archived';
|
|
444
|
+
await this.statuses.set(objId, status);
|
|
445
|
+
if (arch.indexOf(version) < 0) {
|
|
446
|
+
await this.rmObjFiles(objId, version);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
this.storageEventsSink(
|
|
451
|
+
this.userId,
|
|
452
|
+
events.objRemoved.EVENT_NAME,
|
|
453
|
+
{ objId }
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
async deleteArchivedObjVer(
|
|
458
|
+
objId: string|null, version: number
|
|
459
|
+
): Promise<void> {
|
|
460
|
+
const status = await this.statuses.get(objId);
|
|
461
|
+
const arch = status.archivedVersions;
|
|
462
|
+
|
|
463
|
+
// XXX need to put removal transaction, closing it in a finally clause
|
|
464
|
+
|
|
465
|
+
if (!Array.isArray(arch)) { throw SC.OBJ_VER_UNKNOWN; }
|
|
466
|
+
const indInArch = arch.indexOf(version);
|
|
467
|
+
if (indInArch < 0) { throw SC.OBJ_VER_UNKNOWN; }
|
|
468
|
+
arch.splice(indInArch, 1);
|
|
469
|
+
if (arch.length === 0) {
|
|
470
|
+
delete status.archivedVersions;
|
|
471
|
+
}
|
|
472
|
+
await this.statuses.set(objId, status);
|
|
473
|
+
if (!status.archivedVersions && !status.currentVersion) {
|
|
474
|
+
await this.rmObjFiles(objId!, version);
|
|
475
|
+
} else if (status.currentVersion !== version) {
|
|
476
|
+
await this.statuses.deleteWithObj(objId);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
this.storageEventsSink(
|
|
480
|
+
this.userId,
|
|
481
|
+
events.objArchivedVersionRemoved.EVENT_NAME,
|
|
482
|
+
{ objId, archivedVer: version }
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* @param objId is a string object id for non-root objects, and null for
|
|
488
|
+
* root object.
|
|
489
|
+
* @param version identifies exact version of files for removal.
|
|
490
|
+
* @return a promise, resolvable when version files are removed.
|
|
491
|
+
*/
|
|
492
|
+
private async rmObjFiles(objId: string, version: number): Promise<void> {
|
|
493
|
+
const objFolder = this.statuses.objFolder(objId);
|
|
494
|
+
let objFiles = await fs.readdir(objFolder)
|
|
495
|
+
.catch((exc: fs.FileException) => {
|
|
496
|
+
if (exc.notFound) { throw SC.OBJ_UNKNOWN; }
|
|
497
|
+
else { throw exc; }
|
|
498
|
+
});
|
|
499
|
+
const verPart = toFName(version);
|
|
500
|
+
objFiles = objFiles.filter(fName => fName.startsWith(verPart));
|
|
501
|
+
if (objFiles.length === 0) { throw SC.OBJ_VER_UNKNOWN; }
|
|
502
|
+
for (const fName of objFiles) {
|
|
503
|
+
await fs.unlink(join(objFolder, fName)).catch(() => {});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// XXX need both, archiving and removal transactions for atomic locking
|
|
508
|
+
// should these be like empty lock files instead of saving folders,
|
|
509
|
+
// with code distiguishing between the two, while concurrent transaction
|
|
510
|
+
// only need fs.stat for a given name, or a fail of creating lock
|
|
511
|
+
// file/folder.
|
|
512
|
+
|
|
513
|
+
async archiveCurrentObjVersion(
|
|
514
|
+
objId: string, version: number
|
|
515
|
+
): Promise<void> {
|
|
516
|
+
// XXX need to put archiving transaction, closing it in a finally clause
|
|
517
|
+
// - make in memory transactions for archival and removal, or chaining(?)
|
|
518
|
+
|
|
519
|
+
const status = await this.statuses.get(objId);
|
|
520
|
+
if (status.state !== 'current') { throw SC.OBJ_UNKNOWN; }
|
|
521
|
+
if (status.currentVersion !== version) {
|
|
522
|
+
throw makeMismatchedObjVerException(status.currentVersion!);
|
|
523
|
+
}
|
|
524
|
+
const arch = status.archivedVersions;
|
|
525
|
+
if (Array.isArray(arch)) {
|
|
526
|
+
arch.push(status.currentVersion);
|
|
527
|
+
} else {
|
|
528
|
+
status.archivedVersions = [ status.currentVersion ];
|
|
529
|
+
}
|
|
530
|
+
await this.statuses.set(objId, status);
|
|
531
|
+
this.storageEventsSink(
|
|
532
|
+
this.userId,
|
|
533
|
+
events.objVersionArchived.EVENT_NAME,
|
|
534
|
+
{ objId, archivedVer: version }
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
async getObjStatus(objId: string): Promise<ObjStatus> {
|
|
539
|
+
const status = await this.statuses.get(objId);
|
|
540
|
+
return {
|
|
541
|
+
current: status.currentVersion,
|
|
542
|
+
archived: status.archivedVersions
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
async setKeyDerivParams(
|
|
547
|
+
params: any, setDefault: boolean
|
|
548
|
+
): Promise<boolean> {
|
|
549
|
+
if (setDefault) {
|
|
550
|
+
params = {};
|
|
551
|
+
} else if ((typeof params !== 'object') || Array.isArray(params)) {
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
await this.setParam('key-deriv', params);
|
|
555
|
+
return true;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
}
|
|
559
|
+
Object.freeze(Store.prototype);
|
|
560
|
+
Object.freeze(Store);
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
class ObjVerFiles {
|
|
564
|
+
|
|
565
|
+
private cache: TimeWindowCache<string, ObjVersionFile>;
|
|
566
|
+
|
|
567
|
+
constructor(cachePeriodMillis: number) {
|
|
568
|
+
this.cache = new TimeWindowCache(cachePeriodMillis);
|
|
569
|
+
Object.freeze(this);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
async forNewFile(
|
|
573
|
+
objId: string|null, version: number, path: string
|
|
574
|
+
): Promise<ObjVersionFile> {
|
|
575
|
+
const cacheId = this.cacheIdFor(objId, version);
|
|
576
|
+
const objFile = await ObjVersionFile.createNew(path)
|
|
577
|
+
.catch((exc: fs.FileException) => {
|
|
578
|
+
if (exc.alreadyExists) { throw SC.OBJ_VER_EXIST; }
|
|
579
|
+
throw exc;
|
|
580
|
+
});
|
|
581
|
+
this.cache.set(cacheId, objFile);
|
|
582
|
+
return objFile;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
async forExistingFile(
|
|
586
|
+
objId: string|null, version: number, path?: string
|
|
587
|
+
): Promise<ObjVersionFile> {
|
|
588
|
+
const cacheId = this.cacheIdFor(objId, version);
|
|
589
|
+
let objFile = this.cache.get(cacheId);
|
|
590
|
+
if (!objFile) {
|
|
591
|
+
if (!path) { throw new Error(
|
|
592
|
+
`Path to file is not given, while instance of obj version file is not found in cache.`); }
|
|
593
|
+
objFile = await ObjVersionFile.forExisting(path)
|
|
594
|
+
.catch((exc: fs.FileException) => {
|
|
595
|
+
if (exc.notFound) { throw SC.OBJ_VER_UNKNOWN; }
|
|
596
|
+
throw exc;
|
|
597
|
+
});
|
|
598
|
+
this.cache.set(cacheId, objFile);
|
|
599
|
+
}
|
|
600
|
+
return objFile;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
private cacheIdFor(objId: string|null, version: number): string {
|
|
604
|
+
return `${objId}:${version}`;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
uncache = (objId: string|null, version: number): void => {
|
|
608
|
+
this.cache.delete(this.cacheIdFor(objId, version));
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
}
|
|
612
|
+
Object.freeze(ObjVerFiles.prototype);
|
|
613
|
+
Object.freeze(ObjVerFiles);
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
class ObjStatuses {
|
|
617
|
+
|
|
618
|
+
private cache: TimeWindowCache<string|null, ObjStatusInfo>;
|
|
619
|
+
private saveProcs = new NamedProcs();
|
|
620
|
+
|
|
621
|
+
constructor(
|
|
622
|
+
cachePeriodMillis: number,
|
|
623
|
+
public path: string
|
|
624
|
+
) {
|
|
625
|
+
this.cache = new TimeWindowCache(cachePeriodMillis);
|
|
626
|
+
Object.freeze(this);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
objFolder(objId: string|null): string {
|
|
630
|
+
return (objId ?
|
|
631
|
+
join(this.path, 'objects', objId) :
|
|
632
|
+
join(this.path, 'root'));
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
async get(objId: string|null): Promise<ObjStatusInfo> {
|
|
636
|
+
let status = this.cache.get(objId);
|
|
637
|
+
if (!status) {
|
|
638
|
+
status = await this.fromFile(objId);
|
|
639
|
+
this.cache.set(objId, status);
|
|
640
|
+
}
|
|
641
|
+
return status;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
private async fromFile(objId: string|null): Promise<ObjStatusInfo> {
|
|
645
|
+
const filePath = join(this.objFolder(objId), STATUS_FILE);
|
|
646
|
+
const str = await fs.readFile(filePath, { encoding: 'utf8', flag: 'r' }).catch((exc: fs.FileException) => {
|
|
647
|
+
if (exc.notFound) { throw SC.OBJ_UNKNOWN; }
|
|
648
|
+
throw exc;
|
|
649
|
+
});
|
|
650
|
+
try {
|
|
651
|
+
return JSON.parse(str) as ObjStatusInfo;
|
|
652
|
+
} catch (err) {
|
|
653
|
+
throw errWithCause(err, `Can't parse content of obj status file`);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
async set(objId: string|null, status: ObjStatusInfo): Promise<void> {
|
|
658
|
+
this.cache.set(objId, status);
|
|
659
|
+
await this.saveToFile(objId, status)
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
private async saveToFile(
|
|
663
|
+
objId: string|null, status: ObjStatusInfo
|
|
664
|
+
): Promise<void> {
|
|
665
|
+
const filePath = join(this.objFolder(objId), STATUS_FILE);
|
|
666
|
+
const str = JSON.stringify(status);
|
|
667
|
+
const procId = (objId ? objId : 'null');
|
|
668
|
+
await this.saveProcs.startOrChain(procId, () => fs.writeFile(
|
|
669
|
+
filePath, str, { encoding: 'utf8', flag: 'w' }));
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
async deleteWithObj(objId: string|null): Promise<void> {
|
|
673
|
+
this.cache.delete(objId);
|
|
674
|
+
if (objId === null) {
|
|
675
|
+
await fs.unlink(join(this.objFolder(null), STATUS_FILE))
|
|
676
|
+
.catch(() => {});
|
|
677
|
+
} else {
|
|
678
|
+
const objFolder = this.objFolder(objId);
|
|
679
|
+
await fs.rmDirWithContent(objFolder).catch(() => {});
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
async makeNewObj(objId: string|null): Promise<void> {
|
|
684
|
+
if (objId === null) {
|
|
685
|
+
await this.get(objId).then(
|
|
686
|
+
() => { throw SC.OBJ_EXIST; },
|
|
687
|
+
(err) => { if (err !== SC.OBJ_UNKNOWN) { throw err; } });
|
|
688
|
+
} else {
|
|
689
|
+
// creation of folder ensures that id is unique
|
|
690
|
+
const objFolder = this.objFolder(objId);
|
|
691
|
+
await fs.mkdir(objFolder).catch((err: fs.FileException) => {
|
|
692
|
+
if (err.alreadyExists) { throw SC.OBJ_EXIST; }
|
|
693
|
+
else { throw err; }
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
await this.set(objId, {
|
|
697
|
+
state: 'new'
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
objFileReadingPath(objId: string|null, version: number): string {
|
|
702
|
+
return join(this.objFolder(objId), toFName(version));
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
}
|
|
706
|
+
Object.freeze(ObjStatuses.prototype);
|
|
707
|
+
Object.freeze(ObjStatuses);
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
class ObjTransactions {
|
|
711
|
+
|
|
712
|
+
private cache: TimeWindowCache<string|null, TransactionInfo>;
|
|
713
|
+
private saveProcs = new NamedProcs();
|
|
714
|
+
|
|
715
|
+
constructor(
|
|
716
|
+
cachePeriodMillis: number,
|
|
717
|
+
private statuses: ObjStatuses,
|
|
718
|
+
private path: string,
|
|
719
|
+
private uncacheFile: ObjVerFiles['uncache']
|
|
720
|
+
) {
|
|
721
|
+
this.cache = new TimeWindowCache(cachePeriodMillis);
|
|
722
|
+
Object.freeze(this);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
private transactionFolder(objId: string|null): string {
|
|
726
|
+
return (objId ?
|
|
727
|
+
join(this.path, 'transactions', objId) :
|
|
728
|
+
join(this.path, 'root', 'transaction'));
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
async get(
|
|
732
|
+
objId: string|null, transactionId?: string
|
|
733
|
+
): Promise<TransactionInfo> {
|
|
734
|
+
let isInCache = true;
|
|
735
|
+
let trans = this.cache.get(objId);
|
|
736
|
+
if (!trans) {
|
|
737
|
+
isInCache = false;
|
|
738
|
+
trans = await this.fromFile(objId);
|
|
739
|
+
}
|
|
740
|
+
if (transactionId && (trans.transactionId !== transactionId)) {
|
|
741
|
+
throw SC.TRANSACTION_UNKNOWN;
|
|
742
|
+
}
|
|
743
|
+
if (isInCache) {
|
|
744
|
+
this.cache.set(objId, trans);
|
|
745
|
+
}
|
|
746
|
+
return trans;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
private transactionFilePath(objId: string|null): string {
|
|
750
|
+
return join(this.transactionFolder(objId), 'transaction');
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
private async fromFile(objId: string|null): Promise<TransactionInfo> {
|
|
754
|
+
const filePath = this.transactionFilePath(objId);
|
|
755
|
+
const str = await fs.readFile(filePath, { encoding: 'utf8', flag: 'r' })
|
|
756
|
+
.catch(async (exc: fs.FileException) => {
|
|
757
|
+
if (exc.notFound) {
|
|
758
|
+
// it may be due to obj missing, in which case the following
|
|
759
|
+
// throws respective error
|
|
760
|
+
await this.statuses.get(objId);
|
|
761
|
+
// or just missing transaction file
|
|
762
|
+
throw SC.TRANSACTION_UNKNOWN;
|
|
763
|
+
}
|
|
764
|
+
throw exc;
|
|
765
|
+
});
|
|
766
|
+
try {
|
|
767
|
+
return JSON.parse(str) as TransactionInfo;
|
|
768
|
+
} catch (err) {
|
|
769
|
+
throw errWithCause(err, `Can't parse content of obj transaction file`);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
async set(objId: string|null, status: TransactionInfo): Promise<void> {
|
|
774
|
+
this.cache.set(objId, status);
|
|
775
|
+
await this.saveToFile(objId, status)
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
private async saveToFile(
|
|
779
|
+
objId: string|null, transaction: TransactionInfo
|
|
780
|
+
): Promise<void> {
|
|
781
|
+
const filePath = this.transactionFilePath(objId);
|
|
782
|
+
const str = JSON.stringify(transaction);
|
|
783
|
+
const procId = (objId ? objId : 'null');
|
|
784
|
+
await this.saveProcs.startOrChain(procId, () => fs.writeFile(
|
|
785
|
+
filePath, str, { encoding: 'utf8', flag: 'w' }));
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
objFileWritingPath(objId: string|null): string {
|
|
789
|
+
return join(this.transactionFolder(objId), 'new');
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
async startNew(
|
|
793
|
+
objId: string|null, reqTrans: TransactionParams
|
|
794
|
+
): Promise<TransactionInfo> {
|
|
795
|
+
const trans = reqTrans as TransactionInfo;
|
|
796
|
+
trans.transactionId = await stringOfB64UrlSafeChars(10);
|
|
797
|
+
trans.transactionType = 'write';
|
|
798
|
+
await this.makeTransactionFolder(objId);
|
|
799
|
+
await this.set(objId, trans);
|
|
800
|
+
try {
|
|
801
|
+
|
|
802
|
+
// check object status
|
|
803
|
+
if (trans.isNewObj) {
|
|
804
|
+
await this.statuses.makeNewObj(objId);
|
|
805
|
+
} else {
|
|
806
|
+
// get current version, and check new one against it
|
|
807
|
+
const status = await this.statuses.get(objId);
|
|
808
|
+
if (status.state !== 'current') {
|
|
809
|
+
throw SC.OBJ_UNKNOWN;
|
|
810
|
+
} else if (trans.version !== (status.currentVersion! + 1)) {
|
|
811
|
+
throw makeMismatchedObjVerException(status.currentVersion!);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
} catch (err) {
|
|
816
|
+
await this.cancel(objId, trans.transactionId).catch(() => {});
|
|
817
|
+
throw err;
|
|
818
|
+
}
|
|
819
|
+
return trans;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
private async makeTransactionFolder(objId: string|null): Promise<void> {
|
|
823
|
+
try {
|
|
824
|
+
await fs.mkdir(this.transactionFolder(objId));
|
|
825
|
+
} catch (err) {
|
|
826
|
+
if ((<fs.FileException> err).alreadyExists) {
|
|
827
|
+
throw SC.CONCURRENT_TRANSACTION;
|
|
828
|
+
}
|
|
829
|
+
throw err;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
async cancel(objId: string|null, transactionId?: string): Promise<void> {
|
|
834
|
+
|
|
835
|
+
// XXX implement assurance that canceling without transaction id
|
|
836
|
+
// happens only after a certain timeout, i.e. as a method to
|
|
837
|
+
// remove deadlock, when time determines that there is a deadlock
|
|
838
|
+
|
|
839
|
+
const trans = await this.get(objId).catch(
|
|
840
|
+
async (exc: string) => {
|
|
841
|
+
if (exc === SC.TRANSACTION_UNKNOWN) { return null; }
|
|
842
|
+
else { throw exc; }
|
|
843
|
+
});
|
|
844
|
+
if (trans && trans.isNewObj) {
|
|
845
|
+
this.statuses.deleteWithObj(objId);
|
|
846
|
+
}
|
|
847
|
+
this.cache.delete(objId);
|
|
848
|
+
const transFolder = this.transactionFolder(objId);
|
|
849
|
+
await fs.rmDirWithContent(transFolder).catch(() => {});
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
async complete(
|
|
853
|
+
objId: string|null, trans: TransactionInfo, newVerFile: ObjVersionFile
|
|
854
|
+
): Promise<void> {
|
|
855
|
+
this.cache.delete(objId);
|
|
856
|
+
// move new file from transaction folder to obj's one
|
|
857
|
+
const newVerPath = this.statuses.objFileReadingPath(objId, trans.version);
|
|
858
|
+
await newVerFile.moveFile(newVerPath);
|
|
859
|
+
// update object status
|
|
860
|
+
const status = await this.statuses.get(objId);
|
|
861
|
+
if (status.state === 'current') {
|
|
862
|
+
// if needed, remove previous version, right after exposing a new one
|
|
863
|
+
const verToDel = (status.archivedVersions &&
|
|
864
|
+
(status.archivedVersions.indexOf(
|
|
865
|
+
status.currentVersion as number) >= 0)) ?
|
|
866
|
+
undefined : status.currentVersion;
|
|
867
|
+
status.currentVersion = trans.version;
|
|
868
|
+
await this.statuses.set(objId, status);
|
|
869
|
+
if ((verToDel !== undefined) && (trans.baseVersion !== verToDel)) {
|
|
870
|
+
this.uncacheFile(objId, verToDel);
|
|
871
|
+
const rmPath = this.statuses.objFileReadingPath(objId, verToDel);
|
|
872
|
+
await fs.unlink(rmPath);
|
|
873
|
+
}
|
|
874
|
+
} else if (status.state === 'new') {
|
|
875
|
+
status.state = 'current';
|
|
876
|
+
status.currentVersion = trans.version;
|
|
877
|
+
await this.statuses.set(objId, status);
|
|
878
|
+
} else {
|
|
879
|
+
throw new Error(`Object ${objId} has unexpected for transaction completion state: ${status.state}`);
|
|
880
|
+
}
|
|
881
|
+
await fs.rmDirWithContent(this.transactionFolder(objId)).catch(() => {});
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
}
|
|
885
|
+
Object.freeze(ObjStatuses.prototype);
|
|
886
|
+
Object.freeze(ObjStatuses);
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
function toFName(version: number): string {
|
|
890
|
+
return `${version}.v`;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function noop () {}
|
|
894
|
+
|
|
895
|
+
|
|
896
|
+
Object.freeze(exports);
|